From 5bc7a18a046c86a17d4dc33d24c01be43875ac0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20Offenh=C3=A4user?= Date: Mon, 27 Dec 2021 18:16:37 +0100 Subject: [PATCH] FileManager: Add "Create Archive" action This option will appear when you select one or more files or directories. It will then ask the user to enter a name for the new archive (or use the current directories' name if left empty) and create it under that name in the currently opened directory. Note that only .zip files are currently supported. --- Userland/Applications/FileManager/main.cpp | 84 ++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/Userland/Applications/FileManager/main.cpp b/Userland/Applications/FileManager/main.cpp index 27326c9c5a..d560112b19 100644 --- a/Userland/Applications/FileManager/main.cpp +++ b/Userland/Applications/FileManager/main.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -58,6 +59,7 @@ static ErrorOr run_in_windowed_mode(String initial_location, String entry_f static void do_copy(Vector const& selected_file_paths, FileOperation file_operation); static void do_paste(String const& target_directory, GUI::Window* window); static void do_create_link(Vector const& selected_file_paths, GUI::Window* window); +static void do_create_archive(Vector const& selected_file_paths, GUI::Window* window); static void do_unzip_archive(Vector const& selected_file_paths, GUI::Window* window); static void show_properties(String const& container_dir_path, String const& path, Vector const& selected, GUI::Window* window); static bool add_launch_handler_actions_to_menu(RefPtr& menu, DirectoryView const& directory_view, String const& full_path, RefPtr& default_action, NonnullRefPtrVector& current_file_launch_handlers); @@ -187,6 +189,58 @@ void do_create_link(Vector const& selected_file_paths, GUI::Window* wind } } +void do_create_archive(Vector const& selected_file_paths, GUI::Window* window) +{ + String archive_name; + if (GUI::InputBox::show(window, archive_name, "Enter name:", "Create Archive") != GUI::InputBox::ExecOK) + return; + + auto output_directory_path = LexicalPath(selected_file_paths.first()); + + StringBuilder path_builder; + path_builder.append(output_directory_path.dirname()); + path_builder.append("/"); + if (archive_name.is_empty()) { + path_builder.append(output_directory_path.parent().basename()); + path_builder.append(".zip"); + } else { + path_builder.append(archive_name); + if (!archive_name.ends_with(".zip")) + path_builder.append(".zip"); + } + auto output_path = path_builder.build(); + + pid_t zip_pid = fork(); + if (zip_pid < 0) { + perror("fork"); + VERIFY_NOT_REACHED(); + } + + if (!zip_pid) { + Vector relative_paths; + Vector arg_list; + arg_list.append("/bin/zip"); + arg_list.append("-r"); + arg_list.append("-f"); + arg_list.append(output_path.characters()); + for (auto const& path : selected_file_paths) { + relative_paths.append(LexicalPath::relative_path(path, output_directory_path.dirname())); + arg_list.append(relative_paths.last().characters()); + } + arg_list.append(nullptr); + int rc = execvp("/bin/zip", const_cast(arg_list.data())); + if (rc < 0) { + perror("execvp"); + _exit(1); + } + } else { + int status; + int rc = waitpid(zip_pid, &status, 0); + if (rc < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) + GUI::MessageBox::show(window, "Could not create archive", "Archive Error", GUI::MessageBox::Type::Error); + } +} + void do_unzip_archive(Vector const& selected_file_paths, GUI::Window* window) { String archive_file_path = selected_file_paths.first(); @@ -307,6 +361,19 @@ ErrorOr run_in_desktop_mode() window); copy_action->set_enabled(false); + auto create_archive_action + = GUI::Action::create( + "Create &Archive", + Gfx::Bitmap::try_load_from_file("/res/icons/16x16/filetype-archive.png").release_value_but_fixme_should_propagate_errors(), + [&](GUI::Action const&) { + auto paths = directory_view->selected_file_paths(); + if (paths.is_empty()) + return; + + do_create_archive(paths, directory_view->window()); + }, + window); + auto unzip_archive_action = GUI::Action::create( "E&xtract Here", @@ -420,6 +487,7 @@ ErrorOr run_in_desktop_mode() file_context_menu->add_action(paste_action); file_context_menu->add_action(directory_view->delete_action()); file_context_menu->add_action(directory_view->rename_action()); + file_context_menu->add_action(create_archive_action); file_context_menu->add_separator(); if (node.full_path().ends_with(".zip", AK::CaseSensitivity::CaseInsensitive)) { @@ -732,6 +800,20 @@ ErrorOr run_in_windowed_mode(String initial_location, String entry_focused_ }, window); + auto create_archive_action + = GUI::Action::create( + "Create &Archive", + Gfx::Bitmap::try_load_from_file("/res/icons/16x16/filetype-archive.png").release_value_but_fixme_should_propagate_errors(), + [&](GUI::Action const&) { + auto paths = directory_view->selected_file_paths(); + if (paths.is_empty()) + return; + + do_create_archive(paths, directory_view->window()); + refresh_tree_view(); + }, + window); + auto unzip_archive_action = GUI::Action::create( "E&xtract Here", @@ -1049,6 +1131,7 @@ ErrorOr run_in_windowed_mode(String initial_location, String entry_focused_ TRY(directory_context_menu->try_add_action(directory_view->delete_action())); TRY(directory_context_menu->try_add_action(directory_view->rename_action())); TRY(directory_context_menu->try_add_action(shortcut_action)); + TRY(directory_context_menu->try_add_action(create_archive_action)); TRY(directory_context_menu->try_add_separator()); TRY(directory_context_menu->try_add_action(properties_action)); @@ -1098,6 +1181,7 @@ ErrorOr run_in_windowed_mode(String initial_location, String entry_focused_ file_context_menu->add_action(directory_view->delete_action()); file_context_menu->add_action(directory_view->rename_action()); file_context_menu->add_action(shortcut_action); + file_context_menu->add_action(create_archive_action); file_context_menu->add_separator(); if (node.full_path().ends_with(".zip", AK::CaseSensitivity::CaseInsensitive)) {