diff --git a/applications/main/archive/archive.c b/applications/main/archive/archive.c index bbe532c8c..6eedc8ea0 100644 --- a/applications/main/archive/archive.c +++ b/applications/main/archive/archive.c @@ -1,66 +1,90 @@ #include "archive_i.h" -#include "m-string.h" -bool archive_custom_event_callback(void* context, uint32_t event) { +static bool archive_custom_event_callback(void* context, uint32_t event) { furi_assert(context); - ArchiveApp* archive = (ArchiveApp*)context; + ArchiveApp* archive = context; return scene_manager_handle_custom_event(archive->scene_manager, event); } -bool archive_back_event_callback(void* context) { +static bool archive_back_event_callback(void* context) { furi_assert(context); - ArchiveApp* archive = (ArchiveApp*)context; + ArchiveApp* archive = context; return scene_manager_handle_back_event(archive->scene_manager); } -ArchiveApp* archive_alloc() { +static void archive_tick_event_callback(void* context) { + furi_assert(context); + ArchiveApp* archive = context; + scene_manager_handle_tick_event(archive->scene_manager); +} + +static ArchiveApp* archive_alloc() { ArchiveApp* archive = malloc(sizeof(ArchiveApp)); - archive->gui = furi_record_open(RECORD_GUI); - archive->text_input = text_input_alloc(); string_init(archive->fav_move_str); - archive->view_dispatcher = view_dispatcher_alloc(); archive->scene_manager = scene_manager_alloc(&archive_scene_handlers, archive); + archive->view_dispatcher = view_dispatcher_alloc(); - view_dispatcher_enable_queue(archive->view_dispatcher); - view_dispatcher_attach_to_gui( - archive->view_dispatcher, archive->gui, ViewDispatcherTypeFullscreen); + archive->gui = furi_record_open(RECORD_GUI); - 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); + ViewDispatcher* view_dispatcher = archive->view_dispatcher; + view_dispatcher_enable_queue(view_dispatcher); + view_dispatcher_set_event_callback_context(view_dispatcher, archive); + view_dispatcher_set_custom_event_callback(view_dispatcher, archive_custom_event_callback); + view_dispatcher_set_navigation_event_callback(view_dispatcher, archive_back_event_callback); + view_dispatcher_set_tick_event_callback(view_dispatcher, archive_tick_event_callback, 100); - archive->browser = browser_alloc(); + archive->dialogs = furi_record_open(RECORD_DIALOGS); + archive->text_input = text_input_alloc(); view_dispatcher_add_view( - archive->view_dispatcher, ArchiveViewBrowser, archive_browser_get_view(archive->browser)); - - view_dispatcher_add_view( - archive->view_dispatcher, ArchiveViewTextInput, text_input_get_view(archive->text_input)); + view_dispatcher, ArchiveViewTextInput, text_input_get_view(archive->text_input)); archive->widget = widget_alloc(); view_dispatcher_add_view( archive->view_dispatcher, ArchiveViewWidget, widget_get_view(archive->widget)); + archive->view_stack = view_stack_alloc(); + view_dispatcher_add_view( + view_dispatcher, ArchiveViewStack, view_stack_get_view(archive->view_stack)); + + archive->browser = browser_alloc(); + view_dispatcher_add_view( + archive->view_dispatcher, ArchiveViewBrowser, archive_browser_get_view(archive->browser)); + + // Loading + archive->loading = loading_alloc(); + return archive; } void archive_free(ArchiveApp* archive) { furi_assert(archive); + ViewDispatcher* view_dispatcher = archive->view_dispatcher; - view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewBrowser); - view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewTextInput); - view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewWidget); + // Loading + loading_free(archive->loading); + + view_dispatcher_remove_view(view_dispatcher, ArchiveViewTextInput); + text_input_free(archive->text_input); + + view_dispatcher_remove_view(view_dispatcher, ArchiveViewWidget); widget_free(archive->widget); + + view_dispatcher_remove_view(view_dispatcher, ArchiveViewStack); + view_stack_free(archive->view_stack); + + view_dispatcher_remove_view(view_dispatcher, ArchiveViewBrowser); + view_dispatcher_free(archive->view_dispatcher); scene_manager_free(archive->scene_manager); + browser_free(archive->browser); string_clear(archive->fav_move_str); - text_input_free(archive->text_input); + furi_record_close(RECORD_DIALOGS); + archive->dialogs = NULL; furi_record_close(RECORD_GUI); archive->gui = NULL; @@ -68,12 +92,31 @@ void archive_free(ArchiveApp* archive) { free(archive); } +void archive_show_loading_popup(ArchiveApp* context, bool show) { + TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME); + ViewStack* view_stack = context->view_stack; + Loading* loading = context->loading; + + if(show) { + // Raise timer priority so that animations can play + vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1); + view_stack_add_view(view_stack, loading_get_view(loading)); + } else { + view_stack_remove_view(view_stack, loading_get_view(loading)); + // Restore default timer priority + vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY); + } +} + int32_t archive_app(void* p) { UNUSED(p); + ArchiveApp* archive = archive_alloc(); + view_dispatcher_attach_to_gui( + archive->view_dispatcher, archive->gui, ViewDispatcherTypeFullscreen); scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneBrowser); view_dispatcher_run(archive->view_dispatcher); - archive_free(archive); + archive_free(archive); return 0; } diff --git a/applications/main/archive/archive_i.h b/applications/main/archive/archive_i.h index 865e837dc..7f4ff5a7b 100644 --- a/applications/main/archive/archive_i.h +++ b/applications/main/archive/archive_i.h @@ -8,6 +8,9 @@ #include #include #include +#include +#include +#include #include #include "views/archive_browser_view.h" @@ -18,17 +21,24 @@ typedef enum { ArchiveViewTextInput, ArchiveViewWidget, ArchiveViewTotal, + ArchiveViewStack, } ArchiveViewEnum; struct ArchiveApp { Gui* gui; ViewDispatcher* view_dispatcher; + ViewStack* view_stack; SceneManager* scene_manager; ArchiveBrowserView* browser; TextInput* text_input; Widget* widget; + DialogsApp* dialogs; + Loading* loading; FuriPubSubSubscription* loader_stop_subscription; + string_t fav_move_str; char text_store[MAX_NAME_LEN]; char file_extension[MAX_EXT_LEN + 1]; }; + +void archive_show_loading_popup(ArchiveApp* context, bool show); \ No newline at end of file diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index b08e58983..01a1c888c 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -377,6 +377,7 @@ void archive_show_file_menu(ArchiveBrowserView* browser, bool show) { if(archive_is_item_in_array(model, model->item_idx)) { model->menu = true; model->menu_idx = 0; + menu_array_reset(model->context_menu); ArchiveFile_t* selected = files_array_get(model->files, model->item_idx - model->array_offset); selected->fav = archive_is_favorite("%s", string_get_cstr(selected->path)); diff --git a/applications/main/archive/helpers/archive_files.c b/applications/main/archive/helpers/archive_files.c index 9f8b4ee1b..d53c74da4 100644 --- a/applications/main/archive/helpers/archive_files.c +++ b/applications/main/archive/helpers/archive_files.c @@ -109,3 +109,38 @@ void archive_delete_file(void* context, const char* format, ...) { string_clear(filename); } + +FS_Error archive_rename_file_or_dir(void* context, const char* src_path, const char* dst_path) { + furi_assert(context); + + FURI_LOG_I(TAG, "Rename from %s to %s", src_path, dst_path); + + ArchiveBrowserView* browser = context; + Storage* fs_api = furi_record_open(RECORD_STORAGE); + + FileInfo fileinfo; + storage_common_stat(fs_api, src_path, &fileinfo); + + FS_Error error = FSE_OK; + + if(!path_contains_only_ascii(dst_path)) { + error = FSE_INVALID_NAME; + } else { + error = storage_common_rename(fs_api, src_path, dst_path); + } + furi_record_close(RECORD_STORAGE); + + if(archive_is_favorite("%s", src_path)) { + archive_favorites_rename(src_path, dst_path); + } + + if(error == FSE_OK || error == FSE_EXIST) { + FURI_LOG_I(TAG, "Rename from %s to %s is DONE", src_path, dst_path); + archive_refresh_dir(browser); + } else { + FURI_LOG_E( + TAG, "Rename failed: %s, Code: %d", filesystem_api_error_get_desc(error), error); + } + + return error; +} diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h index 84b7e24a6..d4df327a9 100644 --- a/applications/main/archive/helpers/archive_files.h +++ b/applications/main/archive/helpers/archive_files.h @@ -3,6 +3,7 @@ #include #include #include +#include "toolbox/path.h" typedef enum { ArchiveFileTypeIButton, @@ -62,3 +63,4 @@ void archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder bool archive_get_items(void* context, const char* path); void archive_file_append(const char* path, const char* format, ...); void archive_delete_file(void* context, const char* format, ...); +FS_Error archive_rename_file_or_dir(void* context, const char* src_path, const char* dst_path); diff --git a/applications/main/archive/helpers/archive_menu.h b/applications/main/archive/helpers/archive_menu.h new file mode 100644 index 000000000..5df6a1ca2 --- /dev/null +++ b/applications/main/archive/helpers/archive_menu.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include + +typedef struct { + string_t text; + uint32_t event; +} ArchiveContextMenuItem_t; + +static void ArchiveContextMenuItem_t_init(ArchiveContextMenuItem_t* obj) { + string_init(obj->text); + obj->event = 0; // ArchiveBrowserEventFileMenuNone +} + +static void ArchiveContextMenuItem_t_init_set( + ArchiveContextMenuItem_t* obj, + const ArchiveContextMenuItem_t* src) { + string_init_set(obj->text, src->text); + obj->event = src->event; +} + +static void ArchiveContextMenuItem_t_set( + ArchiveContextMenuItem_t* obj, + const ArchiveContextMenuItem_t* src) { + string_init_set(obj->text, src->text); + obj->event = src->event; +} + +static void ArchiveContextMenuItem_t_clear(ArchiveContextMenuItem_t* obj) { + string_clear(obj->text); +} + +ARRAY_DEF( + menu_array, + ArchiveContextMenuItem_t, + (INIT(API_2(ArchiveContextMenuItem_t_init)), + SET(API_6(ArchiveContextMenuItem_t_set)), + INIT_SET(API_6(ArchiveContextMenuItem_t_init_set)), + CLEAR(API_2(ArchiveContextMenuItem_t_clear)))) + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +// Using in applications/archive/views/archive_browser_view.c +static void archive_menu_add_item( + ArchiveContextMenuItem_t* obj, + string_t text, + uint32_t event) { + string_init_move(obj->text, text); + obj->event = event; +} +#pragma GCC diagnostic pop \ No newline at end of file diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index e22ac792f..3e7ac231d 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -132,7 +132,9 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { case ArchiveBrowserEventFileMenuRename: if(favorites) { browser->callback(ArchiveBrowserEventEnterFavMove, browser->context); - } else if((archive_is_known_app(selected->type)) && (selected->is_app == false)) { + //} else if((archive_is_known_app(selected->type)) && (selected->is_app == false)) { + } else { + // Added ability to rename files and folders archive_show_file_menu(browser, false); scene_manager_set_scene_state( archive->scene_manager, ArchiveAppSceneBrowser, SCENE_STATE_NEED_REFRESH); @@ -140,6 +142,13 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { } consumed = true; break; + case ArchiveBrowserEventFileMenuInfo: + archive_show_file_menu(browser, false); + scene_manager_set_scene_state( + archive->scene_manager, ArchiveAppSceneBrowser, SCENE_STATE_DEFAULT); + scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneInfo); + consumed = true; + break; case ArchiveBrowserEventFileMenuDelete: if(archive_get_tab(browser) != ArchiveTabFavorites) { scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneDelete); diff --git a/applications/main/archive/scenes/archive_scene_config.h b/applications/main/archive/scenes/archive_scene_config.h index 4145f43da..6dda7a20d 100644 --- a/applications/main/archive/scenes/archive_scene_config.h +++ b/applications/main/archive/scenes/archive_scene_config.h @@ -1,3 +1,4 @@ ADD_SCENE(archive, browser, Browser) ADD_SCENE(archive, rename, Rename) ADD_SCENE(archive, delete, Delete) +ADD_SCENE(archive, info, Info) diff --git a/applications/main/archive/scenes/archive_scene_delete.c b/applications/main/archive/scenes/archive_scene_delete.c index d882fd066..cca84fea3 100644 --- a/applications/main/archive/scenes/archive_scene_delete.c +++ b/applications/main/archive/scenes/archive_scene_delete.c @@ -1,10 +1,6 @@ #include "../archive_i.h" -#include "../helpers/archive_favorites.h" -#include "../helpers/archive_files.h" #include "../helpers/archive_apps.h" #include "../helpers/archive_browser.h" -#include "toolbox/path.h" -#include "m-string.h" #define SCENE_DELETE_CUSTOM_EVENT (0UL) #define MAX_TEXT_INPUT_LEN 22 diff --git a/applications/main/archive/scenes/archive_scene_info.c b/applications/main/archive/scenes/archive_scene_info.c new file mode 100644 index 000000000..b6fae4bca --- /dev/null +++ b/applications/main/archive/scenes/archive_scene_info.c @@ -0,0 +1,92 @@ +#include "../archive_i.h" +#include "../helpers/archive_browser.h" + +#define TAG "Archive" + +void archive_scene_info_widget_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + ArchiveApp* app = (ArchiveApp*)context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void archive_scene_info_on_enter(void* context) { + furi_assert(context); + ArchiveApp* instance = context; + + widget_add_button_element( + instance->widget, GuiButtonTypeLeft, "Back", archive_scene_info_widget_callback, instance); + + string_t filename; + string_t dirname; + string_t str_size; + string_init(filename); + string_init(dirname); + string_init(str_size); + + ArchiveFile_t* current = archive_get_current_file(instance->browser); + char file_info_message[128]; + Storage* fs_api = furi_record_open(RECORD_STORAGE); + + // Filename + path_extract_filename(current->path, filename, false); + snprintf(file_info_message, sizeof(file_info_message), "\e#%s\e#", string_get_cstr(filename)); + widget_add_text_box_element( + instance->widget, 0, 0, 128, 25, AlignLeft, AlignCenter, file_info_message, false); + + // Directory path + path_extract_dirname(string_get_cstr(current->path), dirname); + string_replace_str(dirname, STORAGE_ANY_PATH_PREFIX, ""); + + // File size + FileInfo fileinfo; + storage_common_stat(fs_api, string_get_cstr(current->path), &fileinfo); + if(fileinfo.size <= 1024) { + string_printf(str_size, "%d", fileinfo.size); + snprintf( + file_info_message, + sizeof(file_info_message), + "Size: \e#%s\e# bytes\n%s", + string_get_cstr(str_size), + string_get_cstr(dirname)); + } else { + string_printf(str_size, "%d", fileinfo.size / 1024); + snprintf( + file_info_message, + sizeof(file_info_message), + "Size: \e#%s\e# Kb.\n%s", + string_get_cstr(str_size), + string_get_cstr(dirname)); + } + widget_add_text_box_element( + instance->widget, 0, 25, 128, 25, AlignLeft, AlignCenter, file_info_message, false); + + // This one to return and cursor select this file + path_extract_filename_no_ext(string_get_cstr(current->path), filename); + strlcpy(instance->text_store, string_get_cstr(filename), MAX_NAME_LEN); + + string_clear(filename); + string_clear(dirname); + string_clear(str_size); + + view_dispatcher_switch_to_view(instance->view_dispatcher, ArchiveViewWidget); +} + +bool archive_scene_info_on_event(void* context, SceneManagerEvent event) { + furi_assert(context); + ArchiveApp* app = (ArchiveApp*)context; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_next_scene(app->scene_manager, ArchiveAppSceneBrowser); + return true; + } + return false; +} + +void archive_scene_info_on_exit(void* context) { + furi_assert(context); + ArchiveApp* app = (ArchiveApp*)context; + + widget_reset(app->widget); +} diff --git a/applications/main/archive/scenes/archive_scene_rename.c b/applications/main/archive/scenes/archive_scene_rename.c index 293fa89af..ca15ee88e 100644 --- a/applications/main/archive/scenes/archive_scene_rename.c +++ b/applications/main/archive/scenes/archive_scene_rename.c @@ -4,6 +4,9 @@ #include "../helpers/archive_browser.h" #include "archive/views/archive_browser_view.h" #include "toolbox/path.h" +#include + +#define TAG "Archive" #define SCENE_RENAME_CUSTOM_EVENT (0UL) #define MAX_TEXT_INPUT_LEN 22 @@ -14,63 +17,87 @@ void archive_scene_rename_text_input_callback(void* context) { } void archive_scene_rename_on_enter(void* context) { - ArchiveApp* archive = (ArchiveApp*)context; + ArchiveApp* archive = context; TextInput* text_input = archive->text_input; ArchiveFile_t* current = archive_get_current_file(archive->browser); - string_t filename; - string_init(filename); - path_extract_filename(current->path, filename, true); - strlcpy(archive->text_store, string_get_cstr(filename), MAX_NAME_LEN); + string_t path_name; + string_init(path_name); - path_extract_extension(current->path, archive->file_extension, MAX_EXT_LEN); + if(current->type == ArchiveFileTypeFolder) { + path_extract_basename(string_get_cstr(current->path), path_name); + strlcpy(archive->text_store, string_get_cstr(path_name), MAX_NAME_LEN); + text_input_set_header_text(text_input, "Rename directory:"); + } else /*if(current->type != ArchiveFileTypeUnknown) */ { + path_extract_filename(current->path, path_name, true); + strlcpy(archive->text_store, string_get_cstr(path_name), MAX_NAME_LEN); - text_input_set_header_text(text_input, "Rename:"); + path_extract_extension(current->path, archive->file_extension, MAX_EXT_LEN); + text_input_set_header_text(text_input, "Rename file:"); + } /*else { + path_extract_filename(current->path, path_name, false); + strlcpy(archive->text_store, string_get_cstr(path_name), MAX_NAME_LEN); + text_input_set_header_text(text_input, "Rename unknown file:"); + }*/ text_input_set_result_callback( text_input, archive_scene_rename_text_input_callback, - archive, + context, archive->text_store, MAX_TEXT_INPUT_LEN, false); - ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( - string_get_cstr(archive->browser->path), archive->file_extension, ""); - text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); - - string_clear(filename); + string_clear(path_name); view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput); } bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) { - ArchiveApp* archive = (ArchiveApp*)context; + ArchiveApp* archive = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == SCENE_RENAME_CUSTOM_EVENT) { - Storage* fs_api = furi_record_open(RECORD_STORAGE); - const char* path_src = archive_get_name(archive->browser); ArchiveFile_t* file = archive_get_current_file(archive->browser); string_t path_dst; string_init(path_dst); - path_extract_dirname(path_src, path_dst); - string_cat_printf(path_dst, "/%s%s", archive->text_store, known_ext[file->type]); - storage_common_rename(fs_api, path_src, string_get_cstr(path_dst)); - furi_record_close(RECORD_STORAGE); - - if(file->fav) { - archive_favorites_rename(path_src, string_get_cstr(path_dst)); + if(file->type == ArchiveFileTypeFolder) { + // Rename folder/dir + path_extract_dirname(path_src, path_dst); + string_cat_printf(path_dst, "/%s", archive->text_store); + } else if(file->type != ArchiveFileTypeUnknown) { + // Rename known type + path_extract_dirname(path_src, path_dst); + string_cat_printf(path_dst, "/%s%s", archive->text_store, known_ext[file->type]); + } else { + // Rename unknown type + path_extract_dirname(path_src, path_dst); + string_cat_printf(path_dst, "/%s%s", archive->text_store, archive->file_extension); } + // Long time process if this is directory + view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewStack); + archive_show_loading_popup(archive, true); + FS_Error error = + archive_rename_file_or_dir(archive->browser, path_src, string_get_cstr(path_dst)); + archive_show_loading_popup(archive, false); + archive_show_file_menu(archive->browser, false); string_clear(path_dst); - scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneBrowser); + if(error == FSE_OK || error == FSE_EXIST) { + scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneBrowser); + } else { + string_t dialog_msg; + string_init(dialog_msg); + string_cat_printf(dialog_msg, "Cannot rename\nCode: %d", error); + dialog_message_show_storage_error(archive->dialogs, string_get_cstr(dialog_msg)); + string_clear(dialog_msg); + } consumed = true; } } @@ -78,12 +105,6 @@ bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) { } void archive_scene_rename_on_exit(void* context) { - ArchiveApp* archive = (ArchiveApp*)context; - - // Clear view - void* validator_context = text_input_get_validator_callback_context(archive->text_input); - text_input_set_validator(archive->text_input, NULL, NULL); - validator_is_file_free(validator_context); - + ArchiveApp* archive = context; text_input_reset(archive->text_input); } diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index ddd6637db..5fa21d150 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -5,6 +5,8 @@ #include "archive_browser_view.h" #include "../helpers/archive_browser.h" +#define TAG "Archive" + static const char* ArchiveTabNames[] = { [ArchiveTabFavorites] = "Favorites", [ArchiveTabIButton] = "iButton", @@ -42,43 +44,142 @@ void archive_browser_set_callback( } static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* 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); + if(menu_array_size(model->context_menu) == 0) { + // Context menu is empty, init array + string_t item_run; + string_t item_pin; + string_t item_info; + string_t item_rename; + string_t item_delete; - string_t menu[MENU_ITEMS]; + string_init_set_str(item_run, "Run In App"); + string_init_set_str(item_pin, "Pin"); + string_init_set_str(item_info, "Info"); + string_init_set_str(item_rename, "Rename"); + string_init_set_str(item_delete, "Delete"); - 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"); + // Need init context menu + ArchiveFile_t* selected = + files_array_get(model->files, model->item_idx - model->array_offset); - ArchiveFile_t* selected = files_array_get(model->files, model->item_idx - model->array_offset); - - if((selected->fav) || (model->tab_idx == ArchiveTabFavorites)) { - string_set_str(menu[1], "Unpin"); - } - - if(!archive_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 == ArchiveTabFavorites) { - string_set_str(menu[2], "Move"); - string_set_str(menu[3], "---"); - } else if(selected->is_app) { - string_set_str(menu[2], "---"); + if((selected->fav) || (model->tab_idx == ArchiveTabFavorites)) { + string_set_str(item_pin, "Unpin"); } + + if(selected->type == ArchiveFileTypeFolder) { + //FURI_LOG_D(TAG, "Directory type"); + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_rename, + ArchiveBrowserEventFileMenuRename); + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_delete, + ArchiveBrowserEventFileMenuDelete); + } else if(!archive_is_known_app(selected->type)) { + //FURI_LOG_D(TAG, "Unknown type"); + + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_info, + ArchiveBrowserEventFileMenuInfo); + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_rename, + ArchiveBrowserEventFileMenuRename); + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_delete, + ArchiveBrowserEventFileMenuDelete); + } else if(model->tab_idx == ArchiveTabFavorites) { + //FURI_LOG_D(TAG, "ArchiveTabFavorites"); + + string_set_str(item_rename, "Move"); + + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_run, + ArchiveBrowserEventFileMenuRun); + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_pin, + ArchiveBrowserEventFileMenuPin); + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_rename, + ArchiveBrowserEventFileMenuRename); + } else if(selected->is_app) { + //FURI_LOG_D(TAG, "3 types"); + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_run, + ArchiveBrowserEventFileMenuRun); + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_info, + ArchiveBrowserEventFileMenuInfo); + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_pin, + ArchiveBrowserEventFileMenuPin); + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_delete, + ArchiveBrowserEventFileMenuDelete); + } else { + //FURI_LOG_D(TAG, "All menu"); + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_run, + ArchiveBrowserEventFileMenuRun); + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_pin, + ArchiveBrowserEventFileMenuPin); + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_info, + ArchiveBrowserEventFileMenuInfo); + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_rename, + ArchiveBrowserEventFileMenuRename); + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_delete, + ArchiveBrowserEventFileMenuDelete); + } + + string_clear(item_run); + string_clear(item_pin); + string_clear(item_info); + string_clear(item_rename); + string_clear(item_delete); + } /*else { + FURI_LOG_D(TAG, "menu_array_size already set: %d", menu_array_size(model->context_menu)); + }*/ + size_t size_menu = menu_array_size(model->context_menu); + const uint8_t menu_height = 48; + const uint8_t line_height = 10; + + canvas_set_color(canvas, ColorWhite); + uint8_t calc_height = menu_height - ((MENU_ITEMS - size_menu) * line_height); + canvas_draw_box(canvas, 71, 11, 57, calc_height + 4); + canvas_set_color(canvas, ColorBlack); + elements_slightly_rounded_frame(canvas, 70, 12, 58, calc_height + 4); + + /*FURI_LOG_D( + TAG, + "size_menu: %d, calc_height: %d, menu_idx: %d", + size_menu, + calc_height, + model->menu_idx);*/ + for(size_t i = 0; i < size_menu; i++) { + ArchiveContextMenuItem_t* current = menu_array_get(model->context_menu, i); + canvas_draw_str(canvas, 82, 21 + i * line_height, string_get_cstr(current->text)); } - 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); + canvas_draw_icon(canvas, 74, 14 + model->menu_idx * line_height, &I_ButtonRight_4x7); } static void archive_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar, bool moving) { @@ -250,32 +351,33 @@ static bool archive_view_input(InputEvent* event, void* context) { }); if(in_menu) { - if(event->type == InputTypeShort) { - if(event->key == InputKeyUp || event->key == InputKeyDown) { - with_view_model( - browser->view, (ArchiveBrowserViewModel * 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) { - uint8_t idx; - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - idx = model->menu_idx; - return false; - }); - browser->callback(file_menu_actions[idx], browser->context); - } else if(event->key == InputKeyBack) { - browser->callback(ArchiveBrowserEventFileMenuClose, browser->context); - } + if(event->type != InputTypeShort) { + return true; // RETURN + } + if(event->key == InputKeyUp || event->key == InputKeyDown) { + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + size_t size_menu = menu_array_size(model->context_menu); + if(event->key == InputKeyUp) { + model->menu_idx = ((model->menu_idx - 1) + size_menu) % size_menu; + } else if(event->key == InputKeyDown) { + model->menu_idx = (model->menu_idx + 1) % size_menu; + } + return true; + }); + } else if(event->key == InputKeyOk) { + uint32_t idx; + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + ArchiveContextMenuItem_t* current = + menu_array_get(model->context_menu, model->menu_idx); + idx = current->event; + return false; + }); + browser->callback(idx, browser->context); + } else if(event->key == InputKeyBack) { + browser->callback(ArchiveBrowserEventFileMenuClose, browser->context); } - } else { if(event->type == InputTypeShort) { if(event->key == InputKeyLeft || event->key == InputKeyRight) { @@ -366,6 +468,7 @@ ArchiveBrowserView* browser_alloc() { with_view_model( browser->view, (ArchiveBrowserViewModel * model) { files_array_init(model->files); + menu_array_init(model->context_menu); model->tab_idx = TAB_DEFAULT; return true; }); @@ -383,6 +486,7 @@ void browser_free(ArchiveBrowserView* browser) { with_view_model( browser->view, (ArchiveBrowserViewModel * model) { files_array_clear(model->files); + menu_array_clear(model->context_menu); return false; }); @@ -390,4 +494,4 @@ void browser_free(ArchiveBrowserView* browser) { view_free(browser->view); free(browser); -} +} \ No newline at end of file diff --git a/applications/main/archive/views/archive_browser_view.h b/applications/main/archive/views/archive_browser_view.h index 5c649c389..e20bd62d3 100644 --- a/applications/main/archive/views/archive_browser_view.h +++ b/applications/main/archive/views/archive_browser_view.h @@ -7,6 +7,7 @@ #include #include #include "../helpers/archive_files.h" +#include "../helpers/archive_menu.h" #include "../helpers/archive_favorites.h" #include "gui/modules/file_browser_worker.h" @@ -14,7 +15,7 @@ #define MAX_NAME_LEN 255 #define MAX_EXT_LEN 6 #define FRAME_HEIGHT 12 -#define MENU_ITEMS 4u +#define MENU_ITEMS 5u #define MOVE_OFFSET 5u typedef enum { @@ -31,12 +32,14 @@ typedef enum { } ArchiveTabEnum; typedef enum { + ArchiveBrowserEventFileMenuNone, ArchiveBrowserEventFileMenuOpen, - ArchiveBrowserEventFileMenuClose, ArchiveBrowserEventFileMenuRun, ArchiveBrowserEventFileMenuPin, ArchiveBrowserEventFileMenuRename, ArchiveBrowserEventFileMenuDelete, + ArchiveBrowserEventFileMenuInfo, + ArchiveBrowserEventFileMenuClose, ArchiveBrowserEventEnterDir, @@ -54,13 +57,6 @@ typedef enum { ArchiveBrowserEventExit, } ArchiveBrowserEvent; -static const uint8_t file_menu_actions[MENU_ITEMS] = { - [0] = ArchiveBrowserEventFileMenuRun, - [1] = ArchiveBrowserEventFileMenuPin, - [2] = ArchiveBrowserEventFileMenuRename, - [3] = ArchiveBrowserEventFileMenuDelete, -}; - typedef struct ArchiveBrowserView ArchiveBrowserView; typedef void (*ArchiveBrowserViewCallback)(ArchiveBrowserEvent event, void* context); @@ -88,6 +84,8 @@ typedef struct { uint8_t menu_idx; bool menu; + menu_array_t context_menu; + bool move_fav; bool list_loading; bool folder_loading; @@ -106,4 +104,5 @@ void archive_browser_set_callback( View* archive_browser_get_view(ArchiveBrowserView* browser); ArchiveBrowserView* browser_alloc(); + void browser_free(ArchiveBrowserView* browser);