diff --git a/applications/main/archive/archive.c b/applications/main/archive/archive.c index 588087fd2..4a5a8b39e 100644 --- a/applications/main/archive/archive.c +++ b/applications/main/archive/archive.c @@ -22,6 +22,7 @@ static ArchiveApp* archive_alloc() { ArchiveApp* archive = malloc(sizeof(ArchiveApp)); archive->fav_move_str = furi_string_alloc(); + archive->dst_path = furi_string_alloc(); archive->scene_manager = scene_manager_alloc(&archive_scene_handlers, archive); archive->view_dispatcher = view_dispatcher_alloc(); @@ -82,6 +83,7 @@ void archive_free(ArchiveApp* archive) { browser_free(archive->browser); furi_string_free(archive->fav_move_str); + furi_string_free(archive->dst_path); furi_record_close(RECORD_DIALOGS); archive->dialogs = NULL; diff --git a/applications/main/archive/archive_i.h b/applications/main/archive/archive_i.h index b96145827..7d612687c 100644 --- a/applications/main/archive/archive_i.h +++ b/applications/main/archive/archive_i.h @@ -37,6 +37,7 @@ struct ArchiveApp { FuriPubSubSubscription* loader_stop_subscription; FuriString* fav_move_str; + FuriString* dst_path; char text_store[MAX_NAME_LEN]; char file_extension[MAX_EXT_LEN + 1]; }; diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 43888cb4d..facf00a3a 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -419,10 +419,12 @@ void archive_show_file_menu(ArchiveBrowserView* browser, bool show) { ArchiveBrowserViewModel * model, { if(show) { + model->menu = true; + model->menu_idx = 0; + menu_array_reset(model->context_menu); if(archive_is_item_in_array(model, model->item_idx)) { - model->menu = true; - model->menu_idx = 0; - menu_array_reset(model->context_menu); + model->menu_file_manage = false; + ArchiveFile_t* selected = files_array_get(model->files, model->item_idx - model->array_offset); selected->fav = @@ -430,7 +432,10 @@ void archive_show_file_menu(ArchiveBrowserView* browser, bool show) { } } else { model->menu = false; + model->menu_file_manage = false; model->menu_idx = 0; + model->menu_can_switch = false; + menu_array_reset(model->context_menu); } }, true); diff --git a/applications/main/archive/helpers/archive_files.c b/applications/main/archive/helpers/archive_files.c index e12576cb2..6e654d5a2 100644 --- a/applications/main/archive/helpers/archive_files.c +++ b/applications/main/archive/helpers/archive_files.c @@ -121,14 +121,19 @@ void archive_delete_file(void* context, const char* format, ...) { furi_string_free(filename); } -FS_Error archive_rename_file_or_dir(void* context, const char* src_path, const char* dst_path) { +FS_Error archive_rename_copy_file_or_dir( + void* context, + const char* src_path, + const char* dst_path, + bool copy) { furi_assert(context); - FURI_LOG_I(TAG, "Rename from %s to %s", src_path, dst_path); + FURI_LOG_I(TAG, "%s from %s to %s", copy ? "Copy" : "Rename/Move", src_path, dst_path); - ArchiveBrowserView* browser = context; Storage* fs_api = furi_record_open(RECORD_STORAGE); + FuriString* temp_str = furi_string_alloc_set(dst_path); + FileInfo fileinfo; storage_common_stat(fs_api, src_path, &fileinfo); @@ -136,22 +141,60 @@ FS_Error archive_rename_file_or_dir(void* context, const char* src_path, const c if(!path_contains_only_ascii(dst_path)) { error = FSE_INVALID_NAME; + } else if(!copy && !strcmp(src_path, dst_path)) { + error = FSE_EXIST; } else { - error = storage_common_rename(fs_api, src_path, dst_path); + if(storage_common_exists(fs_api, dst_path)) { + FuriString* filename = furi_string_alloc(); + char* file_ext = malloc(MAX_EXT_LEN + 1); + strncpy(file_ext, "", MAX_EXT_LEN); + + path_extract_filename(temp_str, filename, true); + path_extract_extension(temp_str, file_ext, MAX_EXT_LEN); + + path_extract_dirname(dst_path, temp_str); + + storage_get_next_filename( + fs_api, + furi_string_get_cstr(temp_str), + furi_string_get_cstr(filename), + file_ext, + filename, + 255); + + furi_string_cat_printf(temp_str, "/%s%s", furi_string_get_cstr(filename), file_ext); + + dst_path = furi_string_get_cstr(temp_str); + + furi_string_free(filename); + free(file_ext); + } + + if(copy) { + error = storage_common_copy(fs_api, src_path, dst_path); + } else { + error = storage_common_rename(fs_api, src_path, dst_path); + } } furi_record_close(RECORD_STORAGE); - if(archive_is_favorite("%s", src_path)) { + if(!copy && 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); + if(error == FSE_OK) { + FURI_LOG_I( + TAG, "%s from %s to %s is DONE", copy ? "Copy" : "Rename/Move", src_path, dst_path); } else { FURI_LOG_E( - TAG, "Rename failed: %s, Code: %d", filesystem_api_error_get_desc(error), error); + TAG, + "%s failed: %s, Code: %d", + copy ? "Copy" : "Rename/Move", + filesystem_api_error_get_desc(error), + error); } + furi_string_free(temp_str); + return error; } diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h index 989198fec..43d9b27d6 100644 --- a/applications/main/archive/helpers/archive_files.h +++ b/applications/main/archive/helpers/archive_files.h @@ -113,4 +113,8 @@ void archive_file_append(const char* path, const char* format, ...) _ATTRIBUTE((__format__(__printf__, 2, 3))); void archive_delete_file(void* context, const char* format, ...) _ATTRIBUTE((__format__(__printf__, 2, 3))); -FS_Error archive_rename_file_or_dir(void* context, const char* src_path, const char* dst_path); +FS_Error archive_rename_copy_file_or_dir( + void* context, + const char* src_path, + const char* dst_path, + bool copy); diff --git a/applications/main/archive/helpers/archive_menu.h b/applications/main/archive/helpers/archive_menu.h index 4a85b851d..bd5200b21 100644 --- a/applications/main/archive/helpers/archive_menu.h +++ b/applications/main/archive/helpers/archive_menu.h @@ -43,7 +43,7 @@ ARRAY_DEF( #pragma GCC diagnostic ignored "-Wunused-function" // Using in applications/archive/views/archive_browser_view.c static void - archive_menu_add_item(ArchiveContextMenuItem_t* obj, FuriString* text, uint32_t event) { + archive_menu_add_item(ArchiveContextMenuItem_t* obj, const char* text, uint32_t event) { obj->text = furi_string_alloc_set(text); obj->event = event; } diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index e512d31d5..1041e2e62 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -156,6 +156,77 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneRename); } consumed = true; + break; + case ArchiveBrowserEventFileMenuNewDir: + archive_show_file_menu(browser, false); + if(!favorites) { + scene_manager_set_scene_state( + archive->scene_manager, ArchiveAppSceneBrowser, SCENE_STATE_NEED_REFRESH); + scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneNewDir); + } + consumed = true; + break; + case ArchiveBrowserEventFileMenuCut: + case ArchiveBrowserEventFileMenuCopy: + archive_show_file_menu(browser, false); + furi_string_set(archive->fav_move_str, selected->path); + + archive_browser_clipboard_set_mode( + browser, + (event.event == ArchiveBrowserEventFileMenuCut) ? CLIPBOARD_MODE_CUT : + CLIPBOARD_MODE_COPY); + consumed = true; + break; + case ArchiveBrowserEventFileMenuPaste_Cut: + case ArchiveBrowserEventFileMenuPaste_Copy: + archive_show_file_menu(browser, false); + + FuriString* path_src = archive->fav_move_str; + FuriString* path_dst = furi_string_alloc(); + FuriString* base = furi_string_alloc(); + + const bool copy = (event.event == ArchiveBrowserEventFileMenuPaste_Copy); + + path_extract_basename(furi_string_get_cstr(path_src), base); + path_concat(furi_string_get_cstr(browser->path), furi_string_get_cstr(base), path_dst); + + if(path_src && path_dst) { + view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewStack); + archive_show_loading_popup(archive, true); + FS_Error error = archive_rename_copy_file_or_dir( + archive->browser, + furi_string_get_cstr(path_src), + furi_string_get_cstr(path_dst), + copy); + archive_show_loading_popup(archive, false); + + if(error != FSE_OK) { + FuriString* dialog_msg; + dialog_msg = furi_string_alloc(); + furi_string_cat_printf( + dialog_msg, + "Cannot %s:\n%s", + copy ? "copy" : "move", + storage_error_get_desc(error)); + dialog_message_show_storage_error( + archive->dialogs, furi_string_get_cstr(dialog_msg)); + furi_string_free(dialog_msg); + } else { + ArchiveFile_t* current = archive_get_current_file(archive->browser); + if(current != NULL) furi_string_set(current->path, path_dst); + view_dispatcher_send_custom_event( + archive->view_dispatcher, ArchiveBrowserEventListRefresh); + } + + view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewBrowser); + } + + furi_string_free(base); + furi_string_free(path_dst); + + archive_browser_clipboard_reset(browser); + furi_string_reset(path_src); + break; case ArchiveBrowserEventFileMenuInfo: archive_show_file_menu(browser, false); diff --git a/applications/main/archive/scenes/archive_scene_config.h b/applications/main/archive/scenes/archive_scene_config.h index d16c6f1a6..2f3ebe771 100644 --- a/applications/main/archive/scenes/archive_scene_config.h +++ b/applications/main/archive/scenes/archive_scene_config.h @@ -3,3 +3,4 @@ ADD_SCENE(archive, rename, Rename) ADD_SCENE(archive, delete, Delete) ADD_SCENE(archive, info, Info) ADD_SCENE(archive, show, Show) +ADD_SCENE(archive, new_dir, NewDir) \ No newline at end of file diff --git a/applications/main/archive/scenes/archive_scene_new_dir.c b/applications/main/archive/scenes/archive_scene_new_dir.c new file mode 100644 index 000000000..6b95b30f5 --- /dev/null +++ b/applications/main/archive/scenes/archive_scene_new_dir.c @@ -0,0 +1,85 @@ +#include "../archive_i.h" +#include "../helpers/archive_favorites.h" +#include "../helpers/archive_files.h" +#include "../helpers/archive_browser.h" +#include "archive/views/archive_browser_view.h" +#include "toolbox/path.h" +#include + +#define TAG "Archive" + +#define SCENE_NEW_DIR_CUSTOM_EVENT (0UL) + +void archive_scene_new_dir_text_input_callback(void* context) { + furi_assert(context); + ArchiveApp* archive = context; + view_dispatcher_send_custom_event(archive->view_dispatcher, SCENE_NEW_DIR_CUSTOM_EVENT); +} + +void archive_scene_new_dir_on_enter(void* context) { + ArchiveApp* archive = context; + + TextInput* text_input = archive->text_input; + + archive->text_store[0] = '\0'; + text_input_set_header_text(text_input, "New directory:"); + + text_input_set_result_callback( + text_input, + archive_scene_new_dir_text_input_callback, + context, + archive->text_store, + MAX_NAME_LEN, + false); + + view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput); +} + +bool archive_scene_new_dir_on_event(void* context, SceneManagerEvent event) { + ArchiveApp* archive = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SCENE_NEW_DIR_CUSTOM_EVENT) { + FuriString* path_dst = furi_string_alloc(); + + path_concat( + furi_string_get_cstr(archive->browser->path), archive->text_store, path_dst); + + view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewStack); + archive_show_loading_popup(archive, true); + FS_Error error; + if(!path_contains_only_ascii(furi_string_get_cstr(path_dst))) { + error = FSE_INVALID_NAME; + } else { + Storage* fs_api = furi_record_open(RECORD_STORAGE); + error = storage_common_mkdir(fs_api, furi_string_get_cstr(path_dst)); + furi_record_close(RECORD_STORAGE); + } + archive_show_loading_popup(archive, false); + + if(error != FSE_OK) { + FuriString* dialog_msg; + dialog_msg = furi_string_alloc(); + furi_string_cat_printf( + dialog_msg, "Cannot mkdir:\n%s", storage_error_get_desc(error)); + dialog_message_show_storage_error( + archive->dialogs, furi_string_get_cstr(dialog_msg)); + furi_string_free(dialog_msg); + } else { + ArchiveFile_t* current = archive_get_current_file(archive->browser); + if(current != NULL) furi_string_set(current->path, path_dst); + } + + furi_string_free(path_dst); + scene_manager_previous_scene(archive->scene_manager); + consumed = true; + } + } + return consumed; +} + +void archive_scene_new_dir_on_exit(void* context) { + ArchiveApp* archive = context; + text_input_reset(archive->text_input); +} diff --git a/applications/main/archive/scenes/archive_scene_rename.c b/applications/main/archive/scenes/archive_scene_rename.c index e6c728f03..21b4e05d8 100644 --- a/applications/main/archive/scenes/archive_scene_rename.c +++ b/applications/main/archive/scenes/archive_scene_rename.c @@ -98,8 +98,8 @@ bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) { // 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, furi_string_get_cstr(path_dst)); + FS_Error error = archive_rename_copy_file_or_dir( + archive->browser, path_src, furi_string_get_cstr(path_dst), false); archive_show_loading_popup(archive, false); archive_show_file_menu(archive->browser, false); diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index fd7de727d..e298583ec 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -50,64 +50,100 @@ void archive_browser_set_callback( browser->context = context; } +static void contex_menu_filemanager_init(ArchiveBrowserViewModel* model) { + if(model->item_cnt > 0) { + if(model->clipboard_mode == CLIPBOARD_MODE_OFF) { + archive_menu_add_item( + menu_array_push_raw(model->context_menu), "Cut", ArchiveBrowserEventFileMenuCut); + archive_menu_add_item( + menu_array_push_raw(model->context_menu), "Copy", ArchiveBrowserEventFileMenuCopy); + } else if(model->clipboard_mode == CLIPBOARD_MODE_CUT) { + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + "Paste", + ArchiveBrowserEventFileMenuPaste_Cut); + } else if(model->clipboard_mode == CLIPBOARD_MODE_COPY) { + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + "Paste", + ArchiveBrowserEventFileMenuPaste_Copy); + } + + archive_menu_add_item( + menu_array_push_raw(model->context_menu), "NewDir", ArchiveBrowserEventFileMenuNewDir); + archive_menu_add_item( + menu_array_push_raw(model->context_menu), "Rename", ArchiveBrowserEventFileMenuRename); + archive_menu_add_item( + menu_array_push_raw(model->context_menu), "Delete", ArchiveBrowserEventFileMenuDelete); + } else { + if(model->clipboard_mode == CLIPBOARD_MODE_CUT) { + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + "Paste", + ArchiveBrowserEventFileMenuPaste_Cut); + } else if(model->clipboard_mode == CLIPBOARD_MODE_COPY) { + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + "Paste", + ArchiveBrowserEventFileMenuPaste_Copy); + } + archive_menu_add_item( + menu_array_push_raw(model->context_menu), "NewDir", ArchiveBrowserEventFileMenuNewDir); + } +} + static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { if(menu_array_size(model->context_menu) == 0) { // Context menu is empty, init array - FuriString* item_run = furi_string_alloc_set("Run In App"); - FuriString* item_pin = furi_string_alloc_set("Pin"); - FuriString* item_info = furi_string_alloc_set("Info"); - FuriString* item_show = furi_string_alloc_set("Show"); - FuriString* item_rename = furi_string_alloc_set("Rename"); - FuriString* item_delete = furi_string_alloc_set("Delete"); + const char* item_pin = "Pin"; // Need init context menu ArchiveFile_t* selected = files_array_get(model->files, model->item_idx - model->array_offset); if((selected->fav) || (model->tab_idx == ArchiveTabFavorites)) { - furi_string_set(item_pin, "Unpin"); + item_pin = "Unpin"; } if(selected->type == ArchiveFileTypeFolder) { + // Folder //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); - if(selected->is_text_file) { + // { Copy/Cut, Paste } NewDir, Rename, Delete + model->menu_file_manage = true; + model->menu_can_switch = false; + + contex_menu_filemanager_init(model); + } else if(!archive_is_known_app(selected->type)) { + // UnKnown app type + //FURI_LOG_D(TAG, "Unknown type"); + model->menu_can_switch = true; + if(model->menu_file_manage) { + // { Copy/Cut, Paste } NewDir, Rename, Delete + contex_menu_filemanager_init(model); + } else { + // Info, [Show], archive_menu_add_item( menu_array_push_raw(model->context_menu), - item_show, - ArchiveBrowserEventFileMenuShow); + "Info", + ArchiveBrowserEventFileMenuInfo); + if(selected->is_text_file) { + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + "Show", + ArchiveBrowserEventFileMenuShow); + } } - 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) { + // Favorites tab + // Run, Unpin, [Show], Move + //FURI_LOG_D(TAG, "ArchiveTabFavorites"); - furi_string_set(item_rename, "Move"); - + model->menu_can_switch = false; archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_run, - ArchiveBrowserEventFileMenuRun); + menu_array_push_raw(model->context_menu), "Run", ArchiveBrowserEventFileMenuRun); archive_menu_add_item( menu_array_push_raw(model->context_menu), item_pin, @@ -115,98 +151,109 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { if(selected->type <= ArchiveFileTypeBadUsb) { archive_menu_add_item( menu_array_push_raw(model->context_menu), - item_show, + "Show", ArchiveBrowserEventFileMenuShow); } archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_rename, - ArchiveBrowserEventFileMenuRename); + menu_array_push_raw(model->context_menu), "Move", ArchiveBrowserEventEnterFavMove); } else if(selected->is_app) { + // Only U2F? + // Run, Info, [Show], Pin, Delete + model->menu_file_manage = false; + //FURI_LOG_D(TAG, "3 types"); archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_run, - ArchiveBrowserEventFileMenuRun); + menu_array_push_raw(model->context_menu), "Run", ArchiveBrowserEventFileMenuRun); archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_info, - ArchiveBrowserEventFileMenuInfo); + menu_array_push_raw(model->context_menu), "Info", ArchiveBrowserEventFileMenuInfo); if(selected->type <= ArchiveFileTypeBadUsb) { archive_menu_add_item( menu_array_push_raw(model->context_menu), - item_show, + "Show", ArchiveBrowserEventFileMenuShow); } 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, + "Delete", ArchiveBrowserEventFileMenuDelete); } else { + // Other //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); - if(selected->type <= ArchiveFileTypeBadUsb) { + + model->menu_can_switch = true; + if(model->menu_file_manage) { + // { Copy/Cut, Paste } NewDir, Rename, Delete + contex_menu_filemanager_init(model); + } else { + // Run, Pin, Info, [Show] + archive_menu_add_item( menu_array_push_raw(model->context_menu), - item_show, - ArchiveBrowserEventFileMenuShow); + "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), + "Info", + ArchiveBrowserEventFileMenuInfo); + if(selected->type <= ArchiveFileTypeBadUsb) { + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + "Show", + ArchiveBrowserEventFileMenuShow); + } } - 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); } - - furi_string_free(item_run); - furi_string_free(item_pin); - furi_string_free(item_info); - furi_string_free(item_show); - furi_string_free(item_rename); - furi_string_free(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_y = 0; 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, 1, 57, calc_height + 4); + uint8_t calc_height = menu_height - ((MENU_ITEMS - size_menu - 1) * line_height); + canvas_draw_box(canvas, 71, menu_y, 57, calc_height + 4 + 2); canvas_set_color(canvas, ColorBlack); - elements_slightly_rounded_frame(canvas, 70, 2, 58, calc_height + 4); + elements_slightly_rounded_frame(canvas, 70, menu_y, 58, calc_height + 5 + 1); + canvas_draw_line(canvas, 70, menu_y + 1 + line_height, 128, menu_y + 1 + line_height); /*FURI_LOG_D( TAG, "size_menu: %d, calc_height: %d, menu_idx: %d", size_menu, calc_height, model->menu_idx);*/ + if(model->menu_file_manage) { + canvas_draw_str(canvas, 82, menu_y + line_height - 1, "Manage"); + } else { + canvas_draw_str(canvas, 82, menu_y + line_height - 1, "Actions"); + } + if(model->menu_can_switch) { + canvas_draw_icon(canvas, 74, menu_y + 2, &I_ButtonLeft_4x7); + canvas_draw_icon(canvas, 120, menu_y + 2, &I_ButtonRight_4x7); + } for(size_t i = 0; i < size_menu; i++) { ArchiveContextMenuItem_t* current = menu_array_get(model->context_menu, i); - canvas_draw_str(canvas, 82, 11 + i * line_height, furi_string_get_cstr(current->text)); + canvas_draw_str( + canvas, + 82, + menu_y + 1 + line_height + (i + 1) * line_height, + furi_string_get_cstr(current->text)); } - canvas_draw_icon(canvas, 74, 4 + model->menu_idx * line_height, &I_ButtonRight_4x7); + canvas_draw_icon( + canvas, 74, menu_y + 4 + (model->menu_idx + 1) * line_height, &I_ButtonRight_4x7); } static void archive_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar, bool moving) { @@ -320,6 +367,7 @@ static void archive_render_status_bar(Canvas* canvas, ArchiveBrowserViewModel* m furi_assert(model); const char* tab_name = ArchiveTabNames[model->tab_idx]; + bool clip = model->clipboard_mode != CLIPBOARD_MODE_OFF; canvas_draw_icon(canvas, 0, 0, &I_Background_128x11); @@ -349,6 +397,26 @@ static void archive_render_status_bar(Canvas* canvas, ArchiveBrowserViewModel* m canvas_draw_dot(canvas, 50, 0); canvas_draw_dot(canvas, 127, 0); + if(clip) { + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 69, 0, 24, 13); + + canvas_set_color(canvas, ColorBlack); + canvas_draw_rframe(canvas, 69, 0, 25, 13, 1); + canvas_draw_line(canvas, 92, 1, 92, 11); + canvas_draw_line(canvas, 70, 11, 92, 11); + canvas_draw_str_aligned( + canvas, + 81, + 9, + AlignCenter, + AlignBottom, + (model->clipboard_mode == CLIPBOARD_MODE_COPY) ? "Copy" : "Cut"); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_dot(canvas, 93, 0); + } + canvas_set_color(canvas, ColorBlack); } @@ -364,6 +432,9 @@ static void archive_view_render(Canvas* canvas, void* mdl) { } else { canvas_draw_str_aligned( canvas, GUI_DISPLAY_WIDTH / 2, 40, AlignCenter, AlignCenter, "Empty"); + if(model->menu) { + render_item_menu(canvas, model); + } } } @@ -392,6 +463,57 @@ static bool is_file_list_load_required(ArchiveBrowserViewModel* model) { return false; } +static inline void + archive_view_menu_input_processing(ArchiveBrowserView* browser, InputEvent* event) { + // only InputShort type + 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; + } + }, + true); + } else if(event->key == InputKeyLeft || event->key == InputKeyRight) { + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { + ArchiveFile_t* selected = + files_array_get(model->files, model->item_idx - model->array_offset); + + if(selected->type != ArchiveFileTypeFolder && + model->tab_idx != ArchiveTabFavorites) { + model->menu_file_manage = !model->menu_file_manage; + model->menu_idx = 0; + menu_array_reset(model->context_menu); + selected->fav = + archive_is_favorite("%s", furi_string_get_cstr(selected->path)); + } + }, + 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; + }, + false); + browser->callback(idx, browser->context); + } else if(event->key == InputKeyBack) { + browser->callback(ArchiveBrowserEventFileMenuClose, browser->context); + } +} + static bool archive_view_input(InputEvent* event, void* context) { furi_assert(event); furi_assert(context); @@ -418,39 +540,18 @@ static bool archive_view_input(InputEvent* event, void* 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; - } - }, - 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; - }, - false); - browser->callback(idx, browser->context); - } else if(event->key == InputKeyBack) { - browser->callback(ArchiveBrowserEventFileMenuClose, browser->context); - } + archive_view_menu_input_processing(browser, event); } else { if(event->type == InputTypeShort) { if(event->key == InputKeyLeft || event->key == InputKeyRight) { if(move_fav_mode) return false; + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { model->clipboard_mode = CLIPBOARD_MODE_OFF; }, + false); archive_switch_tab(browser, event->key); + } else if(event->key == InputKeyBack) { if(move_fav_mode) { browser->callback(ArchiveBrowserEventExitFavMove, browser->context); @@ -542,6 +643,8 @@ static bool archive_view_input(InputEvent* event, void* context) { browser->callback(ArchiveBrowserEventFileMenuOpen, browser->context); } } + } else if(event->type == InputTypeLong) { + browser->callback(ArchiveBrowserEventFileMenuOpen, browser->context); } } } @@ -599,6 +702,7 @@ ArchiveBrowserView* browser_alloc() { files_array_init(model->files); menu_array_init(model->context_menu); model->tab_idx = TAB_DEFAULT; + model->clipboard_mode = CLIPBOARD_MODE_OFF; }, true); @@ -627,4 +731,21 @@ void browser_free(ArchiveBrowserView* browser) { view_free(browser->view); free(browser); +} + +void archive_browser_clipboard_set_mode(ArchiveBrowserView* browser, uint8_t mode) { + furi_assert(browser); + + with_view_model( + browser->view, ArchiveBrowserViewModel * model, { model->clipboard_mode = mode; }, true); +} + +void archive_browser_clipboard_reset(ArchiveBrowserView* browser) { + furi_assert(browser); + + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { model->clipboard_mode = CLIPBOARD_MODE_OFF; }, + true); } \ 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 d3a57f755..5541ba9fe 100644 --- a/applications/main/archive/views/archive_browser_view.h +++ b/applications/main/archive/views/archive_browser_view.h @@ -21,6 +21,10 @@ #define MENU_ITEMS 5u #define MOVE_OFFSET 5u +#define CLIPBOARD_MODE_OFF (0U) +#define CLIPBOARD_MODE_CUT (1U) +#define CLIPBOARD_MODE_COPY (2U) + typedef enum { ArchiveTabFavorites, ArchiveTabSubGhz, @@ -43,6 +47,11 @@ typedef enum { ArchiveBrowserEventFileMenuRun, ArchiveBrowserEventFileMenuPin, ArchiveBrowserEventFileMenuRename, + ArchiveBrowserEventFileMenuNewDir, + ArchiveBrowserEventFileMenuCut, + ArchiveBrowserEventFileMenuCopy, + ArchiveBrowserEventFileMenuPaste_Cut, + ArchiveBrowserEventFileMenuPaste_Copy, ArchiveBrowserEventFileMenuDelete, ArchiveBrowserEventFileMenuInfo, ArchiveBrowserEventFileMenuShow, @@ -93,6 +102,9 @@ typedef struct { uint8_t menu_idx; bool menu; menu_array_t context_menu; + bool menu_file_manage; + bool menu_can_switch; + uint8_t clipboard_mode; bool move_fav; bool list_loading; @@ -117,3 +129,7 @@ View* archive_browser_get_view(ArchiveBrowserView* browser); ArchiveBrowserView* browser_alloc(); void browser_free(ArchiveBrowserView* browser); + +void archive_browser_clipboard_set_mode(ArchiveBrowserView* browser, uint8_t mode); + +void archive_browser_clipboard_reset(ArchiveBrowserView* browser);