Serenity Operating System
at master 171 lines 6.3 kB view raw
1/* 2 * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2022, the SerenityOS developers. 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include "DownloadWidget.h" 9#include <AK/NumberFormat.h> 10#include <AK/StringBuilder.h> 11#include <LibCore/Proxy.h> 12#include <LibCore/StandardPaths.h> 13#include <LibDesktop/Launcher.h> 14#include <LibGUI/BoxLayout.h> 15#include <LibGUI/Button.h> 16#include <LibGUI/CheckBox.h> 17#include <LibGUI/ImageWidget.h> 18#include <LibGUI/Label.h> 19#include <LibGUI/MessageBox.h> 20#include <LibGUI/Progressbar.h> 21#include <LibGUI/Window.h> 22#include <LibWeb/Loader/ResourceLoader.h> 23 24#include <LibConfig/Client.h> 25 26namespace Browser { 27 28DownloadWidget::DownloadWidget(const URL& url) 29 : m_url(url) 30{ 31 { 32 StringBuilder builder; 33 builder.append(Core::StandardPaths::downloads_directory()); 34 builder.append('/'); 35 builder.append(m_url.basename()); 36 m_destination_path = builder.to_deprecated_string(); 37 } 38 39 auto close_on_finish = Config::read_bool("Browser"sv, "Preferences"sv, "CloseDownloadWidgetOnFinish"sv, false); 40 41 m_elapsed_timer.start(); 42 m_download = Web::ResourceLoader::the().connector().start_request("GET", url); 43 VERIFY(m_download); 44 m_download->on_progress = [this](Optional<u32> total_size, u32 downloaded_size) { 45 did_progress(total_size.value(), downloaded_size); 46 }; 47 48 { 49 auto file_or_error = Core::File::open(m_destination_path, Core::File::OpenMode::Write); 50 if (file_or_error.is_error()) { 51 GUI::MessageBox::show(window(), DeprecatedString::formatted("Cannot open {} for writing", m_destination_path), "Download failed"sv, GUI::MessageBox::Type::Error); 52 window()->close(); 53 return; 54 } 55 m_output_file_stream = file_or_error.release_value(); 56 } 57 58 m_download->on_finish = [this](bool success, auto) { did_finish(success); }; 59 m_download->stream_into(*m_output_file_stream); 60 61 set_fill_with_background_color(true); 62 set_layout<GUI::VerticalBoxLayout>(4); 63 64 auto& animation_container = add<GUI::Widget>(); 65 animation_container.set_fixed_height(32); 66 animation_container.set_layout<GUI::HorizontalBoxLayout>(); 67 68 m_browser_image = animation_container.add<GUI::ImageWidget>(); 69 m_browser_image->load_from_file("/res/graphics/download-animation.gif"sv); 70 animation_container.add_spacer().release_value_but_fixme_should_propagate_errors(); 71 72 auto& source_label = add<GUI::Label>(DeprecatedString::formatted("From: {}", url)); 73 source_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); 74 source_label.set_fixed_height(16); 75 76 m_progressbar = add<GUI::Progressbar>(); 77 m_progressbar->set_fixed_height(20); 78 79 m_progress_label = add<GUI::Label>(); 80 m_progress_label->set_text_alignment(Gfx::TextAlignment::CenterLeft); 81 m_progress_label->set_fixed_height(16); 82 83 auto& destination_label = add<GUI::Label>(DeprecatedString::formatted("To: {}", m_destination_path)); 84 destination_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); 85 destination_label.set_fixed_height(16); 86 87 m_close_on_finish_checkbox = add<GUI::CheckBox>("Close when finished"_string.release_value_but_fixme_should_propagate_errors()); 88 m_close_on_finish_checkbox->set_checked(close_on_finish); 89 90 m_close_on_finish_checkbox->on_checked = [&](bool checked) { 91 Config::write_bool("Browser"sv, "Preferences"sv, "CloseDownloadWidgetOnFinish"sv, checked); 92 }; 93 94 auto& button_container = add<GUI::Widget>(); 95 button_container.set_layout<GUI::HorizontalBoxLayout>(); 96 button_container.add_spacer().release_value_but_fixme_should_propagate_errors(); 97 m_cancel_button = button_container.add<GUI::Button>("Cancel"_short_string); 98 m_cancel_button->set_fixed_size(100, 22); 99 m_cancel_button->on_click = [this](auto) { 100 bool success = m_download->stop(); 101 VERIFY(success); 102 window()->close(); 103 }; 104 105 m_close_button = button_container.add<GUI::Button>("OK"_short_string); 106 m_close_button->set_enabled(false); 107 m_close_button->set_fixed_size(100, 22); 108 m_close_button->on_click = [this](auto) { 109 window()->close(); 110 }; 111} 112 113void DownloadWidget::did_progress(Optional<u32> total_size, u32 downloaded_size) 114{ 115 m_progressbar->set_min(0); 116 if (total_size.has_value()) { 117 int percent = roundf(((float)downloaded_size / (float)total_size.value()) * 100.0f); 118 window()->set_progress(percent); 119 m_progressbar->set_max(total_size.value()); 120 } else { 121 m_progressbar->set_max(0); 122 } 123 m_progressbar->set_value(downloaded_size); 124 125 { 126 StringBuilder builder; 127 builder.append("Downloaded "sv); 128 builder.append(human_readable_size(downloaded_size)); 129 builder.appendff(" in {} sec", m_elapsed_timer.elapsed_time().to_seconds()); 130 m_progress_label->set_text(builder.to_deprecated_string()); 131 } 132 133 { 134 StringBuilder builder; 135 if (total_size.has_value()) { 136 int percent = roundf(((float)downloaded_size / (float)total_size.value()) * 100); 137 builder.appendff("{}%", percent); 138 } else { 139 builder.append(human_readable_size(downloaded_size)); 140 } 141 builder.append(" of "sv); 142 builder.append(m_url.basename()); 143 window()->set_title(builder.to_deprecated_string()); 144 } 145} 146 147void DownloadWidget::did_finish(bool success) 148{ 149 dbgln("did_finish, success={}", success); 150 151 m_browser_image->load_from_file("/res/graphics/download-finished.gif"sv); 152 window()->set_title("Download finished!"); 153 m_close_button->set_enabled(true); 154 m_cancel_button->set_text("Open in Folder"_string.release_value_but_fixme_should_propagate_errors()); 155 m_cancel_button->on_click = [this](auto) { 156 Desktop::Launcher::open(URL::create_with_file_scheme(Core::StandardPaths::downloads_directory(), m_url.basename())); 157 window()->close(); 158 }; 159 m_cancel_button->update(); 160 161 if (!success) { 162 GUI::MessageBox::show(window(), DeprecatedString::formatted("Download failed for some reason"), "Download failed"sv, GUI::MessageBox::Type::Error); 163 window()->close(); 164 return; 165 } 166 167 if (m_close_on_finish_checkbox->is_checked()) 168 window()->close(); 169} 170 171}