diff options
Diffstat (limited to 'Userland/Applications')
5 files changed, 292 insertions, 6 deletions
diff --git a/Userland/Applications/FileManager/CMakeLists.txt b/Userland/Applications/FileManager/CMakeLists.txt index 39149a23b9..2ee5c116ba 100644 --- a/Userland/Applications/FileManager/CMakeLists.txt +++ b/Userland/Applications/FileManager/CMakeLists.txt @@ -1,9 +1,12 @@ compile_gml(FileManagerWindow.gml FileManagerWindowGML.h file_manager_window_gml) +compile_gml(FileOperationProgress.gml FileOperationProgressGML.h file_operation_progress_gml) set(SOURCES DesktopWidget.cpp DirectoryView.cpp FileManagerWindowGML.h + FileOperationProgress.gml + FileOperationProgressWidget.cpp FileUtils.cpp main.cpp PropertiesWindow.cpp diff --git a/Userland/Applications/FileManager/DirectoryView.cpp b/Userland/Applications/FileManager/DirectoryView.cpp index b6544fc63a..a13e268847 100644 --- a/Userland/Applications/FileManager/DirectoryView.cpp +++ b/Userland/Applications/FileManager/DirectoryView.cpp @@ -25,6 +25,7 @@ */ #include "DirectoryView.h" +#include "FileOperationProgressWidget.h" #include "FileUtils.h" #include <AK/LexicalPath.h> #include <AK/NumberFormat.h> @@ -45,6 +46,29 @@ namespace FileManager { +enum class FileOperation { + Copy, +}; + +static HashTable<RefPtr<GUI::Window>> file_operation_windows; + +static void run_file_operation([[maybe_unused]] FileOperation operation, const String& source, const String& destination, GUI::Window* parent_window) +{ + // FIXME: Don't use popen() like this, very string injection friendly.. + FILE* helper_pipe = popen(String::formatted("/bin/FileOperation Copy {} {}", source, LexicalPath(destination).dirname()).characters(), "r"); + VERIFY(helper_pipe); + + auto window = GUI::Window::construct(); + file_operation_windows.set(window); + + window->set_title("Copying Files..."); + window->set_main_widget<FileOperationProgressWidget>(helper_pipe); + window->resize(320, 200); + if (parent_window) + window->center_within(*parent_window); + window->show(); +} + NonnullRefPtr<GUI::Action> LauncherHandler::create_launch_action(Function<void(const LauncherHandler&)> launch_handler) { auto icon = GUI::FileIconProvider::icon_for_executable(details().executable).bitmap_for_size(16); @@ -578,12 +602,8 @@ void DirectoryView::handle_drop(const GUI::ModelIndex& index, const GUI::DropEve if (url_to_copy.path() == new_path) continue; - if (auto result = Core::File::copy_file_or_directory(new_path, url_to_copy.path()); result.is_error()) { - auto error_message = String::formatted("Could not copy {} into {}: {}", url_to_copy.to_string(), new_path, result.error().error_code); - GUI::MessageBox::show(window(), error_message, "File Manager", GUI::MessageBox::Type::Error); - } else { - had_accepted_drop = true; - } + run_file_operation(FileOperation::Copy, url_to_copy.path(), new_path, window()); + had_accepted_drop = true; } if (had_accepted_drop && on_accepted_drop) on_accepted_drop(); diff --git a/Userland/Applications/FileManager/FileOperationProgress.gml b/Userland/Applications/FileManager/FileOperationProgress.gml new file mode 100644 index 0000000000..039845a0c4 --- /dev/null +++ b/Userland/Applications/FileManager/FileOperationProgress.gml @@ -0,0 +1,89 @@ +@GUI::Widget { + fill_with_background_color: true + + layout: @GUI::VerticalBoxLayout { + margins: [4, 4, 4, 4] + } + + @GUI::Label { + text: "Copying files..." + text_alignment: "CenterLeft" + font_weight: "Bold" + fixed_height: 32 + } + + @GUI::HorizontalSeparator { + fixed_height: 2 + } + + @GUI::Widget { + fixed_height: 32 + + layout: @GUI::HorizontalBoxLayout { + } + + @GUI::Label { + text: "Copying: " + font_weight: "Bold" + text_alignment: "CenterLeft" + fixed_width: 80 + } + + @GUI::Label { + name: "current_file_label" + text: "Placeholder" + text_alignment: "CenterLeft" + } + } + + @GUI::ProgressBar { + fixed_height: 22 + name: "current_file_progress_bar" + min: 0 + } + + @GUI::Widget { + fixed_height: 32 + + layout: @GUI::HorizontalBoxLayout { + } + + @GUI::Label { + text: "Overall progress: " + font_weight: "Bold" + text_alignment: "CenterLeft" + fixed_width: 120 + } + + @GUI::Label { + name: "overall_progress_label" + text: "Placeholder" + text_alignment: "CenterLeft" + } + } + + @GUI::ProgressBar { + fixed_height: 22 + name: "overall_progress_bar" + min: 0 + } + + @GUI::Widget { + fixed_height: 2 + } + + @GUI::Widget { + shrink_to_fit: true + layout: @GUI::HorizontalBoxLayout { + } + + @GUI::Widget { + } + + @GUI::Button { + text: "Cancel" + name: "button" + fixed_width: 80 + } + } +} diff --git a/Userland/Applications/FileManager/FileOperationProgressWidget.cpp b/Userland/Applications/FileManager/FileOperationProgressWidget.cpp new file mode 100644 index 0000000000..25b758a88c --- /dev/null +++ b/Userland/Applications/FileManager/FileOperationProgressWidget.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2021, Andreas Kling <kling@serenityos.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "FileOperationProgressWidget.h" +#include <Applications/FileManager/FileOperationProgressGML.h> +#include <LibCore/Notifier.h> +#include <LibGUI/Button.h> +#include <LibGUI/Label.h> +#include <LibGUI/MessageBox.h> +#include <LibGUI/ProgressBar.h> +#include <LibGUI/Window.h> + +namespace FileManager { + +FileOperationProgressWidget::FileOperationProgressWidget(FILE* helper_pipe) + : m_helper_pipe(helper_pipe) +{ + load_from_gml(file_operation_progress_gml); + + auto& button = *find_descendant_of_type_named<GUI::Button>("button"); + + button.on_click = [this] { + close_pipe(); + window()->close(); + }; + + m_notifier = Core::Notifier::construct(fileno(m_helper_pipe), Core::Notifier::Read); + m_notifier->on_ready_to_read = [this] { + char buffer[8192]; + if (!fgets(buffer, sizeof(buffer), m_helper_pipe)) { + did_error(); + return; + } + auto parts = StringView(buffer).split_view(' '); + VERIFY(!parts.is_empty()); + + if (parts[0] == "FINISH\n"sv) { + did_finish(); + return; + } + + if (parts[0] == "PROGRESS"sv) { + VERIFY(parts.size() >= 6); + did_progress( + parts[3].to_uint().value_or(0), + parts[4].to_uint().value_or(0), + parts[1].to_uint().value_or(0), + parts[2].to_uint().value_or(0), + parts[5]); + } + }; +} + +FileOperationProgressWidget::~FileOperationProgressWidget() +{ + close_pipe(); +} + +void FileOperationProgressWidget::did_finish() +{ + close_pipe(); + window()->close(); +} + +void FileOperationProgressWidget::did_error() +{ + // FIXME: Communicate more with the user about errors. + close_pipe(); + GUI::MessageBox::show(window(), "An error occurred", "Error", GUI::MessageBox::Type::Error, GUI::MessageBox::InputType::OK); + window()->close(); +} + +void FileOperationProgressWidget::did_progress(off_t bytes_done, off_t total_byte_count, size_t files_done, size_t total_file_count, const StringView& current_file_name) +{ + auto& current_file_label = *find_descendant_of_type_named<GUI::Label>("current_file_label"); + auto& current_file_progress_bar = *find_descendant_of_type_named<GUI::ProgressBar>("current_file_progress_bar"); + auto& overall_progress_label = *find_descendant_of_type_named<GUI::Label>("overall_progress_label"); + auto& overall_progress_bar = *find_descendant_of_type_named<GUI::ProgressBar>("overall_progress_bar"); + + current_file_label.set_text(current_file_name); + current_file_progress_bar.set_max(total_byte_count); + current_file_progress_bar.set_value(bytes_done); + + overall_progress_label.set_text(String::formatted("{} of {}", files_done, total_file_count)); + overall_progress_bar.set_max(total_file_count); + overall_progress_bar.set_value(files_done); +} + +void FileOperationProgressWidget::close_pipe() +{ + if (!m_helper_pipe) + return; + pclose(m_helper_pipe); + m_helper_pipe = nullptr; + if (m_notifier) + m_notifier->on_ready_to_read = nullptr; + m_notifier = nullptr; +} + +} diff --git a/Userland/Applications/FileManager/FileOperationProgressWidget.h b/Userland/Applications/FileManager/FileOperationProgressWidget.h new file mode 100644 index 0000000000..1bd9b5d2c1 --- /dev/null +++ b/Userland/Applications/FileManager/FileOperationProgressWidget.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021, Andreas Kling <kling@serenityos.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <LibGUI/Widget.h> + +namespace FileManager { + +class FileOperationProgressWidget : public GUI::Widget { + C_OBJECT(FileOperationProgressWidget); + +public: + virtual ~FileOperationProgressWidget() override; + +private: + explicit FileOperationProgressWidget(FILE* helper_pipe); + + void did_finish(); + void did_error(); + void did_progress(off_t bytes_done, off_t total_byte_count, size_t files_done, size_t total_file_count, const StringView& current_file_name); + + void close_pipe(); + + RefPtr<Core::Notifier> m_notifier; + FILE* m_helper_pipe { nullptr }; +}; +} |