summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Userland/Applications/FileManager/CMakeLists.txt3
-rw-r--r--Userland/Applications/FileManager/DirectoryView.cpp32
-rw-r--r--Userland/Applications/FileManager/FileOperationProgress.gml89
-rw-r--r--Userland/Applications/FileManager/FileOperationProgressWidget.cpp123
-rw-r--r--Userland/Applications/FileManager/FileOperationProgressWidget.h51
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 };
+};
+}