diff --git a/applications/archive/archive.c b/applications/archive/archive.c index c9667e66..68fc7287 100644 --- a/applications/archive/archive.c +++ b/applications/archive/archive.c @@ -1,831 +1,69 @@ #include "archive_i.h" -static bool archive_get_filenames(ArchiveApp* archive); - -static void update_offset(ArchiveApp* archive) { - furi_assert(archive); - - with_view_model( - archive->view_archive_main, (ArchiveViewModel * model) { - size_t array_size = files_array_size(model->files); - uint16_t bounds = array_size > 3 ? 2 : array_size; - - if(array_size > 3 && model->idx >= array_size - 1) { - model->list_offset = model->idx - 3; - } else if(model->list_offset < model->idx - bounds) { - model->list_offset = CLAMP(model->list_offset + 1, array_size - bounds, 0); - } else if(model->list_offset > model->idx - bounds) { - model->list_offset = CLAMP(model->idx - 1, array_size - bounds, 0); - } - return true; - }); -} - -static void archive_update_last_idx(ArchiveApp* archive) { - furi_assert(archive); - - with_view_model( - archive->view_archive_main, (ArchiveViewModel * model) { - archive->browser.last_idx[archive->browser.depth] = - CLAMP(model->idx, files_array_size(model->files) - 1, 0); - model->idx = 0; - return true; - }); -} - -static void archive_switch_dir(ArchiveApp* archive, const char* path) { - furi_assert(archive); - furi_assert(path); - string_set(archive->browser.path, path); - archive_get_filenames(archive); - update_offset(archive); -} - -static void archive_switch_tab(ArchiveApp* archive) { - furi_assert(archive); - - with_view_model( - archive->view_archive_main, (ArchiveViewModel * model) { - model->tab_idx = archive->browser.tab_id; - model->idx = 0; - - return true; - }); - - archive->browser.depth = 0; - archive_switch_dir(archive, tab_default_paths[archive->browser.tab_id]); -} - -static void archive_leave_dir(ArchiveApp* archive) { - furi_assert(archive); - - char* last_char_ptr = strrchr(string_get_cstr(archive->browser.path), '/'); - - if(last_char_ptr) { - size_t pos = last_char_ptr - string_get_cstr(archive->browser.path); - string_left(archive->browser.path, pos); - } - - archive->browser.depth = CLAMP(archive->browser.depth - 1, MAX_DEPTH, 0); - - with_view_model( - archive->view_archive_main, (ArchiveViewModel * model) { - model->idx = archive->browser.last_idx[archive->browser.depth]; - model->list_offset = - model->idx - - (files_array_size(model->files) > 3 ? 3 : files_array_size(model->files)); - return true; - }); - - archive_switch_dir(archive, string_get_cstr(archive->browser.path)); - update_offset(archive); -} - -static void archive_enter_dir(ArchiveApp* archive, string_t name) { - furi_assert(archive); - furi_assert(name); - - archive_update_last_idx(archive); - archive->browser.depth = CLAMP(archive->browser.depth + 1, MAX_DEPTH, 0); - - string_cat(archive->browser.path, "/"); - string_cat(archive->browser.path, archive->browser.name); - - archive_switch_dir(archive, string_get_cstr(archive->browser.path)); -} - -static bool filter_by_extension(ArchiveApp* archive, FileInfo* file_info, const char* name) { - furi_assert(archive); - furi_assert(file_info); - furi_assert(name); - - bool result = false; - const char* filter_ext_ptr = get_tab_ext(archive->browser.tab_id); - - if(strcmp(filter_ext_ptr, "*") == 0) { - result = true; - } else if(strstr(name, filter_ext_ptr) != NULL) { - result = true; - } else if(file_info->flags & FSF_DIRECTORY) { - result = true; - } - - return result; -} - -static void set_file_type(ArchiveFile_t* file, FileInfo* file_info) { - furi_assert(file); - furi_assert(file_info); - - for(size_t i = 0; i < SIZEOF_ARRAY(known_ext); i++) { - if(string_search_str(file->name, known_ext[i], 0) != STRING_FAILURE) { - file->type = i; - return; - } - } - - if(file_info->flags & FSF_DIRECTORY) { - file->type = ArchiveFileTypeFolder; - } else { - file->type = ArchiveFileTypeUnknown; - } -} - -static void archive_file_append(ArchiveApp* archive, const char* path, string_t string) { - furi_assert(archive); - furi_assert(path); - furi_assert(string); - - FileWorker* file_worker = file_worker_alloc(false); - - if(!file_worker_open(file_worker, path, FSAM_WRITE, FSOM_OPEN_APPEND)) { - FURI_LOG_E("Archive", "Append open error"); - } - - if(!file_worker_write(file_worker, string_get_cstr(string), string_size(string))) { - FURI_LOG_E("Archive", "Append write error"); - } - - file_worker_close(file_worker); - file_worker_free(file_worker); -} - -static void archive_view_add_item(ArchiveApp* archive, FileInfo* file_info, const char* name) { - furi_assert(archive); - furi_assert(file_info); - furi_assert(name); - - ArchiveFile_t item; - - if(filter_by_extension(archive, file_info, name)) { - ArchiveFile_t_init(&item); - string_init_set_str(item.name, name); - set_file_type(&item, file_info); - - with_view_model( - archive->view_archive_main, (ArchiveViewModel * model) { - files_array_push_back(model->files, item); - return true; - }); - - ArchiveFile_t_clear(&item); - } -} - -static bool archive_is_favorite(ArchiveApp* archive, ArchiveFile_t* selected) { - furi_assert(selected); - string_t path; - string_t buffer; - string_init(buffer); - bool found = false; - - string_init_printf( - path, "%s/%s", string_get_cstr(archive->browser.path), string_get_cstr(selected->name)); - - bool load_result = - file_worker_open(archive->file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_ALWAYS); - - if(load_result) { - while(1) { - if(!file_worker_read_until(archive->file_worker, buffer, '\n')) { - break; - } - if(!string_size(buffer)) { - break; - } - if(!string_search(buffer, path)) { - found = true; - break; - } - } - } - - string_clear(buffer); - string_clear(path); - file_worker_close(archive->file_worker); - - return found; -} - -static bool archive_favorites_read(ArchiveApp* archive) { - string_t buffer; - FileInfo file_info; - string_init(buffer); - - bool load_result = - file_worker_open(archive->file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); - - if(load_result) { - while(1) { - if(!file_worker_read_until(archive->file_worker, buffer, '\n')) { - break; - } - if(!string_size(buffer)) { - break; - } - - archive_view_add_item(archive, &file_info, string_get_cstr(buffer)); - string_clean(buffer); - } - } - string_clear(buffer); - file_worker_close(archive->file_worker); - - return load_result; -} - -static bool - archive_favorites_rename(ArchiveApp* archive, ArchiveFile_t* selected, const char* dst) { - furi_assert(selected); - string_t path; - string_t buffer; - string_t temp; - - string_init(buffer); - string_init(temp); - - string_init_printf( - path, "%s/%s", string_get_cstr(archive->browser.path), string_get_cstr(selected->name)); - bool load_result = - file_worker_open(archive->file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); - - if(load_result) { - while(1) { - if(!file_worker_read_until(archive->file_worker, buffer, '\n')) { - break; - } - if(!string_size(buffer)) { - break; - } - - string_printf( - temp, "%s\r\n", string_search(buffer, path) ? string_get_cstr(buffer) : dst); - archive_file_append(archive, ARCHIVE_FAV_TEMP_PATH, temp); - string_clean(temp); - } - } - - string_clear(temp); - string_clear(buffer); - string_clear(path); - - file_worker_close(archive->file_worker); - file_worker_remove(archive->file_worker, ARCHIVE_FAV_PATH); - file_worker_rename(archive->file_worker, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); - - return load_result; -} - -static bool archive_favorites_delete(ArchiveApp* archive, ArchiveFile_t* selected) { - furi_assert(selected); - string_t path; - string_t buffer; - string_init(buffer); - - string_init_printf( - path, "%s/%s", string_get_cstr(archive->browser.path), string_get_cstr(selected->name)); - - bool load_result = - file_worker_open(archive->file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); - if(load_result) { - while(1) { - if(!file_worker_read_until(archive->file_worker, buffer, '\n')) { - break; - } - if(!string_size(buffer)) { - break; - } - - if(string_search(buffer, path)) { - string_t temp; - string_init_printf(temp, "%s\r\n", string_get_cstr(buffer)); - archive_file_append(archive, ARCHIVE_FAV_TEMP_PATH, temp); - string_clear(temp); - } - } - } - - string_clear(buffer); - string_clear(path); - - file_worker_close(archive->file_worker); - file_worker_remove(archive->file_worker, ARCHIVE_FAV_PATH); - file_worker_rename(archive->file_worker, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); - - return load_result; -} - -static bool archive_read_dir(ArchiveApp* archive) { - FileInfo file_info; - File* directory = storage_file_alloc(archive->api); - char name[MAX_NAME_LEN]; - - if(!storage_dir_open(directory, string_get_cstr(archive->browser.path))) { - storage_dir_close(directory); - storage_file_free(directory); - return false; - } - while(1) { - if(!storage_dir_read(directory, &file_info, name, MAX_NAME_LEN)) { - break; - } - - uint16_t files_cnt; - with_view_model( - archive->view_archive_main, (ArchiveViewModel * model) { - files_cnt = files_array_size(model->files); - - return true; - }); - - if(files_cnt > MAX_FILES) { - break; - } else if(storage_file_get_error(directory) == FSE_OK) { - archive_view_add_item(archive, &file_info, name); - } else { - storage_dir_close(directory); - storage_file_free(directory); - return false; - } - } - storage_dir_close(directory); - storage_file_free(directory); - - return true; -} - -static bool archive_get_filenames(ArchiveApp* archive) { - furi_assert(archive); - with_view_model( - archive->view_archive_main, (ArchiveViewModel * model) { - files_array_clean(model->files); - return true; - }); - - if(archive->browser.tab_id != ArchiveTabFavorites) { - archive_read_dir(archive); - } else { - archive_favorites_read(archive); - } - return true; -} -static void archive_exit_callback(ArchiveApp* archive) { - furi_assert(archive); - - AppEvent event; - event.type = EventTypeExit; - furi_check(osMessageQueuePut(archive->event_queue, &event, 0, osWaitForever) == osOK); -} - -static uint32_t archive_previous_callback(void* context) { - return ArchiveViewMain; -} - -/* file menu */ -static void archive_add_to_favorites(ArchiveApp* archive) { - furi_assert(archive); - string_t buffer_src; - - string_init_printf( - buffer_src, - "%s/%s\r\n", - string_get_cstr(archive->browser.path), - string_get_cstr(archive->browser.name)); - - archive_file_append(archive, ARCHIVE_FAV_PATH, buffer_src); - - string_clear(buffer_src); -} - -static void archive_text_input_callback(void* context) { +bool archive_custom_event_callback(void* context, uint32_t event) { furi_assert(context); - ArchiveApp* archive = (ArchiveApp*)context; - - string_t buffer_src; - string_t buffer_dst; - - string_init_printf( - buffer_src, - "%s/%s", - string_get_cstr(archive->browser.path), - string_get_cstr(archive->browser.name)); - string_init_printf( - buffer_dst, - "%s/%s", - string_get_cstr(archive->browser.path), - archive->browser.text_input_buffer); - - string_set(archive->browser.name, archive->browser.text_input_buffer); - // append extension - - ArchiveFile_t* file; - - with_view_model( - archive->view_archive_main, (ArchiveViewModel * model) { - file = files_array_get( - model->files, CLAMP(model->idx, files_array_size(model->files) - 1, 0)); - file->fav = archive_is_favorite(archive, file); - - return true; - }); - - string_cat(buffer_dst, known_ext[file->type]); - storage_common_rename(archive->api, string_get_cstr(buffer_src), string_get_cstr(buffer_dst)); - - if(file->fav) { - archive_favorites_rename(archive, file, string_get_cstr(buffer_dst)); - } - - view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewMain); - archive_get_filenames(archive); - - with_view_model( - archive->view_archive_main, (ArchiveViewModel * model) { - model->idx = 0; - while(model->idx < files_array_size(model->files)) { - ArchiveFile_t* current = files_array_get(model->files, model->idx); - if(!string_search(current->name, archive->browser.text_input_buffer)) { - break; - } - ++model->idx; - } - return true; - }); - - update_offset(archive); - - string_clear(buffer_src); - string_clear(buffer_dst); + return scene_manager_handle_custom_event(archive->scene_manager, event); } -static void archive_enter_text_input(ArchiveApp* archive) { - furi_assert(archive); - *archive->browser.text_input_buffer = '\0'; - - strlcpy( - archive->browser.text_input_buffer, string_get_cstr(archive->browser.name), MAX_NAME_LEN); - - archive_trim_file_ext(archive->browser.text_input_buffer); - - text_input_set_header_text(archive->text_input, "Rename:"); - - text_input_set_result_callback( - archive->text_input, - archive_text_input_callback, - archive, - archive->browser.text_input_buffer, - MAX_NAME_LEN, - false); - - view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput); -} - -static void archive_show_file_menu(ArchiveApp* archive) { - furi_assert(archive); - - archive->browser.menu = true; - - with_view_model( - archive->view_archive_main, (ArchiveViewModel * model) { - ArchiveFile_t* selected; - selected = files_array_get(model->files, model->idx); - model->menu = true; - model->menu_idx = 0; - selected->fav = is_known_app(selected->type) ? archive_is_favorite(archive, selected) : - false; - - return true; - }); -} - -static void archive_close_file_menu(ArchiveApp* archive) { - furi_assert(archive); - - archive->browser.menu = false; - - with_view_model( - archive->view_archive_main, (ArchiveViewModel * model) { - model->menu = false; - model->menu_idx = 0; - return true; - }); -} - -static void archive_open_app(ArchiveApp* archive, const char* app_name, const char* args) { - furi_assert(archive); - furi_assert(app_name); - - loader_start(archive->loader, app_name, args); -} - -static void archive_delete_file(ArchiveApp* archive, ArchiveFile_t* file) { - furi_assert(archive); - furi_assert(file); - - string_t path; - string_init(path); - - string_printf( - path, "%s/%s", string_get_cstr(archive->browser.path), string_get_cstr(file->name)); - - if(archive_is_favorite(archive, file)) { // remove from favorites - archive_favorites_delete(archive, file); - } - - file_worker_remove(archive->file_worker, string_get_cstr(path)); - - string_clear(path); - archive_get_filenames(archive); - - with_view_model( - archive->view_archive_main, (ArchiveViewModel * model) { - model->idx = CLAMP(model->idx, files_array_size(model->files) - 1, 0); - return true; - }); - - update_offset(archive); -} - -static void - archive_run_in_app(ArchiveApp* archive, ArchiveFile_t* selected, bool full_path_provided) { - string_t full_path; - - if(!full_path_provided) { - string_init_printf( - full_path, - "%s/%s", - string_get_cstr(archive->browser.path), - string_get_cstr(selected->name)); - } else { - string_init_set(full_path, selected->name); - } - - archive_open_app(archive, flipper_app_name[selected->type], string_get_cstr(full_path)); - string_clear(full_path); -} - -static void archive_file_menu_callback(ArchiveApp* archive) { - furi_assert(archive); - - ArchiveFile_t* selected; - uint8_t idx = 0; - - with_view_model( - archive->view_archive_main, (ArchiveViewModel * model) { - selected = files_array_get(model->files, model->idx); - idx = model->menu_idx; - return true; - }); - - switch(idx) { - case 0: - if(is_known_app(selected->type)) { - archive_run_in_app(archive, selected, false); - } - break; - case 1: - if(is_known_app(selected->type)) { - if(!archive_is_favorite(archive, selected)) { - string_set(archive->browser.name, selected->name); - archive_add_to_favorites(archive); - } else { - // delete from favorites - archive_favorites_delete(archive, selected); - } - archive_close_file_menu(archive); - } - break; - case 2: - // open rename view - if(is_known_app(selected->type)) { - archive_enter_text_input(archive); - } - break; - case 3: - // confirmation? - archive_delete_file(archive, selected); - archive_close_file_menu(archive); - - break; - - default: - archive_close_file_menu(archive); - break; - } - selected = NULL; -} - -static void menu_input_handler(ArchiveApp* archive, InputEvent* event) { - furi_assert(archive); - furi_assert(archive); - - if(event->type == InputTypeShort) { - if(event->key == InputKeyUp || event->key == InputKeyDown) { - with_view_model( - archive->view_archive_main, (ArchiveViewModel * model) { - if(event->key == InputKeyUp) { - model->menu_idx = ((model->menu_idx - 1) + MENU_ITEMS) % MENU_ITEMS; - } else if(event->key == InputKeyDown) { - model->menu_idx = (model->menu_idx + 1) % MENU_ITEMS; - } - return true; - }); - } - - if(event->key == InputKeyOk) { - archive_file_menu_callback(archive); - } else if(event->key == InputKeyBack) { - archive_close_file_menu(archive); - } - } -} - -/* main controls */ - -static bool archive_view_input(InputEvent* event, void* context) { - furi_assert(event); +bool archive_back_event_callback(void* context) { furi_assert(context); - - ArchiveApp* archive = context; - bool in_menu = archive->browser.menu; - - if(in_menu) { - menu_input_handler(archive, event); - return true; - } - - if(event->type == InputTypeShort) { - if(event->key == InputKeyLeft) { - if(archive->browser.tab_id > 0) { - archive->browser.tab_id = CLAMP(archive->browser.tab_id - 1, ArchiveTabTotal, 0); - archive_switch_tab(archive); - return true; - } - } else if(event->key == InputKeyRight) { - if(archive->browser.tab_id < ArchiveTabTotal - 1) { - archive->browser.tab_id = - CLAMP(archive->browser.tab_id + 1, ArchiveTabTotal - 1, 0); - archive_switch_tab(archive); - return true; - } - - } else if(event->key == InputKeyBack) { - if(archive->browser.depth == 0) { - archive_exit_callback(archive); - } else { - archive_leave_dir(archive); - } - - return true; - } - } - if(event->key == InputKeyUp || event->key == InputKeyDown) { - with_view_model( - archive->view_archive_main, (ArchiveViewModel * model) { - uint16_t num_elements = (uint16_t)files_array_size(model->files); - if((event->type == InputTypeShort || event->type == InputTypeRepeat)) { - if(event->key == InputKeyUp) { - model->idx = ((model->idx - 1) + num_elements) % num_elements; - } else if(event->key == InputKeyDown) { - model->idx = (model->idx + 1) % num_elements; - } - } - - return true; - }); - update_offset(archive); - } - - if(event->key == InputKeyOk) { - ArchiveFile_t* selected; - - with_view_model( - archive->view_archive_main, (ArchiveViewModel * model) { - selected = files_array_size(model->files) > 0 ? - files_array_get(model->files, model->idx) : - NULL; - return true; - }); - - if(selected) { - string_set(archive->browser.name, selected->name); - - if(selected->type == ArchiveFileTypeFolder) { - if(event->type == InputTypeShort) { - archive_enter_dir(archive, archive->browser.name); - } else if(event->type == InputTypeLong) { - archive_show_file_menu(archive); - } - } else { - if(event->type == InputTypeShort) { - if(archive->browser.tab_id == ArchiveTabFavorites) { - if(is_known_app(selected->type)) { - archive_run_in_app(archive, selected, true); - } - } else { - archive_show_file_menu(archive); - } - } - } - } - } - - update_offset(archive); - - return true; -} - -void archive_free(ArchiveApp* archive) { - furi_assert(archive); - - file_worker_free(archive->file_worker); - - view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewMain); - view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewTextInput); - view_dispatcher_free(archive->view_dispatcher); - - with_view_model( - archive->view_archive_main, (ArchiveViewModel * model) { - files_array_clear(model->files); - return false; - }); - - view_free(archive->view_archive_main); - - string_clear(archive->browser.name); - string_clear(archive->browser.path); - - text_input_free(archive->text_input); - - furi_record_close("storage"); - archive->api = NULL; - furi_record_close("gui"); - archive->gui = NULL; - furi_record_close("loader"); - archive->loader = NULL; - furi_thread_free(archive->app_thread); - furi_check(osMessageQueueDelete(archive->event_queue) == osOK); - - free(archive); + ArchiveApp* archive = (ArchiveApp*)context; + return scene_manager_handle_back_event(archive->scene_manager); } ArchiveApp* archive_alloc() { ArchiveApp* archive = furi_alloc(sizeof(ArchiveApp)); - archive->event_queue = osMessageQueueNew(8, sizeof(AppEvent), NULL); - archive->app_thread = furi_thread_alloc(); archive->gui = furi_record_open("gui"); - archive->loader = furi_record_open("loader"); - archive->api = furi_record_open("storage"); archive->text_input = text_input_alloc(); - archive->view_archive_main = view_alloc(); - archive->file_worker = file_worker_alloc(true); - furi_check(archive->event_queue); - - view_allocate_model( - archive->view_archive_main, ViewModelTypeLocking, sizeof(ArchiveViewModel)); - with_view_model( - archive->view_archive_main, (ArchiveViewModel * model) { - files_array_init(model->files); - return false; - }); - - view_set_context(archive->view_archive_main, archive); - view_set_draw_callback(archive->view_archive_main, archive_view_render); - view_set_input_callback(archive->view_archive_main, archive_view_input); - view_set_previous_callback( - text_input_get_view(archive->text_input), archive_previous_callback); - - // View Dispatcher archive->view_dispatcher = view_dispatcher_alloc(); - view_dispatcher_add_view( - archive->view_dispatcher, ArchiveViewMain, archive->view_archive_main); - view_dispatcher_add_view( - archive->view_dispatcher, ArchiveViewTextInput, text_input_get_view(archive->text_input)); + archive->scene_manager = scene_manager_alloc(&archive_scene_handlers, archive); + + view_dispatcher_enable_queue(archive->view_dispatcher); view_dispatcher_attach_to_gui( archive->view_dispatcher, archive->gui, ViewDispatcherTypeFullscreen); - view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveTabFavorites); + view_dispatcher_set_event_callback_context(archive->view_dispatcher, archive); + view_dispatcher_set_custom_event_callback( + archive->view_dispatcher, archive_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + archive->view_dispatcher, archive_back_event_callback); + + archive->main_view = main_view_alloc(); + + view_dispatcher_add_view( + archive->view_dispatcher, ArchiveViewBrowser, archive_main_get_view(archive->main_view)); + + view_dispatcher_add_view( + archive->view_dispatcher, ArchiveViewTextInput, text_input_get_view(archive->text_input)); return archive; } +void archive_free(ArchiveApp* archive) { + furi_assert(archive); + + view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewBrowser); + view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewTextInput); + view_dispatcher_free(archive->view_dispatcher); + scene_manager_free(archive->scene_manager); + main_view_free(archive->main_view); + + text_input_free(archive->text_input); + + furi_record_close("gui"); + archive->gui = NULL; + + free(archive); +} + int32_t archive_app(void* p) { ArchiveApp* archive = archive_alloc(); - - // default tab - archive_switch_tab(archive); - - AppEvent event; - while(1) { - furi_check(osMessageQueueGet(archive->event_queue, &event, NULL, osWaitForever) == osOK); - if(event.type == EventTypeExit) { - break; - } - } - + scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneBrowser); + view_dispatcher_run(archive->view_dispatcher); archive_free(archive); + return 0; } diff --git a/applications/archive/archive_i.h b/applications/archive/archive_i.h index a7c3a901..474b18e5 100644 --- a/applications/archive/archive_i.h +++ b/applications/archive/archive_i.h @@ -5,44 +5,27 @@ #include #include #include +#include #include #include #include #include #include -#include "archive_views.h" #include "applications.h" #include "file-worker.h" -#define MAX_DEPTH 32 -#define MAX_FILES 100 //temp +#include "views/archive_main_view.h" +#include "scenes/archive_scene.h" + #define MAX_FILE_SIZE 128 -#define ARCHIVE_FAV_PATH "/any/favorites.txt" -#define ARCHIVE_FAV_TEMP_PATH "/any/favorites.tmp" typedef enum { - ArchiveViewMain, + ArchiveViewBrowser, ArchiveViewTextInput, ArchiveViewTotal, } ArchiveViewEnum; -static const char* flipper_app_name[] = { - [ArchiveFileTypeIButton] = "iButton", - [ArchiveFileTypeNFC] = "NFC", - [ArchiveFileTypeSubGhz] = "Sub-GHz", - [ArchiveFileTypeLFRFID] = "125 kHz RFID", - [ArchiveFileTypeIrda] = "Infrared", -}; - -static const char* known_ext[] = { - [ArchiveFileTypeIButton] = ".ibtn", - [ArchiveFileTypeNFC] = ".nfc", - [ArchiveFileTypeSubGhz] = ".sub", - [ArchiveFileTypeLFRFID] = ".rfid", - [ArchiveFileTypeIrda] = ".ir", -}; - static const char* tab_default_paths[] = { [ArchiveTabFavorites] = "/any/favorites", [ArchiveTabIButton] = "/any/ibutton", @@ -53,23 +36,6 @@ static const char* tab_default_paths[] = { [ArchiveTabBrowser] = "/any", }; -static inline const char* get_tab_ext(ArchiveTabEnum tab) { - switch(tab) { - case ArchiveTabIButton: - return known_ext[ArchiveFileTypeIButton]; - case ArchiveTabNFC: - return known_ext[ArchiveFileTypeNFC]; - case ArchiveTabSubGhz: - return known_ext[ArchiveFileTypeSubGhz]; - case ArchiveTabLFRFID: - return known_ext[ArchiveFileTypeLFRFID]; - case ArchiveTabIrda: - return known_ext[ArchiveFileTypeIrda]; - default: - return "*"; - } -} - static inline const char* get_default_path(ArchiveFileTypeEnum type) { switch(type) { case ArchiveFileTypeIButton: @@ -104,35 +70,11 @@ typedef struct { EventType type; } AppEvent; -typedef enum { - FavoritesCheck, - FavoritesRead, - FavoritesDelete, - FavoritesRename, -} FavActionsEnum; - -typedef struct { - ArchiveTabEnum tab_id; - string_t name; - string_t path; - char text_input_buffer[MAX_NAME_LEN]; - - uint8_t depth; - uint16_t last_idx[MAX_DEPTH]; - - bool menu; -} ArchiveBrowser; - struct ArchiveApp { - osMessageQueueId_t event_queue; - FuriThread* app_thread; - Loader* loader; Gui* gui; ViewDispatcher* view_dispatcher; - View* view_archive_main; + SceneManager* scene_manager; + ArchiveMainView* main_view; TextInput* text_input; - - Storage* api; - FileWorker* file_worker; - ArchiveBrowser browser; + char text_store[MAX_NAME_LEN]; }; diff --git a/applications/archive/archive_views.c b/applications/archive/archive_views.c deleted file mode 100644 index 00f4d0f3..00000000 --- a/applications/archive/archive_views.c +++ /dev/null @@ -1,168 +0,0 @@ -#include "archive_views.h" - -static const char* ArchiveTabNames[] = { - [ArchiveTabFavorites] = "Favorites", - [ArchiveTabIButton] = "iButton", - [ArchiveTabNFC] = "NFC", - [ArchiveTabSubGhz] = "Sub-GHz", - [ArchiveTabLFRFID] = "RFID LF", - [ArchiveTabIrda] = "Infrared", - [ArchiveTabBrowser] = "Browser"}; - -static const Icon* ArchiveItemIcons[] = { - [ArchiveFileTypeIButton] = &I_ibutt_10px, - [ArchiveFileTypeNFC] = &I_Nfc_10px, - [ArchiveFileTypeSubGhz] = &I_sub1_10px, - [ArchiveFileTypeLFRFID] = &I_125_10px, - [ArchiveFileTypeIrda] = &I_ir_10px, - [ArchiveFileTypeFolder] = &I_dir_10px, - [ArchiveFileTypeUnknown] = &I_unknown_10px, -}; - -static void render_item_menu(Canvas* canvas, ArchiveViewModel* model) { - canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, 71, 17, 57, 46); - canvas_set_color(canvas, ColorBlack); - elements_slightly_rounded_frame(canvas, 70, 16, 58, 48); - - string_t menu[MENU_ITEMS]; - - string_init_set_str(menu[0], "Run in app"); - string_init_set_str(menu[1], "Pin"); - string_init_set_str(menu[2], "Rename"); - string_init_set_str(menu[3], "Delete"); - - ArchiveFile_t* selected = files_array_get(model->files, model->idx); - - if(!is_known_app(selected->type)) { - string_set_str(menu[0], "---"); - string_set_str(menu[1], "---"); - string_set_str(menu[2], "---"); - } else if(model->tab_idx == 0 || selected->fav) { - string_set_str(menu[1], "Unpin"); - } - - for(size_t i = 0; i < MENU_ITEMS; i++) { - canvas_draw_str(canvas, 82, 27 + i * 11, string_get_cstr(menu[i])); - string_clear(menu[i]); - } - - canvas_draw_icon(canvas, 74, 20 + model->menu_idx * 11, &I_ButtonRight_4x7); -} - -void archive_trim_file_ext(char* name) { - size_t str_len = strlen(name); - char* end = name + str_len; - while(end > name && *end != '.' && *end != '\\' && *end != '/') { - --end; - } - if((end > name && *end == '.') && (*(end - 1) != '\\' && *(end - 1) != '/')) { - *end = '\0'; - } -} - -static void archive_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) { - canvas_set_color(canvas, ColorBlack); - canvas_draw_box(canvas, 0, 15 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT); - - canvas_set_color(canvas, ColorWhite); - canvas_draw_dot(canvas, 0, 15 + idx * FRAME_HEIGHT); - canvas_draw_dot(canvas, 1, 15 + idx * FRAME_HEIGHT); - canvas_draw_dot(canvas, 0, (15 + idx * FRAME_HEIGHT) + 1); - - canvas_draw_dot(canvas, 0, (15 + idx * FRAME_HEIGHT) + 11); - canvas_draw_dot(canvas, scrollbar ? 121 : 126, 15 + idx * FRAME_HEIGHT); - canvas_draw_dot(canvas, scrollbar ? 121 : 126, (15 + idx * FRAME_HEIGHT) + 11); -} - -static void draw_list(Canvas* canvas, ArchiveViewModel* model) { - furi_assert(model); - - size_t array_size = files_array_size(model->files); - bool scrollbar = array_size > 4; - - for(size_t i = 0; i < MIN(array_size, MENU_ITEMS); ++i) { - string_t str_buff; - char cstr_buff[MAX_NAME_LEN]; - - size_t idx = CLAMP(i + model->list_offset, array_size, 0); - ArchiveFile_t* file = files_array_get(model->files, CLAMP(idx, array_size - 1, 0)); - - string_init_set(str_buff, file->name); - string_right(str_buff, string_search_rchar(str_buff, '/') + 1); - strlcpy(cstr_buff, string_get_cstr(str_buff), string_size(str_buff) + 1); - - if(is_known_app(file->type)) archive_trim_file_ext(cstr_buff); - - string_clean(str_buff); - string_set_str(str_buff, cstr_buff); - - elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX); - - if(model->idx == idx) { - archive_draw_frame(canvas, i, scrollbar); - } else { - canvas_set_color(canvas, ColorBlack); - } - - canvas_draw_icon(canvas, 2, 16 + i * FRAME_HEIGHT, ArchiveItemIcons[file->type]); - canvas_draw_str(canvas, 15, 24 + i * FRAME_HEIGHT, string_get_cstr(str_buff)); - string_clear(str_buff); - } - - if(scrollbar) { - elements_scrollbar_pos(canvas, 126, 15, 49, model->idx, array_size); - } - - if(model->menu) { - render_item_menu(canvas, model); - } -} - -static void archive_render_status_bar(Canvas* canvas, ArchiveViewModel* model) { - furi_assert(model); - - const char* tab_name = ArchiveTabNames[model->tab_idx]; - - canvas_draw_icon(canvas, 0, 0, &I_Background_128x11); - - canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, 0, 0, 50, 13); - canvas_draw_box(canvas, 107, 0, 20, 13); - - canvas_set_color(canvas, ColorBlack); - canvas_draw_frame(canvas, 1, 0, 50, 12); - canvas_draw_line(canvas, 0, 1, 0, 11); - canvas_draw_line(canvas, 1, 12, 49, 12); - canvas_draw_str_aligned(canvas, 26, 9, AlignCenter, AlignBottom, tab_name); - - canvas_draw_frame(canvas, 108, 0, 20, 12); - canvas_draw_line(canvas, 107, 1, 107, 11); - canvas_draw_line(canvas, 108, 12, 126, 12); - - if(model->tab_idx > 0) { - canvas_draw_icon(canvas, 112, 2, &I_ButtonLeft_4x7); - } - if(model->tab_idx < SIZEOF_ARRAY(ArchiveTabNames) - 1) { - canvas_draw_icon(canvas, 120, 2, &I_ButtonRight_4x7); - } - - canvas_set_color(canvas, ColorWhite); - canvas_draw_dot(canvas, 50, 0); - canvas_draw_dot(canvas, 127, 0); - - canvas_set_color(canvas, ColorBlack); -} - -void archive_view_render(Canvas* canvas, void* model) { - ArchiveViewModel* m = model; - - archive_render_status_bar(canvas, model); - - if(files_array_size(m->files) > 0) { - draw_list(canvas, m); - } else { - canvas_draw_str_aligned( - canvas, GUI_DISPLAY_WIDTH / 2, 40, AlignCenter, AlignCenter, "Empty"); - } -} \ No newline at end of file diff --git a/applications/archive/helpers/archive_favorites.c b/applications/archive/helpers/archive_favorites.c new file mode 100644 index 00000000..6eeefdbb --- /dev/null +++ b/applications/archive/helpers/archive_favorites.c @@ -0,0 +1,171 @@ +#include "archive_favorites.h" +#include "archive_files.h" +#include "../views/archive_main_view.h" + +bool archive_favorites_read(void* context) { + furi_assert(context); + + ArchiveMainView* archive_view = context; + FileWorker* file_worker = file_worker_alloc(true); + + string_t buffer; + FileInfo file_info; + string_init(buffer); + + bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_ALWAYS); + + if(result) { + while(1) { + if(!file_worker_read_until(file_worker, buffer, '\n')) { + break; + } + if(!string_size(buffer)) { + break; + } + + archive_view_add_item(archive_view, &file_info, string_get_cstr(buffer)); + string_clean(buffer); + } + } + string_clear(buffer); + file_worker_close(file_worker); + file_worker_free(file_worker); + return result; +} + +bool archive_favorites_delete(const char* file_path, const char* name) { + furi_assert(file_path); + furi_assert(name); + + FileWorker* file_worker = file_worker_alloc(true); + + string_t path; + string_t buffer; + string_init(buffer); + + string_init_printf(path, "%s/%s", file_path, name); + + bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); + if(result) { + while(1) { + if(!file_worker_read_until(file_worker, buffer, '\n')) { + break; + } + if(!string_size(buffer)) { + break; + } + + if(string_search(buffer, path)) { + string_t temp; + string_init_printf(temp, "%s\r\n", string_get_cstr(buffer)); + archive_file_append(ARCHIVE_FAV_TEMP_PATH, temp); + string_clear(temp); + } + } + } + + string_clear(buffer); + string_clear(path); + + file_worker_close(file_worker); + file_worker_remove(file_worker, ARCHIVE_FAV_PATH); + file_worker_rename(file_worker, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); + + file_worker_free(file_worker); + + return result; +} + +bool archive_is_favorite(const char* file_path, const char* name) { + furi_assert(file_path); + furi_assert(name); + + FileWorker* file_worker = file_worker_alloc(true); + + string_t path; + string_t buffer; + string_init(buffer); + bool found = false; + + string_init_printf(path, "%s/%s", file_path, name); + bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_ALWAYS); + + if(result) { + while(1) { + if(!file_worker_read_until(file_worker, buffer, '\n')) { + break; + } + if(!string_size(buffer)) { + break; + } + if(!string_search(buffer, path)) { + found = true; + break; + } + } + } + + string_clear(buffer); + string_clear(path); + file_worker_close(file_worker); + file_worker_free(file_worker); + + return found; +} + +bool archive_favorites_rename(const char* file_path, const char* src, const char* dst) { + furi_assert(file_path); + furi_assert(src); + furi_assert(dst); + + FileWorker* file_worker = file_worker_alloc(true); + + string_t path; + string_t buffer; + string_t temp; + + string_init(buffer); + string_init(temp); + string_init(path); + + string_printf(path, "%s/%s", file_path, src); + bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); + + if(result) { + while(1) { + if(!file_worker_read_until(file_worker, buffer, '\n')) { + break; + } + if(!string_size(buffer)) { + break; + } + string_printf( + temp, "%s\r\n", string_search(buffer, path) ? string_get_cstr(buffer) : dst); + archive_file_append(ARCHIVE_FAV_TEMP_PATH, temp); + string_clean(temp); + } + } + + string_clear(temp); + string_clear(buffer); + string_clear(path); + + file_worker_close(file_worker); + file_worker_remove(file_worker, ARCHIVE_FAV_PATH); + file_worker_rename(file_worker, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); + + file_worker_free(file_worker); + + return result; +} + +void archive_add_to_favorites(const char* file_path, const char* name) { + furi_assert(file_path); + furi_assert(name); + + string_t buffer_src; + + string_init_printf(buffer_src, "%s/%s\r\n", file_path, name); + archive_file_append(ARCHIVE_FAV_PATH, buffer_src); + string_clear(buffer_src); +} diff --git a/applications/archive/helpers/archive_favorites.h b/applications/archive/helpers/archive_favorites.h new file mode 100644 index 00000000..7a8fce00 --- /dev/null +++ b/applications/archive/helpers/archive_favorites.h @@ -0,0 +1,11 @@ +#pragma once +#include "file-worker.h" + +#define ARCHIVE_FAV_PATH "/any/favorites.txt" +#define ARCHIVE_FAV_TEMP_PATH "/any/favorites.tmp" + +bool archive_favorites_read(void* context); +bool archive_favorites_delete(const char* file_path, const char* name); +bool archive_is_favorite(const char* file_path, const char* name); +bool archive_favorites_rename(const char* file_path, const char* src, const char* dst); +void archive_add_to_favorites(const char* file_path, const char* name); diff --git a/applications/archive/helpers/archive_files.c b/applications/archive/helpers/archive_files.c new file mode 100644 index 00000000..036df166 --- /dev/null +++ b/applications/archive/helpers/archive_files.c @@ -0,0 +1,143 @@ +#include "archive_files.h" +#include "archive_favorites.h" +#include "../archive_i.h" + +bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name) { + furi_assert(file_info); + furi_assert(tab_ext); + furi_assert(name); + + bool result = false; + + if(strcmp(tab_ext, "*") == 0) { + result = true; + } else if(strstr(name, tab_ext) != NULL) { + result = true; + } else if(file_info->flags & FSF_DIRECTORY) { + result = true; + } + + return result; +} + +void archive_trim_file_ext(char* name) { + size_t str_len = strlen(name); + char* end = name + str_len; + while(end > name && *end != '.' && *end != '\\' && *end != '/') { + --end; + } + if((end > name && *end == '.') && (*(end - 1) != '\\' && *(end - 1) != '/')) { + *end = '\0'; + } +} + +void set_file_type(ArchiveFile_t* file, FileInfo* file_info) { + furi_assert(file); + furi_assert(file_info); + + for(size_t i = 0; i < SIZEOF_ARRAY(known_ext); i++) { + if(string_search_str(file->name, known_ext[i], 0) != STRING_FAILURE) { + file->type = i; + return; + } + } + + if(file_info->flags & FSF_DIRECTORY) { + file->type = ArchiveFileTypeFolder; + } else { + file->type = ArchiveFileTypeUnknown; + } +} + +bool archive_get_filenames(void* context, uint8_t tab_id, const char* path) { + furi_assert(context); + + ArchiveMainView* main_view = context; + archive_file_array_clean(main_view); + + if(tab_id != ArchiveTabFavorites) { + archive_read_dir(main_view, path); + } else { + archive_favorites_read(main_view); + } + return true; +} + +bool archive_read_dir(void* context, const char* path) { + furi_assert(context); + + ArchiveMainView* main_view = context; + FileInfo file_info; + Storage* fs_api = furi_record_open("storage"); + File* directory = storage_file_alloc(fs_api); + char name[MAX_NAME_LEN]; + + if(!storage_dir_open(directory, path)) { + storage_dir_close(directory); + storage_file_free(directory); + return false; + } + + while(1) { + if(!storage_dir_read(directory, &file_info, name, MAX_NAME_LEN)) { + break; + } + + uint16_t files_cnt = archive_file_array_size(main_view); + + if(files_cnt > MAX_FILES) { + break; + } else if(storage_file_get_error(directory) == FSE_OK) { + archive_view_add_item(main_view, &file_info, name); + } else { + storage_dir_close(directory); + storage_file_free(directory); + return false; + } + } + storage_dir_close(directory); + storage_file_free(directory); + + furi_record_close("storage"); + + return true; +} + +void archive_file_append(const char* path, string_t string) { + furi_assert(path); + furi_assert(string); + + FileWorker* file_worker = file_worker_alloc(false); + + if(!file_worker_open(file_worker, path, FSAM_WRITE, FSOM_OPEN_APPEND)) { + FURI_LOG_E("Archive", "Append open error"); + } + + if(!file_worker_write(file_worker, string_get_cstr(string), string_size(string))) { + FURI_LOG_E("Archive", "Append write error"); + } + + file_worker_close(file_worker); + file_worker_free(file_worker); +} + +void archive_delete_file(void* context, string_t path, string_t name) { + furi_assert(context); + furi_assert(path); + furi_assert(name); + ArchiveMainView* main_view = context; + FileWorker* file_worker = file_worker_alloc(false); + + string_t full_path; + string_init(full_path); + string_printf(full_path, "%s/%s", string_get_cstr(path), string_get_cstr(name)); + file_worker_remove(file_worker, string_get_cstr(full_path)); + file_worker_free(file_worker); + string_clear(full_path); + + if(archive_is_favorite(string_get_cstr(path), string_get_cstr(name))) { + archive_favorites_delete(string_get_cstr(path), string_get_cstr(name)); + } + + archive_file_array_remove_selected(main_view); +} diff --git a/applications/archive/archive_views.h b/applications/archive/helpers/archive_files.h similarity index 58% rename from applications/archive/archive_views.h rename to applications/archive/helpers/archive_files.h index 9c9cd8f6..71864fe9 100644 --- a/applications/archive/archive_views.h +++ b/applications/archive/helpers/archive_files.h @@ -1,15 +1,7 @@ #pragma once +#include "file-worker.h" -#include -#include -#include -#include -#include - -#define MAX_LEN_PX 100 -#define MAX_NAME_LEN 255 -#define FRAME_HEIGHT 12 -#define MENU_ITEMS 4 +#define MAX_FILES 100 //temp typedef enum { ArchiveFileTypeIButton, @@ -22,17 +14,6 @@ typedef enum { AppIdTotal, } ArchiveFileTypeEnum; -typedef enum { - ArchiveTabFavorites, - ArchiveTabLFRFID, - ArchiveTabSubGhz, - ArchiveTabNFC, - ArchiveTabIButton, - ArchiveTabIrda, - ArchiveTabBrowser, - ArchiveTabTotal, -} ArchiveTabEnum; - typedef struct { string_t name; ArchiveFileTypeEnum type; @@ -66,18 +47,10 @@ ARRAY_DEF( INIT_SET(API_6(ArchiveFile_t_init_set)), CLEAR(API_2(ArchiveFile_t_clear)))) -typedef struct { - uint8_t tab_idx; - uint8_t menu_idx; - uint16_t idx; - uint16_t list_offset; - files_array_t files; - bool menu; -} ArchiveViewModel; - -void archive_view_render(Canvas* canvas, void* model); +bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name); +void set_file_type(ArchiveFile_t* file, FileInfo* file_info); void archive_trim_file_ext(char* name); - -static inline bool is_known_app(ArchiveFileTypeEnum type) { - return (type != ArchiveFileTypeFolder && type != ArchiveFileTypeUnknown); -} +bool archive_get_filenames(void* context, uint8_t tab_id, const char* path); +bool archive_read_dir(void* context, const char* path); +void archive_file_append(const char* path, string_t string); +void archive_delete_file(void* context, string_t path, string_t name); \ No newline at end of file diff --git a/applications/archive/scenes/archive_scene.c b/applications/archive/scenes/archive_scene.c new file mode 100644 index 00000000..d5f5ae2c --- /dev/null +++ b/applications/archive/scenes/archive_scene.c @@ -0,0 +1,30 @@ +#include "archive_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const archive_on_enter_handlers[])(void*) = { +#include "archive_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const archive_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "archive_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const archive_on_exit_handlers[])(void* context) = { +#include "archive_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers archive_scene_handlers = { + .on_enter_handlers = archive_on_enter_handlers, + .on_event_handlers = archive_on_event_handlers, + .on_exit_handlers = archive_on_exit_handlers, + .scene_num = ArchiveAppSceneNum, +}; diff --git a/applications/archive/scenes/archive_scene.h b/applications/archive/scenes/archive_scene.h new file mode 100644 index 00000000..3f8063fa --- /dev/null +++ b/applications/archive/scenes/archive_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) ArchiveAppScene##id, +typedef enum { +#include "archive_scene_config.h" + ArchiveAppSceneNum, +} ArchiveAppScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers archive_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "archive_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "archive_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "archive_scene_config.h" +#undef ADD_SCENE diff --git a/applications/archive/scenes/archive_scene_browser.c b/applications/archive/scenes/archive_scene_browser.c new file mode 100644 index 00000000..ed6204ff --- /dev/null +++ b/applications/archive/scenes/archive_scene_browser.c @@ -0,0 +1,42 @@ +#include "../archive_i.h" +#include "../views/archive_main_view.h" + +void archive_scene_browser_callback(ArchiveBrowserEvent event, void* context) { + ArchiveApp* archive = (ArchiveApp*)context; + view_dispatcher_send_custom_event(archive->view_dispatcher, event); +} + +const void archive_scene_browser_on_enter(void* context) { + ArchiveApp* archive = (ArchiveApp*)context; + ArchiveMainView* main_view = archive->main_view; + + archive_browser_set_callback(main_view, archive_scene_browser_callback, archive); + archive_browser_update(main_view); + view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewBrowser); +} + +const bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { + ArchiveApp* archive = (ArchiveApp*)context; + bool consumed; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case ArchiveBrowserEventRename: + scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneRename); + consumed = true; + break; + case ArchiveBrowserEventExit: + view_dispatcher_stop(archive->view_dispatcher); + consumed = true; + break; + + default: + break; + } + } + return consumed; +} + +const void archive_scene_browser_on_exit(void* context) { + // ArchiveApp* archive = (ArchiveApp*)context; +} diff --git a/applications/archive/scenes/archive_scene_config.h b/applications/archive/scenes/archive_scene_config.h new file mode 100644 index 00000000..2ed4e00c --- /dev/null +++ b/applications/archive/scenes/archive_scene_config.h @@ -0,0 +1,2 @@ +ADD_SCENE(archive, browser, Browser) +ADD_SCENE(archive, rename, Rename) diff --git a/applications/archive/scenes/archive_scene_rename.c b/applications/archive/scenes/archive_scene_rename.c new file mode 100644 index 00000000..51918e5a --- /dev/null +++ b/applications/archive/scenes/archive_scene_rename.c @@ -0,0 +1,80 @@ +#include "../archive_i.h" +#include "../helpers/archive_favorites.h" +#include "../helpers/archive_files.h" + +#define SCENE_RENAME_CUSTOM_EVENT (0UL) + +void archive_scene_rename_text_input_callback(void* context) { + ArchiveApp* archive = (ArchiveApp*)context; + view_dispatcher_send_custom_event(archive->view_dispatcher, SCENE_RENAME_CUSTOM_EVENT); +} + +const void archive_scene_rename_on_enter(void* context) { + ArchiveApp* archive = (ArchiveApp*)context; + + TextInput* text_input = archive->text_input; + ArchiveFile_t* current = archive_get_current_file(archive->main_view); + strlcpy(archive->text_store, string_get_cstr(current->name), MAX_NAME_LEN); + + archive_trim_file_ext(archive->text_store); + + text_input_set_header_text(text_input, "Rename:"); + + text_input_set_result_callback( + text_input, + archive_scene_rename_text_input_callback, + archive, + archive->text_store, + MAX_NAME_LEN, + false); + + view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput); +} + +const bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) { + ArchiveApp* archive = (ArchiveApp*)context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SCENE_RENAME_CUSTOM_EVENT) { + Storage* fs_api = furi_record_open("storage"); + + string_t buffer_src; + string_t buffer_dst; + + const char* path = archive_get_path(archive->main_view); + const char* name = archive_get_name(archive->main_view); + + string_init_printf(buffer_src, "%s/%s", path, name); + string_init_printf(buffer_dst, "%s/%s", path, archive->text_store); + + archive_set_name(archive->main_view, archive->text_store); + + // append extension + ArchiveFile_t* file = archive_get_current_file(archive->main_view); + + string_cat(buffer_dst, known_ext[file->type]); + storage_common_rename( + fs_api, string_get_cstr(buffer_src), string_get_cstr(buffer_dst)); + furi_record_close("storage"); + + if(file->fav) { + archive_favorites_rename(path, name, string_get_cstr(buffer_dst)); + } + + string_clear(buffer_src); + string_clear(buffer_dst); + + scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneBrowser); + consumed = true; + } + } + return consumed; +} + +const void archive_scene_rename_on_exit(void* context) { + ArchiveApp* archive = (ArchiveApp*)context; + // Clear view + text_input_set_header_text(archive->text_input, NULL); + text_input_set_result_callback(archive->text_input, NULL, NULL, NULL, 0, false); +} diff --git a/applications/archive/views/archive_main_view.c b/applications/archive/views/archive_main_view.c new file mode 100644 index 00000000..826a7483 --- /dev/null +++ b/applications/archive/views/archive_main_view.c @@ -0,0 +1,642 @@ +#include +#include "../archive_i.h" +#include "archive_main_view.h" + +static const char* flipper_app_name[] = { + [ArchiveFileTypeIButton] = "iButton", + [ArchiveFileTypeNFC] = "NFC", + [ArchiveFileTypeSubGhz] = "Sub-GHz", + [ArchiveFileTypeLFRFID] = "125 kHz RFID", + [ArchiveFileTypeIrda] = "Infrared", +}; + +static const char* ArchiveTabNames[] = { + [ArchiveTabFavorites] = "Favorites", + [ArchiveTabIButton] = "iButton", + [ArchiveTabNFC] = "NFC", + [ArchiveTabSubGhz] = "Sub-GHz", + [ArchiveTabLFRFID] = "RFID LF", + [ArchiveTabIrda] = "Infrared", + [ArchiveTabBrowser] = "Browser"}; + +static const Icon* ArchiveItemIcons[] = { + [ArchiveFileTypeIButton] = &I_ibutt_10px, + [ArchiveFileTypeNFC] = &I_Nfc_10px, + [ArchiveFileTypeSubGhz] = &I_sub1_10px, + [ArchiveFileTypeLFRFID] = &I_125_10px, + [ArchiveFileTypeIrda] = &I_ir_10px, + [ArchiveFileTypeFolder] = &I_dir_10px, + [ArchiveFileTypeUnknown] = &I_unknown_10px, +}; + +void archive_browser_set_callback( + ArchiveMainView* main_view, + ArchiveMainViewCallback callback, + void* context) { + furi_assert(main_view); + furi_assert(callback); + main_view->callback = callback; + main_view->context = context; +} + +void update_offset(ArchiveMainView* main_view) { + furi_assert(main_view); + + with_view_model( + main_view->view, (ArchiveMainViewModel * model) { + size_t array_size = files_array_size(model->files); + uint16_t bounds = array_size > 3 ? 2 : array_size; + + if(array_size > 3 && model->idx >= array_size - 1) { + model->list_offset = model->idx - 3; + } else if(model->list_offset < model->idx - bounds) { + model->list_offset = CLAMP(model->list_offset + 1, array_size - bounds, 0); + } else if(model->list_offset > model->idx - bounds) { + model->list_offset = CLAMP(model->idx - 1, array_size - bounds, 0); + } + return true; + }); +} + +size_t archive_file_array_size(ArchiveMainView* main_view) { + uint16_t size = 0; + with_view_model( + main_view->view, (ArchiveMainViewModel * model) { + size = files_array_size(model->files); + return true; + }); + return size; +} + +void archive_file_array_remove_selected(ArchiveMainView* main_view) { + with_view_model( + main_view->view, (ArchiveMainViewModel * model) { + files_array_remove_v(model->files, model->idx, model->idx + 1); + model->idx = CLAMP(model->idx, files_array_size(model->files) - 1, 0); + return true; + }); + + update_offset(main_view); +} + +void archive_file_array_clean(ArchiveMainView* main_view) { + with_view_model( + main_view->view, (ArchiveMainViewModel * model) { + files_array_clean(model->files); + return true; + }); +} + +ArchiveFile_t* archive_get_current_file(ArchiveMainView* main_view) { + ArchiveFile_t* selected; + with_view_model( + main_view->view, (ArchiveMainViewModel * model) { + selected = files_array_size(model->files) > 0 ? + files_array_get(model->files, model->idx) : + NULL; + return true; + }); + return selected; +} + +ArchiveTabEnum archive_get_tab(ArchiveMainView* main_view) { + ArchiveTabEnum tab_id; + with_view_model( + main_view->view, (ArchiveMainViewModel * model) { + tab_id = model->tab_idx; + return true; + }); + return tab_id; +} + +void archive_set_tab(ArchiveMainView* main_view, ArchiveTabEnum tab) { + with_view_model( + main_view->view, (ArchiveMainViewModel * model) { + model->tab_idx = tab; + return true; + }); +} + +uint8_t archive_get_depth(ArchiveMainView* main_view) { + uint8_t depth; + with_view_model( + main_view->view, (ArchiveMainViewModel * model) { + depth = model->depth; + return true; + }); + + return depth; +} + +const char* archive_get_path(ArchiveMainView* main_view) { + return string_get_cstr(main_view->path); +} +const char* archive_get_name(ArchiveMainView* main_view) { + ArchiveFile_t* selected = archive_get_current_file(main_view); + return string_get_cstr(selected->name); +} + +void archive_set_name(ArchiveMainView* main_view, const char* name) { + furi_assert(main_view); + furi_assert(name); + + string_set(main_view->name, name); +} + +void archive_browser_update(ArchiveMainView* main_view) { + furi_assert(main_view); + + archive_get_filenames(main_view, archive_get_tab(main_view), string_get_cstr(main_view->path)); + + with_view_model( + main_view->view, (ArchiveMainViewModel * model) { + uint16_t idx = 0; + while(idx < files_array_size(model->files)) { + ArchiveFile_t* current = files_array_get(model->files, idx); + if(!string_search(current->name, string_get_cstr(main_view->name))) { + model->idx = idx; + break; + } + ++idx; + } + return true; + }); + + update_offset(main_view); +} + +void archive_view_add_item(ArchiveMainView* main_view, FileInfo* file_info, const char* name) { + furi_assert(main_view); + furi_assert(file_info); + furi_assert(name); + + ArchiveFile_t item; + + if(filter_by_extension(file_info, get_tab_ext(archive_get_tab(main_view)), name)) { + ArchiveFile_t_init(&item); + string_init_set_str(item.name, name); + set_file_type(&item, file_info); + + with_view_model( + main_view->view, (ArchiveMainViewModel * model) { + files_array_push_back(model->files, item); + return true; + }); + + ArchiveFile_t_clear(&item); + } +} + +static void render_item_menu(Canvas* canvas, ArchiveMainViewModel* model) { + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 71, 17, 57, 46); + canvas_set_color(canvas, ColorBlack); + elements_slightly_rounded_frame(canvas, 70, 16, 58, 48); + + string_t menu[MENU_ITEMS]; + + string_init_set_str(menu[0], "Run in app"); + string_init_set_str(menu[1], "Pin"); + string_init_set_str(menu[2], "Rename"); + string_init_set_str(menu[3], "Delete"); + + ArchiveFile_t* selected = files_array_get(model->files, model->idx); + + if(!is_known_app(selected->type)) { + string_set_str(menu[0], "---"); + string_set_str(menu[1], "---"); + string_set_str(menu[2], "---"); + } else if(selected->fav) { + string_set_str(menu[1], "Unpin"); + } + + for(size_t i = 0; i < MENU_ITEMS; i++) { + canvas_draw_str(canvas, 82, 27 + i * 11, string_get_cstr(menu[i])); + string_clear(menu[i]); + } + + canvas_draw_icon(canvas, 74, 20 + model->menu_idx * 11, &I_ButtonRight_4x7); +} + +static void archive_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 0, 15 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_dot(canvas, 0, 15 + idx * FRAME_HEIGHT); + canvas_draw_dot(canvas, 1, 15 + idx * FRAME_HEIGHT); + canvas_draw_dot(canvas, 0, (15 + idx * FRAME_HEIGHT) + 1); + + canvas_draw_dot(canvas, 0, (15 + idx * FRAME_HEIGHT) + 11); + canvas_draw_dot(canvas, scrollbar ? 121 : 126, 15 + idx * FRAME_HEIGHT); + canvas_draw_dot(canvas, scrollbar ? 121 : 126, (15 + idx * FRAME_HEIGHT) + 11); +} + +static void draw_list(Canvas* canvas, ArchiveMainViewModel* model) { + furi_assert(model); + + size_t array_size = files_array_size(model->files); + bool scrollbar = array_size > 4; + + for(size_t i = 0; i < MIN(array_size, MENU_ITEMS); ++i) { + string_t str_buff; + char cstr_buff[MAX_NAME_LEN]; + + size_t idx = CLAMP(i + model->list_offset, array_size, 0); + ArchiveFile_t* file = files_array_get(model->files, CLAMP(idx, array_size - 1, 0)); + + string_init_set(str_buff, file->name); + string_right(str_buff, string_search_rchar(str_buff, '/') + 1); + strlcpy(cstr_buff, string_get_cstr(str_buff), string_size(str_buff) + 1); + + if(is_known_app(file->type)) archive_trim_file_ext(cstr_buff); + + string_clean(str_buff); + string_set_str(str_buff, cstr_buff); + + elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX); + + if(model->idx == idx) { + archive_draw_frame(canvas, i, scrollbar); + } else { + canvas_set_color(canvas, ColorBlack); + } + + canvas_draw_icon(canvas, 2, 16 + i * FRAME_HEIGHT, ArchiveItemIcons[file->type]); + canvas_draw_str(canvas, 15, 24 + i * FRAME_HEIGHT, string_get_cstr(str_buff)); + string_clear(str_buff); + } + + if(scrollbar) { + elements_scrollbar_pos(canvas, 126, 15, 49, model->idx, array_size); + } + + if(model->action == BrowserActionItemMenu) { + render_item_menu(canvas, model); + } +} + +static void archive_render_status_bar(Canvas* canvas, ArchiveMainViewModel* model) { + furi_assert(model); + + const char* tab_name = ArchiveTabNames[model->tab_idx]; + + canvas_draw_icon(canvas, 0, 0, &I_Background_128x11); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 0, 0, 50, 13); + canvas_draw_box(canvas, 107, 0, 20, 13); + + canvas_set_color(canvas, ColorBlack); + canvas_draw_frame(canvas, 1, 0, 50, 12); + canvas_draw_line(canvas, 0, 1, 0, 11); + canvas_draw_line(canvas, 1, 12, 49, 12); + canvas_draw_str_aligned(canvas, 26, 9, AlignCenter, AlignBottom, tab_name); + + canvas_draw_frame(canvas, 108, 0, 20, 12); + canvas_draw_line(canvas, 107, 1, 107, 11); + canvas_draw_line(canvas, 108, 12, 126, 12); + + if(model->tab_idx > 0) { + canvas_draw_icon(canvas, 112, 2, &I_ButtonLeft_4x7); + } + if(model->tab_idx < SIZEOF_ARRAY(ArchiveTabNames) - 1) { + canvas_draw_icon(canvas, 120, 2, &I_ButtonRight_4x7); + } + + canvas_set_color(canvas, ColorWhite); + canvas_draw_dot(canvas, 50, 0); + canvas_draw_dot(canvas, 127, 0); + + canvas_set_color(canvas, ColorBlack); +} + +void archive_view_render(Canvas* canvas, void* model) { + ArchiveMainViewModel* m = model; + + archive_render_status_bar(canvas, model); + + if(files_array_size(m->files) > 0) { + draw_list(canvas, m); + } else { + canvas_draw_str_aligned( + canvas, GUI_DISPLAY_WIDTH / 2, 40, AlignCenter, AlignCenter, "Empty"); + } +} + +View* archive_main_get_view(ArchiveMainView* main_view) { + furi_assert(main_view); + return main_view->view; +} + +static void archive_show_file_menu(ArchiveMainView* main_view) { + furi_assert(main_view); + with_view_model( + main_view->view, (ArchiveMainViewModel * model) { + ArchiveFile_t* selected; + selected = files_array_get(model->files, model->idx); + model->action = BrowserActionItemMenu; + model->menu_idx = 0; + selected->fav = is_known_app(selected->type) ? archive_is_favorite( + string_get_cstr(main_view->path), + string_get_cstr(selected->name)) : + false; + + return true; + }); +} + +static void archive_close_file_menu(ArchiveMainView* main_view) { + furi_assert(main_view); + + with_view_model( + main_view->view, (ArchiveMainViewModel * model) { + model->action = BrowserActionBrowse; + model->menu_idx = 0; + return true; + }); +} + +static void archive_run_in_app( + ArchiveMainView* main_view, + ArchiveFile_t* selected, + bool full_path_provided) { + Loader* loader = furi_record_open("loader"); + + string_t full_path; + + if(!full_path_provided) { + string_init_printf( + full_path, "%s/%s", string_get_cstr(main_view->path), string_get_cstr(selected->name)); + } else { + string_init_set(full_path, selected->name); + } + loader_start(loader, flipper_app_name[selected->type], string_get_cstr(full_path)); + + string_clear(full_path); + furi_record_close("loader"); +} + +static void archive_file_menu_callback(ArchiveMainView* main_view) { + furi_assert(main_view); + + ArchiveFile_t* selected = archive_get_current_file(main_view); + const char* path = archive_get_path(main_view); + const char* name = archive_get_name(main_view); + + uint8_t idx; + with_view_model( + main_view->view, (ArchiveMainViewModel * model) { + idx = model->menu_idx; + return true; + }); + + switch(idx) { + case 0: + if(is_known_app(selected->type)) { + archive_run_in_app(main_view, selected, false); + } + break; + case 1: + if(is_known_app(selected->type)) { + if(!archive_is_favorite(path, name)) { + archive_set_name(main_view, string_get_cstr(selected->name)); + archive_add_to_favorites(path, name); + } else { + // delete from favorites + archive_favorites_delete(path, name); + } + archive_close_file_menu(main_view); + } + break; + case 2: + // open rename view + if(is_known_app(selected->type)) { + main_view->callback(ArchiveBrowserEventRename, main_view->context); + } + break; + case 3: + // confirmation? + archive_delete_file(main_view, main_view->path, selected->name); + archive_close_file_menu(main_view); + break; + + default: + archive_close_file_menu(main_view); + break; + } + selected = NULL; +} + +static void archive_switch_dir(ArchiveMainView* main_view, const char* path) { + furi_assert(main_view); + furi_assert(path); + + string_set(main_view->path, path); + archive_get_filenames(main_view, archive_get_tab(main_view), string_get_cstr(main_view->path)); + update_offset(main_view); +} + +void archive_switch_tab(ArchiveMainView* main_view) { + furi_assert(main_view); + + with_view_model( + main_view->view, (ArchiveMainViewModel * model) { + model->idx = 0; + model->depth = 0; + return true; + }); + + archive_switch_dir(main_view, tab_default_paths[archive_get_tab(main_view)]); +} + +static void archive_enter_dir(ArchiveMainView* main_view, string_t name) { + furi_assert(main_view); + furi_assert(name); + + // update last index + with_view_model( + main_view->view, (ArchiveMainViewModel * model) { + model->last_idx[model->depth] = + CLAMP(model->idx, files_array_size(model->files) - 1, 0); + model->idx = 0; + model->depth = CLAMP(model->depth + 1, MAX_DEPTH, 0); + return true; + }); + + string_cat(main_view->path, "/"); + string_cat(main_view->path, main_view->name); + + archive_switch_dir(main_view, string_get_cstr(main_view->path)); +} + +static void archive_leave_dir(ArchiveMainView* main_view) { + furi_assert(main_view); + + char* last_char_ptr = strrchr(string_get_cstr(main_view->path), '/'); + + if(last_char_ptr) { + size_t pos = last_char_ptr - string_get_cstr(main_view->path); + string_left(main_view->path, pos); + } + + with_view_model( + main_view->view, (ArchiveMainViewModel * model) { + model->depth = CLAMP(model->depth - 1, MAX_DEPTH, 0); + model->idx = model->last_idx[model->depth]; + return true; + }); + + archive_switch_dir(main_view, string_get_cstr(main_view->path)); +} + +bool archive_view_input(InputEvent* event, void* context) { + furi_assert(event); + furi_assert(context); + + ArchiveMainView* main_view = context; + + BrowserActionEnum action; + with_view_model( + main_view->view, (ArchiveMainViewModel * model) { + action = model->action; + return true; + }); + + switch(action) { + case BrowserActionItemMenu: + + if(event->type == InputTypeShort) { + if(event->key == InputKeyUp || event->key == InputKeyDown) { + with_view_model( + main_view->view, (ArchiveMainViewModel * model) { + if(event->key == InputKeyUp) { + model->menu_idx = ((model->menu_idx - 1) + MENU_ITEMS) % MENU_ITEMS; + } else if(event->key == InputKeyDown) { + model->menu_idx = (model->menu_idx + 1) % MENU_ITEMS; + } + return true; + }); + } + + if(event->key == InputKeyOk) { + archive_file_menu_callback(main_view); + } else if(event->key == InputKeyBack) { + archive_close_file_menu(main_view); + } + } + break; + + case BrowserActionBrowse: + + if(event->type == InputTypeShort) { + if(event->key == InputKeyLeft) { + ArchiveTabEnum tab = archive_get_tab(main_view); + if(tab) { + archive_set_tab(main_view, CLAMP(tab - 1, ArchiveTabTotal, 0)); + archive_switch_tab(main_view); + return true; + } + } else if(event->key == InputKeyRight) { + ArchiveTabEnum tab = archive_get_tab(main_view); + + if(tab < ArchiveTabTotal - 1) { + archive_set_tab(main_view, CLAMP(tab + 1, ArchiveTabTotal - 1, 0)); + archive_switch_tab(main_view); + return true; + } + + } else if(event->key == InputKeyBack) { + if(!archive_get_depth(main_view)) { + main_view->callback(ArchiveBrowserEventExit, main_view->context); + } else { + archive_leave_dir(main_view); + } + + return true; + } + } + if(event->key == InputKeyUp || event->key == InputKeyDown) { + with_view_model( + main_view->view, (ArchiveMainViewModel * model) { + uint16_t num_elements = (uint16_t)files_array_size(model->files); + if((event->type == InputTypeShort || event->type == InputTypeRepeat)) { + if(event->key == InputKeyUp) { + model->idx = ((model->idx - 1) + num_elements) % num_elements; + } else if(event->key == InputKeyDown) { + model->idx = (model->idx + 1) % num_elements; + } + } + + return true; + }); + update_offset(main_view); + } + + if(event->key == InputKeyOk) { + ArchiveFile_t* selected = archive_get_current_file(main_view); + + if(selected) { + archive_set_name(main_view, string_get_cstr(selected->name)); + if(selected->type == ArchiveFileTypeFolder) { + if(event->type == InputTypeShort) { + archive_enter_dir(main_view, main_view->name); + } else if(event->type == InputTypeLong) { + archive_show_file_menu(main_view); + } + } else { + if(event->type == InputTypeShort) { + if(archive_get_tab(main_view) == ArchiveTabFavorites) { + if(is_known_app(selected->type)) { + archive_run_in_app(main_view, selected, true); + } + } else { + archive_show_file_menu(main_view); + } + } + } + } + } + break; + default: + break; + } + + return true; +} + +ArchiveMainView* main_view_alloc() { + ArchiveMainView* main_view = furi_alloc(sizeof(ArchiveMainView)); + main_view->view = view_alloc(); + view_allocate_model(main_view->view, ViewModelTypeLocking, sizeof(ArchiveMainViewModel)); + view_set_context(main_view->view, main_view); + view_set_draw_callback(main_view->view, (ViewDrawCallback)archive_view_render); + view_set_input_callback(main_view->view, archive_view_input); + + string_init(main_view->name); + string_init(main_view->path); + + with_view_model( + main_view->view, (ArchiveMainViewModel * model) { + files_array_init(model->files); + return true; + }); + + return main_view; +} + +void main_view_free(ArchiveMainView* main_view) { + furi_assert(main_view); + + with_view_model( + main_view->view, (ArchiveMainViewModel * model) { + files_array_clear(model->files); + return false; + }); + + string_clear(main_view->name); + string_clear(main_view->path); + + view_free(main_view->view); + free(main_view); +} diff --git a/applications/archive/views/archive_main_view.h b/applications/archive/views/archive_main_view.h new file mode 100644 index 00000000..b3d07879 --- /dev/null +++ b/applications/archive/views/archive_main_view.h @@ -0,0 +1,117 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include "../helpers/archive_files.h" +#include "../helpers/archive_favorites.h" + +#define MAX_LEN_PX 110 +#define MAX_NAME_LEN 255 +#define FRAME_HEIGHT 12 +#define MENU_ITEMS 4 +#define MAX_DEPTH 32 + +typedef enum { + ArchiveTabFavorites, + ArchiveTabLFRFID, + ArchiveTabSubGhz, + ArchiveTabNFC, + ArchiveTabIButton, + ArchiveTabIrda, + ArchiveTabBrowser, + ArchiveTabTotal, +} ArchiveTabEnum; + +static const char* known_ext[] = { + [ArchiveFileTypeIButton] = ".ibtn", + [ArchiveFileTypeNFC] = ".nfc", + [ArchiveFileTypeSubGhz] = ".sub", + [ArchiveFileTypeLFRFID] = ".rfid", + [ArchiveFileTypeIrda] = ".ir", +}; + +static inline const char* get_tab_ext(ArchiveTabEnum tab) { + switch(tab) { + case ArchiveTabIButton: + return known_ext[ArchiveFileTypeIButton]; + case ArchiveTabNFC: + return known_ext[ArchiveFileTypeNFC]; + case ArchiveTabSubGhz: + return known_ext[ArchiveFileTypeSubGhz]; + case ArchiveTabLFRFID: + return known_ext[ArchiveFileTypeLFRFID]; + case ArchiveTabIrda: + return known_ext[ArchiveFileTypeIrda]; + default: + return "*"; + } +} + +typedef enum { + ArchiveBrowserEventRename, + ArchiveBrowserEventExit, + ArchiveBrowserEventLeaveDir, +} ArchiveBrowserEvent; + +typedef struct ArchiveMainView ArchiveMainView; + +typedef void (*ArchiveMainViewCallback)(ArchiveBrowserEvent event, void* context); + +typedef enum { + BrowserActionBrowse, + BrowserActionItemMenu, + BrowserActionTotal, +} BrowserActionEnum; + +struct ArchiveMainView { + View* view; + ArchiveMainViewCallback callback; + void* context; + + string_t name; + string_t path; +}; + +typedef struct { + ArchiveTabEnum tab_idx; + BrowserActionEnum action; + files_array_t files; + + uint8_t depth; + uint8_t menu_idx; + + uint16_t idx; + uint16_t last_idx[MAX_DEPTH]; + uint16_t list_offset; + +} ArchiveMainViewModel; + +void archive_browser_set_callback( + ArchiveMainView* main_view, + ArchiveMainViewCallback callback, + void* context); + +View* archive_main_get_view(ArchiveMainView* main_view); + +ArchiveMainView* main_view_alloc(); +void main_view_free(ArchiveMainView* main_view); + +void archive_file_array_remove_selected(ArchiveMainView* main_view); +void archive_file_array_clean(ArchiveMainView* main_view); + +void archive_view_add_item(ArchiveMainView* main_view, FileInfo* file_info, const char* name); +void archive_browser_update(ArchiveMainView* main_view); + +size_t archive_file_array_size(ArchiveMainView* main_view); +ArchiveFile_t* archive_get_current_file(ArchiveMainView* main_view); +const char* archive_get_path(ArchiveMainView* main_view); +const char* archive_get_name(ArchiveMainView* main_view); +void archive_set_name(ArchiveMainView* main_view, const char* name); + +static inline bool is_known_app(ArchiveFileTypeEnum type) { + return (type != ArchiveFileTypeFolder && type != ArchiveFileTypeUnknown); +}