1 /**
2  * @file lv_file_explorer.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_file_explorer_private.h"
10 #include "../../misc/lv_fs_private.h"
11 #include "../../core/lv_obj_class_private.h"
12 #if LV_USE_FILE_EXPLORER != 0
13 
14 #include "../../lvgl.h"
15 #include "../../core/lv_global.h"
16 
17 /*********************
18  *      DEFINES
19  *********************/
20 #define MY_CLASS (&lv_file_explorer_class)
21 
22 #define FILE_EXPLORER_QUICK_ACCESS_AREA_WIDTH       (22)
23 #define FILE_EXPLORER_BROWSER_AREA_WIDTH            (100 - FILE_EXPLORER_QUICK_ACCESS_AREA_WIDTH)
24 
25 #define quick_access_list_button_style (LV_GLOBAL_DEFAULT()->fe_list_button_style)
26 
27 #define LV_FILE_NAVIGATION_CURRENT_DIR  "."
28 #define LV_FILE_NAVIGATION_PARENT_DIR   "Back"
29 
30 /**********************
31  *      TYPEDEFS
32  **********************/
33 
34 /**********************
35  *  STATIC PROTOTYPES
36  **********************/
37 static void lv_file_explorer_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
38 static void init_style(lv_obj_t * obj);
39 
40 #if LV_FILE_EXPLORER_QUICK_ACCESS
41     static void quick_access_event_handler(lv_event_t * e);
42     static void quick_access_area_event_handler(lv_event_t * e);
43 #endif
44 
45 static void browser_file_event_handler(lv_event_t * e);
46 static void show_dir(lv_obj_t * obj, const char * path);
47 static void strip_ext(char * dir);
48 static void exch_table_item(lv_obj_t * tb, int16_t i, int16_t j);
49 static void file_explorer_sort(lv_obj_t * obj);
50 static void sort_by_file_kind(lv_obj_t * tb, int16_t lo, int16_t hi);
51 static bool is_end_with(const char * str1, const char * str2);
52 
53 /**********************
54  *  STATIC VARIABLES
55  **********************/
56 
57 const lv_obj_class_t lv_file_explorer_class = {
58     .constructor_cb = lv_file_explorer_constructor,
59     .width_def      = LV_SIZE_CONTENT,
60     .height_def     = LV_SIZE_CONTENT,
61     .instance_size  = sizeof(lv_file_explorer_t),
62     .base_class     = &lv_obj_class,
63     .name = "file-explorer",
64 };
65 
66 /**********************
67  *      MACROS
68  **********************/
69 
70 /**********************
71  *   GLOBAL FUNCTIONS
72  **********************/
73 
lv_file_explorer_create(lv_obj_t * parent)74 lv_obj_t * lv_file_explorer_create(lv_obj_t * parent)
75 {
76     LV_LOG_INFO("begin");
77     lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
78     lv_obj_class_init_obj(obj);
79     return obj;
80 }
81 
82 /*=====================
83  * Setter functions
84  *====================*/
85 #if LV_FILE_EXPLORER_QUICK_ACCESS
lv_file_explorer_set_quick_access_path(lv_obj_t * obj,lv_file_explorer_dir_t dir,const char * path)86 void lv_file_explorer_set_quick_access_path(lv_obj_t * obj, lv_file_explorer_dir_t dir, const char * path)
87 {
88     LV_ASSERT_OBJ(obj, MY_CLASS);
89 
90     lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
91 
92     /*If path is unavailable */
93     if((path == NULL) || (lv_strlen(path) <= 0)) return;
94 
95     char ** dir_str = NULL;
96     switch(dir) {
97         case LV_EXPLORER_HOME_DIR:
98             dir_str = &(explorer->home_dir);
99             break;
100         case LV_EXPLORER_MUSIC_DIR:
101             dir_str = &(explorer->music_dir);
102             break;
103         case LV_EXPLORER_PICTURES_DIR:
104             dir_str = &(explorer->pictures_dir);
105             break;
106         case LV_EXPLORER_VIDEO_DIR:
107             dir_str = &(explorer->video_dir);
108             break;
109         case LV_EXPLORER_DOCS_DIR:
110             dir_str = &(explorer->docs_dir);
111             break;
112         case LV_EXPLORER_FS_DIR:
113             dir_str = &(explorer->fs_dir);
114             break;
115 
116         default:
117             return;
118             break;
119     }
120 
121     /*Free the old text*/
122     if(*dir_str != NULL) {
123         lv_free(*dir_str);
124         *dir_str = NULL;
125     }
126 
127     /*Allocate space for the new text*/
128     *dir_str = lv_strdup(path);
129 }
130 
131 #endif
132 
lv_file_explorer_set_sort(lv_obj_t * obj,lv_file_explorer_sort_t sort)133 void lv_file_explorer_set_sort(lv_obj_t * obj, lv_file_explorer_sort_t sort)
134 {
135     LV_ASSERT_OBJ(obj, MY_CLASS);
136 
137     lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
138 
139     explorer->sort = sort;
140 
141     file_explorer_sort(obj);
142 }
143 
144 /*=====================
145  * Getter functions
146  *====================*/
lv_file_explorer_get_selected_file_name(const lv_obj_t * obj)147 const char * lv_file_explorer_get_selected_file_name(const lv_obj_t * obj)
148 {
149     LV_ASSERT_OBJ(obj, MY_CLASS);
150 
151     lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
152 
153     return explorer->sel_fn;
154 }
155 
lv_file_explorer_get_current_path(const lv_obj_t * obj)156 const char * lv_file_explorer_get_current_path(const lv_obj_t * obj)
157 {
158     LV_ASSERT_OBJ(obj, MY_CLASS);
159 
160     lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
161 
162     return explorer->current_path;
163 }
164 
lv_file_explorer_get_file_table(lv_obj_t * obj)165 lv_obj_t * lv_file_explorer_get_file_table(lv_obj_t * obj)
166 {
167     LV_ASSERT_OBJ(obj, MY_CLASS);
168 
169     lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
170 
171     return explorer->file_table;
172 }
173 
lv_file_explorer_get_header(lv_obj_t * obj)174 lv_obj_t * lv_file_explorer_get_header(lv_obj_t * obj)
175 {
176     LV_ASSERT_OBJ(obj, MY_CLASS);
177 
178     lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
179 
180     return explorer->head_area;
181 }
182 
lv_file_explorer_get_path_label(lv_obj_t * obj)183 lv_obj_t * lv_file_explorer_get_path_label(lv_obj_t * obj)
184 {
185     LV_ASSERT_OBJ(obj, MY_CLASS);
186 
187     lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
188 
189     return explorer->path_label;
190 }
191 
192 #if LV_FILE_EXPLORER_QUICK_ACCESS
lv_file_explorer_get_quick_access_area(lv_obj_t * obj)193 lv_obj_t * lv_file_explorer_get_quick_access_area(lv_obj_t * obj)
194 {
195     LV_ASSERT_OBJ(obj, MY_CLASS);
196 
197     lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
198 
199     return explorer->quick_access_area;
200 }
201 
lv_file_explorer_get_places_list(lv_obj_t * obj)202 lv_obj_t * lv_file_explorer_get_places_list(lv_obj_t * obj)
203 {
204     LV_ASSERT_OBJ(obj, MY_CLASS);
205 
206     lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
207 
208     return explorer->list_places;
209 }
210 
lv_file_explorer_get_device_list(lv_obj_t * obj)211 lv_obj_t * lv_file_explorer_get_device_list(lv_obj_t * obj)
212 {
213     LV_ASSERT_OBJ(obj, MY_CLASS);
214 
215     lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
216 
217     return explorer->list_device;
218 }
219 
220 #endif
221 
lv_file_explorer_get_sort(const lv_obj_t * obj)222 lv_file_explorer_sort_t lv_file_explorer_get_sort(const lv_obj_t * obj)
223 {
224     LV_ASSERT_OBJ(obj, MY_CLASS);
225 
226     lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
227 
228     return explorer->sort;
229 }
230 
231 /*=====================
232  * Other functions
233  *====================*/
lv_file_explorer_open_dir(lv_obj_t * obj,const char * dir)234 void lv_file_explorer_open_dir(lv_obj_t * obj, const char * dir)
235 {
236     LV_ASSERT_OBJ(obj, MY_CLASS);
237 
238     show_dir(obj, dir);
239 }
240 
241 /**********************
242  *   STATIC FUNCTIONS
243  **********************/
lv_file_explorer_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)244 static void lv_file_explorer_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
245 {
246     LV_UNUSED(class_p);
247     LV_TRACE_OBJ_CREATE("begin");
248 
249     lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
250 
251 #if LV_FILE_EXPLORER_QUICK_ACCESS
252     explorer->home_dir = NULL;
253     explorer->video_dir = NULL;
254     explorer->pictures_dir = NULL;
255     explorer->music_dir = NULL;
256     explorer->docs_dir = NULL;
257     explorer->fs_dir = NULL;
258 #endif
259 
260     explorer->sort = LV_EXPLORER_SORT_NONE;
261 
262     lv_memzero(explorer->current_path, sizeof(explorer->current_path));
263 
264     lv_obj_set_size(obj, LV_PCT(100), LV_PCT(100));
265     lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_COLUMN);
266 
267     explorer->cont = lv_obj_create(obj);
268     lv_obj_set_width(explorer->cont, LV_PCT(100));
269     lv_obj_set_flex_grow(explorer->cont, 1);
270 
271 #if LV_FILE_EXPLORER_QUICK_ACCESS
272     /*Quick access bar area on the left*/
273     explorer->quick_access_area = lv_obj_create(explorer->cont);
274     lv_obj_set_size(explorer->quick_access_area, LV_PCT(FILE_EXPLORER_QUICK_ACCESS_AREA_WIDTH), LV_PCT(100));
275     lv_obj_set_flex_flow(explorer->quick_access_area, LV_FLEX_FLOW_COLUMN);
276     lv_obj_add_event_cb(explorer->quick_access_area, quick_access_area_event_handler, LV_EVENT_ALL,
277                         explorer);
278 #endif
279 
280     /*File table area on the right*/
281     explorer->browser_area = lv_obj_create(explorer->cont);
282 #if LV_FILE_EXPLORER_QUICK_ACCESS
283     lv_obj_set_size(explorer->browser_area, LV_PCT(FILE_EXPLORER_BROWSER_AREA_WIDTH), LV_PCT(100));
284 #else
285     lv_obj_set_size(explorer->browser_area, LV_PCT(100), LV_PCT(100));
286 #endif
287     lv_obj_set_flex_flow(explorer->browser_area, LV_FLEX_FLOW_COLUMN);
288 
289     /*The area displayed above the file browse list(head)*/
290     explorer->head_area = lv_obj_create(explorer->browser_area);
291     lv_obj_set_size(explorer->head_area, LV_PCT(100), LV_PCT(14));
292     lv_obj_remove_flag(explorer->head_area, LV_OBJ_FLAG_SCROLLABLE);
293 
294 #if LV_FILE_EXPLORER_QUICK_ACCESS
295     /*Two lists of quick access bar*/
296     lv_obj_t * btn;
297     /*list 1*/
298     explorer->list_device = lv_list_create(explorer->quick_access_area);
299     lv_obj_set_size(explorer->list_device, LV_PCT(100), LV_SIZE_CONTENT);
300     lv_obj_set_style_bg_color(lv_list_add_text(explorer->list_device, "DEVICE"), lv_palette_main(LV_PALETTE_ORANGE), 0);
301 
302     btn = lv_list_add_button(explorer->list_device, NULL, LV_SYMBOL_DRIVE " File System");
303     lv_obj_add_event_cb(btn, quick_access_event_handler, LV_EVENT_CLICKED, obj);
304 
305     /*list 2*/
306     explorer->list_places = lv_list_create(explorer->quick_access_area);
307     lv_obj_set_size(explorer->list_places, LV_PCT(100), LV_SIZE_CONTENT);
308     lv_obj_set_style_bg_color(lv_list_add_text(explorer->list_places, "PLACES"), lv_palette_main(LV_PALETTE_LIME), 0);
309 
310     btn = lv_list_add_button(explorer->list_places, NULL, LV_SYMBOL_HOME " HOME");
311     lv_obj_add_event_cb(btn, quick_access_event_handler, LV_EVENT_CLICKED, obj);
312     btn = lv_list_add_button(explorer->list_places, NULL, LV_SYMBOL_VIDEO " Video");
313     lv_obj_add_event_cb(btn, quick_access_event_handler, LV_EVENT_CLICKED, obj);
314     btn = lv_list_add_button(explorer->list_places, NULL, LV_SYMBOL_IMAGE " Pictures");
315     lv_obj_add_event_cb(btn, quick_access_event_handler, LV_EVENT_CLICKED, obj);
316     btn = lv_list_add_button(explorer->list_places, NULL, LV_SYMBOL_AUDIO " Music");
317     lv_obj_add_event_cb(btn, quick_access_event_handler, LV_EVENT_CLICKED, obj);
318     btn = lv_list_add_button(explorer->list_places, NULL, LV_SYMBOL_FILE "  Documents");
319     lv_obj_add_event_cb(btn, quick_access_event_handler, LV_EVENT_CLICKED, obj);
320 #endif
321 
322     /*Show current path*/
323     explorer->path_label = lv_label_create(explorer->head_area);
324     lv_label_set_text(explorer->path_label, LV_SYMBOL_EYE_OPEN"https://lvgl.io");
325     lv_obj_center(explorer->path_label);
326 
327     /*Table showing the contents of the table of contents*/
328     explorer->file_table = lv_table_create(explorer->browser_area);
329     lv_obj_set_size(explorer->file_table, LV_PCT(100), LV_PCT(86));
330     lv_table_set_column_width(explorer->file_table, 0, LV_PCT(100));
331     lv_table_set_column_count(explorer->file_table, 1);
332     lv_obj_add_event_cb(explorer->file_table, browser_file_event_handler, LV_EVENT_ALL, obj);
333 
334     /*only scroll up and down*/
335     lv_obj_set_scroll_dir(explorer->file_table, LV_DIR_TOP | LV_DIR_BOTTOM);
336 
337     /*Initialize style*/
338     init_style(obj);
339 
340     LV_TRACE_OBJ_CREATE("finished");
341 }
342 
init_style(lv_obj_t * obj)343 static void init_style(lv_obj_t * obj)
344 {
345     lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
346 
347     /*lv_file_explorer obj style*/
348     lv_obj_set_style_radius(obj, 0, 0);
349     lv_obj_set_style_bg_color(obj, lv_color_hex(0xf2f1f6), 0);
350 
351     /*main container style*/
352     lv_obj_set_style_radius(explorer->cont, 0, 0);
353     lv_obj_set_style_bg_opa(explorer->cont, LV_OPA_0, 0);
354     lv_obj_set_style_border_width(explorer->cont, 0, 0);
355     lv_obj_set_style_outline_width(explorer->cont, 0, 0);
356     lv_obj_set_style_pad_column(explorer->cont, 0, 0);
357     lv_obj_set_style_pad_row(explorer->cont, 0, 0);
358     lv_obj_set_style_flex_flow(explorer->cont, LV_FLEX_FLOW_ROW, 0);
359     lv_obj_set_style_pad_all(explorer->cont, 0, 0);
360     lv_obj_set_style_layout(explorer->cont, LV_LAYOUT_FLEX, 0);
361 
362     /*head cont style*/
363     lv_obj_set_style_radius(explorer->head_area, 0, 0);
364     lv_obj_set_style_border_width(explorer->head_area, 0, 0);
365     lv_obj_set_style_pad_top(explorer->head_area, 0, 0);
366 
367 #if LV_FILE_EXPLORER_QUICK_ACCESS
368     /*Quick access bar container style*/
369     lv_obj_set_style_pad_all(explorer->quick_access_area, 0, 0);
370     lv_obj_set_style_pad_row(explorer->quick_access_area, 20, 0);
371     lv_obj_set_style_radius(explorer->quick_access_area, 0, 0);
372     lv_obj_set_style_border_width(explorer->quick_access_area, 1, 0);
373     lv_obj_set_style_outline_width(explorer->quick_access_area, 0, 0);
374     lv_obj_set_style_bg_color(explorer->quick_access_area, lv_color_hex(0xf2f1f6), 0);
375 #endif
376 
377     /*File browser container style*/
378     lv_obj_set_style_pad_all(explorer->browser_area, 0, 0);
379     lv_obj_set_style_pad_row(explorer->browser_area, 0, 0);
380     lv_obj_set_style_radius(explorer->browser_area, 0, 0);
381     lv_obj_set_style_border_width(explorer->browser_area, 0, 0);
382     lv_obj_set_style_outline_width(explorer->browser_area, 0, 0);
383     lv_obj_set_style_bg_color(explorer->browser_area, lv_color_hex(0xffffff), 0);
384 
385     /*Style of the table in the browser container*/
386     lv_obj_set_style_bg_color(explorer->file_table, lv_color_hex(0xffffff), 0);
387     lv_obj_set_style_pad_all(explorer->file_table, 0, 0);
388     lv_obj_set_style_radius(explorer->file_table, 0, 0);
389     lv_obj_set_style_border_width(explorer->file_table, 0, 0);
390     lv_obj_set_style_outline_width(explorer->file_table, 0, 0);
391 
392 #if LV_FILE_EXPLORER_QUICK_ACCESS
393     /*Style of the list in the quick access bar*/
394     lv_obj_set_style_border_width(explorer->list_device, 0, 0);
395     lv_obj_set_style_outline_width(explorer->list_device, 0, 0);
396     lv_obj_set_style_radius(explorer->list_device, 0, 0);
397     lv_obj_set_style_pad_all(explorer->list_device, 0, 0);
398 
399     lv_obj_set_style_border_width(explorer->list_places, 0, 0);
400     lv_obj_set_style_outline_width(explorer->list_places, 0, 0);
401     lv_obj_set_style_radius(explorer->list_places, 0, 0);
402     lv_obj_set_style_pad_all(explorer->list_places, 0, 0);
403 
404     /*Style of the quick access list btn in the quick access bar*/
405     lv_style_init(&quick_access_list_button_style);
406     lv_style_set_border_width(&quick_access_list_button_style, 0);
407     lv_style_set_bg_color(&quick_access_list_button_style, lv_color_hex(0xf2f1f6));
408 
409     uint32_t ch_cnt = lv_obj_get_child_count(explorer->quick_access_area);
410 
411     for(uint32_t i = 0; i < ch_cnt; i++) {
412         lv_obj_t * child = lv_obj_get_child(explorer->quick_access_area, i);
413 
414         if(lv_obj_check_type(child, &lv_list_class)) {
415             uint32_t list_ch_cnt = lv_obj_get_child_count(child);
416 
417             for(uint32_t j = 0; j < list_ch_cnt; j++) {
418                 lv_obj_t * list_child = lv_obj_get_child(child, j);
419                 if(lv_obj_check_type(list_child, &lv_list_button_class)) {
420                     lv_obj_add_style(list_child, &quick_access_list_button_style, 0);
421                 }
422             }
423         }
424     }
425 #endif
426 
427 }
428 
429 #if LV_FILE_EXPLORER_QUICK_ACCESS
quick_access_event_handler(lv_event_t * e)430 static void quick_access_event_handler(lv_event_t * e)
431 {
432     lv_event_code_t code = lv_event_get_code(e);
433     lv_obj_t * btn = lv_event_get_current_target(e);
434     lv_obj_t * obj = lv_event_get_user_data(e);
435 
436     lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
437 
438     if(code == LV_EVENT_CLICKED) {
439         char ** path = NULL;
440         lv_obj_t * label = lv_obj_get_child(btn, -1);
441         char * label_text = lv_label_get_text(label);
442 
443         if((lv_strcmp(label_text, LV_SYMBOL_HOME " HOME") == 0)) {
444             path = &(explorer->home_dir);
445         }
446         else if((lv_strcmp(label_text, LV_SYMBOL_VIDEO " Video") == 0)) {
447             path = &(explorer->video_dir);
448         }
449         else if((lv_strcmp(label_text, LV_SYMBOL_IMAGE " Pictures") == 0)) {
450             path = &(explorer->pictures_dir);
451         }
452         else if((lv_strcmp(label_text, LV_SYMBOL_AUDIO " Music") == 0)) {
453             path = &(explorer->music_dir);
454         }
455         else if((lv_strcmp(label_text, LV_SYMBOL_FILE "  Documents") == 0)) {
456             path = &(explorer->docs_dir);
457         }
458         else if((lv_strcmp(label_text, LV_SYMBOL_DRIVE " File System") == 0)) {
459             path = &(explorer->fs_dir);
460         }
461 
462         if(path != NULL)
463             show_dir(obj, *path);
464     }
465 }
466 
quick_access_area_event_handler(lv_event_t * e)467 static void quick_access_area_event_handler(lv_event_t * e)
468 {
469     lv_event_code_t code = lv_event_get_code(e);
470     lv_obj_t * area = lv_event_get_current_target(e);
471     lv_obj_t * obj = lv_event_get_user_data(e);
472 
473     lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
474 
475     if(code == LV_EVENT_LAYOUT_CHANGED) {
476         if(lv_obj_has_flag(area, LV_OBJ_FLAG_HIDDEN))
477             lv_obj_set_size(explorer->browser_area, LV_PCT(100), LV_PCT(100));
478         else
479             lv_obj_set_size(explorer->browser_area, LV_PCT(FILE_EXPLORER_BROWSER_AREA_WIDTH), LV_PCT(100));
480     }
481 }
482 #endif
483 
browser_file_event_handler(lv_event_t * e)484 static void browser_file_event_handler(lv_event_t * e)
485 {
486     lv_event_code_t code = lv_event_get_code(e);
487     lv_obj_t * obj = lv_event_get_user_data(e);
488 
489     lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
490 
491     if(code == LV_EVENT_VALUE_CHANGED) {
492         char file_name[LV_FILE_EXPLORER_PATH_MAX_LEN];
493         const char * selected_text = NULL;
494         uint32_t row;
495         uint32_t col;
496         uint8_t navigate_to_parent_dir = 0;
497         uint8_t navigate_to_child = 0;
498 
499         lv_memzero(file_name, sizeof(file_name));
500         lv_table_get_selected_cell(explorer->file_table, &row, &col);
501         selected_text = lv_table_get_cell_value(explorer->file_table, row, col);
502 
503         selected_text = selected_text + 5; /* skip table cell format */
504 
505         /* Three navigation modes are supported:
506          * - Navigate to current directory
507          * - Navigate to parent directory
508          * - Navigate to (current directory) child */
509         navigate_to_parent_dir = (lv_strcmp(selected_text, LV_FILE_NAVIGATION_PARENT_DIR) == 0);
510         navigate_to_child = !navigate_to_parent_dir;
511 
512 
513         if((navigate_to_parent_dir) && (lv_strlen(explorer->current_path) > 3)) {
514             strip_ext(explorer->current_path);
515             /*Remove the last '/' character*/
516             strip_ext(explorer->current_path);
517             lv_snprintf((char *)file_name, sizeof(file_name), "%s/", explorer->current_path); /* Append / at the end */
518         }
519         else {
520             if(navigate_to_child) {
521                 lv_snprintf((char *)file_name, sizeof(file_name), "%s%s", explorer->current_path, selected_text);
522             }
523             else if(navigate_to_parent_dir) { /* We are most likely in the drive letter directory, doesn't have parent directory */
524                 return;
525             }
526             else { /* Nothing to do*/ }
527         }
528 
529         lv_fs_dir_t dir;
530         if(lv_fs_dir_open(&dir, file_name) == LV_FS_RES_OK) {
531             lv_fs_dir_close(&dir);
532             show_dir(obj, (char *)file_name);
533         }
534         else {
535             if(navigate_to_child) {
536                 explorer->sel_fn = selected_text;
537                 lv_obj_send_event(obj, LV_EVENT_VALUE_CHANGED, NULL);
538             }
539         }
540     }
541     else if(code == LV_EVENT_SIZE_CHANGED) {
542         lv_table_set_column_width(explorer->file_table, 0, lv_obj_get_width(explorer->file_table));
543     }
544     else if((code == LV_EVENT_CLICKED) || (code == LV_EVENT_RELEASED)) {
545         lv_obj_send_event(obj, LV_EVENT_CLICKED, NULL);
546     }
547 }
548 
show_dir(lv_obj_t * obj,const char * path)549 static void show_dir(lv_obj_t * obj, const char * path)
550 {
551     lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
552 
553     char fn[LV_FILE_EXPLORER_PATH_MAX_LEN];
554     uint16_t index = 0;
555     lv_fs_dir_t dir;
556     lv_fs_res_t res;
557 
558     res = lv_fs_dir_open(&dir, path);
559     if(res != LV_FS_RES_OK) {
560         LV_LOG_USER("Open dir error %d!", res);
561         return;
562     }
563 
564     lv_table_set_cell_value(explorer->file_table, index++, 0, LV_SYMBOL_LEFT "  " LV_FILE_NAVIGATION_PARENT_DIR);
565     lv_table_set_cell_value(explorer->file_table, 0, 1, "0");
566     lv_table_set_cell_value(explorer->file_table, 1, 1, "0");
567 
568     while(1) {
569         res = lv_fs_dir_read(&dir, fn, sizeof(fn));
570         if(res != LV_FS_RES_OK) {
571             LV_LOG_USER("Driver, file or directory does not exist %d!", res);
572             break;
573         }
574 
575         /*fn is empty, if not more files to read*/
576         if(lv_strlen(fn) == 0) {
577             LV_LOG_USER("No more files to read!");
578             break;
579         }
580 
581         if((is_end_with(fn, ".png") == true)  || (is_end_with(fn, ".PNG") == true)  || \
582            (is_end_with(fn, ".jpg") == true) || (is_end_with(fn, ".JPG") == true) || \
583            (is_end_with(fn, ".bmp") == true) || (is_end_with(fn, ".BMP") == true) || \
584            (is_end_with(fn, ".gif") == true) || (is_end_with(fn, ".GIF") == true)) {
585             lv_table_set_cell_value_fmt(explorer->file_table, index, 0, LV_SYMBOL_IMAGE "  %s", fn);
586             lv_table_set_cell_value(explorer->file_table, index, 1, "1");
587         }
588         else if((is_end_with(fn, ".mp3") == true) || (is_end_with(fn, ".MP3") == true) || \
589                 (is_end_with(fn, ".wav") == true) || (is_end_with(fn, ".WAV") == true)) {
590             lv_table_set_cell_value_fmt(explorer->file_table, index, 0, LV_SYMBOL_AUDIO "  %s", fn);
591             lv_table_set_cell_value(explorer->file_table, index, 1, "2");
592         }
593         else if((is_end_with(fn, ".mp4") == true) || (is_end_with(fn, ".MP4") == true)) {
594             lv_table_set_cell_value_fmt(explorer->file_table, index, 0, LV_SYMBOL_VIDEO "  %s", fn);
595             lv_table_set_cell_value(explorer->file_table, index, 1, "3");
596         }
597         else if((is_end_with(fn, ".") == true) || (is_end_with(fn, "..") == true)) {
598             /*is dir*/
599             continue;
600         }
601         else if(fn[0] == '/') {/*is dir*/
602             lv_table_set_cell_value_fmt(explorer->file_table, index, 0, LV_SYMBOL_DIRECTORY "  %s", fn + 1);
603             lv_table_set_cell_value(explorer->file_table, index, 1, "0");
604         }
605         else {
606             lv_table_set_cell_value_fmt(explorer->file_table, index, 0, LV_SYMBOL_FILE "  %s", fn);
607             lv_table_set_cell_value(explorer->file_table, index, 1, "4");
608         }
609 
610         index++;
611     }
612 
613     lv_fs_dir_close(&dir);
614 
615     lv_table_set_row_count(explorer->file_table, index);
616     file_explorer_sort(obj);
617     lv_obj_send_event(obj, LV_EVENT_READY, NULL);
618 
619     /*Move the table to the top*/
620     lv_obj_scroll_to_y(explorer->file_table, 0, LV_ANIM_OFF);
621 
622     lv_strncpy(explorer->current_path, path, sizeof(explorer->current_path));
623     lv_label_set_text_fmt(explorer->path_label, LV_SYMBOL_EYE_OPEN" %s", path);
624 
625     size_t current_path_len = lv_strlen(explorer->current_path);
626     if((explorer->current_path[current_path_len - 1] != '/') && (current_path_len < LV_FILE_EXPLORER_PATH_MAX_LEN)) {
627         *((explorer->current_path) + current_path_len) = '/';
628     }
629 }
630 
631 /*Remove the specified suffix*/
strip_ext(char * dir)632 static void strip_ext(char * dir)
633 {
634     char * end = dir + lv_strlen(dir);
635 
636     while(end >= dir && *end != '/') {
637         --end;
638     }
639 
640     if(end > dir) {
641         *end = '\0';
642     }
643     else if(end == dir) {
644         *(end + 1) = '\0';
645     }
646 }
647 
exch_table_item(lv_obj_t * tb,int16_t i,int16_t j)648 static void exch_table_item(lv_obj_t * tb, int16_t i, int16_t j)
649 {
650     if(i == j) return;
651 
652     const char * tmp;
653     tmp = lv_table_get_cell_value(tb, i, 0);
654     lv_table_set_cell_value(tb, 0, 2, tmp);
655     lv_table_set_cell_value(tb, i, 0, lv_table_get_cell_value(tb, j, 0));
656     lv_table_set_cell_value(tb, j, 0, lv_table_get_cell_value(tb, 0, 2));
657 
658     tmp = lv_table_get_cell_value(tb, i, 1);
659     lv_table_set_cell_value(tb, 0, 2, tmp);
660     lv_table_set_cell_value(tb, i, 1, lv_table_get_cell_value(tb, j, 1));
661     lv_table_set_cell_value(tb, j, 1, lv_table_get_cell_value(tb, 0, 2));
662 }
663 
file_explorer_sort(lv_obj_t * obj)664 static void file_explorer_sort(lv_obj_t * obj)
665 {
666     LV_ASSERT_OBJ(obj, MY_CLASS);
667 
668     lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
669 
670     uint16_t sum = lv_table_get_row_count(explorer->file_table);
671 
672     if(sum > 1) {
673         switch(explorer->sort) {
674             case LV_EXPLORER_SORT_NONE:
675                 break;
676             case LV_EXPLORER_SORT_KIND:
677                 sort_by_file_kind(explorer->file_table, 0, (sum - 1));
678                 break;
679             default:
680                 break;
681         }
682     }
683 }
684 
685 /*Quick sort 3 way*/
sort_by_file_kind(lv_obj_t * tb,int16_t lo,int16_t hi)686 static void sort_by_file_kind(lv_obj_t * tb, int16_t lo, int16_t hi)
687 {
688     if(lo >= hi) return;
689 
690     int16_t lt = lo;
691     int16_t i = lo + 1;
692     int16_t gt = hi;
693     const char * v = lv_table_get_cell_value(tb, lo, 1);
694     while(i <= gt) {
695         int strcmp_result = lv_strcmp(lv_table_get_cell_value(tb, i, 1), v);
696         if(strcmp_result < 0)
697             exch_table_item(tb, lt++, i++);
698         else if(strcmp_result > 0)
699             exch_table_item(tb, i, gt--);
700         else
701             i++;
702     }
703 
704     sort_by_file_kind(tb, lo, lt - 1);
705     sort_by_file_kind(tb, gt + 1, hi);
706 }
707 
is_end_with(const char * str1,const char * str2)708 static bool is_end_with(const char * str1, const char * str2)
709 {
710     if(str1 == NULL || str2 == NULL)
711         return false;
712 
713     size_t len1 = lv_strlen(str1);
714     size_t len2 = lv_strlen(str2);
715     if((len1 < len2) || (len1 == 0 || len2 == 0))
716         return false;
717 
718     while(len2 >= 1) {
719         if(str2[len2 - 1] != str1[len1 - 1])
720             return false;
721 
722         len2--;
723         len1--;
724     }
725 
726     return true;
727 }
728 
729 #endif  /*LV_USE_FILE_EXPLORER*/
730