Serenity Operating System
at master 148 lines 4.7 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2021-2022, Sam Atkins <atkinssj@serenityos.org> 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include "FileUtils.h" 9#include "FileOperationProgressWidget.h" 10#include <AK/LexicalPath.h> 11#include <LibCore/DeprecatedFile.h> 12#include <LibCore/MimeData.h> 13#include <LibCore/System.h> 14#include <LibGUI/Event.h> 15#include <LibGUI/MessageBox.h> 16#include <unistd.h> 17 18namespace FileManager { 19 20HashTable<NonnullRefPtr<GUI::Window>> file_operation_windows; 21 22void delete_paths(Vector<DeprecatedString> const& paths, bool should_confirm, GUI::Window* parent_window) 23{ 24 DeprecatedString message; 25 if (paths.size() == 1) { 26 message = DeprecatedString::formatted("Are you sure you want to delete {}?", LexicalPath::basename(paths[0])); 27 } else { 28 message = DeprecatedString::formatted("Are you sure you want to delete {} files?", paths.size()); 29 } 30 31 if (should_confirm) { 32 auto result = GUI::MessageBox::show(parent_window, 33 message, 34 "Confirm deletion"sv, 35 GUI::MessageBox::Type::Warning, 36 GUI::MessageBox::InputType::OKCancel); 37 if (result == GUI::MessageBox::ExecResult::Cancel) 38 return; 39 } 40 41 if (run_file_operation(FileOperation::Delete, paths, {}, parent_window).is_error()) 42 _exit(1); 43} 44 45ErrorOr<void> run_file_operation(FileOperation operation, Vector<DeprecatedString> const& sources, DeprecatedString const& destination, GUI::Window* parent_window) 46{ 47 auto pipe_fds = TRY(Core::System::pipe2(0)); 48 49 pid_t child_pid = TRY(Core::System::fork()); 50 51 if (!child_pid) { 52 TRY(Core::System::close(pipe_fds[0])); 53 TRY(Core::System::dup2(pipe_fds[1], STDOUT_FILENO)); 54 55 Vector<StringView> file_operation_args; 56 file_operation_args.append("/bin/FileOperation"sv); 57 58 switch (operation) { 59 case FileOperation::Copy: 60 file_operation_args.append("Copy"sv); 61 break; 62 case FileOperation::Move: 63 file_operation_args.append("Move"sv); 64 break; 65 case FileOperation::Delete: 66 file_operation_args.append("Delete"sv); 67 break; 68 default: 69 VERIFY_NOT_REACHED(); 70 } 71 72 for (auto& source : sources) 73 file_operation_args.append(source.view()); 74 75 if (operation != FileOperation::Delete) 76 file_operation_args.append(destination.view()); 77 78 TRY(Core::System::exec(file_operation_args.first(), file_operation_args, Core::System::SearchInPath::Yes)); 79 VERIFY_NOT_REACHED(); 80 } else { 81 TRY(Core::System::close(pipe_fds[1])); 82 } 83 84 auto window = TRY(GUI::Window::try_create()); 85 TRY(file_operation_windows.try_set(window)); 86 87 switch (operation) { 88 case FileOperation::Copy: 89 window->set_title("Copying Files..."); 90 break; 91 case FileOperation::Move: 92 window->set_title("Moving Files..."); 93 break; 94 case FileOperation::Delete: 95 window->set_title("Deleting Files..."); 96 break; 97 default: 98 VERIFY_NOT_REACHED(); 99 } 100 101 auto pipe_input_file = TRY(Core::File::adopt_fd(pipe_fds[0], Core::File::OpenMode::Read)); 102 auto buffered_pipe = TRY(Core::BufferedFile::create(move(pipe_input_file))); 103 104 (void)TRY(window->set_main_widget<FileOperationProgressWidget>(operation, move(buffered_pipe), pipe_fds[0])); 105 window->resize(320, 190); 106 if (parent_window) 107 window->center_within(*parent_window); 108 window->show(); 109 110 return {}; 111} 112 113ErrorOr<bool> handle_drop(GUI::DropEvent const& event, DeprecatedString const& destination, GUI::Window* window) 114{ 115 bool has_accepted_drop = false; 116 117 if (!event.mime_data().has_urls()) 118 return has_accepted_drop; 119 auto const urls = event.mime_data().urls(); 120 if (urls.is_empty()) { 121 dbgln("No files to drop"); 122 return has_accepted_drop; 123 } 124 125 auto const target = LexicalPath::canonicalized_path(destination); 126 127 if (!Core::DeprecatedFile::is_directory(target)) 128 return has_accepted_drop; 129 130 Vector<DeprecatedString> paths_to_copy; 131 for (auto& url_to_copy : urls) { 132 if (!url_to_copy.is_valid() || url_to_copy.path() == target) 133 continue; 134 auto new_path = DeprecatedString::formatted("{}/{}", target, LexicalPath::basename(url_to_copy.path())); 135 if (url_to_copy.path() == new_path) 136 continue; 137 138 paths_to_copy.append(url_to_copy.path()); 139 has_accepted_drop = true; 140 } 141 142 if (!paths_to_copy.is_empty()) 143 TRY(run_file_operation(FileOperation::Copy, paths_to_copy, target, window)); 144 145 return has_accepted_drop; 146} 147 148}