diff options
author | Andreas Kling <kling@serenityos.org> | 2021-01-12 12:05:23 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-01-12 12:05:23 +0100 |
commit | dc28c07fa526841e05e16161c74a6c23984f1dd5 (patch) | |
tree | d68796bc7708eba33fbf7247e1a92188ac5acf6f /Applications | |
parent | aa939c4b4b8a7eb1d22b166ebb5fb737d6e66714 (diff) | |
download | serenity-dc28c07fa526841e05e16161c74a6c23984f1dd5.zip |
Applications: Move to Userland/Applications/
Diffstat (limited to 'Applications')
285 files changed, 0 insertions, 37234 deletions
diff --git a/Applications/About/CMakeLists.txt b/Applications/About/CMakeLists.txt deleted file mode 100644 index 0b3023c819..0000000000 --- a/Applications/About/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -set(SOURCES - main.cpp -) - -execute_process(COMMAND "git rev-parse --short HEAD" OUTPUT_VARIABLE GIT_COMMIT) -execute_process(COMMAND "git rev-parse --abbrev-ref HEAD" OUTPUT_VARIABLE GIT_BRANCH) -execute_process(COMMAND "git diff-index --quiet HEAD -- && echo tracked || echo untracked" OUTPUT_VARIABLE GIT_CHANGES) - -add_definitions(-DGIT_COMMIT="${GIT_COMMIT}" -DGIT_BRANCH="${GIT_BRANCH}" -DGIT_CHANGES="${GIT_CHANGES}") - -serenity_bin(About) -target_link_libraries(About LibGUI) diff --git a/Applications/About/main.cpp b/Applications/About/main.cpp deleted file mode 100644 index b7e18caee3..0000000000 --- a/Applications/About/main.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 <LibGUI/AboutDialog.h> -#include <LibGUI/Application.h> -#include <LibGUI/Icon.h> -#include <LibGfx/Bitmap.h> -#include <stdio.h> -#include <sys/utsname.h> - -int main(int argc, char** argv) -{ - if (pledge("stdio shared_buffer accept rpath unix cpath fattr", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto app = GUI::Application::construct(argc, argv); - - if (pledge("stdio shared_buffer accept rpath", nullptr) < 0) { - perror("pledge"); - return 1; - } - - if (unveil("/res", "r") < 0) { - perror("unveil"); - return 1; - } - - unveil(nullptr, nullptr); - - auto app_icon = GUI::Icon::default_icon("ladybug"); - GUI::AboutDialog::show("SerenityOS", nullptr, nullptr, app_icon.bitmap_for_size(32)); - return app->exec(); -} diff --git a/Applications/Browser/BookmarksBarWidget.cpp b/Applications/Browser/BookmarksBarWidget.cpp deleted file mode 100644 index ef9af48a60..0000000000 --- a/Applications/Browser/BookmarksBarWidget.cpp +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright (c) 2020, Emanuel Sprung <emanuel.sprung@gmail.com> - * 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 "BookmarksBarWidget.h" -#include <LibGUI/Action.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Button.h> -#include <LibGUI/Event.h> -#include <LibGUI/JsonArrayModel.h> -#include <LibGUI/Menu.h> -#include <LibGUI/Model.h> -#include <LibGUI/Widget.h> -#include <LibGUI/Window.h> -#include <LibGfx/Palette.h> - -namespace Browser { - -static BookmarksBarWidget* s_the; - -BookmarksBarWidget& BookmarksBarWidget::the() -{ - return *s_the; -} - -BookmarksBarWidget::BookmarksBarWidget(const String& bookmarks_file, bool enabled) -{ - s_the = this; - set_layout<GUI::HorizontalBoxLayout>(); - layout()->set_spacing(0); - - set_fixed_height(20); - - if (!enabled) - set_visible(false); - - m_additional = GUI::Button::construct(); - m_additional->set_button_style(Gfx::ButtonStyle::CoolBar); - m_additional->set_text(">"); - m_additional->set_fixed_size(14, 20); - m_additional->set_focus_policy(GUI::FocusPolicy::TabFocus); - m_additional->on_click = [this](auto) { - if (m_additional_menu) { - m_additional_menu->popup(m_additional->relative_position().translated(relative_position().translated(m_additional->window()->position()))); - } - }; - - m_separator = GUI::Widget::construct(); - - m_context_menu = GUI::Menu::construct(); - auto default_action = GUI::Action::create("Open", [this](auto&) { - if (on_bookmark_click) - on_bookmark_click(m_context_menu_url, Mod_None); - }); - m_context_menu_default_action = default_action; - m_context_menu->add_action(default_action); - m_context_menu->add_action(GUI::Action::create("Open in new tab", [this](auto&) { - if (on_bookmark_click) - on_bookmark_click(m_context_menu_url, Mod_Ctrl); - })); - m_context_menu->add_action(GUI::Action::create("Delete", [this](auto&) { - remove_bookmark(m_context_menu_url); - })); - - Vector<GUI::JsonArrayModel::FieldSpec> fields; - fields.empend("title", "Title", Gfx::TextAlignment::CenterLeft); - fields.empend("url", "Url", Gfx::TextAlignment::CenterRight); - set_model(GUI::JsonArrayModel::create(bookmarks_file, move(fields))); - model()->update(); -} - -BookmarksBarWidget::~BookmarksBarWidget() -{ - if (m_model) - m_model->unregister_client(*this); -} - -void BookmarksBarWidget::set_model(RefPtr<GUI::Model> model) -{ - if (model == m_model) - return; - if (m_model) - m_model->unregister_client(*this); - m_model = move(model); - m_model->register_client(*this); -} - -void BookmarksBarWidget::resize_event(GUI::ResizeEvent& event) -{ - Widget::resize_event(event); - update_content_size(); -} - -void BookmarksBarWidget::model_did_update(unsigned) -{ - remove_all_children(); - - m_bookmarks.clear(); - - int width = 0; - for (int item_index = 0; item_index < model()->row_count(); ++item_index) { - - auto title = model()->index(item_index, 0).data().to_string(); - auto url = model()->index(item_index, 1).data().to_string(); - - Gfx::IntRect rect { width, 0, font().width(title) + 32, height() }; - - auto& button = add<GUI::Button>(); - m_bookmarks.append(button); - - button.set_button_style(Gfx::ButtonStyle::CoolBar); - button.set_text(title); - button.set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-html.png")); - button.set_fixed_size(font().width(title) + 32, 20); - button.set_relative_rect(rect); - button.set_focus_policy(GUI::FocusPolicy::TabFocus); - button.set_tooltip(url); - - button.on_click = [title, url, this](auto modifiers) { - if (on_bookmark_click) - on_bookmark_click(url, modifiers); - }; - - button.on_context_menu_request = [this, url](auto& context_menu_event) { - m_context_menu_url = url; - m_context_menu->popup(context_menu_event.screen_position(), m_context_menu_default_action); - }; - - width += rect.width(); - } - - add_child(*m_separator); - add_child(*m_additional); - - update_content_size(); - update(); -} - -void BookmarksBarWidget::update_content_size() -{ - int x_position = 0; - m_last_visible_index = -1; - - for (size_t i = 0; i < m_bookmarks.size(); ++i) { - auto& bookmark = m_bookmarks.at(i); - if (x_position + bookmark.width() > width()) { - m_last_visible_index = i; - break; - } - bookmark.set_x(x_position); - bookmark.set_visible(true); - x_position += bookmark.width(); - } - - if (m_last_visible_index < 0) { - m_additional->set_visible(false); - } else { - // hide all items > m_last_visible_index and create new bookmarks menu for them - m_additional->set_visible(true); - m_additional_menu = GUI::Menu::construct("Additional Bookmarks"); - for (size_t i = m_last_visible_index; i < m_bookmarks.size(); ++i) { - auto& bookmark = m_bookmarks.at(i); - bookmark.set_visible(false); - m_additional_menu->add_action(GUI::Action::create(bookmark.text(), - Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-html.png"), - [&](auto&) { - bookmark.on_click(0); - })); - } - } -} - -bool BookmarksBarWidget::contains_bookmark(const String& url) -{ - for (int item_index = 0; item_index < model()->row_count(); ++item_index) { - - auto item_title = model()->index(item_index, 0).data().to_string(); - auto item_url = model()->index(item_index, 1).data().to_string(); - if (item_url == url) { - return true; - } - } - return false; -} - -bool BookmarksBarWidget::remove_bookmark(const String& url) -{ - for (int item_index = 0; item_index < model()->row_count(); ++item_index) { - - auto item_title = model()->index(item_index, 0).data().to_string(); - auto item_url = model()->index(item_index, 1).data().to_string(); - if (item_url == url) { - auto& json_model = *static_cast<GUI::JsonArrayModel*>(model()); - - const auto item_removed = json_model.remove(item_index); - if (item_removed) - json_model.store(); - - return item_removed; - } - } - - return false; -} -bool BookmarksBarWidget::add_bookmark(const String& url, const String& title) -{ - Vector<JsonValue> values; - values.append(title); - values.append(url); - - auto& json_model = *static_cast<GUI::JsonArrayModel*>(model()); - if (json_model.add(move(values))) { - json_model.store(); - return true; - } - return false; -} - -} diff --git a/Applications/Browser/BookmarksBarWidget.h b/Applications/Browser/BookmarksBarWidget.h deleted file mode 100644 index 2eeed8fec5..0000000000 --- a/Applications/Browser/BookmarksBarWidget.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2020, Emanuel Sprung <emanuel.sprung@gmail.com> - * 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/Forward.h> -#include <LibGUI/Model.h> -#include <LibGUI/Widget.h> - -namespace Browser { - -class BookmarksBarWidget final - : public GUI::Widget - , private GUI::ModelClient { - C_OBJECT(BookmarksBarWidget); - -public: - static BookmarksBarWidget& the(); - - virtual ~BookmarksBarWidget() override; - - void set_model(RefPtr<GUI::Model>); - GUI::Model* model() { return m_model.ptr(); } - const GUI::Model* model() const { return m_model.ptr(); } - - Function<void(const String& url, unsigned modifiers)> on_bookmark_click; - Function<void(const String&, const String&)> on_bookmark_hover; - - bool contains_bookmark(const String& url); - bool remove_bookmark(const String& url); - bool add_bookmark(const String& url, const String& title); - -private: - BookmarksBarWidget(const String&, bool enabled); - - // ^GUI::ModelClient - virtual void model_did_update(unsigned) override; - - // ^GUI::Widget - virtual void resize_event(GUI::ResizeEvent&) override; - - void update_content_size(); - - RefPtr<GUI::Model> m_model; - RefPtr<GUI::Button> m_additional; - RefPtr<GUI::Widget> m_separator; - RefPtr<GUI::Menu> m_additional_menu; - - RefPtr<GUI::Menu> m_context_menu; - RefPtr<GUI::Action> m_context_menu_default_action; - String m_context_menu_url; - - NonnullRefPtrVector<GUI::Button> m_bookmarks; - - int m_last_visible_index { -1 }; -}; - -} diff --git a/Applications/Browser/Browser.h b/Applications/Browser/Browser.h deleted file mode 100644 index a4a4eefe1f..0000000000 --- a/Applications/Browser/Browser.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 <AK/String.h> - -namespace Browser { - -extern String g_home_url; - -} diff --git a/Applications/Browser/BrowserConsoleClient.cpp b/Applications/Browser/BrowserConsoleClient.cpp deleted file mode 100644 index ce1dd7e5cb..0000000000 --- a/Applications/Browser/BrowserConsoleClient.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2020, Hunter Salyer <thefalsehonesty@gmail.com> - * 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 "BrowserConsoleClient.h" -#include "ConsoleWidget.h" -#include <AK/StringBuilder.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/JSSyntaxHighlighter.h> -#include <LibGUI/TextBox.h> -#include <LibWeb/DOM/DocumentType.h> -#include <LibWeb/DOM/ElementFactory.h> -#include <LibWeb/DOM/Text.h> -#include <LibWeb/DOMTreeModel.h> -#include <LibWeb/HTML/HTMLBodyElement.h> - -namespace Browser { - -JS::Value BrowserConsoleClient::log() -{ - m_console_widget.print_html(vm().join_arguments()); - return JS::js_undefined(); -} - -JS::Value BrowserConsoleClient::info() -{ - StringBuilder html; - html.append("<span class=\"info\">"); - html.append("(i) "); - html.append(vm().join_arguments()); - html.append("</span>"); - m_console_widget.print_html(html.string_view()); - return JS::js_undefined(); -} - -JS::Value BrowserConsoleClient::debug() -{ - StringBuilder html; - html.append("<span class=\"debug\">"); - html.append("(d) "); - html.append(vm().join_arguments()); - html.append("</span>"); - m_console_widget.print_html(html.string_view()); - return JS::js_undefined(); -} - -JS::Value BrowserConsoleClient::warn() -{ - StringBuilder html; - html.append("<span class=\"warn\">"); - html.append("(w) "); - html.append(vm().join_arguments()); - html.append("</span>"); - m_console_widget.print_html(html.string_view()); - return JS::js_undefined(); -} - -JS::Value BrowserConsoleClient::error() -{ - StringBuilder html; - html.append("<span class=\"error\">"); - html.append("(e) "); - html.append(vm().join_arguments()); - html.append("</span>"); - m_console_widget.print_html(html.string_view()); - return JS::js_undefined(); -} - -JS::Value BrowserConsoleClient::clear() -{ - m_console_widget.clear_output(); - return JS::js_undefined(); -} - -JS::Value BrowserConsoleClient::trace() -{ - StringBuilder html; - html.append(vm().join_arguments()); - auto trace = get_trace(); - for (auto& function_name : trace) { - if (function_name.is_empty()) - function_name = "<anonymous>"; - html.appendff(" -> {}<br>", function_name); - } - m_console_widget.print_html(html.string_view()); - return JS::js_undefined(); -} - -JS::Value BrowserConsoleClient::count() -{ - auto label = vm().argument_count() ? vm().argument(0).to_string_without_side_effects() : "default"; - auto counter_value = m_console.counter_increment(label); - m_console_widget.print_html(String::formatted("{}: {}", label, counter_value)); - return JS::js_undefined(); -} - -JS::Value BrowserConsoleClient::count_reset() -{ - auto label = vm().argument_count() ? vm().argument(0).to_string_without_side_effects() : "default"; - if (m_console.counter_reset(label)) { - m_console_widget.print_html(String::formatted("{}: 0", label)); - } else { - m_console_widget.print_html(String::formatted("\"{}\" doesn't have a count", label)); - } - return JS::js_undefined(); -} - -} diff --git a/Applications/Browser/BrowserConsoleClient.h b/Applications/Browser/BrowserConsoleClient.h deleted file mode 100644 index e87f6e32b0..0000000000 --- a/Applications/Browser/BrowserConsoleClient.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2020, Hunter Salyer <thefalsehonesty@gmail.com> - * 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> -#include <LibJS/Console.h> -#include <LibJS/Forward.h> -#include <LibWeb/InProcessWebView.h> - -namespace Browser { - -class ConsoleWidget; - -class BrowserConsoleClient final : public JS::ConsoleClient { -public: - BrowserConsoleClient(JS::Console& console, ConsoleWidget& console_widget) - : ConsoleClient(console) - , m_console_widget(console_widget) - { - } - -private: - virtual JS::Value log() override; - virtual JS::Value info() override; - virtual JS::Value debug() override; - virtual JS::Value warn() override; - virtual JS::Value error() override; - virtual JS::Value clear() override; - virtual JS::Value trace() override; - virtual JS::Value count() override; - virtual JS::Value count_reset() override; - - ConsoleWidget& m_console_widget; -}; - -} diff --git a/Applications/Browser/BrowserWindow.gml b/Applications/Browser/BrowserWindow.gml deleted file mode 100644 index f6ec7ef1af..0000000000 --- a/Applications/Browser/BrowserWindow.gml +++ /dev/null @@ -1,15 +0,0 @@ -@GUI::Widget { - name: "browser" - fill_with_background_color: true - - layout: @GUI::VerticalBoxLayout { - spacing: 2 - } - - @GUI::TabWidget { - name: "tab_widget" - container_padding: 0 - uniform_tabs: true - text_alignment: "CenterLeft" - } -} diff --git a/Applications/Browser/CMakeLists.txt b/Applications/Browser/CMakeLists.txt deleted file mode 100644 index 4652ec8b11..0000000000 --- a/Applications/Browser/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -compile_gml(BrowserWindow.gml BrowserWindowGML.h browser_window_gml) -compile_gml(Tab.gml TabGML.h tab_gml) - -set(SOURCES - BookmarksBarWidget.cpp - BrowserConsoleClient.cpp - ConsoleWidget.cpp - DownloadWidget.cpp - History.cpp - InspectorWidget.cpp - main.cpp - Tab.cpp - WindowActions.cpp - BrowserWindowGML.h - TabGML.h -) - -serenity_app(Browser ICON app-browser) -target_link_libraries(Browser LibWeb LibProtocol LibGUI LibDesktop) diff --git a/Applications/Browser/ConsoleWidget.cpp b/Applications/Browser/ConsoleWidget.cpp deleted file mode 100644 index 55c545dc55..0000000000 --- a/Applications/Browser/ConsoleWidget.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (c) 2020, Hunter Salyer <thefalsehonesty@gmail.com> - * 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 "ConsoleWidget.h" -#include <AK/StringBuilder.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Button.h> -#include <LibGUI/JSSyntaxHighlighter.h> -#include <LibGUI/TextBox.h> -#include <LibGfx/FontDatabase.h> -#include <LibJS/Interpreter.h> -#include <LibJS/MarkupGenerator.h> -#include <LibJS/Parser.h> -#include <LibJS/Runtime/Error.h> -#include <LibWeb/DOM/DocumentType.h> -#include <LibWeb/DOM/ElementFactory.h> -#include <LibWeb/DOM/Text.h> -#include <LibWeb/DOMTreeModel.h> -#include <LibWeb/HTML/HTMLBodyElement.h> - -namespace Browser { - -ConsoleWidget::ConsoleWidget() -{ - set_layout<GUI::VerticalBoxLayout>(); - set_fill_with_background_color(true); - - auto base_document = Web::DOM::Document::create(); - base_document->append_child(adopt(*new Web::DOM::DocumentType(base_document))); - auto html_element = base_document->create_element("html"); - base_document->append_child(html_element); - auto head_element = base_document->create_element("head"); - html_element->append_child(head_element); - auto body_element = base_document->create_element("body"); - html_element->append_child(body_element); - m_output_container = body_element; - - m_output_view = add<Web::InProcessWebView>(); - m_output_view->set_document(base_document); - - auto& bottom_container = add<GUI::Widget>(); - bottom_container.set_layout<GUI::HorizontalBoxLayout>(); - bottom_container.set_fixed_height(22); - - m_input = bottom_container.add<GUI::TextBox>(); - m_input->set_syntax_highlighter(make<GUI::JSSyntaxHighlighter>()); - // FIXME: Syntax Highlighting breaks the cursor's position on non fixed-width fonts. - m_input->set_font(Gfx::FontDatabase::default_fixed_width_font()); - m_input->set_history_enabled(true); - - m_input->on_return_pressed = [this] { - auto js_source = m_input->text(); - - // FIXME: An is_blank check to check if there is only whitespace would probably be preferable. - if (js_source.is_empty()) - return; - - m_input->add_current_text_to_history(); - m_input->clear(); - - print_source_line(js_source); - - auto parser = JS::Parser(JS::Lexer(js_source)); - auto program = parser.parse_program(); - - StringBuilder output_html; - if (parser.has_errors()) { - auto error = parser.errors()[0]; - auto hint = error.source_location_hint(js_source); - if (!hint.is_empty()) - output_html.append(String::formatted("<pre>{}</pre>", escape_html_entities(hint))); - m_interpreter->vm().throw_exception<JS::SyntaxError>(m_interpreter->global_object(), error.to_string()); - } else { - m_interpreter->run(m_interpreter->global_object(), *program); - } - - if (m_interpreter->exception()) { - output_html.append("Uncaught exception: "); - output_html.append(JS::MarkupGenerator::html_from_value(m_interpreter->exception()->value())); - print_html(output_html.string_view()); - - m_interpreter->vm().clear_exception(); - return; - } - - print_html(JS::MarkupGenerator::html_from_value(m_interpreter->vm().last_value())); - }; - - set_focus_proxy(m_input); - - auto& clear_button = bottom_container.add<GUI::Button>(); - clear_button.set_fixed_size(22, 22); - clear_button.set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/delete.png")); - clear_button.set_tooltip("Clear the console output"); - clear_button.on_click = [this](auto) { - clear_output(); - }; -} - -ConsoleWidget::~ConsoleWidget() -{ -} - -void ConsoleWidget::set_interpreter(WeakPtr<JS::Interpreter> interpreter) -{ - if (m_interpreter.ptr() == interpreter.ptr()) - return; - - m_interpreter = interpreter; - m_console_client = make<BrowserConsoleClient>(interpreter->global_object().console(), *this); - interpreter->global_object().console().set_client(*m_console_client.ptr()); - - clear_output(); -} - -void ConsoleWidget::print_source_line(const StringView& source) -{ - StringBuilder html; - html.append("<span class=\"repl-indicator\">"); - html.append("> "); - html.append("</span>"); - - html.append(JS::MarkupGenerator::html_from_source(source)); - - print_html(html.string_view()); -} - -void ConsoleWidget::print_html(const StringView& line) -{ - auto paragraph = m_output_container->document().create_element("p"); - paragraph->set_inner_html(line); - - m_output_container->append_child(paragraph); - m_output_container->document().invalidate_layout(); - m_output_container->document().update_layout(); - - m_output_view->scroll_to_bottom(); -} - -void ConsoleWidget::clear_output() -{ - m_output_container->remove_all_children(); - m_output_view->update(); -} - -} diff --git a/Applications/Browser/ConsoleWidget.h b/Applications/Browser/ConsoleWidget.h deleted file mode 100644 index 813ceed2ed..0000000000 --- a/Applications/Browser/ConsoleWidget.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2020, Hunter Salyer <thefalsehonesty@gmail.com> - * 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 "BrowserConsoleClient.h" -#include "History.h" -#include <LibGUI/Widget.h> -#include <LibJS/Forward.h> -#include <LibWeb/InProcessWebView.h> - -namespace Browser { - -class ConsoleWidget final : public GUI::Widget { - C_OBJECT(ConsoleWidget) -public: - virtual ~ConsoleWidget(); - - void set_interpreter(WeakPtr<JS::Interpreter>); - void print_source_line(const StringView&); - void print_html(const StringView&); - void clear_output(); - -private: - ConsoleWidget(); - - RefPtr<GUI::TextBox> m_input; - RefPtr<Web::InProcessWebView> m_output_view; - RefPtr<Web::DOM::Element> m_output_container; - WeakPtr<JS::Interpreter> m_interpreter; - OwnPtr<BrowserConsoleClient> m_console_client; -}; - -} diff --git a/Applications/Browser/DownloadWidget.cpp b/Applications/Browser/DownloadWidget.cpp deleted file mode 100644 index 39d931e216..0000000000 --- a/Applications/Browser/DownloadWidget.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (c) 2020, 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 "DownloadWidget.h" -#include <AK/NumberFormat.h> -#include <AK/SharedBuffer.h> -#include <AK/StringBuilder.h> -#include <LibCore/File.h> -#include <LibCore/FileStream.h> -#include <LibCore/StandardPaths.h> -#include <LibDesktop/Launcher.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Button.h> -#include <LibGUI/ImageWidget.h> -#include <LibGUI/Label.h> -#include <LibGUI/MessageBox.h> -#include <LibGUI/ProgressBar.h> -#include <LibGUI/Window.h> -#include <LibProtocol/Client.h> -#include <LibWeb/Loader/ResourceLoader.h> -#include <math.h> - -namespace Browser { - -DownloadWidget::DownloadWidget(const URL& url) - : m_url(url) -{ - { - StringBuilder builder; - builder.append(Core::StandardPaths::downloads_directory()); - builder.append('/'); - builder.append(m_url.basename()); - m_destination_path = builder.to_string(); - } - - m_elapsed_timer.start(); - m_download = Web::ResourceLoader::the().protocol_client().start_download("GET", url.to_string()); - ASSERT(m_download); - m_download->on_progress = [this](Optional<u32> total_size, u32 downloaded_size) { - did_progress(total_size.value(), downloaded_size); - }; - - { - auto file_or_error = Core::File::open(m_destination_path, Core::IODevice::WriteOnly); - if (file_or_error.is_error()) { - GUI::MessageBox::show(window(), String::formatted("Cannot open {} for writing", m_destination_path), "Download failed", GUI::MessageBox::Type::Error); - window()->close(); - return; - } - m_output_file_stream = make<Core::OutputFileStream>(*file_or_error.value()); - } - - m_download->on_finish = [this](bool success, auto) { did_finish(success); }; - m_download->stream_into(*m_output_file_stream); - - set_fill_with_background_color(true); - auto& layout = set_layout<GUI::VerticalBoxLayout>(); - layout.set_margins({ 4, 4, 4, 4 }); - - auto& animation_container = add<GUI::Widget>(); - animation_container.set_fixed_height(32); - auto& animation_layout = animation_container.set_layout<GUI::HorizontalBoxLayout>(); - - auto& browser_image = animation_container.add<GUI::ImageWidget>(); - browser_image.load_from_file("/res/graphics/download-animation.gif"); - animation_layout.add_spacer(); - - auto& source_label = add<GUI::Label>(String::formatted("From: {}", url)); - source_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - source_label.set_fixed_height(16); - - m_progress_bar = add<GUI::ProgressBar>(); - m_progress_bar->set_fixed_height(20); - - m_progress_label = add<GUI::Label>(); - m_progress_label->set_text_alignment(Gfx::TextAlignment::CenterLeft); - m_progress_label->set_fixed_height(16); - - auto& destination_label = add<GUI::Label>(String::formatted("To: {}", m_destination_path)); - destination_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - destination_label.set_fixed_height(16); - - auto& button_container = add<GUI::Widget>(); - auto& button_container_layout = button_container.set_layout<GUI::HorizontalBoxLayout>(); - button_container_layout.add_spacer(); - m_cancel_button = button_container.add<GUI::Button>("Cancel"); - m_cancel_button->set_fixed_size(100, 22); - m_cancel_button->on_click = [this](auto) { - bool success = m_download->stop(); - ASSERT(success); - window()->close(); - }; - - m_close_button = button_container.add<GUI::Button>("OK"); - m_close_button->set_enabled(false); - m_close_button->set_fixed_size(100, 22); - m_close_button->on_click = [this](auto) { - window()->close(); - }; -} - -DownloadWidget::~DownloadWidget() -{ -} - -void DownloadWidget::did_progress(Optional<u32> total_size, u32 downloaded_size) -{ - m_progress_bar->set_min(0); - if (total_size.has_value()) { - int percent = roundf(((float)downloaded_size / (float)total_size.value()) * 100.0f); - window()->set_progress(percent); - m_progress_bar->set_max(total_size.value()); - } else { - m_progress_bar->set_max(0); - } - m_progress_bar->set_value(downloaded_size); - - { - StringBuilder builder; - builder.append("Downloaded "); - builder.append(human_readable_size(downloaded_size)); - builder.appendff(" in {} sec", m_elapsed_timer.elapsed() / 1000); - m_progress_label->set_text(builder.to_string()); - } - - { - StringBuilder builder; - if (total_size.has_value()) { - int percent = roundf(((float)downloaded_size / (float)total_size.value()) * 100); - builder.appendff("{}%", percent); - } else { - builder.append(human_readable_size(downloaded_size)); - } - builder.append(" of "); - builder.append(m_url.basename()); - window()->set_title(builder.to_string()); - } -} - -void DownloadWidget::did_finish(bool success) -{ - dbgln("did_finish, success={}", success); - - m_close_button->set_enabled(true); - m_cancel_button->set_text("Open in Folder"); - m_cancel_button->on_click = [this](auto) { - Desktop::Launcher::open(URL::create_with_file_protocol(Core::StandardPaths::downloads_directory())); - window()->close(); - }; - m_cancel_button->update(); - - if (!success) { - GUI::MessageBox::show(window(), String::formatted("Download failed for some reason"), "Download failed", GUI::MessageBox::Type::Error); - window()->close(); - return; - } -} - -} diff --git a/Applications/Browser/DownloadWidget.h b/Applications/Browser/DownloadWidget.h deleted file mode 100644 index 44fc21c60d..0000000000 --- a/Applications/Browser/DownloadWidget.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2020, 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 <AK/URL.h> -#include <LibCore/ElapsedTimer.h> -#include <LibCore/FileStream.h> -#include <LibGUI/ProgressBar.h> -#include <LibGUI/Widget.h> -#include <LibProtocol/Download.h> - -namespace Browser { - -class DownloadWidget final : public GUI::Widget { - C_OBJECT(DownloadWidget); - -public: - virtual ~DownloadWidget() override; - -private: - explicit DownloadWidget(const URL&); - - void did_progress(Optional<u32> total_size, u32 downloaded_size); - void did_finish(bool success); - - URL m_url; - String m_destination_path; - RefPtr<Protocol::Download> m_download; - RefPtr<GUI::ProgressBar> m_progress_bar; - RefPtr<GUI::Label> m_progress_label; - RefPtr<GUI::Button> m_cancel_button; - RefPtr<GUI::Button> m_close_button; - OwnPtr<Core::OutputFileStream> m_output_file_stream; - Core::ElapsedTimer m_elapsed_timer; -}; - -} diff --git a/Applications/Browser/History.cpp b/Applications/Browser/History.cpp deleted file mode 100644 index cecf28477c..0000000000 --- a/Applications/Browser/History.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2020, 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 "History.h" - -namespace Browser { - -void History::dump() const -{ - dbgln("Dump {} items(s)", m_items.size()); - int i = 0; - for (auto& item : m_items) { - dbgln("[{}] {} {}", i, item, m_current == i ? '*' : ' '); - ++i; - } -} - -void History::push(const URL& url) -{ - m_items.shrink(m_current + 1); - m_items.append(url); - m_current++; -} - -URL History::current() const -{ - if (m_current == -1) - return {}; - return m_items[m_current]; -} - -void History::go_back() -{ - ASSERT(can_go_back()); - m_current--; -} - -void History::go_forward() -{ - ASSERT(can_go_forward()); - m_current++; -} - -void History::clear() -{ - m_items = {}; - m_current = -1; -} - -} diff --git a/Applications/Browser/History.h b/Applications/Browser/History.h deleted file mode 100644 index 84fc4151d7..0000000000 --- a/Applications/Browser/History.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 <AK/URL.h> -#include <AK/Vector.h> - -namespace Browser { - -class History { -public: - void dump() const; - - void push(const URL&); - URL current() const; - - void go_back(); - void go_forward(); - - bool can_go_back() { return m_current > 0; } - bool can_go_forward() { return m_current + 1 < static_cast<int>(m_items.size()); } - - void clear(); - -private: - Vector<URL> m_items; - int m_current { -1 }; -}; - -} diff --git a/Applications/Browser/InspectorWidget.cpp b/Applications/Browser/InspectorWidget.cpp deleted file mode 100644 index 7e9111e513..0000000000 --- a/Applications/Browser/InspectorWidget.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "InspectorWidget.h" -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Splitter.h> -#include <LibGUI/TabWidget.h> -#include <LibGUI/TableView.h> -#include <LibGUI/TreeView.h> -#include <LibWeb/DOM/Document.h> -#include <LibWeb/DOM/Element.h> -#include <LibWeb/DOMTreeModel.h> -#include <LibWeb/LayoutTreeModel.h> -#include <LibWeb/StylePropertiesModel.h> - -namespace Browser { - -void InspectorWidget::set_inspected_node(Web::DOM::Node* node) -{ - m_document->set_inspected_node(node); - if (node && node->is_element()) { - auto& element = downcast<Web::DOM::Element>(*node); - if (element.specified_css_values()) { - m_style_table_view->set_model(Web::StylePropertiesModel::create(*element.specified_css_values())); - m_computed_style_table_view->set_model(Web::StylePropertiesModel::create(*element.computed_style())); - } - } else { - m_style_table_view->set_model(nullptr); - m_computed_style_table_view->set_model(nullptr); - } -} - -InspectorWidget::InspectorWidget() -{ - set_layout<GUI::VerticalBoxLayout>(); - auto& splitter = add<GUI::VerticalSplitter>(); - - auto& top_tab_widget = splitter.add<GUI::TabWidget>(); - - m_dom_tree_view = top_tab_widget.add_tab<GUI::TreeView>("DOM"); - m_dom_tree_view->on_selection = [this](auto& index) { - auto* node = static_cast<Web::DOM::Node*>(index.internal_data()); - set_inspected_node(node); - }; - - m_layout_tree_view = top_tab_widget.add_tab<GUI::TreeView>("Layout"); - m_layout_tree_view->on_selection = [this](auto& index) { - auto* node = static_cast<Web::Layout::Node*>(index.internal_data()); - set_inspected_node(node->dom_node()); - }; - - auto& bottom_tab_widget = splitter.add<GUI::TabWidget>(); - - m_style_table_view = bottom_tab_widget.add_tab<GUI::TableView>("Styles"); - m_computed_style_table_view = bottom_tab_widget.add_tab<GUI::TableView>("Computed"); -} - -InspectorWidget::~InspectorWidget() -{ -} - -void InspectorWidget::set_document(Web::DOM::Document* document) -{ - if (m_document == document) - return; - m_document = document; - m_dom_tree_view->set_model(Web::DOMTreeModel::create(*document)); - m_layout_tree_view->set_model(Web::LayoutTreeModel::create(*document)); -} - -} diff --git a/Applications/Browser/InspectorWidget.h b/Applications/Browser/InspectorWidget.h deleted file mode 100644 index 37f0f95754..0000000000 --- a/Applications/Browser/InspectorWidget.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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> -#include <LibWeb/Forward.h> - -namespace Browser { - -class InspectorWidget final : public GUI::Widget { - C_OBJECT(InspectorWidget) -public: - virtual ~InspectorWidget(); - - void set_document(Web::DOM::Document*); - -private: - InspectorWidget(); - - void set_inspected_node(Web::DOM::Node*); - - RefPtr<GUI::TreeView> m_dom_tree_view; - RefPtr<GUI::TreeView> m_layout_tree_view; - RefPtr<GUI::TableView> m_style_table_view; - RefPtr<GUI::TableView> m_computed_style_table_view; - RefPtr<Web::DOM::Document> m_document; -}; - -} diff --git a/Applications/Browser/Tab.cpp b/Applications/Browser/Tab.cpp deleted file mode 100644 index ea23d61d73..0000000000 --- a/Applications/Browser/Tab.cpp +++ /dev/null @@ -1,550 +0,0 @@ -/* - * Copyright (c) 2020, 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 "Tab.h" -#include "BookmarksBarWidget.h" -#include "Browser.h" -#include "ConsoleWidget.h" -#include "DownloadWidget.h" -#include "InspectorWidget.h" -#include "WindowActions.h" -#include <AK/StringBuilder.h> -#include <Applications/Browser/TabGML.h> -#include <LibGUI/Action.h> -#include <LibGUI/Application.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Button.h> -#include <LibGUI/Clipboard.h> -#include <LibGUI/Menu.h> -#include <LibGUI/MenuBar.h> -#include <LibGUI/StatusBar.h> -#include <LibGUI/TabWidget.h> -#include <LibGUI/TextBox.h> -#include <LibGUI/ToolBar.h> -#include <LibGUI/ToolBarContainer.h> -#include <LibGUI/Window.h> -#include <LibJS/Interpreter.h> -#include <LibWeb/CSS/Parser/CSSParser.h> -#include <LibWeb/DOM/Element.h> -#include <LibWeb/DOMTreeModel.h> -#include <LibWeb/Dump.h> -#include <LibWeb/InProcessWebView.h> -#include <LibWeb/Layout/BlockBox.h> -#include <LibWeb/Layout/InitialContainingBlockBox.h> -#include <LibWeb/Layout/InlineNode.h> -#include <LibWeb/Layout/Node.h> -#include <LibWeb/Loader/ResourceLoader.h> -#include <LibWeb/OutOfProcessWebView.h> -#include <LibWeb/Page/Frame.h> - -namespace Browser { - -URL url_from_user_input(const String& input) -{ - auto url = URL(input); - if (url.is_valid()) - return url; - - StringBuilder builder; - builder.append("http://"); - builder.append(input); - return URL(builder.build()); -} - -static void start_download(const URL& url) -{ - auto window = GUI::Window::construct(); - window->resize(300, 150); - window->set_title(String::formatted("0% of {}", url.basename())); - window->set_resizable(false); - window->set_main_widget<DownloadWidget>(url); - window->show(); - [[maybe_unused]] auto& unused = window.leak_ref(); -} - -Tab::Tab(Type type) - : m_type(type) -{ - load_from_gml(tab_gml); - - m_toolbar_container = *find_descendant_of_type_named<GUI::ToolBarContainer>("toolbar_container"); - auto& toolbar = *find_descendant_of_type_named<GUI::ToolBar>("toolbar"); - - auto& webview_container = *find_descendant_of_type_named<GUI::Widget>("webview_container"); - - if (m_type == Type::InProcessWebView) - m_page_view = webview_container.add<Web::InProcessWebView>(); - else - m_web_content_view = webview_container.add<Web::OutOfProcessWebView>(); - - m_go_back_action = GUI::CommonActions::make_go_back_action([this](auto&) { go_back(); }, this); - m_go_forward_action = GUI::CommonActions::make_go_forward_action([this](auto&) { go_forward(); }, this); - - toolbar.add_action(*m_go_back_action); - toolbar.add_action(*m_go_forward_action); - - toolbar.add_action(GUI::CommonActions::make_go_home_action([this](auto&) { load(g_home_url); }, this)); - m_reload_action = GUI::CommonActions::make_reload_action([this](auto&) { reload(); }, this); - - toolbar.add_action(*m_reload_action); - - m_location_box = toolbar.add<GUI::TextBox>(); - m_location_box->set_placeholder("Address"); - - m_location_box->on_return_pressed = [this] { - auto url = url_from_user_input(m_location_box->text()); - load(url); - view().set_focus(true); - }; - - m_location_box->add_custom_context_menu_action(GUI::Action::create("Paste & Go", [this](auto&) { - m_location_box->set_text(GUI::Clipboard::the().data()); - m_location_box->on_return_pressed(); - })); - - m_bookmark_button = toolbar.add<GUI::Button>(); - m_bookmark_button->set_button_style(Gfx::ButtonStyle::CoolBar); - m_bookmark_button->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/bookmark-contour.png")); - m_bookmark_button->set_fixed_size(22, 22); - - m_bookmark_button->on_click = [this](auto) { - auto url = this->url().to_string(); - if (BookmarksBarWidget::the().contains_bookmark(url)) { - BookmarksBarWidget::the().remove_bookmark(url); - } else { - BookmarksBarWidget::the().add_bookmark(url, m_title); - } - update_bookmark_button(url); - }; - - hooks().on_load_start = [this](auto& url) { - m_location_box->set_icon(nullptr); - m_location_box->set_text(url.to_string()); - - // don't add to history if back or forward is pressed - if (!m_is_history_navigation) - m_history.push(url); - m_is_history_navigation = false; - - update_actions(); - update_bookmark_button(url.to_string()); - }; - - hooks().on_link_click = [this](auto& url, auto& target, unsigned modifiers) { - if (target == "_blank" || modifiers == Mod_Ctrl) { - on_tab_open_request(url); - } else { - load(url); - } - }; - - m_link_context_menu = GUI::Menu::construct(); - auto link_default_action = GUI::Action::create("Open", [this](auto&) { - hooks().on_link_click(m_link_context_menu_url, "", 0); - }); - m_link_context_menu->add_action(link_default_action); - m_link_context_menu_default_action = link_default_action; - m_link_context_menu->add_action(GUI::Action::create("Open in new tab", [this](auto&) { - hooks().on_link_click(m_link_context_menu_url, "_blank", 0); - })); - m_link_context_menu->add_separator(); - m_link_context_menu->add_action(GUI::Action::create("Copy link", [this](auto&) { - GUI::Clipboard::the().set_plain_text(m_link_context_menu_url.to_string()); - })); - m_link_context_menu->add_separator(); - m_link_context_menu->add_action(GUI::Action::create("Download", [this](auto&) { - start_download(m_link_context_menu_url); - })); - - hooks().on_link_context_menu_request = [this](auto& url, auto& screen_position) { - m_link_context_menu_url = url; - m_link_context_menu->popup(screen_position, m_link_context_menu_default_action); - }; - - m_image_context_menu = GUI::Menu::construct(); - m_image_context_menu->add_action(GUI::Action::create("Open image", [this](auto&) { - hooks().on_link_click(m_image_context_menu_url, "", 0); - })); - m_image_context_menu->add_action(GUI::Action::create("Open image in new tab", [this](auto&) { - hooks().on_link_click(m_image_context_menu_url, "_blank", 0); - })); - m_image_context_menu->add_separator(); - m_image_context_menu->add_action(GUI::Action::create("Copy image", [this](auto&) { - if (m_image_context_menu_bitmap.is_valid()) - GUI::Clipboard::the().set_bitmap(*m_image_context_menu_bitmap.bitmap()); - })); - m_image_context_menu->add_action(GUI::Action::create("Copy image URL", [this](auto&) { - GUI::Clipboard::the().set_plain_text(m_image_context_menu_url.to_string()); - })); - m_image_context_menu->add_separator(); - m_image_context_menu->add_action(GUI::Action::create("Download", [this](auto&) { - start_download(m_image_context_menu_url); - })); - - hooks().on_image_context_menu_request = [this](auto& image_url, auto& screen_position, const Gfx::ShareableBitmap& shareable_bitmap) { - m_image_context_menu_url = image_url; - m_image_context_menu_bitmap = shareable_bitmap; - m_image_context_menu->popup(screen_position); - }; - - hooks().on_link_middle_click = [this](auto& href, auto&, auto) { - hooks().on_link_click(href, "_blank", 0); - }; - - hooks().on_title_change = [this](auto& title) { - if (title.is_null()) { - m_title = url().to_string(); - } else { - m_title = title; - } - if (on_title_change) - on_title_change(m_title); - }; - - hooks().on_favicon_change = [this](auto& icon) { - m_icon = icon; - m_location_box->set_icon(&icon); - if (on_favicon_change) - on_favicon_change(icon); - }; - - // FIXME: Support JS console in multi-process mode. - if (m_type == Type::InProcessWebView) { - hooks().on_set_document = [this](auto* document) { - if (document && m_console_window) { - auto* console_widget = static_cast<ConsoleWidget*>(m_console_window->main_widget()); - console_widget->set_interpreter(document->interpreter().make_weak_ptr()); - } - }; - } - - auto focus_location_box_action = GUI::Action::create( - "Focus location box", { Mod_Ctrl, Key_L }, [this](auto&) { - m_location_box->select_all(); - m_location_box->set_focus(true); - }, - this); - - m_statusbar = *find_descendant_of_type_named<GUI::StatusBar>("statusbar"); - - hooks().on_link_hover = [this](auto& url) { - if (url.is_valid()) - m_statusbar->set_text(url.to_string()); - else - m_statusbar->set_text(""); - }; - - hooks().on_url_drop = [this](auto& url) { - load(url); - }; - - m_menubar = GUI::MenuBar::construct(); - - auto& app_menu = m_menubar->add_menu("Browser"); - app_menu.add_action(WindowActions::the().create_new_tab_action()); - app_menu.add_action(GUI::Action::create( - "Close tab", { Mod_Ctrl, Key_W }, Gfx::Bitmap::load_from_file("/res/icons/16x16/close-tab.png"), [this](auto&) { - on_tab_close_request(*this); - }, - this)); - - app_menu.add_action(*m_reload_action); - app_menu.add_separator(); - app_menu.add_action(GUI::CommonActions::make_quit_action([](auto&) { - GUI::Application::the()->quit(); - })); - - auto& view_menu = m_menubar->add_menu("View"); - view_menu.add_action(GUI::CommonActions::make_fullscreen_action( - [this](auto&) { - window()->set_fullscreen(!window()->is_fullscreen()); - - auto is_fullscreen = window()->is_fullscreen(); - auto* tab_widget = static_cast<GUI::TabWidget*>(parent_widget()); - tab_widget->set_bar_visible(!is_fullscreen && tab_widget->children().size() > 1); - m_toolbar_container->set_visible(!is_fullscreen); - m_statusbar->set_visible(!is_fullscreen); - }, - this)); - - auto view_source_action = GUI::Action::create( - "View source", { Mod_Ctrl, Key_U }, [this](auto&) { - if (m_type == Type::InProcessWebView) { - ASSERT(m_page_view->document()); - auto url = m_page_view->document()->url().to_string(); - auto source = m_page_view->document()->source(); - auto window = GUI::Window::construct(); - auto& editor = window->set_main_widget<GUI::TextEditor>(); - editor.set_text(source); - editor.set_mode(GUI::TextEditor::ReadOnly); - editor.set_ruler_visible(true); - window->resize(640, 480); - window->set_title(url); - window->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-text.png")); - window->show(); - [[maybe_unused]] auto& unused = window.leak_ref(); - } else { - TODO(); - } - }, - this); - - auto inspect_dom_tree_action = GUI::Action::create( - "Inspect DOM tree", { Mod_None, Key_F12 }, [this](auto&) { - if (m_type == Type::InProcessWebView) { - if (!m_dom_inspector_window) { - m_dom_inspector_window = GUI::Window::construct(); - m_dom_inspector_window->resize(300, 500); - m_dom_inspector_window->set_title("DOM inspector"); - m_dom_inspector_window->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/inspector-object.png")); - m_dom_inspector_window->set_main_widget<InspectorWidget>(); - } - auto* inspector_widget = static_cast<InspectorWidget*>(m_dom_inspector_window->main_widget()); - inspector_widget->set_document(m_page_view->document()); - m_dom_inspector_window->show(); - m_dom_inspector_window->move_to_front(); - } else { - TODO(); - } - }, - this); - - auto& inspect_menu = m_menubar->add_menu("Inspect"); - inspect_menu.add_action(*view_source_action); - inspect_menu.add_action(*inspect_dom_tree_action); - - inspect_menu.add_action(GUI::Action::create( - "Open JS Console", { Mod_Ctrl, Key_I }, [this](auto&) { - if (m_type == Type::InProcessWebView) { - if (!m_console_window) { - m_console_window = GUI::Window::construct(); - m_console_window->resize(500, 300); - m_console_window->set_title("JS Console"); - m_console_window->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-javascript.png")); - m_console_window->set_main_widget<ConsoleWidget>(); - } - auto* console_widget = static_cast<ConsoleWidget*>(m_console_window->main_widget()); - console_widget->set_interpreter(m_page_view->document()->interpreter().make_weak_ptr()); - m_console_window->show(); - m_console_window->move_to_front(); - } else { - TODO(); - } - }, - this)); - - auto& debug_menu = m_menubar->add_menu("Debug"); - debug_menu.add_action(GUI::Action::create( - "Dump DOM tree", [this](auto&) { - if (m_type == Type::InProcessWebView) { - Web::dump_tree(*m_page_view->document()); - } else { - TODO(); - } - }, - this)); - debug_menu.add_action(GUI::Action::create( - "Dump Layout tree", [this](auto&) { - if (m_type == Type::InProcessWebView) { - Web::dump_tree(*m_page_view->document()->layout_node()); - } else { - TODO(); - } - }, - this)); - debug_menu.add_action(GUI::Action::create( - "Dump Style sheets", [this](auto&) { - if (m_type == Type::InProcessWebView) { - for (auto& sheet : m_page_view->document()->style_sheets().sheets()) { - Web::dump_sheet(sheet); - } - } else { - TODO(); - } - }, - this)); - debug_menu.add_action(GUI::Action::create("Dump history", { Mod_Ctrl, Key_H }, [&](auto&) { - m_history.dump(); - })); - debug_menu.add_separator(); - auto line_box_borders_action = GUI::Action::create_checkable( - "Line box borders", [this](auto& action) { - if (m_type == Type::InProcessWebView) { - m_page_view->set_should_show_line_box_borders(action.is_checked()); - m_page_view->update(); - } else { - TODO(); - } - }, - this); - line_box_borders_action->set_checked(false); - debug_menu.add_action(line_box_borders_action); - - debug_menu.add_separator(); - debug_menu.add_action(GUI::Action::create("Collect garbage", { Mod_Ctrl | Mod_Shift, Key_G }, [this](auto&) { - if (m_type == Type::InProcessWebView) { - if (auto* document = m_page_view->document()) { - document->interpreter().heap().collect_garbage(JS::Heap::CollectionType::CollectGarbage, true); - } - } else { - TODO(); - } - })); - - auto& bookmarks_menu = m_menubar->add_menu("Bookmarks"); - bookmarks_menu.add_action(WindowActions::the().show_bookmarks_bar_action()); - - auto& help_menu = m_menubar->add_menu("Help"); - help_menu.add_action(WindowActions::the().about_action()); - - m_tab_context_menu = GUI::Menu::construct(); - m_tab_context_menu->add_action(GUI::Action::create("Reload Tab", [this](auto&) { - m_reload_action->activate(); - })); - m_tab_context_menu->add_action(GUI::Action::create("Close Tab", [this](auto&) { - on_tab_close_request(*this); - })); - - m_page_context_menu = GUI::Menu::construct(); - m_page_context_menu->add_action(*m_go_back_action); - m_page_context_menu->add_action(*m_go_forward_action); - m_page_context_menu->add_action(*m_reload_action); - m_page_context_menu->add_separator(); - m_page_context_menu->add_action(*view_source_action); - m_page_context_menu->add_action(*inspect_dom_tree_action); - hooks().on_context_menu_request = [&](auto& screen_position) { - m_page_context_menu->popup(screen_position); - }; -} - -Tab::~Tab() -{ -} - -void Tab::load(const URL& url, LoadType load_type) -{ - m_is_history_navigation = (load_type == LoadType::HistoryNavigation); - - if (m_type == Type::InProcessWebView) - m_page_view->load(url); - else - m_web_content_view->load(url); -} - -URL Tab::url() const -{ - if (m_type == Type::InProcessWebView) - return m_page_view->url(); - return m_web_content_view->url(); -} - -void Tab::reload() -{ - load(url()); -} - -void Tab::go_back() -{ - m_history.go_back(); - update_actions(); - load(m_history.current(), LoadType::HistoryNavigation); -} - -void Tab::go_forward() -{ - m_history.go_forward(); - update_actions(); - load(m_history.current(), LoadType::HistoryNavigation); -} - -void Tab::update_actions() -{ - m_go_back_action->set_enabled(m_history.can_go_back()); - m_go_forward_action->set_enabled(m_history.can_go_forward()); -} - -void Tab::update_bookmark_button(const String& url) -{ - if (BookmarksBarWidget::the().contains_bookmark(url)) { - m_bookmark_button->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/bookmark-filled.png")); - m_bookmark_button->set_tooltip("Remove Bookmark"); - } else { - m_bookmark_button->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/bookmark-contour.png")); - m_bookmark_button->set_tooltip("Add Bookmark"); - } -} - -void Tab::did_become_active() -{ - Web::ResourceLoader::the().on_load_counter_change = [this] { - if (Web::ResourceLoader::the().pending_loads() == 0) { - m_statusbar->set_text(""); - return; - } - m_statusbar->set_text(String::formatted("Loading ({} pending resources...)", Web::ResourceLoader::the().pending_loads())); - }; - - BookmarksBarWidget::the().on_bookmark_click = [this](auto& url, unsigned modifiers) { - if (modifiers & Mod_Ctrl) - on_tab_open_request(url); - else - load(url); - }; - - BookmarksBarWidget::the().on_bookmark_hover = [this](auto&, auto& url) { - m_statusbar->set_text(url); - }; - - BookmarksBarWidget::the().remove_from_parent(); - m_toolbar_container->add_child(BookmarksBarWidget::the()); - - auto is_fullscreen = window()->is_fullscreen(); - m_toolbar_container->set_visible(!is_fullscreen); - m_statusbar->set_visible(!is_fullscreen); - - GUI::Application::the()->set_menubar(m_menubar); -} - -void Tab::context_menu_requested(const Gfx::IntPoint& screen_position) -{ - m_tab_context_menu->popup(screen_position); -} - -GUI::Widget& Tab::view() -{ - if (m_type == Type::InProcessWebView) - return *m_page_view; - return *m_web_content_view; -} - -Web::WebViewHooks& Tab::hooks() -{ - if (m_type == Type::InProcessWebView) - return *m_page_view; - return *m_web_content_view; -} - -} diff --git a/Applications/Browser/Tab.gml b/Applications/Browser/Tab.gml deleted file mode 100644 index 24c7456d77..0000000000 --- a/Applications/Browser/Tab.gml +++ /dev/null @@ -1,22 +0,0 @@ -@GUI::Widget { - layout: @GUI::VerticalBoxLayout { - } - - @GUI::ToolBarContainer { - name: "toolbar_container" - - @GUI::ToolBar { - name: "toolbar" - } - } - - @GUI::Widget { - name: "webview_container" - layout: @GUI::VerticalBoxLayout { - } - } - - @GUI::StatusBar { - name: "statusbar" - } -} diff --git a/Applications/Browser/Tab.h b/Applications/Browser/Tab.h deleted file mode 100644 index 79f86e4931..0000000000 --- a/Applications/Browser/Tab.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2020, 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 "History.h" -#include <AK/URL.h> -#include <LibGUI/Widget.h> -#include <LibGfx/ShareableBitmap.h> -#include <LibHTTP/HttpJob.h> -#include <LibWeb/Forward.h> - -namespace Web { -class OutOfProcessWebView; -class WebViewHooks; -} - -namespace Browser { - -class Tab final : public GUI::Widget { - C_OBJECT(Tab); - -public: - enum class Type { - InProcessWebView, - OutOfProcessWebView, - }; - - virtual ~Tab() override; - - URL url() const; - - enum class LoadType { - Normal, - HistoryNavigation, - }; - - void load(const URL&, LoadType = LoadType::Normal); - void reload(); - void go_back(); - void go_forward(); - - void did_become_active(); - void context_menu_requested(const Gfx::IntPoint& screen_position); - - Function<void(String)> on_title_change; - Function<void(const URL&)> on_tab_open_request; - Function<void(Tab&)> on_tab_close_request; - Function<void(const Gfx::Bitmap&)> on_favicon_change; - - const String& title() const { return m_title; } - const Gfx::Bitmap* icon() const { return m_icon; } - - GUI::Widget& view(); - -private: - explicit Tab(Type); - - Web::WebViewHooks& hooks(); - void update_actions(); - void update_bookmark_button(const String& url); - - Type m_type; - - History m_history; - - RefPtr<Web::InProcessWebView> m_page_view; - RefPtr<Web::OutOfProcessWebView> m_web_content_view; - - RefPtr<GUI::Action> m_go_back_action; - RefPtr<GUI::Action> m_go_forward_action; - RefPtr<GUI::Action> m_reload_action; - RefPtr<GUI::TextBox> m_location_box; - RefPtr<GUI::Button> m_bookmark_button; - RefPtr<GUI::Window> m_dom_inspector_window; - RefPtr<GUI::Window> m_console_window; - RefPtr<GUI::StatusBar> m_statusbar; - RefPtr<GUI::MenuBar> m_menubar; - RefPtr<GUI::ToolBarContainer> m_toolbar_container; - - RefPtr<GUI::Menu> m_link_context_menu; - RefPtr<GUI::Action> m_link_context_menu_default_action; - URL m_link_context_menu_url; - - RefPtr<GUI::Menu> m_image_context_menu; - Gfx::ShareableBitmap m_image_context_menu_bitmap; - URL m_image_context_menu_url; - - RefPtr<GUI::Menu> m_tab_context_menu; - RefPtr<GUI::Menu> m_page_context_menu; - - String m_title; - RefPtr<const Gfx::Bitmap> m_icon; - - bool m_is_history_navigation { false }; -}; - -URL url_from_user_input(const String& input); - -} diff --git a/Applications/Browser/WindowActions.cpp b/Applications/Browser/WindowActions.cpp deleted file mode 100644 index ca0b12bd84..0000000000 --- a/Applications/Browser/WindowActions.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2020, 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 "WindowActions.h" -#include <LibGUI/Icon.h> -#include <LibGUI/Window.h> -#include <LibGfx/Bitmap.h> - -namespace Browser { - -static WindowActions* s_the; - -WindowActions& WindowActions::the() -{ - ASSERT(s_the); - return *s_the; -} - -WindowActions::WindowActions(GUI::Window& window) -{ - ASSERT(!s_the); - s_the = this; - m_create_new_tab_action = GUI::Action::create( - "New tab", { Mod_Ctrl, Key_T }, Gfx::Bitmap::load_from_file("/res/icons/16x16/new-tab.png"), [this](auto&) { - if (on_create_new_tab) - on_create_new_tab(); - }, - &window); - - m_next_tab_action = GUI::Action::create( - "Next tab", { Mod_Ctrl, Key_PageDown }, [this](auto&) { - if (on_next_tab) - on_next_tab(); - }, - &window); - - m_previous_tab_action = GUI::Action::create( - "Previous tab", { Mod_Ctrl, Key_PageUp }, [this](auto&) { - if (on_previous_tab) - on_previous_tab(); - }, - &window); - - m_about_action = GUI::Action::create( - "About Browser", GUI::Icon::default_icon("app-browser").bitmap_for_size(16), [this](const GUI::Action&) { - if (on_about) - on_about(); - }, - &window); - m_show_bookmarks_bar_action = GUI::Action::create_checkable( - "Show bookmarks bar", - [this](auto& action) { - if (on_show_bookmarks_bar) - on_show_bookmarks_bar(action); - }, - &window); -} - -} diff --git a/Applications/Browser/WindowActions.h b/Applications/Browser/WindowActions.h deleted file mode 100644 index 3212321837..0000000000 --- a/Applications/Browser/WindowActions.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2020, 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/Action.h> - -namespace Browser { - -class WindowActions { -public: - static WindowActions& the(); - - WindowActions(GUI::Window&); - - Function<void()> on_create_new_tab; - Function<void()> on_next_tab; - Function<void()> on_previous_tab; - Function<void()> on_about; - Function<void(GUI::Action&)> on_show_bookmarks_bar; - - GUI::Action& create_new_tab_action() { return *m_create_new_tab_action; } - GUI::Action& next_tab_action() { return *m_next_tab_action; } - GUI::Action& previous_tab_action() { return *m_previous_tab_action; } - GUI::Action& about_action() { return *m_about_action; } - GUI::Action& show_bookmarks_bar_action() { return *m_show_bookmarks_bar_action; } - -private: - RefPtr<GUI::Action> m_create_new_tab_action; - RefPtr<GUI::Action> m_next_tab_action; - RefPtr<GUI::Action> m_previous_tab_action; - RefPtr<GUI::Action> m_about_action; - RefPtr<GUI::Action> m_show_bookmarks_bar_action; -}; - -} diff --git a/Applications/Browser/main.cpp b/Applications/Browser/main.cpp deleted file mode 100644 index d83b68f5f4..0000000000 --- a/Applications/Browser/main.cpp +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "BookmarksBarWidget.h" -#include "Browser.h" -#include "InspectorWidget.h" -#include "Tab.h" -#include "WindowActions.h" -#include <AK/StringBuilder.h> -#include <Applications/Browser/BrowserWindowGML.h> -#include <LibCore/ArgsParser.h> -#include <LibCore/ConfigFile.h> -#include <LibCore/File.h> -#include <LibCore/StandardPaths.h> -#include <LibDesktop/Launcher.h> -#include <LibGUI/AboutDialog.h> -#include <LibGUI/Application.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Icon.h> -#include <LibGUI/TabWidget.h> -#include <LibGUI/Window.h> -#include <LibGfx/Bitmap.h> -#include <LibWeb/Loader/ContentFilter.h> -#include <LibWeb/Loader/ResourceLoader.h> -#include <stdio.h> -#include <stdlib.h> - -namespace Browser { - -String g_home_url; -bool g_multi_process = false; - -static String bookmarks_file_path() -{ - StringBuilder builder; - builder.append(Core::StandardPaths::config_directory()); - builder.append("/bookmarks.json"); - return builder.to_string(); -} - -} - -int main(int argc, char** argv) -{ - if (getuid() == 0) { - warnln("Refusing to run as root"); - return 1; - } - - if (pledge("stdio shared_buffer accept unix cpath rpath wpath fattr sendfd recvfd", nullptr) < 0) { - perror("pledge"); - return 1; - } - - const char* specified_url = nullptr; - - Core::ArgsParser args_parser; - args_parser.add_option(Browser::g_multi_process, "Multi-process mode", "multi-process", 'm'); - args_parser.add_positional_argument(specified_url, "URL to open", "url", Core::ArgsParser::Required::No); - args_parser.parse(argc, argv); - - auto app = GUI::Application::construct(argc, argv); - - // Connect to the ProtocolServer immediately so we can drop the "unix" pledge. - Web::ResourceLoader::the(); - - // Connect to LaunchServer immediately and let it know that we won't ask for anything other than opening - // the user's downloads directory. - // FIXME: This should go away with a standalone download manager at some point. - if (!Desktop::Launcher::add_allowed_url(URL::create_with_file_protocol(Core::StandardPaths::downloads_directory())) - || !Desktop::Launcher::seal_allowlist()) { - warnln("Failed to set up allowed launch URLs"); - return 1; - } - - if (pledge("stdio shared_buffer accept unix cpath rpath wpath sendfd recvfd", nullptr) < 0) { - perror("pledge"); - return 1; - } - - if (unveil("/home", "rwc") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/res", "r") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/etc/passwd", "r") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/tmp/portal/image", "rw") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/tmp/portal/webcontent", "rw") < 0) { - perror("unveil"); - return 1; - } - - unveil(nullptr, nullptr); - - auto app_icon = GUI::Icon::default_icon("app-browser"); - - auto m_config = Core::ConfigFile::get_for_app("Browser"); - Browser::g_home_url = m_config->read_entry("Preferences", "Home", "about:blank"); - - auto ad_filter_list_or_error = Core::File::open(String::formatted("{}/BrowserContentFilters.txt", Core::StandardPaths::config_directory()), Core::IODevice::ReadOnly); - if (!ad_filter_list_or_error.is_error()) { - auto& ad_filter_list = *ad_filter_list_or_error.value(); - while (!ad_filter_list.eof()) { - auto line = ad_filter_list.read_line(); - if (line.is_empty()) - continue; - Web::ContentFilter::the().add_pattern(line); - } - } - - bool bookmarksbar_enabled = true; - auto bookmarks_bar = Browser::BookmarksBarWidget::construct(Browser::bookmarks_file_path(), bookmarksbar_enabled); - - auto window = GUI::Window::construct(); - window->resize(640, 480); - window->set_icon(app_icon.bitmap_for_size(16)); - window->set_title("Browser"); - - auto& widget = window->set_main_widget<GUI::Widget>(); - widget.load_from_gml(browser_window_gml); - - auto& tab_widget = *widget.find_descendant_of_type_named<GUI::TabWidget>("tab_widget"); - - auto default_favicon = Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-html.png"); - ASSERT(default_favicon); - - tab_widget.on_change = [&](auto& active_widget) { - auto& tab = static_cast<Browser::Tab&>(active_widget); - window->set_title(String::formatted("{} - Browser", tab.title())); - tab.did_become_active(); - }; - - tab_widget.on_middle_click = [&](auto& clicked_widget) { - auto& tab = static_cast<Browser::Tab&>(clicked_widget); - tab.on_tab_close_request(tab); - }; - - tab_widget.on_context_menu_request = [&](auto& clicked_widget, const GUI::ContextMenuEvent& context_menu_event) { - auto& tab = static_cast<Browser::Tab&>(clicked_widget); - tab.context_menu_requested(context_menu_event.screen_position()); - }; - - Browser::WindowActions window_actions(*window); - - Function<void(URL url, bool activate)> create_new_tab; - create_new_tab = [&](auto url, auto activate) { - auto type = Browser::g_multi_process ? Browser::Tab::Type::OutOfProcessWebView : Browser::Tab::Type::InProcessWebView; - auto& new_tab = tab_widget.add_tab<Browser::Tab>("New tab", type); - - tab_widget.set_bar_visible(!window->is_fullscreen() && tab_widget.children().size() > 1); - tab_widget.set_tab_icon(new_tab, default_favicon); - - new_tab.on_title_change = [&](auto title) { - tab_widget.set_tab_title(new_tab, title); - if (tab_widget.active_widget() == &new_tab) - window->set_title(String::formatted("{} - Browser", title)); - }; - - new_tab.on_favicon_change = [&](auto& bitmap) { - tab_widget.set_tab_icon(new_tab, &bitmap); - }; - - new_tab.on_tab_open_request = [&](auto& url) { - create_new_tab(url, true); - }; - - new_tab.on_tab_close_request = [&](auto& tab) { - tab_widget.deferred_invoke([&](auto&) { - tab_widget.remove_tab(tab); - tab_widget.set_bar_visible(!window->is_fullscreen() && tab_widget.children().size() > 1); - if (tab_widget.children().is_empty()) - app->quit(); - }); - }; - - new_tab.load(url); - - dbgln("Added new tab {:p}, loading {}", &new_tab, url); - - if (activate) - tab_widget.set_active_widget(&new_tab); - }; - - URL first_url = Browser::g_home_url; - if (specified_url) { - if (Core::File::exists(specified_url)) { - first_url = URL::create_with_file_protocol(Core::File::real_path_for(specified_url)); - } else { - first_url = Browser::url_from_user_input(specified_url); - } - } - - window_actions.on_create_new_tab = [&] { - create_new_tab(Browser::g_home_url, true); - }; - - window_actions.on_next_tab = [&] { - tab_widget.activate_next_tab(); - }; - - window_actions.on_previous_tab = [&] { - tab_widget.activate_previous_tab(); - }; - - window_actions.on_about = [&] { - GUI::AboutDialog::show("Browser", app_icon.bitmap_for_size(32), window); - }; - - window_actions.on_show_bookmarks_bar = [&](auto& action) { - Browser::BookmarksBarWidget::the().set_visible(action.is_checked()); - }; - window_actions.show_bookmarks_bar_action().set_checked(bookmarksbar_enabled); - - create_new_tab(first_url, true); - window->show(); - - return app->exec(); -} diff --git a/Applications/CMakeLists.txt b/Applications/CMakeLists.txt deleted file mode 100644 index 41803202e6..0000000000 --- a/Applications/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -add_subdirectory(About) -add_subdirectory(Browser) -add_subdirectory(Calculator) -add_subdirectory(Calendar) -add_subdirectory(CrashReporter) -add_subdirectory(Debugger) -add_subdirectory(DisplaySettings) -add_subdirectory(FileManager) -add_subdirectory(FontEditor) -add_subdirectory(Help) -add_subdirectory(HexEditor) -add_subdirectory(IRCClient) -add_subdirectory(KeyboardMapper) -add_subdirectory(KeyboardSettings) -add_subdirectory(MouseSettings) -add_subdirectory(Piano) -add_subdirectory(PixelPaint) -add_subdirectory(QuickShow) -add_subdirectory(SoundPlayer) -add_subdirectory(SpaceAnalyzer) -add_subdirectory(Spreadsheet) -add_subdirectory(SystemMonitor) -add_subdirectory(ThemeEditor) -add_subdirectory(Terminal) -add_subdirectory(TextEditor) -add_subdirectory(Welcome) diff --git a/Applications/Calculator/CMakeLists.txt b/Applications/Calculator/CMakeLists.txt deleted file mode 100644 index d72624c308..0000000000 --- a/Applications/Calculator/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -compile_gml(CalculatorWindow.gml CalculatorGML.h calculator_gml) -set(SOURCES - main.cpp - Calculator.cpp - CalculatorWidget.cpp - Keypad.cpp - CalculatorGML.h -) - -serenity_app(Calculator ICON app-calculator) -target_link_libraries(Calculator LibGUI) diff --git a/Applications/Calculator/Calculator.cpp b/Applications/Calculator/Calculator.cpp deleted file mode 100644 index c324fb84b9..0000000000 --- a/Applications/Calculator/Calculator.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@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 "Calculator.h" -#include <AK/Assertions.h> -#include <math.h> - -Calculator::Calculator() -{ -} - -Calculator::~Calculator() -{ -} - -double Calculator::begin_operation(Operation operation, double argument) -{ - double res = 0.0; - - switch (operation) { - case Operation::None: - ASSERT_NOT_REACHED(); - - case Operation::Add: - case Operation::Subtract: - case Operation::Multiply: - case Operation::Divide: - m_saved_argument = argument; - m_operation_in_progress = operation; - return argument; - - case Operation::Sqrt: - if (argument < 0.0) { - m_has_error = true; - return argument; - } - res = sqrt(argument); - clear_operation(); - break; - case Operation::Inverse: - if (argument == 0.0) { - m_has_error = true; - return argument; - } - res = 1 / argument; - clear_operation(); - break; - case Operation::Percent: - res = argument * 0.01; - break; - case Operation::ToggleSign: - res = -argument; - break; - - case Operation::MemClear: - m_mem = 0.0; - res = argument; - break; - case Operation::MemRecall: - res = m_mem; - break; - case Operation::MemSave: - m_mem = argument; - res = argument; - break; - case Operation::MemAdd: - m_mem += argument; - res = m_mem; - break; - } - - return res; -} - -double Calculator::finish_operation(double argument) -{ - double res = 0.0; - - switch (m_operation_in_progress) { - case Operation::None: - return argument; - - case Operation::Add: - res = m_saved_argument + argument; - break; - case Operation::Subtract: - res = m_saved_argument - argument; - break; - case Operation::Multiply: - res = m_saved_argument * argument; - break; - case Operation::Divide: - if (argument == 0.0) { - m_has_error = true; - return argument; - } - res = m_saved_argument / argument; - break; - - case Operation::Sqrt: - case Operation::Inverse: - case Operation::Percent: - case Operation::ToggleSign: - case Operation::MemClear: - case Operation::MemRecall: - case Operation::MemSave: - case Operation::MemAdd: - ASSERT_NOT_REACHED(); - } - - clear_operation(); - return res; -} - -void Calculator::clear_operation() -{ - m_operation_in_progress = Operation::None; - m_saved_argument = 0.0; - clear_error(); -} diff --git a/Applications/Calculator/Calculator.h b/Applications/Calculator/Calculator.h deleted file mode 100644 index c53e31c397..0000000000 --- a/Applications/Calculator/Calculator.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@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 - -// This type implements the regular calculator -// behavior, such as performing arithmetic -// operations and providing a memory cell. -// It does not deal with number input; you -// have to pass in already parsed double -// values. - -class Calculator final { -public: - Calculator(); - ~Calculator(); - - enum class Operation { - None, - Add, - Subtract, - Multiply, - Divide, - - Sqrt, - Inverse, - Percent, - ToggleSign, - - MemClear, - MemRecall, - MemSave, - MemAdd - }; - - double begin_operation(Operation, double); - double finish_operation(double); - - bool has_error() const { return m_has_error; } - - void clear_operation(); - void clear_error() { m_has_error = false; } - -private: - Operation m_operation_in_progress { Operation::None }; - double m_saved_argument { 0.0 }; - double m_mem { 0.0 }; - bool m_has_error { false }; -}; diff --git a/Applications/Calculator/CalculatorWidget.cpp b/Applications/Calculator/CalculatorWidget.cpp deleted file mode 100644 index 083edae93b..0000000000 --- a/Applications/Calculator/CalculatorWidget.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org> - * Copyright (c) 2021 Glenford Williams <gw_dev@outlook.com> - * 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 "CalculatorWidget.h" -#include "Applications/Calculator/CalculatorGML.h" -#include <AK/Assertions.h> -#include <LibGUI/Button.h> -#include <LibGUI/Label.h> -#include <LibGUI/TextBox.h> -#include <LibGfx/Font.h> -#include <LibGfx/FontDatabase.h> -#include <LibGfx/Palette.h> - -CalculatorWidget::CalculatorWidget() -{ - load_from_gml(calculator_gml); - - m_entry = *find_descendant_of_type_named<GUI::TextBox>("entry_textbox"); - m_entry->set_relative_rect(5, 5, 244, 26); - m_entry->set_text_alignment(Gfx::TextAlignment::CenterRight); - m_entry->set_font(Gfx::FontDatabase::default_fixed_width_font()); - - m_label = *find_descendant_of_type_named<GUI::Label>("label"); - - m_label->set_frame_shadow(Gfx::FrameShadow::Sunken); - m_label->set_frame_shape(Gfx::FrameShape::Container); - m_label->set_frame_thickness(2); - - for (int i = 0; i < 10; i++) { - m_digit_button[i] = *find_descendant_of_type_named<GUI::Button>(String::formatted("{}_button", i)); - add_digit_button(*m_digit_button[i], i); - } - - m_mem_add_button = *find_descendant_of_type_named<GUI::Button>("mem_add_button"); - add_operation_button(*m_mem_add_button, Calculator::Operation::MemAdd); - - m_mem_save_button = *find_descendant_of_type_named<GUI::Button>("mem_save_button"); - add_operation_button(*m_mem_save_button, Calculator::Operation::MemSave); - - m_mem_recall_button = *find_descendant_of_type_named<GUI::Button>("mem_recall_button"); - add_operation_button(*m_mem_recall_button, Calculator::Operation::MemRecall); - - m_mem_clear_button = *find_descendant_of_type_named<GUI::Button>("mem_clear_button"); - add_operation_button(*m_mem_clear_button, Calculator::Operation::MemClear); - - m_clear_button = *find_descendant_of_type_named<GUI::Button>("clear_button"); - m_clear_button->on_click = [this](auto) { - m_keypad.set_value(0.0); - m_calculator.clear_operation(); - update_display(); - }; - - m_clear_error_button = *find_descendant_of_type_named<GUI::Button>("clear_error_button"); - m_clear_error_button->on_click = [this](auto) { - m_keypad.set_value(0.0); - update_display(); - }; - - m_backspace_button = *find_descendant_of_type_named<GUI::Button>("backspace_button"); - m_backspace_button->on_click = [this](auto) { - m_keypad.type_backspace(); - update_display(); - }; - - m_decimal_point_button = *find_descendant_of_type_named<GUI::Button>("decimal_button"); - m_decimal_point_button->on_click = [this](auto) { - m_keypad.type_decimal_point(); - update_display(); - }; - - m_sign_button = *find_descendant_of_type_named<GUI::Button>("sign_button"); - add_operation_button(*m_sign_button, Calculator::Operation::ToggleSign); - - m_add_button = *find_descendant_of_type_named<GUI::Button>("add_button"); - add_operation_button(*m_add_button, Calculator::Operation::Add); - - m_subtract_button = *find_descendant_of_type_named<GUI::Button>("subtract_button"); - add_operation_button(*m_subtract_button, Calculator::Operation::Subtract); - - m_multiply_button = *find_descendant_of_type_named<GUI::Button>("multiply_button"); - add_operation_button(*m_multiply_button, Calculator::Operation::Multiply); - - m_divide_button = *find_descendant_of_type_named<GUI::Button>("divide_button"); - add_operation_button(*m_divide_button, Calculator::Operation::Divide); - - m_sqrt_button = *find_descendant_of_type_named<GUI::Button>("sqrt_button"); - add_operation_button(*m_sqrt_button, Calculator::Operation::Sqrt); - - m_inverse_button = *find_descendant_of_type_named<GUI::Button>("inverse_button"); - add_operation_button(*m_inverse_button, Calculator::Operation::Inverse); - - m_percent_button = *find_descendant_of_type_named<GUI::Button>("mod_button"); - add_operation_button(*m_percent_button, Calculator::Operation::Percent); - - m_equals_button = *find_descendant_of_type_named<GUI::Button>("equal_button"); - m_equals_button->on_click = [this](auto) { - double argument = m_keypad.value(); - double res = m_calculator.finish_operation(argument); - m_keypad.set_value(res); - update_display(); - }; -} - -CalculatorWidget::~CalculatorWidget() -{ -} - -void CalculatorWidget::add_operation_button(GUI::Button& button, Calculator::Operation operation) -{ - button.on_click = [this, operation](auto) { - double argument = m_keypad.value(); - double res = m_calculator.begin_operation(operation, argument); - m_keypad.set_value(res); - update_display(); - }; -} - -void CalculatorWidget::add_digit_button(GUI::Button& button, int digit) -{ - button.on_click = [this, digit](auto) { - m_keypad.type_digit(digit); - update_display(); - }; -} - -void CalculatorWidget::update_display() -{ - m_entry->set_text(m_keypad.to_string()); - if (m_calculator.has_error()) - m_label->set_text("E"); - else - m_label->set_text(""); -} - -void CalculatorWidget::keydown_event(GUI::KeyEvent& event) -{ - //Clear button selection when we are typing - m_equals_button->set_focus(true); - m_equals_button->set_focus(false); - - if (event.key() == KeyCode::Key_Return) { - m_keypad.set_value(m_calculator.finish_operation(m_keypad.value())); - - } else if (event.key() >= KeyCode::Key_0 && event.key() <= KeyCode::Key_9) { - m_keypad.type_digit(atoi(event.text().characters())); - - } else if (event.key() == KeyCode::Key_Period) { - m_keypad.type_decimal_point(); - - } else if (event.key() == KeyCode::Key_Escape) { - m_keypad.set_value(0.0); - m_calculator.clear_operation(); - - } else if (event.key() == KeyCode::Key_Backspace) { - m_keypad.type_backspace(); - - } else { - Calculator::Operation operation; - - switch (event.key()) { - case KeyCode::Key_Plus: - operation = Calculator::Operation::Add; - break; - case KeyCode::Key_Minus: - operation = Calculator::Operation::Subtract; - break; - case KeyCode::Key_Asterisk: - operation = Calculator::Operation::Multiply; - break; - case KeyCode::Key_Slash: - operation = Calculator::Operation::Divide; - break; - case KeyCode::Key_Percent: - operation = Calculator::Operation::Percent; - break; - default: - return; - } - - m_keypad.set_value(m_calculator.begin_operation(operation, m_keypad.value())); - } - - update_display(); -} diff --git a/Applications/Calculator/CalculatorWidget.h b/Applications/Calculator/CalculatorWidget.h deleted file mode 100644 index 31d9548b3c..0000000000 --- a/Applications/Calculator/CalculatorWidget.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org> - * Copyright (c) 2021 Glenford Williams <gw_dev@outlook.com> - * 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 "Calculator.h" -#include "Keypad.h" -#include <AK/Vector.h> -#include <LibGUI/Widget.h> - -class CalculatorWidget final : public GUI::Widget { - C_OBJECT(CalculatorWidget) -public: - virtual ~CalculatorWidget() override; - -private: - CalculatorWidget(); - void add_operation_button(GUI::Button&, Calculator::Operation); - void add_digit_button(GUI::Button&, int digit); - - void update_display(); - - virtual void keydown_event(GUI::KeyEvent&) override; - - Calculator m_calculator; - Keypad m_keypad; - - RefPtr<GUI::TextBox> m_entry; - RefPtr<GUI::Label> m_label; - - RefPtr<GUI::Button> m_digit_button[10]; - RefPtr<GUI::Button> m_mem_add_button; - RefPtr<GUI::Button> m_mem_save_button; - RefPtr<GUI::Button> m_mem_recall_button; - RefPtr<GUI::Button> m_mem_clear_button; - RefPtr<GUI::Button> m_clear_button; - RefPtr<GUI::Button> m_clear_error_button; - RefPtr<GUI::Button> m_backspace_button; - RefPtr<GUI::Button> m_decimal_point_button; - RefPtr<GUI::Button> m_sign_button; - RefPtr<GUI::Button> m_add_button; - RefPtr<GUI::Button> m_subtract_button; - RefPtr<GUI::Button> m_multiply_button; - RefPtr<GUI::Button> m_divide_button; - RefPtr<GUI::Button> m_sqrt_button; - RefPtr<GUI::Button> m_inverse_button; - RefPtr<GUI::Button> m_percent_button; - RefPtr<GUI::Button> m_equals_button; -}; diff --git a/Applications/Calculator/CalculatorWindow.gml b/Applications/Calculator/CalculatorWindow.gml deleted file mode 100644 index 4763a4d8c2..0000000000 --- a/Applications/Calculator/CalculatorWindow.gml +++ /dev/null @@ -1,272 +0,0 @@ -@GUI::Widget { - fixed_width: 254 - fixed_height: 213 - fill_with_background_color: true - - layout: @GUI::VerticalBoxLayout { - margins: [10, 0, 10, 0] - } - - @GUI::TextBox { - name: "entry_textbox" - } - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout { - } - - @GUI::Label { - name: "label" - fixed_width: 35 - fixed_height: 27 - } - - @GUI::Widget { - fixed_width: 5 - } - - @GUI::Button { - name: "backspace_button" - text: "Backspace" - fixed_width: 65 - fixed_height: 28 - foreground_color: "brown" - } - - @GUI::Button { - name: "clear_error_button" - text: "CE" - fixed_width: 55 - fixed_height: 28 - foreground_color: "brown" - } - - @GUI::Button { - name: "clear_button" - text: "C" - fixed_width: 60 - fixed_height: 28 - foreground_color: "brown" - } - } - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout { - } - - @GUI::Button { - name: "mem_clear_button" - text: "MC" - fixed_width: 35 - fixed_height: 28 - foreground_color: "red" - } - - @GUI::Widget { - fixed_width: 5 - } - - @GUI::Button { - name: "7_button" - text: "7" - fixed_width: 35 - fixed_height: 28 - foreground_color: "blue" - } - - @GUI::Button { - name: "8_button" - text: "8" - fixed_width: 35 - fixed_height: 28 - foreground_color: "blue" - } - - @GUI::Button { - name: "9_button" - text: "9" - fixed_width: 35 - fixed_height: 28 - } - - @GUI::Button { - name: "divide_button" - text: "/" - fixed_width: 35 - fixed_height: 28 - } - - @GUI::Button { - name: "sqrt_button" - text: "sqrt" - fixed_width: 35 - fixed_height: 28 - foreground_color: "blue" - } - } - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout {} - - @GUI::Button { - name: "mem_recall_button" - text: "MR" - fixed_width: 35 - fixed_height: 28 - foreground_color: "red" - } - - @GUI::Widget { - fixed_width: 5 - } - - @GUI::Button { - name: "4_button" - text: "4" - fixed_width: 35 - fixed_height: 28 - foreground_color: "blue" - } - - @GUI::Button { - name: "5_button" - text: "5" - fixed_width: 35 - fixed_height: 28 - foreground_color: "blue" - } - - @GUI::Button { - name: "6_button" - text: "6" - fixed_width: 35 - fixed_height: 28 - foreground_color: "blue" - } - - @GUI::Button { - name: "multiply_button" - text: "*" - fixed_width: 35 - fixed_height: 28 - } - - @GUI::Button { - name: "mod_button" - text: "%" - fixed_width: 35 - fixed_height: 28 - foreground_color: "blue" - } - } - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout {} - - @GUI::Button { - name: "mem_save_button" - text: "MS" - fixed_width: 35 - fixed_height: 28 - foreground_color: "red" - } - - @GUI::Widget { - fixed_width: 5 - } - - @GUI::Button { - name: "1_button" - text: "1" - fixed_width: 35 - fixed_height: 28 - foreground_color: "blue" - } - - @GUI::Button { - name: "2_button" - text: "2" - fixed_width: 35 - fixed_height: 28 - foreground_color: "blue" - } - - @GUI::Button { - name: "3_button" - text: "3" - fixed_width: 35 - fixed_height: 28 - foreground_color: "blue" - } - - @GUI::Button { - name: "subtract_button" - text: "-" - fixed_width: 35 - fixed_height: 28 - } - - @GUI::Button { - name: "inverse_button" - text: "1/x" - fixed_width: 35 - fixed_height: 28 - foreground_color: "blue" - } - } - - @GUI::Widget { - layout: @GUI::HorizontalBoxLayout {} - - @GUI::Button { - name: "mem_add_button" - text: "M+" - fixed_width: 35 - fixed_height: 28 - foreground_color: "red" - } - - @GUI::Widget { - fixed_width: 5 - } - - @GUI::Button { - name: "0_button" - text: "0" - fixed_width: 35 - fixed_height: 28 - foreground_color: "blue" - } - - @GUI::Button { - name: "sign_button" - text: "+/-" - fixed_width: 35 - fixed_height: 28 - foreground_color: "blue" - } - - @GUI::Button { - name: "decimal_button" - text: "." - fixed_width: 35 - fixed_height: 28 - foreground_color: "blue" - } - - @GUI::Button { - name: "add_button" - text: "+" - fixed_width: 35 - fixed_height: 28 - } - - @GUI::Button { - name: "equal_button" - text: "=" - fixed_width: 35 - fixed_height: 28 - foreground_color: "red" - } - } -} diff --git a/Applications/Calculator/Keypad.cpp b/Applications/Calculator/Keypad.cpp deleted file mode 100644 index 9b2d672af5..0000000000 --- a/Applications/Calculator/Keypad.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@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 "Keypad.h" -#include <AK/StringBuilder.h> -#include <math.h> - -Keypad::Keypad() -{ -} - -Keypad::~Keypad() -{ -} - -void Keypad::type_digit(int digit) -{ - switch (m_state) { - case State::External: - m_state = State::TypingInteger; - m_negative = false; - m_int_value = digit; - m_frac_value = 0; - m_frac_length = 0; - break; - case State::TypingInteger: - ASSERT(m_frac_value == 0); - ASSERT(m_frac_length == 0); - m_int_value *= 10; - m_int_value += digit; - break; - case State::TypingDecimal: - if (m_frac_length > 6) - break; - m_frac_value *= 10; - m_frac_value += digit; - m_frac_length++; - break; - } -} - -void Keypad::type_decimal_point() -{ - switch (m_state) { - case State::External: - m_negative = false; - m_int_value = 0; - m_frac_value = 0; - m_frac_length = 0; - break; - case State::TypingInteger: - ASSERT(m_frac_value == 0); - ASSERT(m_frac_length == 0); - m_state = State::TypingDecimal; - break; - case State::TypingDecimal: - // Ignore it. - break; - } -} - -void Keypad::type_backspace() -{ - switch (m_state) { - case State::External: - m_negative = false; - m_int_value = 0; - m_frac_value = 0; - m_frac_length = 0; - break; - case State::TypingDecimal: - if (m_frac_length > 0) { - m_frac_value /= 10; - m_frac_length--; - break; - } - ASSERT(m_frac_value == 0); - m_state = State::TypingInteger; - [[fallthrough]]; - case State::TypingInteger: - ASSERT(m_frac_value == 0); - ASSERT(m_frac_length == 0); - m_int_value /= 10; - if (m_int_value == 0) - m_negative = false; - break; - } -} - -double Keypad::value() const -{ - double res = 0.0; - - long frac = m_frac_value; - for (int i = 0; i < m_frac_length; i++) { - int digit = frac % 10; - res += digit; - res /= 10.0; - frac /= 10; - } - - res += m_int_value; - if (m_negative) - res = -res; - - return res; -} - -void Keypad::set_value(double value) -{ - m_state = State::External; - - if (value < 0.0) { - m_negative = true; - value = -value; - } else - m_negative = false; - - m_int_value = value; - value -= m_int_value; - - m_frac_value = 0; - m_frac_length = 0; - while (value != 0) { - value *= 10.0; - int digit = value; - m_frac_value *= 10; - m_frac_value += digit; - m_frac_length++; - value -= digit; - - if (m_frac_length > 6) - break; - } -} - -String Keypad::to_string() const -{ - StringBuilder builder; - if (m_negative) - builder.append("-"); - builder.appendff("{}", m_int_value); - - if (m_frac_length > 0) - builder.appendff(".{:0{}}", m_frac_value, m_frac_length); - - return builder.to_string(); -} diff --git a/Applications/Calculator/Keypad.h b/Applications/Calculator/Keypad.h deleted file mode 100644 index dae427a046..0000000000 --- a/Applications/Calculator/Keypad.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@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 <AK/String.h> - -// This type implements number typing and -// displaying mechanics. It does not perform -// any arithmetic operations or anything on -// the values it deals with. - -class Keypad final { -public: - Keypad(); - ~Keypad(); - - void type_digit(int digit); - void type_decimal_point(); - void type_backspace(); - - double value() const; - void set_value(double); - - String to_string() const; - -private: - // Internal representation of the current decimal value. - bool m_negative { false }; - long m_int_value { 0 }; - long m_frac_value { 0 }; - int m_frac_length { 0 }; - // E.g. for -35.004200, - // m_negative = true - // m_int_value = 35 - // m_frac_value = 4200 - // m_frac_length = 6 - - enum class State { - External, - TypingInteger, - TypingDecimal - }; - - State m_state { State::External }; -}; diff --git a/Applications/Calculator/main.cpp b/Applications/Calculator/main.cpp deleted file mode 100644 index 7ff18b94bf..0000000000 --- a/Applications/Calculator/main.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@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 "CalculatorWidget.h" -#include <LibGUI/Action.h> -#include <LibGUI/Application.h> -#include <LibGUI/Icon.h> -#include <LibGUI/Menu.h> -#include <LibGUI/MenuBar.h> -#include <LibGUI/Window.h> -#include <LibGfx/Bitmap.h> -#include <stdio.h> - -int main(int argc, char** argv) -{ - if (pledge("stdio shared_buffer rpath accept unix cpath fattr", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto app = GUI::Application::construct(argc, argv); - - if (pledge("stdio shared_buffer rpath accept", nullptr) < 0) { - perror("pledge"); - return 1; - } - - if (unveil("/res", "r") < 0) { - perror("unveil"); - return 1; - } - - unveil(nullptr, nullptr); - - auto app_icon = GUI::Icon::default_icon("app-calculator"); - - auto window = GUI::Window::construct(); - window->set_title("Calculator"); - window->set_resizable(false); - window->resize(254, 213); - - window->set_main_widget<CalculatorWidget>(); - - window->show(); - window->set_icon(app_icon.bitmap_for_size(16)); - - auto menubar = GUI::MenuBar::construct(); - - auto& app_menu = menubar->add_menu("Calculator"); - app_menu.add_action(GUI::CommonActions::make_quit_action([](auto&) { - GUI::Application::the()->quit(); - return; - })); - - auto& help_menu = menubar->add_menu("Help"); - help_menu.add_action(GUI::CommonActions::make_about_action("Calculator", app_icon)); - - app->set_menubar(move(menubar)); - - return app->exec(); -} diff --git a/Applications/Calendar/AddEventDialog.cpp b/Applications/Calendar/AddEventDialog.cpp deleted file mode 100644 index dbb70d33c1..0000000000 --- a/Applications/Calendar/AddEventDialog.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (c) 2019-2020, Ryan Grieb <ryan.m.grieb@gmail.com> - * 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 "AddEventDialog.h" -#include <LibCore/DateTime.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Button.h> -#include <LibGUI/ComboBox.h> -#include <LibGUI/Label.h> -#include <LibGUI/Layout.h> -#include <LibGUI/Painter.h> -#include <LibGUI/SpinBox.h> -#include <LibGUI/TextBox.h> -#include <LibGUI/Widget.h> -#include <LibGUI/Window.h> -#include <LibGfx/Color.h> -#include <LibGfx/Font.h> -#include <LibGfx/FontDatabase.h> - -static const char* short_month_names[] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" -}; - -AddEventDialog::AddEventDialog(Core::DateTime date_time, Window* parent_window) - : Dialog(parent_window) - , m_date_time(date_time) -{ - resize(158, 100); - set_title("Add Event"); - set_resizable(false); - set_icon(parent_window->icon()); - - auto& widget = set_main_widget<GUI::Widget>(); - widget.set_fill_with_background_color(true); - widget.set_layout<GUI::VerticalBoxLayout>(); - - auto& top_container = widget.add<GUI::Widget>(); - top_container.set_layout<GUI::VerticalBoxLayout>(); - top_container.set_fixed_height(45); - top_container.layout()->set_margins({ 4, 4, 4, 4 }); - - auto& add_label = top_container.add<GUI::Label>("Add title & date:"); - add_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - add_label.set_fixed_height(14); - add_label.set_font(Gfx::FontDatabase::default_bold_font()); - - auto& event_title_textbox = top_container.add<GUI::TextBox>(); - event_title_textbox.set_fixed_height(20); - - auto& middle_container = widget.add<GUI::Widget>(); - middle_container.set_layout<GUI::HorizontalBoxLayout>(); - middle_container.set_fixed_height(25); - middle_container.layout()->set_margins({ 4, 4, 4, 4 }); - - auto& starting_month_combo = middle_container.add<GUI::ComboBox>(); - starting_month_combo.set_only_allow_values_from_model(true); - starting_month_combo.set_fixed_size(50, 20); - starting_month_combo.set_model(MonthListModel::create()); - starting_month_combo.set_selected_index(m_date_time.month() - 1); - - auto& starting_day_combo = middle_container.add<GUI::SpinBox>(); - starting_day_combo.set_fixed_size(40, 20); - starting_day_combo.set_value(m_date_time.day()); - starting_day_combo.set_min(1); - - auto& starting_year_combo = middle_container.add<GUI::SpinBox>(); - starting_year_combo.set_fixed_size(55, 20); - starting_year_combo.set_range(0, 9999); - starting_year_combo.set_value(m_date_time.year()); - - widget.layout()->add_spacer(); - - auto& button_container = widget.add<GUI::Widget>(); - button_container.set_fixed_height(20); - button_container.set_layout<GUI::HorizontalBoxLayout>(); - button_container.layout()->add_spacer(); - auto& ok_button = button_container.add<GUI::Button>("OK"); - ok_button.set_fixed_size(80, 20); - ok_button.on_click = [this](auto) { - dbgln("TODO: Add event icon on specific tile"); - done(Dialog::ExecOK); - }; - - event_title_textbox.set_focus(true); -} - -AddEventDialog::~AddEventDialog() -{ -} - -AddEventDialog::MonthListModel::MonthListModel() -{ -} - -AddEventDialog::MonthListModel::~MonthListModel() -{ -} - -void AddEventDialog::MonthListModel::update() -{ -} - -int AddEventDialog::MonthListModel::row_count(const GUI::ModelIndex&) const -{ - return 12; -} - -String AddEventDialog::MonthListModel::column_name(int column) const -{ - switch (column) { - case Column::Month: - return "Month"; - default: - ASSERT_NOT_REACHED(); - } -} - -GUI::Variant AddEventDialog::MonthListModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const -{ - auto& month = short_month_names[index.row()]; - if (role == GUI::ModelRole::Display) { - switch (index.column()) { - case Column::Month: - return month; - default: - ASSERT_NOT_REACHED(); - } - } - return {}; -} diff --git a/Applications/Calendar/AddEventDialog.h b/Applications/Calendar/AddEventDialog.h deleted file mode 100644 index c84e494527..0000000000 --- a/Applications/Calendar/AddEventDialog.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2019-2020, Ryan Grieb <ryan.m.grieb@gmail.com> - * 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/Calendar.h> -#include <LibGUI/Dialog.h> -#include <LibGUI/Model.h> -#include <LibGUI/Window.h> - -class AddEventDialog final : public GUI::Dialog { - C_OBJECT(AddEventDialog) -public: - virtual ~AddEventDialog() override; - - static void show(Core::DateTime date_time, Window* parent_window = nullptr) - { - auto dialog = AddEventDialog::construct(date_time, parent_window); - dialog->exec(); - } - -private: - AddEventDialog(Core::DateTime date_time, Window* parent_window = nullptr); - - class MonthListModel final : public GUI::Model { - public: - enum Column { - Month, - __Count, - }; - - static NonnullRefPtr<MonthListModel> create() { return adopt(*new MonthListModel); } - virtual ~MonthListModel() override; - - virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override; - virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return Column::__Count; } - virtual String column_name(int) const override; - virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override; - virtual void update() override; - - private: - MonthListModel(); - }; - - Core::DateTime m_date_time; -}; diff --git a/Applications/Calendar/CMakeLists.txt b/Applications/Calendar/CMakeLists.txt deleted file mode 100644 index c23a6743cc..0000000000 --- a/Applications/Calendar/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -set(SOURCES - AddEventDialog.cpp - main.cpp -) - -serenity_app(Calendar ICON app-calendar) -target_link_libraries(Calendar LibGUI) diff --git a/Applications/Calendar/main.cpp b/Applications/Calendar/main.cpp deleted file mode 100644 index aebc5c4fed..0000000000 --- a/Applications/Calendar/main.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (c) 2019-2020, Ryan Grieb <ryan.m.grieb@gmail.com> - * 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 "AddEventDialog.h" -#include <LibGUI/Action.h> -#include <LibGUI/Application.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Button.h> -#include <LibGUI/Calendar.h> -#include <LibGUI/Icon.h> -#include <LibGUI/Menu.h> -#include <LibGUI/MenuBar.h> -#include <LibGUI/ToolBar.h> -#include <LibGUI/ToolBarContainer.h> -#include <LibGUI/Window.h> -#include <LibGfx/Bitmap.h> -#include <LibGfx/Color.h> -#include <LibGfx/Font.h> -#include <LibGfx/FontDatabase.h> -#include <stdio.h> - -int main(int argc, char** argv) -{ - - if (pledge("stdio shared_buffer rpath accept unix cpath fattr", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto app = GUI::Application::construct(argc, argv); - - if (pledge("stdio shared_buffer rpath accept", nullptr) < 0) { - perror("pledge"); - return 1; - } - - if (unveil("/res", "r") < 0) { - perror("unveil"); - return 1; - } - - unveil(nullptr, nullptr); - - auto app_icon = GUI::Icon::default_icon("app-calendar"); - auto window = GUI::Window::construct(); - window->set_title("Calendar"); - window->resize(600, 480); - window->set_icon(app_icon.bitmap_for_size(16)); - - auto& root_container = window->set_main_widget<GUI::Widget>(); - root_container.set_fill_with_background_color(true); - root_container.set_layout<GUI::VerticalBoxLayout>(); - - auto& toolbar_container = root_container.add<GUI::ToolBarContainer>(); - auto& toolbar = toolbar_container.add<GUI::ToolBar>(); - - auto& calendar_container = root_container.add<GUI::Frame>(); - calendar_container.set_layout<GUI::VerticalBoxLayout>(); - calendar_container.layout()->set_margins({ 2, 2, 2, 2 }); - auto& calendar_widget = calendar_container.add<GUI::Calendar>(Core::DateTime::now()); - - RefPtr<GUI::Button> selected_calendar_button; - - auto prev_date_action = GUI::Action::create("Previous date", { Mod_Alt, Key_Left }, Gfx::Bitmap::load_from_file("/res/icons/16x16/go-back.png"), [&](const GUI::Action&) { - unsigned int target_month = calendar_widget.selected_month(); - unsigned int target_year = calendar_widget.selected_year(); - - if (calendar_widget.mode() == GUI::Calendar::Month) { - target_month--; - if (calendar_widget.selected_month() <= 1) { - target_month = 12; - target_year--; - } - } else { - target_year--; - } - - calendar_widget.update_tiles(target_year, target_month); - selected_calendar_button->set_text(calendar_widget.selected_calendar_text()); - }); - - auto next_date_action = GUI::Action::create("Next date", { Mod_Alt, Key_Right }, Gfx::Bitmap::load_from_file("/res/icons/16x16/go-forward.png"), [&](const GUI::Action&) { - unsigned int target_month = calendar_widget.selected_month(); - unsigned int target_year = calendar_widget.selected_year(); - - if (calendar_widget.mode() == GUI::Calendar::Month) { - target_month++; - if (calendar_widget.selected_month() >= 12) { - target_month = 1; - target_year++; - } - } else { - target_year++; - } - - calendar_widget.update_tiles(target_year, target_month); - selected_calendar_button->set_text(calendar_widget.selected_calendar_text()); - }); - - auto add_event_action = GUI::Action::create("Add event", {}, Gfx::Bitmap::load_from_file("/res/icons/16x16/add-event.png"), [&](const GUI::Action&) { - AddEventDialog::show(calendar_widget.selected_date(), window); - }); - - auto jump_to_action = GUI::Action::create("Jump to today", {}, Gfx::Bitmap::load_from_file("/res/icons/16x16/calendar-date.png"), [&](const GUI::Action&) { - if (calendar_widget.mode() == GUI::Calendar::Year) - calendar_widget.toggle_mode(); - calendar_widget.set_selected_date(Core::DateTime::now()); - calendar_widget.update_tiles(Core::DateTime::now().year(), Core::DateTime::now().month()); - selected_calendar_button->set_text(calendar_widget.selected_calendar_text()); - }); - - toolbar.add_action(prev_date_action); - selected_calendar_button = toolbar.add<GUI::Button>(calendar_widget.selected_calendar_text()); - selected_calendar_button->set_fixed_width(70); - selected_calendar_button->set_button_style(Gfx::ButtonStyle::CoolBar); - selected_calendar_button->set_font(Gfx::FontDatabase::default_bold_fixed_width_font()); - selected_calendar_button->on_click = [&](auto) { - calendar_widget.toggle_mode(); - selected_calendar_button->set_text(calendar_widget.selected_calendar_text()); - }; - toolbar.add_action(next_date_action); - toolbar.add_separator(); - toolbar.add_action(jump_to_action); - toolbar.add_action(add_event_action); - - calendar_widget.on_calendar_tile_click = [&] { - selected_calendar_button->set_text(calendar_widget.selected_calendar_text()); - }; - - calendar_widget.on_calendar_tile_doubleclick = [&] { - AddEventDialog::show(calendar_widget.selected_date(), window); - }; - - calendar_widget.on_month_tile_click = [&] { - selected_calendar_button->set_text(calendar_widget.selected_calendar_text()); - }; - - auto menubar = GUI::MenuBar::construct(); - auto& app_menu = menubar->add_menu("Calendar"); - app_menu.add_action(GUI::Action::create("Add Event", { Mod_Ctrl | Mod_Shift, Key_E }, Gfx::Bitmap::load_from_file("/res/icons/16x16/add-event.png"), - [&](const GUI::Action&) { - AddEventDialog::show(calendar_widget.selected_date(), window); - return; - })); - - app_menu.add_separator(); - - app_menu.add_action(GUI::CommonActions::make_quit_action([](auto&) { - GUI::Application::the()->quit(); - return; - })); - - auto& help_menu = menubar->add_menu("Help"); - help_menu.add_action(GUI::CommonActions::make_about_action("Calendar", app_icon)); - - app->set_menubar(move(menubar)); - window->show(); - app->exec(); -} diff --git a/Applications/CrashReporter/CMakeLists.txt b/Applications/CrashReporter/CMakeLists.txt deleted file mode 100644 index d391aa6b84..0000000000 --- a/Applications/CrashReporter/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -compile_gml(CrashReporterWindow.gml CrashReporterWindowGML.h crash_reporter_window_gml) - - -set(SOURCES - main.cpp - CrashReporterWindowGML.h -) - -serenity_app(CrashReporter ICON app-crash-reporter) -target_link_libraries(CrashReporter LibCore LibCoreDump LibDesktop LibGUI) diff --git a/Applications/CrashReporter/CrashReporterWindow.gml b/Applications/CrashReporter/CrashReporterWindow.gml deleted file mode 100644 index acca32bd13..0000000000 --- a/Applications/CrashReporter/CrashReporterWindow.gml +++ /dev/null @@ -1,95 +0,0 @@ -@GUI::Widget { - fill_with_background_color: true - - layout: @GUI::VerticalBoxLayout { - margins: [5, 5, 5, 5] - } - - @GUI::Widget { - fixed_height: 44 - - layout: @GUI::HorizontalBoxLayout { - spacing: 10 - } - - @GUI::ImageWidget { - name: "icon" - } - - @GUI::Label { - name: "description" - text_alignment: "CenterLeft" - } - } - - @GUI::Widget { - fixed_height: 18 - - layout: @GUI::HorizontalBoxLayout { - } - - @GUI::Label { - text: "Executable path:" - text_alignment: "CenterLeft" - fixed_width: 90 - } - - @GUI::LinkLabel { - name: "executable_link" - text_alignment: "CenterLeft" - } - } - - @GUI::Widget { - fixed_height: 18 - - layout: @GUI::HorizontalBoxLayout { - } - - @GUI::Label { - text: "Coredump path:" - text_alignment: "CenterLeft" - fixed_width: 90 - } - - @GUI::LinkLabel { - name: "coredump_link" - text_alignment: "CenterLeft" - } - } - - @GUI::Widget { - fixed_height: 18 - - layout: @GUI::HorizontalBoxLayout { - } - - @GUI::Label { - text: "Backtrace:" - text_alignment: "CenterLeft" - } - } - - @GUI::TextEditor { - name: "backtrace_text_editor" - mode: "ReadOnly" - } - - @GUI::Widget { - fixed_height: 32 - - layout: @GUI::HorizontalBoxLayout { - } - - // HACK: We need something like Layout::add_spacer() in GML! :^) - @GUI::Widget { - } - - @GUI::Button { - name: "close_button" - text: "Close" - fixed_width: 70 - fixed_height: 22 - } - } -} diff --git a/Applications/CrashReporter/main.cpp b/Applications/CrashReporter/main.cpp deleted file mode 100644 index 047a696ea3..0000000000 --- a/Applications/CrashReporter/main.cpp +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (c) 2020, Linus Groh <mail@linusgroh.de> - * 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 <AK/LexicalPath.h> -#include <AK/StringBuilder.h> -#include <AK/Types.h> -#include <AK/URL.h> -#include <Applications/CrashReporter/CrashReporterWindowGML.h> -#include <LibCore/ArgsParser.h> -#include <LibCoreDump/Backtrace.h> -#include <LibCoreDump/Reader.h> -#include <LibDesktop/AppFile.h> -#include <LibDesktop/Launcher.h> -#include <LibELF/CoreDump.h> -#include <LibGUI/Application.h> -#include <LibGUI/Button.h> -#include <LibGUI/FileIconProvider.h> -#include <LibGUI/Icon.h> -#include <LibGUI/ImageWidget.h> -#include <LibGUI/Label.h> -#include <LibGUI/Layout.h> -#include <LibGUI/LinkLabel.h> -#include <LibGUI/TextEditor.h> -#include <LibGUI/Window.h> -#include <string.h> - -static String build_backtrace(const CoreDump::Reader& coredump) -{ - StringBuilder builder; - - auto assertion = coredump.metadata().get("assertion"); - if (assertion.has_value() && !assertion.value().is_empty()) { - builder.append("ASSERTION FAILED: "); - builder.append(assertion.value().characters()); - builder.append('\n'); - builder.append('\n'); - } - - auto first = true; - for (auto& entry : coredump.backtrace().entries()) { - if (first) - first = false; - else - builder.append('\n'); - builder.append(entry.to_string()); - } - - return builder.build(); -} - -int main(int argc, char** argv) -{ - if (pledge("stdio shared_buffer accept cpath rpath unix fattr", nullptr) < 0) { - perror("pledge"); - return 1; - } - - const char* coredump_path = nullptr; - - Core::ArgsParser args_parser; - args_parser.set_general_help("Show information from an application crash coredump."); - args_parser.add_positional_argument(coredump_path, "Coredump path", "coredump-path"); - args_parser.parse(argc, argv); - - String backtrace; - String executable_path; - int pid { 0 }; - u8 termination_signal { 0 }; - - { - auto coredump = CoreDump::Reader::create(coredump_path); - if (!coredump) { - warnln("Could not open coredump '{}'", coredump_path); - return 1; - } - auto& process_info = coredump->process_info(); - backtrace = build_backtrace(*coredump); - executable_path = String(process_info.executable_path); - pid = process_info.pid; - termination_signal = process_info.termination_signal; - } - - auto app = GUI::Application::construct(argc, argv); - - if (pledge("stdio shared_buffer accept rpath unix", nullptr) < 0) { - perror("pledge"); - return 1; - } - - if (unveil(executable_path.characters(), "r") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/res", "r") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/tmp/portal/launch", "rw") < 0) { - perror("unveil"); - return 1; - } - - if (unveil(nullptr, nullptr) < 0) { - perror("unveil"); - return 1; - } - - auto app_icon = GUI::Icon::default_icon("app-crash-reporter"); - - auto window = GUI::Window::construct(); - window->set_title("Crash Reporter"); - window->set_icon(app_icon.bitmap_for_size(16)); - window->resize(460, 340); - window->center_on_screen(); - - auto& widget = window->set_main_widget<GUI::Widget>(); - widget.load_from_gml(crash_reporter_window_gml); - - auto& icon_image_widget = *widget.find_descendant_of_type_named<GUI::ImageWidget>("icon"); - icon_image_widget.set_bitmap(GUI::FileIconProvider::icon_for_executable(executable_path).bitmap_for_size(32)); - - auto app_name = LexicalPath(executable_path).basename(); - auto af = Desktop::AppFile::get_for_app(app_name); - if (af->is_valid()) - app_name = af->name(); - - auto& description_label = *widget.find_descendant_of_type_named<GUI::Label>("description"); - description_label.set_text(String::formatted("\"{}\" (PID {}) has crashed - {} (signal {})", app_name, pid, strsignal(termination_signal), termination_signal)); - - auto& executable_link_label = *widget.find_descendant_of_type_named<GUI::LinkLabel>("executable_link"); - executable_link_label.set_text(LexicalPath::canonicalized_path(executable_path)); - executable_link_label.on_click = [&] { - Desktop::Launcher::open(URL::create_with_file_protocol(LexicalPath(executable_path).dirname())); - }; - - auto& coredump_link_label = *widget.find_descendant_of_type_named<GUI::LinkLabel>("coredump_link"); - coredump_link_label.set_text(LexicalPath::canonicalized_path(coredump_path)); - coredump_link_label.on_click = [&] { - Desktop::Launcher::open(URL::create_with_file_protocol(LexicalPath(coredump_path).dirname())); - }; - - auto& backtrace_text_editor = *widget.find_descendant_of_type_named<GUI::TextEditor>("backtrace_text_editor"); - backtrace_text_editor.set_text(backtrace); - backtrace_text_editor.set_should_hide_unnecessary_scrollbars(true); - - auto& close_button = *widget.find_descendant_of_type_named<GUI::Button>("close_button"); - close_button.on_click = [&](auto) { - app->quit(); - }; - - window->show(); - - return app->exec(); -} diff --git a/Applications/Debugger/CMakeLists.txt b/Applications/Debugger/CMakeLists.txt deleted file mode 100644 index 7e6f200ea4..0000000000 --- a/Applications/Debugger/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -set(SOURCES - main.cpp -) - -serenity_bin(Debugger) -target_link_libraries(Debugger LibCore LibDebug LibX86 LibLine) diff --git a/Applications/Debugger/main.cpp b/Applications/Debugger/main.cpp deleted file mode 100644 index ef5bbe9671..0000000000 --- a/Applications/Debugger/main.cpp +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. <itamar8910@gmail.com> - * 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 <AK/Assertions.h> -#include <AK/ByteBuffer.h> -#include <AK/Demangle.h> -#include <AK/LogStream.h> -#include <AK/StringBuilder.h> -#include <AK/kmalloc.h> -#include <LibC/sys/arch/i386/regs.h> -#include <LibCore/ArgsParser.h> -#include <LibCore/File.h> -#include <LibDebug/DebugInfo.h> -#include <LibDebug/DebugSession.h> -#include <LibLine/Editor.h> -#include <LibX86/Disassembler.h> -#include <LibX86/Instruction.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -RefPtr<Line::Editor> editor; - -OwnPtr<Debug::DebugSession> g_debug_session; - -static void handle_sigint(int) -{ - outln("Debugger: SIGINT"); - - // The destructor of DebugSession takes care of detaching - g_debug_session = nullptr; -} - -static void handle_print_registers(const PtraceRegisters& regs) -{ - outln("eax={:08x} ebx={:08x} ecx={:08x} edx={:08x}", regs.eax, regs.ebx, regs.ecx, regs.edx); - outln("esp={:08x} ebp={:08x} esi={:08x} edi={:08x}", regs.esp, regs.ebp, regs.esi, regs.edi); - outln("eip={:08x} eflags={:08x}", regs.eip, regs.eflags); -} - -static bool handle_disassemble_command(const String& command, void* first_instruction) -{ - auto parts = command.split(' '); - size_t number_of_instructions_to_disassemble = 5; - if (parts.size() == 2) { - auto number = parts[1].to_uint(); - if (!number.has_value()) - return false; - number_of_instructions_to_disassemble = number.value(); - } - - // FIXME: Instead of using a fixed "dump_size", - // we can feed instructions to the disassembler one by one - constexpr size_t dump_size = 0x100; - ByteBuffer code; - for (size_t i = 0; i < dump_size / sizeof(u32); ++i) { - auto value = g_debug_session->peek(reinterpret_cast<u32*>(first_instruction) + i); - if (!value.has_value()) - break; - code.append(&value, sizeof(u32)); - } - - X86::SimpleInstructionStream stream(code.data(), code.size()); - X86::Disassembler disassembler(stream); - - for (size_t i = 0; i < number_of_instructions_to_disassemble; ++i) { - auto offset = stream.offset(); - auto insn = disassembler.next(); - if (!insn.has_value()) - break; - - outln(" {:p} <+{}>:\t{}", offset + reinterpret_cast<size_t>(first_instruction), offset, insn.value().to_string(offset)); - } - - return true; -} - -static bool insert_breakpoint_at_address(FlatPtr address) -{ - return g_debug_session->insert_breakpoint((void*)address); -} - -static bool insert_breakpoint_at_source_position(const String& file, size_t line) -{ - auto result = g_debug_session->insert_breakpoint(file, line); - if (!result.has_value()) { - warnln("Could not insert breakpoint at {}:{}", file, line); - return false; - } - outln("Breakpoint inserted [{}:{} ({}:{:p})]", result.value().file_name, result.value().line_number, result.value().library_name, result.value().address); - return true; -} - -static bool insert_breakpoint_at_symbol(const String& symbol) -{ - auto result = g_debug_session->insert_breakpoint(symbol); - if (!result.has_value()) { - warnln("Could not insert breakpoint at symbol: {}", symbol); - return false; - } - outln("Breakpoint inserted [{}:{:p}]", result.value().library_name, result.value().address); - return true; -} - -static bool handle_breakpoint_command(const String& command) -{ - auto parts = command.split(' '); - if (parts.size() != 2) - return false; - - auto argument = parts[1]; - if (argument.is_empty()) - return false; - - if (argument.contains(":")) { - auto source_arguments = argument.split(':'); - if (source_arguments.size() != 2) - return false; - auto line = source_arguments[1].to_uint(); - if (!line.has_value()) - return false; - auto file = source_arguments[0]; - return insert_breakpoint_at_source_position(file, line.value()); - } - if ((argument.starts_with("0x"))) { - return insert_breakpoint_at_address(strtoul(argument.characters() + 2, nullptr, 16)); - } - - return insert_breakpoint_at_symbol(argument); -} - -static bool handle_examine_command(const String& command) -{ - auto parts = command.split(' '); - if (parts.size() != 2) - return false; - - auto argument = parts[1]; - if (argument.is_empty()) - return false; - - if (!(argument.starts_with("0x"))) { - return false; - } - u32 address = strtoul(argument.characters() + 2, nullptr, 16); - auto res = g_debug_session->peek((u32*)address); - if (!res.has_value()) { - printf("could not examine memory at address 0x%x\n", address); - return true; - } - printf("0x%x\n", res.value()); - return true; -} - -static void print_help() -{ - out("Options:\n" - "cont - Continue execution\n" - "si - step to the next instruction\n" - "sl - step to the next source line\n" - "line - show the position of the current instruction in the source code\n" - "regs - Print registers\n" - "dis [number of instructions] - Print disassembly\n" - "bp <address/symbol/file:line> - Insert a breakpoint\n" - "x <address> - examine dword in memory\n"); -} - -int main(int argc, char** argv) -{ - editor = Line::Editor::construct(); - - if (pledge("stdio proc ptrace exec rpath tty sigaction cpath unix fattr", nullptr) < 0) { - perror("pledge"); - return 1; - } - - const char* command = nullptr; - Core::ArgsParser args_parser; - args_parser.add_positional_argument(command, - "The program to be debugged, along with its arguments", - "program", Core::ArgsParser::Required::Yes); - args_parser.parse(argc, argv); - - auto result = Debug::DebugSession::exec_and_attach(command); - if (!result) { - warnln("Failed to start debugging session for: \"{}\"", command); - exit(1); - } - g_debug_session = result.release_nonnull(); - - struct sigaction sa; - memset(&sa, 0, sizeof(struct sigaction)); - sa.sa_handler = handle_sigint; - sigaction(SIGINT, &sa, nullptr); - - Debug::DebugInfo::SourcePosition previous_source_position; - bool in_step_line = false; - - g_debug_session->run(Debug::DebugSession::DesiredInitialDebugeeState::Stopped, [&](Debug::DebugSession::DebugBreakReason reason, Optional<PtraceRegisters> optional_regs) { - if (reason == Debug::DebugSession::DebugBreakReason::Exited) { - outln("Program exited."); - return Debug::DebugSession::DebugDecision::Detach; - } - - ASSERT(optional_regs.has_value()); - const PtraceRegisters& regs = optional_regs.value(); - - auto symbol_at_ip = g_debug_session->symbolicate(regs.eip); - - auto source_position = g_debug_session->get_source_position(regs.eip); - - if (in_step_line) { - bool no_source_info = !source_position.has_value(); - if (no_source_info || source_position.value() != previous_source_position) { - if (no_source_info) - outln("No source information for current instruction! stoppoing."); - in_step_line = false; - } else { - return Debug::DebugSession::DebugDecision::SingleStep; - } - } - - if (symbol_at_ip.has_value()) - outln("Program is stopped at: {:p} ({}:{})", regs.eip, symbol_at_ip.value().library_name, symbol_at_ip.value().symbol); - else - outln("Program is stopped at: {:p}", regs.eip); - - if (source_position.has_value()) { - previous_source_position = source_position.value(); - outln("Source location: {}:{}", source_position.value().file_path, source_position.value().line_number); - } else { - outln("(No source location information for the current instruction)"); - } - - for (;;) { - auto command_result = editor->get_line("(sdb) "); - - if (command_result.is_error()) - return Debug::DebugSession::DebugDecision::Detach; - - auto& command = command_result.value(); - - bool success = false; - Optional<Debug::DebugSession::DebugDecision> decision; - - if (command.is_empty() && !editor->history().is_empty()) { - command = editor->history().last().entry; - } - if (command == "cont") { - decision = Debug::DebugSession::DebugDecision::Continue; - success = true; - } else if (command == "si") { - decision = Debug::DebugSession::DebugDecision::SingleStep; - success = true; - } else if (command == "sl") { - if (source_position.has_value()) { - decision = Debug::DebugSession::DebugDecision::SingleStep; - in_step_line = true; - success = true; - } else { - outln("No source location information for the current instruction"); - } - } else if (command == "regs") { - handle_print_registers(regs); - success = true; - - } else if (command.starts_with("dis")) { - success = handle_disassemble_command(command, reinterpret_cast<void*>(regs.eip)); - - } else if (command.starts_with("bp")) { - success = handle_breakpoint_command(command); - } else if (command.starts_with("x")) { - success = handle_examine_command(command); - } - - if (success && !command.is_empty()) { - // Don't add repeated commands to history - if (editor->history().is_empty() || editor->history().last().entry != command) - editor->add_to_history(command); - } - if (!success) { - print_help(); - } - if (decision.has_value()) - return decision.value(); - } - }); -} diff --git a/Applications/DisplaySettings/CMakeLists.txt b/Applications/DisplaySettings/CMakeLists.txt deleted file mode 100644 index b541894194..0000000000 --- a/Applications/DisplaySettings/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -compile_gml(DisplaySettingsWindow.gml DisplaySettingsWindowGML.h display_settings_window_gml) - -set(SOURCES - DisplaySettings.cpp - DisplaySettingsWindowGML.h - main.cpp - MonitorWidget.cpp -) - -serenity_app(DisplaySettings ICON app-display-settings) -target_link_libraries(DisplaySettings LibGUI) diff --git a/Applications/DisplaySettings/DisplaySettings.cpp b/Applications/DisplaySettings/DisplaySettings.cpp deleted file mode 100644 index ad68ebf827..0000000000 --- a/Applications/DisplaySettings/DisplaySettings.cpp +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (c) 2019-2020, Jesse Buhagiar <jooster669@gmail.com> - * Copyright (c) 2020, 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 "DisplaySettings.h" -#include <AK/StringBuilder.h> -#include <Applications/DisplaySettings/DisplaySettingsWindowGML.h> -#include <LibCore/ConfigFile.h> -#include <LibCore/DirIterator.h> -#include <LibGUI/Application.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Button.h> -#include <LibGUI/ComboBox.h> -#include <LibGUI/Desktop.h> -#include <LibGUI/FilePicker.h> -#include <LibGUI/ItemListModel.h> -#include <LibGUI/Label.h> -#include <LibGUI/MessageBox.h> -#include <LibGUI/WindowServerConnection.h> -#include <LibGfx/Palette.h> -#include <LibGfx/SystemTheme.h> - -REGISTER_WIDGET(DisplaySettings, MonitorWidget) - -DisplaySettingsWidget::DisplaySettingsWidget() -{ - create_resolution_list(); - create_wallpaper_list(); - - create_frame(); - - load_current_settings(); -} - -void DisplaySettingsWidget::create_resolution_list() -{ - // TODO: Find a better way to get the default resolution - m_resolutions.append({ 640, 480 }); - m_resolutions.append({ 800, 600 }); - m_resolutions.append({ 1024, 768 }); - m_resolutions.append({ 1280, 720 }); - m_resolutions.append({ 1280, 768 }); - m_resolutions.append({ 1280, 1024 }); - m_resolutions.append({ 1360, 768 }); - m_resolutions.append({ 1368, 768 }); - m_resolutions.append({ 1440, 900 }); - m_resolutions.append({ 1600, 900 }); - m_resolutions.append({ 1920, 1080 }); - m_resolutions.append({ 2560, 1080 }); -} - -void DisplaySettingsWidget::create_wallpaper_list() -{ - Core::DirIterator iterator("/res/wallpapers/", Core::DirIterator::Flags::SkipDots); - - m_wallpapers.append("Use background color"); - - while (iterator.has_next()) { - m_wallpapers.append(iterator.next_path()); - } - - m_modes.append("simple"); - m_modes.append("tile"); - m_modes.append("center"); - m_modes.append("scaled"); -} - -void DisplaySettingsWidget::create_frame() -{ - load_from_gml(display_settings_window_gml); - - m_monitor_widget = *find_descendant_of_type_named<DisplaySettings::MonitorWidget>("monitor_widget"); - - m_wallpaper_combo = *find_descendant_of_type_named<GUI::ComboBox>("wallpaper_combo"); - m_wallpaper_combo->set_only_allow_values_from_model(true); - m_wallpaper_combo->set_model(*GUI::ItemListModel<AK::String>::create(m_wallpapers)); - m_wallpaper_combo->on_change = [this](auto& text, const GUI::ModelIndex& index) { - String path = text; - if (path.starts_with("/") && m_monitor_widget->set_wallpaper(path)) { - m_monitor_widget->update(); - return; - } - - if (index.row() == 0) { - path = ""; - } else { - if (index.is_valid()) { - StringBuilder builder; - builder.append("/res/wallpapers/"); - builder.append(path); - path = builder.to_string(); - } - } - - m_monitor_widget->set_wallpaper(path); - m_monitor_widget->update(); - }; - - auto& button = *find_descendant_of_type_named<GUI::Button>("wallpaper_open_button"); - button.set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/open.png")); - button.on_click = [this](auto) { - Optional<String> open_path = GUI::FilePicker::get_open_filepath(nullptr, "Select wallpaper from file system."); - - if (!open_path.has_value()) - return; - - m_wallpaper_combo->set_only_allow_values_from_model(false); - m_wallpaper_combo->set_text(open_path.value()); - m_wallpaper_combo->set_only_allow_values_from_model(true); - }; - - m_mode_combo = *find_descendant_of_type_named<GUI::ComboBox>("mode_combo"); - m_mode_combo->set_only_allow_values_from_model(true); - m_mode_combo->set_model(*GUI::ItemListModel<AK::String>::create(m_modes)); - m_mode_combo->on_change = [this](auto&, const GUI::ModelIndex& index) { - m_monitor_widget->set_wallpaper_mode(m_modes.at(index.row())); - m_monitor_widget->update(); - }; - - m_resolution_combo = *find_descendant_of_type_named<GUI::ComboBox>("resolution_combo"); - m_resolution_combo->set_only_allow_values_from_model(true); - m_resolution_combo->set_model(*GUI::ItemListModel<Gfx::IntSize>::create(m_resolutions)); - m_resolution_combo->on_change = [this](auto&, const GUI::ModelIndex& index) { - m_monitor_widget->set_desktop_resolution(m_resolutions.at(index.row())); - m_monitor_widget->update(); - }; - - m_color_input = *find_descendant_of_type_named<GUI::ColorInput>("color_input"); - m_color_input->set_color_has_alpha_channel(false); - m_color_input->set_color_picker_title("Select color for desktop"); - m_color_input->on_change = [this] { - m_monitor_widget->set_background_color(m_color_input->color()); - m_monitor_widget->update(); - }; - - auto& ok_button = *find_descendant_of_type_named<GUI::Button>("ok_button"); - ok_button.on_click = [this](auto) { - send_settings_to_window_server(); - GUI::Application::the()->quit(); - }; - - auto& cancel_button = *find_descendant_of_type_named<GUI::Button>("cancel_button"); - cancel_button.on_click = [](auto) { - GUI::Application::the()->quit(); - }; - - auto& apply_button = *find_descendant_of_type_named<GUI::Button>("apply_button"); - apply_button.on_click = [this](auto) { - send_settings_to_window_server(); - }; -} - -void DisplaySettingsWidget::load_current_settings() -{ - auto ws_config(Core::ConfigFile::open("/etc/WindowServer/WindowServer.ini")); - auto wm_config = Core::ConfigFile::get_for_app("WindowManager"); - - /// Wallpaper path //////////////////////////////////////////////////////////////////////////// - /// Read wallpaper path from config file and set value to monitor widget and combo box. - auto selected_wallpaper = wm_config->read_entry("Background", "Wallpaper", ""); - if (!selected_wallpaper.is_empty()) { - m_monitor_widget->set_wallpaper(selected_wallpaper); - - Optional<size_t> optional_index; - if (selected_wallpaper.starts_with("/res/wallpapers/")) { - auto name_parts = selected_wallpaper.split('/', true); - optional_index = m_wallpapers.find_first_index(name_parts[2]); - - if (optional_index.has_value()) { - m_wallpaper_combo->set_selected_index(optional_index.value()); - } - } - - if (!optional_index.has_value()) { - m_wallpaper_combo->set_only_allow_values_from_model(false); - m_wallpaper_combo->set_text(selected_wallpaper); - m_wallpaper_combo->set_only_allow_values_from_model(true); - } - } else { - m_wallpaper_combo->set_selected_index(0); - } - - size_t index; - - /// Mode ////////////////////////////////////////////////////////////////////////////////////// - auto mode = ws_config->read_entry("Background", "Mode", "simple"); - if (!m_modes.contains_slow(mode)) { - warnln("Invalid background mode '{}' in WindowServer config, falling back to 'simple'", mode); - mode = "simple"; - } - m_monitor_widget->set_wallpaper_mode(mode); - index = m_modes.find_first_index(mode).value(); - m_mode_combo->set_selected_index(index); - - /// Resolution //////////////////////////////////////////////////////////////////////////////// - Gfx::IntSize find_size; - - // Let's attempt to find the current resolution and select it! - find_size.set_width(ws_config->read_num_entry("Screen", "Width", 1024)); - find_size.set_height(ws_config->read_num_entry("Screen", "Height", 768)); - - index = m_resolutions.find_first_index(find_size).value_or(0); - Gfx::IntSize m_current_resolution = m_resolutions.at(index); - m_monitor_widget->set_desktop_resolution(m_current_resolution); - m_resolution_combo->set_selected_index(index); - - /// Color ///////////////////////////////////////////////////////////////////////////////////// - /// If presend read from config file. If not paint with palet color. - Color palette_desktop_color = palette().desktop_background(); - - auto background_color = ws_config->read_entry("Background", "Color", ""); - if (!background_color.is_empty()) { - auto opt_color = Color::from_string(background_color); - if (opt_color.has_value()) - palette_desktop_color = opt_color.value(); - } - - m_color_input->set_color(palette_desktop_color); - m_monitor_widget->set_background_color(palette_desktop_color); - - m_monitor_widget->update(); -} - -void DisplaySettingsWidget::send_settings_to_window_server() -{ - auto result = GUI::WindowServerConnection::the().send_sync<Messages::WindowServer::SetResolution>(m_monitor_widget->desktop_resolution()); - if (!result->success()) { - GUI::MessageBox::show(nullptr, String::formatted("Reverting to resolution {}x{}", result->resolution().width(), result->resolution().height()), - "Unable to set resolution", GUI::MessageBox::Type::Error); - } - - if (!m_monitor_widget->wallpaper().is_empty()) { - GUI::Desktop::the().set_wallpaper(m_monitor_widget->wallpaper()); - } else { - dbgln("Setting color input: __{}__", m_color_input->text()); - GUI::Desktop::the().set_wallpaper(""); - GUI::Desktop::the().set_background_color(m_color_input->text()); - } - - GUI::Desktop::the().set_wallpaper_mode(m_monitor_widget->wallpaper_mode()); -} diff --git a/Applications/DisplaySettings/DisplaySettings.h b/Applications/DisplaySettings/DisplaySettings.h deleted file mode 100644 index 30fb26ebed..0000000000 --- a/Applications/DisplaySettings/DisplaySettings.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2019-2020, Jesse Buhagiar <jooster669@gmail.com> - * 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 "MonitorWidget.h" -#include <LibGUI/ColorInput.h> -#include <LibGUI/ComboBox.h> - -class DisplaySettingsWidget : public GUI::Widget { - C_OBJECT(DisplaySettingsWidget); - -public: - DisplaySettingsWidget(); - -private: - void create_frame(); - void create_wallpaper_list(); - void create_resolution_list(); - void load_current_settings(); - void send_settings_to_window_server(); // Apply the settings to the Window Server - - Vector<String> m_wallpapers; - Vector<String> m_modes; - Vector<Gfx::IntSize> m_resolutions; - - RefPtr<DisplaySettings::MonitorWidget> m_monitor_widget; - RefPtr<GUI::ComboBox> m_wallpaper_combo; - RefPtr<GUI::ComboBox> m_mode_combo; - RefPtr<GUI::ComboBox> m_resolution_combo; - RefPtr<GUI::ColorInput> m_color_input; -}; diff --git a/Applications/DisplaySettings/DisplaySettingsWindow.gml b/Applications/DisplaySettings/DisplaySettingsWindow.gml deleted file mode 100644 index af9f204f03..0000000000 --- a/Applications/DisplaySettings/DisplaySettingsWindow.gml +++ /dev/null @@ -1,117 +0,0 @@ -@GUI::Widget { - fill_with_background_color: true - - layout: @GUI::VerticalBoxLayout { - margins: [4, 4, 4, 4] - } - - @DisplaySettings::MonitorWidget { - name: "monitor_widget" - fixed_width: 338 - fixed_height: 248 - } - - @GUI::Widget { - shrink_to_fit: true - layout: @GUI::HorizontalBoxLayout { - } - - @GUI::Label { - text: "Wallpaper:" - text_alignment: "CenterLeft" - fixed_width: 70 - } - - @GUI::ComboBox { - name: "wallpaper_combo" - } - - @GUI::Button { - name: "wallpaper_open_button" - tooltip: "Select wallpaper from file system." - button_style: "CoolBar" - fixed_width: 22 - fixed_height: 22 - } - } - - @GUI::Widget { - shrink_to_fit: true - layout: @GUI::HorizontalBoxLayout { - } - - @GUI::Label { - text: "Modes:" - text_alignment: "CenterLeft" - fixed_width: 70 - } - - @GUI::ComboBox { - name: "mode_combo" - } - } - - @GUI::Widget { - shrink_to_fit: true - layout: @GUI::HorizontalBoxLayout { - } - - @GUI::Label { - text: "Resolution:" - text_alignment: "CenterLeft" - fixed_width: 70 - } - - @GUI::ComboBox { - name: "resolution_combo" - } - } - - - @GUI::Widget { - shrink_to_fit: true - layout: @GUI::HorizontalBoxLayout { - } - - @GUI::Label { - text: "Color:" - text_alignment: "CenterLeft" - fixed_width: 70 - } - - @GUI::ColorInput { - name: "color_input" - fixed_width: 90 - } - } - - @GUI::Widget { - } - - @GUI::Widget { - shrink_to_fit: true - layout: @GUI::HorizontalBoxLayout { - } - - @GUI::Widget { - } - - @GUI::Button { - name: "ok_button" - text: "OK" - fixed_width: 60 - } - - @GUI::Button { - name: "cancel_button" - text: "Cancel" - fixed_width: 60 - } - - @GUI::Button { - name: "apply_button" - text: "Apply" - fixed_width: 60 - } - } -} diff --git a/Applications/DisplaySettings/MonitorWidget.cpp b/Applications/DisplaySettings/MonitorWidget.cpp deleted file mode 100644 index d6b809d449..0000000000 --- a/Applications/DisplaySettings/MonitorWidget.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (c) 2020, Hüseyin Aslıtürk <asliturk@hotmail.com> - * 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 "MonitorWidget.h" -#include <LibGUI/Painter.h> -#include <LibGfx/Bitmap.h> - -namespace DisplaySettings { - -MonitorWidget::MonitorWidget() -{ - m_monitor_bitmap = Gfx::Bitmap::load_from_file("/res/graphics/monitor.png"); - m_monitor_rect = { 8, 9, 320, 180 }; -} - -bool MonitorWidget::set_wallpaper(String path) -{ - auto bitmap_ptr = Gfx::Bitmap::load_from_file(path); - if (!bitmap_ptr && !path.is_empty()) - return false; - m_desktop_wallpaper_path = path; - m_desktop_wallpaper_bitmap = bitmap_ptr; - return true; -} - -String MonitorWidget::wallpaper() -{ - return m_desktop_wallpaper_path; -} - -void MonitorWidget::set_wallpaper_mode(String mode) -{ - m_desktop_wallpaper_mode = mode; -} - -String MonitorWidget::wallpaper_mode() -{ - return m_desktop_wallpaper_mode; -} - -void MonitorWidget::set_desktop_resolution(Gfx::IntSize resolution) -{ - m_desktop_resolution = resolution; -} - -Gfx::IntSize MonitorWidget::desktop_resolution() -{ - return m_desktop_resolution; -} - -void MonitorWidget::set_background_color(Gfx::Color color) -{ - m_desktop_color = color; -} - -Gfx::Color MonitorWidget::background_color() -{ - return m_desktop_color; -} - -void MonitorWidget::paint_event(GUI::PaintEvent& event) -{ - Gfx::IntRect screen_rect = { 0, 0, m_desktop_resolution.width(), m_desktop_resolution.height() }; - auto screen_bitmap = Gfx::Bitmap::create(m_monitor_bitmap->format(), m_desktop_resolution); - GUI::Painter screen_painter(*screen_bitmap); - screen_painter.fill_rect(screen_rect, m_desktop_color); - - if (!m_desktop_wallpaper_bitmap.is_null()) { - if (m_desktop_wallpaper_mode == "simple") { - screen_painter.blit({ 0, 0 }, *m_desktop_wallpaper_bitmap, m_desktop_wallpaper_bitmap->rect()); - } else if (m_desktop_wallpaper_mode == "center") { - Gfx::IntPoint offset { screen_rect.width() / 2 - m_desktop_wallpaper_bitmap->size().width() / 2, screen_rect.height() / 2 - m_desktop_wallpaper_bitmap->size().height() / 2 }; - screen_painter.blit_offset(screen_rect.location(), *m_desktop_wallpaper_bitmap, screen_rect, offset); - } else if (m_desktop_wallpaper_mode == "tile") { - screen_painter.draw_tiled_bitmap(screen_bitmap->rect(), *m_desktop_wallpaper_bitmap); - } else if (m_desktop_wallpaper_mode == "scaled") { - screen_painter.draw_scaled_bitmap(screen_bitmap->rect(), *m_desktop_wallpaper_bitmap, m_desktop_wallpaper_bitmap->rect()); - } else { - ASSERT_NOT_REACHED(); - } - } - - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - - painter.blit({ 0, 0 }, *m_monitor_bitmap, m_monitor_bitmap->rect()); - painter.draw_scaled_bitmap(m_monitor_rect, *screen_bitmap, screen_bitmap->rect()); - - if (!m_desktop_resolution.is_null()) { - painter.draw_text(m_monitor_rect.translated(1, 1), m_desktop_resolution.to_string(), Gfx::TextAlignment::Center, Color::Black); - painter.draw_text(m_monitor_rect, m_desktop_resolution.to_string(), Gfx::TextAlignment::Center, Color::White); - } -} - -} diff --git a/Applications/DisplaySettings/MonitorWidget.h b/Applications/DisplaySettings/MonitorWidget.h deleted file mode 100644 index 1961e91330..0000000000 --- a/Applications/DisplaySettings/MonitorWidget.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2020, Hüseyin Aslıtürk <asliturk@hotmail.com> - * 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 DisplaySettings { - -class MonitorWidget final : public GUI::Widget { - C_OBJECT(MonitorWidget); - -public: - bool set_wallpaper(String path); - String wallpaper(); - - void set_wallpaper_mode(String mode); - String wallpaper_mode(); - - void set_desktop_resolution(Gfx::IntSize resolution); - Gfx::IntSize desktop_resolution(); - - void set_background_color(Gfx::Color background_color); - Gfx::Color background_color(); - -private: - MonitorWidget(); - - virtual void paint_event(GUI::PaintEvent& event) override; - - Gfx::IntRect m_monitor_rect; - RefPtr<Gfx::Bitmap> m_monitor_bitmap; - - String m_desktop_wallpaper_path; - RefPtr<Gfx::Bitmap> m_desktop_wallpaper_bitmap; - String m_desktop_wallpaper_mode; - Gfx::IntSize m_desktop_resolution; - Gfx::Color m_desktop_color; -}; - -} diff --git a/Applications/DisplaySettings/main.cpp b/Applications/DisplaySettings/main.cpp deleted file mode 100644 index 5018893470..0000000000 --- a/Applications/DisplaySettings/main.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2019-2020, Jesse Buhagiar <jooster669@gmail.com> - * 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 "DisplaySettings.h" -#include <LibGUI/Action.h> -#include <LibGUI/Application.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Icon.h> -#include <LibGUI/Menu.h> -#include <LibGUI/MenuBar.h> -#include <LibGUI/TabWidget.h> -#include <LibGUI/Widget.h> -#include <LibGUI/Window.h> -#include <LibGfx/Bitmap.h> -#include <stdio.h> - -int main(int argc, char** argv) -{ - if (pledge("stdio thread shared_buffer rpath accept cpath wpath unix fattr", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto app = GUI::Application::construct(argc, argv); - - if (pledge("stdio thread shared_buffer rpath accept cpath wpath", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto app_icon = GUI::Icon::default_icon("app-display-settings"); - - // Let's create the tab pane that we'll hook our widgets up to :^) - auto tab_widget = GUI::TabWidget::construct(); - tab_widget->add_tab<DisplaySettingsWidget>("Display Settings"); - tab_widget->set_fill_with_background_color(true); // No black backgrounds! - - auto window = GUI::Window::construct(); - dbgln("main window: {}", window); - window->set_title("Display Settings"); - window->resize(360, 410); - window->set_resizable(false); - window->set_main_widget(tab_widget.ptr()); - window->set_icon(app_icon.bitmap_for_size(16)); - - auto menubar = GUI::MenuBar::construct(); - - auto& app_menu = menubar->add_menu("Display Settings"); - app_menu.add_action(GUI::CommonActions::make_quit_action([&](const GUI::Action&) { - app->quit(); - })); - - auto& help_menu = menubar->add_menu("Help"); - help_menu.add_action(GUI::CommonActions::make_about_action("Display Settings", app_icon)); - - app->set_menubar(move(menubar)); - window->show(); - return app->exec(); -} diff --git a/Applications/FileManager/CMakeLists.txt b/Applications/FileManager/CMakeLists.txt deleted file mode 100644 index d81fc89bef..0000000000 --- a/Applications/FileManager/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -compile_gml(FileManagerWindow.gml FileManagerWindowGML.h file_manager_window_gml) - -set(SOURCES - DesktopWidget.cpp - DirectoryView.cpp - FileManagerWindowGML.h - FileUtils.cpp - main.cpp - PropertiesWindow.cpp -) - -serenity_app(FileManager ICON filetype-folder) -target_link_libraries(FileManager LibGUI LibDesktop) diff --git a/Applications/FileManager/DesktopWidget.cpp b/Applications/FileManager/DesktopWidget.cpp deleted file mode 100644 index 7ea01de9e8..0000000000 --- a/Applications/FileManager/DesktopWidget.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2020, 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 "DesktopWidget.h" -#include <LibGUI/Painter.h> - -namespace FileManager { - -DesktopWidget::DesktopWidget() -{ -} - -DesktopWidget::~DesktopWidget() -{ -} - -void DesktopWidget::paint_event(GUI::PaintEvent& event) -{ - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - painter.clear_rect(event.rect(), Color(0, 0, 0, 0)); -} - -} diff --git a/Applications/FileManager/DesktopWidget.h b/Applications/FileManager/DesktopWidget.h deleted file mode 100644 index a6fd5b614b..0000000000 --- a/Applications/FileManager/DesktopWidget.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2020, 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 DesktopWidget final : public GUI::Widget { - C_OBJECT(DesktopWidget); - -public: - virtual ~DesktopWidget() override; - -private: - virtual void paint_event(GUI::PaintEvent&) override; - - DesktopWidget(); -}; - -} diff --git a/Applications/FileManager/DirectoryView.cpp b/Applications/FileManager/DirectoryView.cpp deleted file mode 100644 index 6d47d89671..0000000000 --- a/Applications/FileManager/DirectoryView.cpp +++ /dev/null @@ -1,591 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "DirectoryView.h" -#include "FileUtils.h" -#include <AK/LexicalPath.h> -#include <AK/NumberFormat.h> -#include <AK/StringBuilder.h> -#include <LibCore/MimeData.h> -#include <LibCore/StandardPaths.h> -#include <LibGUI/FileIconProvider.h> -#include <LibGUI/InputBox.h> -#include <LibGUI/Label.h> -#include <LibGUI/MessageBox.h> -#include <LibGUI/ModelEditingDelegate.h> -#include <LibGUI/SortingProxyModel.h> -#include <serenity.h> -#include <spawn.h> -#include <stdio.h> -#include <unistd.h> - -namespace FileManager { - -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); - return GUI::Action::create(details().name, move(icon), [this, launch_handler = move(launch_handler)](auto&) { - launch_handler(*this); - }); -} - -RefPtr<LauncherHandler> DirectoryView::get_default_launch_handler(const NonnullRefPtrVector<LauncherHandler>& handlers) -{ - // If this is an application, pick it first - for (size_t i = 0; i < handlers.size(); i++) { - if (handlers[i].details().launcher_type == Desktop::Launcher::LauncherType::Application) - return handlers[i]; - } - // If there's a handler preferred by the user, pick this first - for (size_t i = 0; i < handlers.size(); i++) { - if (handlers[i].details().launcher_type == Desktop::Launcher::LauncherType::UserPreferred) - return handlers[i]; - } - // Otherwise, use the user's default, if available - for (size_t i = 0; i < handlers.size(); i++) { - if (handlers[i].details().launcher_type == Desktop::Launcher::LauncherType::UserDefault) - return handlers[i]; - } - // If still no match, use the first one we find - if (!handlers.is_empty()) { - return handlers[0]; - } - - return {}; -} - -NonnullRefPtrVector<LauncherHandler> DirectoryView::get_launch_handlers(const URL& url) -{ - NonnullRefPtrVector<LauncherHandler> handlers; - for (auto& h : Desktop::Launcher::get_handlers_with_details_for_url(url)) { - handlers.append(adopt(*new LauncherHandler(h))); - } - return handlers; -} - -NonnullRefPtrVector<LauncherHandler> DirectoryView::get_launch_handlers(const String& path) -{ - return get_launch_handlers(URL::create_with_file_protocol(path)); -} - -void DirectoryView::handle_activation(const GUI::ModelIndex& index) -{ - if (!index.is_valid()) - return; - dbgln("on activation: {},{}, this={:p}, m_model={:p}", index.row(), index.column(), this, m_model.ptr()); - auto& node = this->node(index); - auto path = node.full_path(); - - struct stat st; - if (stat(path.characters(), &st) < 0) { - perror("stat"); - return; - } - - if (S_ISDIR(st.st_mode)) { - if (is_desktop()) { - Desktop::Launcher::open(URL::create_with_file_protocol(path)); - return; - } - open(path); - return; - } - - auto url = URL::create_with_file_protocol(path); - auto launcher_handlers = get_launch_handlers(url); - auto default_launcher = get_default_launch_handler(launcher_handlers); - if (default_launcher) { - launch(url, *default_launcher); - } else { - auto error_message = String::format("Could not open %s", path.characters()); - GUI::MessageBox::show(window(), error_message, "File Manager", GUI::MessageBox::Type::Error); - } -} - -DirectoryView::DirectoryView(Mode mode) - : m_mode(mode) - , m_model(GUI::FileSystemModel::create({})) - , m_sorting_model(GUI::SortingProxyModel::create(m_model)) -{ - set_active_widget(nullptr); - set_content_margins({ 2, 2, 2, 2 }); - - setup_actions(); - - m_error_label = add<GUI::Label>(); - m_error_label->set_font(m_error_label->font().bold_variant()); - - setup_model(); - - setup_icon_view(); - if (mode != Mode::Desktop) { - setup_columns_view(); - setup_table_view(); - } - - set_view_mode(ViewMode::Icon); -} - -const GUI::FileSystemModel::Node& DirectoryView::node(const GUI::ModelIndex& index) const -{ - return model().node(m_sorting_model->map_to_source(index)); -} - -void DirectoryView::setup_model() -{ - m_model->on_error = [this](int, const char* error_string) { - auto failed_path = m_model->root_path(); - auto error_message = String::formatted("Could not read {}:\n{}", failed_path, error_string); - m_error_label->set_text(error_message); - set_active_widget(m_error_label); - - m_mkdir_action->set_enabled(false); - m_touch_action->set_enabled(false); - - add_path_to_history(model().root_path()); - - if (on_path_change) - on_path_change(failed_path, false); - }; - - m_model->on_complete = [this] { - if (m_table_view) - m_table_view->selection().clear(); - if (m_icon_view) - m_icon_view->selection().clear(); - - add_path_to_history(model().root_path()); - - bool can_write_in_path = access(model().root_path().characters(), W_OK) == 0; - - m_mkdir_action->set_enabled(can_write_in_path); - m_touch_action->set_enabled(can_write_in_path); - - if (on_path_change) - on_path_change(model().root_path(), can_write_in_path); - }; - - m_model->register_client(*this); - - m_model->on_thumbnail_progress = [this](int done, int total) { - if (on_thumbnail_progress) - on_thumbnail_progress(done, total); - }; - - if (is_desktop()) - m_model->set_root_path(Core::StandardPaths::desktop_directory()); -} - -void DirectoryView::setup_icon_view() -{ - m_icon_view = add<GUI::IconView>(); - m_icon_view->set_should_hide_unnecessary_scrollbars(true); - m_icon_view->set_selection_mode(GUI::AbstractView::SelectionMode::MultiSelection); - m_icon_view->set_editable(true); - m_icon_view->set_edit_triggers(GUI::AbstractView::EditTrigger::EditKeyPressed); - m_icon_view->aid_create_editing_delegate = [](auto&) { - return make<GUI::StringModelEditingDelegate>(); - }; - - if (is_desktop()) { - m_icon_view->set_frame_shape(Gfx::FrameShape::NoFrame); - m_icon_view->set_scrollbars_enabled(false); - m_icon_view->set_fill_with_background_color(false); - m_icon_view->set_draw_item_text_with_shadow(true); - m_icon_view->set_flow_direction(GUI::IconView::FlowDirection::TopToBottom); - } - - m_icon_view->set_model(m_sorting_model); - m_icon_view->set_model_column(GUI::FileSystemModel::Column::Name); - m_icon_view->on_activation = [&](auto& index) { - handle_activation(index); - }; - m_icon_view->on_selection_change = [this] { - handle_selection_change(); - }; - m_icon_view->on_context_menu_request = [this](auto& index, auto& event) { - if (on_context_menu_request) - on_context_menu_request(index, event); - }; - m_icon_view->on_drop = [this](auto& index, auto& event) { - handle_drop(index, event); - }; -} - -void DirectoryView::setup_columns_view() -{ - m_columns_view = add<GUI::ColumnsView>(); - m_columns_view->set_should_hide_unnecessary_scrollbars(true); - m_columns_view->set_selection_mode(GUI::AbstractView::SelectionMode::MultiSelection); - m_columns_view->set_editable(true); - m_columns_view->set_edit_triggers(GUI::AbstractView::EditTrigger::EditKeyPressed); - m_columns_view->aid_create_editing_delegate = [](auto&) { - return make<GUI::StringModelEditingDelegate>(); - }; - - m_columns_view->set_model(m_sorting_model); - m_columns_view->set_model_column(GUI::FileSystemModel::Column::Name); - - m_columns_view->on_activation = [&](auto& index) { - handle_activation(index); - }; - - m_columns_view->on_selection_change = [this] { - handle_selection_change(); - }; - - m_columns_view->on_context_menu_request = [this](auto& index, auto& event) { - if (on_context_menu_request) - on_context_menu_request(index, event); - }; - - m_columns_view->on_drop = [this](auto& index, auto& event) { - handle_drop(index, event); - }; -} - -void DirectoryView::setup_table_view() -{ - m_table_view = add<GUI::TableView>(); - m_table_view->set_should_hide_unnecessary_scrollbars(true); - m_table_view->set_selection_mode(GUI::AbstractView::SelectionMode::MultiSelection); - m_table_view->set_editable(true); - m_table_view->set_edit_triggers(GUI::AbstractView::EditTrigger::EditKeyPressed); - m_table_view->aid_create_editing_delegate = [](auto&) { - return make<GUI::StringModelEditingDelegate>(); - }; - - m_table_view->set_model(m_sorting_model); - m_table_view->set_key_column_and_sort_order(GUI::FileSystemModel::Column::Name, GUI::SortOrder::Ascending); - - m_table_view->on_activation = [&](auto& index) { - handle_activation(index); - }; - - m_table_view->on_selection_change = [this] { - handle_selection_change(); - }; - - m_table_view->on_context_menu_request = [this](auto& index, auto& event) { - if (on_context_menu_request) - on_context_menu_request(index, event); - }; - - m_table_view->on_drop = [this](auto& index, auto& event) { - handle_drop(index, event); - }; -} - -DirectoryView::~DirectoryView() -{ - m_model->unregister_client(*this); -} - -void DirectoryView::model_did_update(unsigned flags) -{ - if (flags & GUI::Model::UpdateFlag::InvalidateAllIndexes) { - for_each_view_implementation([](auto& view) { - view.selection().clear(); - }); - } - update_statusbar(); -} - -void DirectoryView::set_view_mode(ViewMode mode) -{ - if (m_view_mode == mode) - return; - m_view_mode = mode; - update(); - if (mode == ViewMode::Table) { - set_active_widget(m_table_view); - return; - } - if (mode == ViewMode::Columns) { - set_active_widget(m_columns_view); - return; - } - if (mode == ViewMode::Icon) { - set_active_widget(m_icon_view); - return; - } - ASSERT_NOT_REACHED(); -} - -void DirectoryView::add_path_to_history(const StringView& path) -{ - if (m_path_history.size() && m_path_history.at(m_path_history_position) == path) - return; - - if (m_path_history_position < m_path_history.size()) - m_path_history.resize(m_path_history_position + 1); - - m_path_history.append(path); - m_path_history_position = m_path_history.size() - 1; -} - -void DirectoryView::open(const StringView& path) -{ - if (model().root_path() == path) { - model().update(); - return; - } - - set_active_widget(¤t_view()); - model().set_root_path(path); -} - -void DirectoryView::set_status_message(const StringView& message) -{ - if (on_status_message) - on_status_message(message); -} - -void DirectoryView::open_parent_directory() -{ - auto path = String::formatted("{}/..", model().root_path()); - model().set_root_path(path); -} - -void DirectoryView::refresh() -{ - model().update(); -} - -void DirectoryView::open_previous_directory() -{ - if (m_path_history_position > 0) { - set_active_widget(¤t_view()); - m_path_history_position--; - model().set_root_path(m_path_history[m_path_history_position]); - } -} -void DirectoryView::open_next_directory() -{ - if (m_path_history_position < m_path_history.size() - 1) { - set_active_widget(¤t_view()); - m_path_history_position++; - model().set_root_path(m_path_history[m_path_history_position]); - } -} - -void DirectoryView::update_statusbar() -{ - // If we're triggered during widget construction, just ignore it. - if (m_view_mode == ViewMode::Invalid) - return; - - size_t total_size = model().node({}).total_size; - if (current_view().selection().is_empty()) { - set_status_message(String::formatted("{} item(s) ({})", - model().row_count(), - human_readable_size(total_size))); - return; - } - - int selected_item_count = current_view().selection().size(); - size_t selected_byte_count = 0; - - current_view().selection().for_each_index([&](auto& index) { - auto& model = *current_view().model(); - auto size_index = model.index(index.row(), GUI::FileSystemModel::Column::Size, model.parent_index(index)); - auto file_size = size_index.data().to_i32(); - selected_byte_count += file_size; - }); - - StringBuilder builder; - builder.append(String::number(selected_item_count)); - builder.append(" item"); - if (selected_item_count != 1) - builder.append('s'); - builder.append(" selected ("); - builder.append(human_readable_size(selected_byte_count).characters()); - builder.append(')'); - - if (selected_item_count == 1) { - auto& node = this->node(current_view().selection().first()); - if (!node.symlink_target.is_empty()) { - builder.append(" -> "); - builder.append(node.symlink_target); - } - } - - set_status_message(builder.to_string()); -} - -void DirectoryView::set_should_show_dotfiles(bool show_dotfiles) -{ - m_model->set_should_show_dotfiles(show_dotfiles); -} - -void DirectoryView::launch(const URL&, const LauncherHandler& launcher_handler) -{ - pid_t child; - if (launcher_handler.details().launcher_type == Desktop::Launcher::LauncherType::Application) { - const char* argv[] = { launcher_handler.details().name.characters(), nullptr }; - posix_spawn(&child, launcher_handler.details().executable.characters(), nullptr, nullptr, const_cast<char**>(argv), environ); - if (disown(child) < 0) - perror("disown"); - } else { - for (auto& path : selected_file_paths()) { - const char* argv[] = { launcher_handler.details().name.characters(), path.characters(), nullptr }; - posix_spawn(&child, launcher_handler.details().executable.characters(), nullptr, nullptr, const_cast<char**>(argv), environ); - if (disown(child) < 0) - perror("disown"); - } - } -} - -Vector<String> DirectoryView::selected_file_paths() const -{ - Vector<String> paths; - auto& view = current_view(); - auto& model = *view.model(); - view.selection().for_each_index([&](const GUI::ModelIndex& index) { - auto parent_index = model.parent_index(index); - auto name_index = model.index(index.row(), GUI::FileSystemModel::Column::Name, parent_index); - auto path = name_index.data(GUI::ModelRole::Custom).to_string(); - paths.append(path); - }); - return paths; -} - -void DirectoryView::do_delete(bool should_confirm) -{ - auto paths = selected_file_paths(); - ASSERT(!paths.is_empty()); - FileUtils::delete_paths(paths, should_confirm, window()); -} - -void DirectoryView::handle_selection_change() -{ - update_statusbar(); - - bool can_delete = !current_view().selection().is_empty() && access(path().characters(), W_OK) == 0; - m_delete_action->set_enabled(can_delete); - m_force_delete_action->set_enabled(can_delete); - - if (on_selection_change) - on_selection_change(current_view()); -} - -void DirectoryView::setup_actions() -{ - m_mkdir_action = GUI::Action::create("New directory...", { Mod_Ctrl | Mod_Shift, Key_N }, Gfx::Bitmap::load_from_file("/res/icons/16x16/mkdir.png"), [&](const GUI::Action&) { - String value; - if (GUI::InputBox::show(value, window(), "Enter name:", "New directory") == GUI::InputBox::ExecOK && !value.is_empty()) { - auto new_dir_path = LexicalPath::canonicalized_path(String::formatted("{}/{}", path(), value)); - int rc = mkdir(new_dir_path.characters(), 0777); - if (rc < 0) { - auto saved_errno = errno; - GUI::MessageBox::show(window(), String::formatted("mkdir(\"{}\") failed: {}", new_dir_path, strerror(saved_errno)), "Error", GUI::MessageBox::Type::Error); - } - } - }); - - m_touch_action = GUI::Action::create("New file...", { Mod_Ctrl | Mod_Shift, Key_F }, Gfx::Bitmap::load_from_file("/res/icons/16x16/new.png"), [&](const GUI::Action&) { - String value; - if (GUI::InputBox::show(value, window(), "Enter name:", "New file") == GUI::InputBox::ExecOK && !value.is_empty()) { - auto new_file_path = LexicalPath::canonicalized_path(String::formatted("{}/{}", path(), value)); - struct stat st; - int rc = stat(new_file_path.characters(), &st); - if ((rc < 0 && errno != ENOENT)) { - auto saved_errno = errno; - GUI::MessageBox::show(window(), String::formatted("stat(\"{}\") failed: {}", new_file_path, strerror(saved_errno)), "Error", GUI::MessageBox::Type::Error); - return; - } - if (rc == 0) { - GUI::MessageBox::show(window(), String::formatted("{}: Already exists", new_file_path), "Error", GUI::MessageBox::Type::Error); - return; - } - int fd = creat(new_file_path.characters(), 0666); - if (fd < 0) { - auto saved_errno = errno; - GUI::MessageBox::show(window(), String::formatted("creat(\"{}\") failed: {}", new_file_path, strerror(saved_errno)), "Error", GUI::MessageBox::Type::Error); - return; - } - rc = close(fd); - ASSERT(rc >= 0); - } - }); - - m_open_terminal_action = GUI::Action::create("Open Terminal here", Gfx::Bitmap::load_from_file("/res/icons/16x16/app-terminal.png"), [&](auto&) { - posix_spawn_file_actions_t spawn_actions; - posix_spawn_file_actions_init(&spawn_actions); - posix_spawn_file_actions_addchdir(&spawn_actions, path().characters()); - pid_t pid; - const char* argv[] = { "Terminal", nullptr }; - if ((errno = posix_spawn(&pid, "/bin/Terminal", &spawn_actions, nullptr, const_cast<char**>(argv), environ))) { - perror("posix_spawn"); - } else { - if (disown(pid) < 0) - perror("disown"); - } - posix_spawn_file_actions_destroy(&spawn_actions); - }); - - m_delete_action = GUI::CommonActions::make_delete_action([this](auto&) { do_delete(true); }, window()); - - m_force_delete_action = GUI::Action::create( - "Delete without confirmation", { Mod_Shift, Key_Delete }, - [this](auto&) { do_delete(false); }, - window()); -} - -void DirectoryView::handle_drop(const GUI::ModelIndex& index, const GUI::DropEvent& event) -{ - if (!event.mime_data().has_urls()) - return; - auto urls = event.mime_data().urls(); - if (urls.is_empty()) { - dbgln("No files to drop"); - return; - } - - auto& target_node = node(index); - if (!target_node.is_directory()) - return; - - bool had_accepted_drop = false; - for (auto& url_to_copy : urls) { - if (!url_to_copy.is_valid() || url_to_copy.path() == target_node.full_path()) - continue; - auto new_path = String::formatted("{}/{}", target_node.full_path(), LexicalPath(url_to_copy.path()).basename()); - if (url_to_copy.path() == new_path) - continue; - - if (!FileUtils::copy_file_or_directory(url_to_copy.path(), new_path)) { - auto error_message = String::formatted("Could not copy {} into {}.", url_to_copy.to_string(), new_path); - GUI::MessageBox::show(window(), error_message, "File Manager", GUI::MessageBox::Type::Error); - } else { - had_accepted_drop = true; - } - } - if (had_accepted_drop && on_accepted_drop) - on_accepted_drop(); -} - -} diff --git a/Applications/FileManager/DirectoryView.h b/Applications/FileManager/DirectoryView.h deleted file mode 100644 index a72b63aaf5..0000000000 --- a/Applications/FileManager/DirectoryView.h +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 <AK/URL.h> -#include <AK/Vector.h> -#include <LibDesktop/Launcher.h> -#include <LibGUI/Action.h> -#include <LibGUI/ColumnsView.h> -#include <LibGUI/FileSystemModel.h> -#include <LibGUI/IconView.h> -#include <LibGUI/StackWidget.h> -#include <LibGUI/TableView.h> -#include <sys/stat.h> - -namespace FileManager { - -class LauncherHandler : public RefCounted<LauncherHandler> { -public: - LauncherHandler(const NonnullRefPtr<Desktop::Launcher::Details>& details) - : m_details(details) - { - } - - NonnullRefPtr<GUI::Action> create_launch_action(Function<void(const LauncherHandler&)>); - const Desktop::Launcher::Details& details() const { return *m_details; } - -private: - NonnullRefPtr<Desktop::Launcher::Details> m_details; -}; - -class DirectoryView final - : public GUI::StackWidget - , private GUI::ModelClient { - C_OBJECT(DirectoryView); - -public: - enum class Mode { - Desktop, - Normal, - }; - - virtual ~DirectoryView() override; - - void open(const StringView& path); - String path() const { return model().root_path(); } - void open_parent_directory(); - void open_previous_directory(); - void open_next_directory(); - int path_history_size() const { return m_path_history.size(); } - int path_history_position() const { return m_path_history_position; } - static RefPtr<LauncherHandler> get_default_launch_handler(const NonnullRefPtrVector<LauncherHandler>& handlers); - NonnullRefPtrVector<LauncherHandler> get_launch_handlers(const URL& url); - NonnullRefPtrVector<LauncherHandler> get_launch_handlers(const String& path); - - void refresh(); - - void launch(const AK::URL&, const LauncherHandler&); - - Function<void(const StringView& path, bool can_write_in_path)> on_path_change; - Function<void(GUI::AbstractView&)> on_selection_change; - Function<void(const GUI::ModelIndex&, const GUI::ContextMenuEvent&)> on_context_menu_request; - Function<void(const StringView&)> on_status_message; - Function<void(int done, int total)> on_thumbnail_progress; - Function<void()> on_accepted_drop; - - enum ViewMode { - Invalid, - Table, - Columns, - Icon - }; - void set_view_mode(ViewMode); - ViewMode view_mode() const { return m_view_mode; } - - GUI::AbstractView& current_view() - { - switch (m_view_mode) { - case ViewMode::Table: - return *m_table_view; - case ViewMode::Columns: - return *m_columns_view; - case ViewMode::Icon: - return *m_icon_view; - default: - ASSERT_NOT_REACHED(); - } - } - - const GUI::AbstractView& current_view() const - { - return const_cast<DirectoryView*>(this)->current_view(); - } - - template<typename Callback> - void for_each_view_implementation(Callback callback) - { - if (m_icon_view) - callback(*m_icon_view); - if (m_table_view) - callback(*m_table_view); - if (m_columns_view) - callback(*m_columns_view); - } - - void set_should_show_dotfiles(bool); - - const GUI::FileSystemModel::Node& node(const GUI::ModelIndex&) const; - - bool is_desktop() const { return m_mode == Mode::Desktop; } - - Vector<String> selected_file_paths() const; - - GUI::Action& mkdir_action() { return *m_mkdir_action; } - GUI::Action& touch_action() { return *m_touch_action; } - GUI::Action& open_terminal_action() { return *m_open_terminal_action; } - GUI::Action& delete_action() { return *m_delete_action; } - GUI::Action& force_delete_action() { return *m_force_delete_action; } - -private: - explicit DirectoryView(Mode); - - const GUI::FileSystemModel& model() const { return *m_model; } - GUI::FileSystemModel& model() { return *m_model; } - - void handle_selection_change(); - void handle_drop(const GUI::ModelIndex&, const GUI::DropEvent&); - void do_delete(bool should_confirm); - - // ^GUI::ModelClient - virtual void model_did_update(unsigned) override; - - void setup_actions(); - void setup_model(); - void setup_icon_view(); - void setup_columns_view(); - void setup_table_view(); - - void handle_activation(const GUI::ModelIndex&); - - void set_status_message(const StringView&); - void update_statusbar(); - - Mode m_mode { Mode::Normal }; - ViewMode m_view_mode { Invalid }; - - NonnullRefPtr<GUI::FileSystemModel> m_model; - NonnullRefPtr<GUI::SortingProxyModel> m_sorting_model; - size_t m_path_history_position { 0 }; - Vector<String> m_path_history; - void add_path_to_history(const StringView& path); - - RefPtr<GUI::Label> m_error_label; - - RefPtr<GUI::TableView> m_table_view; - RefPtr<GUI::IconView> m_icon_view; - RefPtr<GUI::ColumnsView> m_columns_view; - - RefPtr<GUI::Action> m_mkdir_action; - RefPtr<GUI::Action> m_touch_action; - RefPtr<GUI::Action> m_open_terminal_action; - RefPtr<GUI::Action> m_delete_action; - RefPtr<GUI::Action> m_force_delete_action; -}; - -} diff --git a/Applications/FileManager/FileManagerWindow.gml b/Applications/FileManager/FileManagerWindow.gml deleted file mode 100644 index a13218dd16..0000000000 --- a/Applications/FileManager/FileManagerWindow.gml +++ /dev/null @@ -1,53 +0,0 @@ -@GUI::Widget { - fill_with_background_color: true - layout: @GUI::VerticalBoxLayout { - spacing: 2 - } - - @GUI::ToolBarContainer { - @GUI::ToolBar { - name: "main_toolbar" - } - @GUI::ToolBar { - name: "location_toolbar" - visible: false - - @GUI::Label { - text: "Location: " - autosize: true - } - - @GUI::TextBox { - name: "location_textbox" - fixed_height: 22 - } - } - @GUI::ToolBar { - name: "breadcrumb_toolbar" - - @GUI::BreadcrumbBar { - name: "breadcrumb_bar" - } - } - } - - @GUI::HorizontalSplitter { - name: "splitter" - - @GUI::TreeView { - name: "tree_view" - fixed_width: 175 - } - - } - - @GUI::StatusBar { - name: "statusbar" - - @GUI::ProgressBar { - name: "progressbar" - text: "Generating thumbnails: " - visible: false - } - } -} diff --git a/Applications/FileManager/FileUtils.cpp b/Applications/FileManager/FileUtils.cpp deleted file mode 100644 index 76d2ee0079..0000000000 --- a/Applications/FileManager/FileUtils.cpp +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "FileUtils.h" -#include <AK/LexicalPath.h> -#include <AK/ScopeGuard.h> -#include <AK/StringBuilder.h> -#include <LibCore/DirIterator.h> -#include <LibCore/File.h> -#include <LibGUI/MessageBox.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/stat.h> -#include <unistd.h> - -namespace FileUtils { - -void delete_path(const String& path, GUI::Window* parent_window) -{ - struct stat st; - if (lstat(path.characters(), &st)) { - GUI::MessageBox::show(parent_window, - String::formatted("lstat({}) failed: {}", path, strerror(errno)), - "Delete failed", - GUI::MessageBox::Type::Error); - } - - if (S_ISDIR(st.st_mode)) { - String error_path; - int error = FileUtils::delete_directory(path, error_path); - - if (error) { - GUI::MessageBox::show(parent_window, - String::formatted("Failed to delete directory \"{}\": {}", error_path, strerror(error)), - "Delete failed", - GUI::MessageBox::Type::Error); - } - } else if (unlink(path.characters()) < 0) { - int saved_errno = errno; - GUI::MessageBox::show(parent_window, - String::formatted("unlink(\"{}\") failed: {}", path, strerror(saved_errno)), - "Delete failed", - GUI::MessageBox::Type::Error); - } -} - -void delete_paths(const Vector<String>& paths, bool should_confirm, GUI::Window* parent_window) -{ - String message; - if (paths.size() == 1) { - message = String::formatted("Really delete {}?", LexicalPath(paths[0]).basename()); - } else { - message = String::formatted("Really delete {} files?", paths.size()); - } - - if (should_confirm) { - auto result = GUI::MessageBox::show(parent_window, - message, - "Confirm deletion", - GUI::MessageBox::Type::Warning, - GUI::MessageBox::InputType::OKCancel); - if (result == GUI::MessageBox::ExecCancel) - return; - } - - for (auto& path : paths) { - delete_path(path, parent_window); - } -} - -int delete_directory(String directory, String& file_that_caused_error) -{ - Core::DirIterator iterator(directory, Core::DirIterator::SkipDots); - if (iterator.has_error()) { - file_that_caused_error = directory; - return -1; - } - - while (iterator.has_next()) { - auto file_to_delete = String::formatted("{}/{}", directory, iterator.next_path()); - struct stat st; - - if (lstat(file_to_delete.characters(), &st)) { - file_that_caused_error = file_to_delete; - return errno; - } - - if (S_ISDIR(st.st_mode)) { - if (delete_directory(file_to_delete, file_to_delete)) { - file_that_caused_error = file_to_delete; - return errno; - } - } else if (unlink(file_to_delete.characters())) { - file_that_caused_error = file_to_delete; - return errno; - } - } - - if (rmdir(directory.characters())) { - file_that_caused_error = directory; - return errno; - } - - return 0; -} - -bool copy_file_or_directory(const String& src_path, const String& dst_path) -{ - int duplicate_count = 0; - while (access(get_duplicate_name(dst_path, duplicate_count).characters(), F_OK) == 0) { - ++duplicate_count; - } - if (duplicate_count != 0) { - return copy_file_or_directory(src_path, get_duplicate_name(dst_path, duplicate_count)); - } - - auto source_or_error = Core::File::open(src_path, Core::IODevice::ReadOnly); - if (source_or_error.is_error()) - return false; - - auto& source = *source_or_error.value(); - - struct stat src_stat; - int rc = fstat(source.fd(), &src_stat); - if (rc < 0) - return false; - - if (source.is_directory()) - return copy_directory(src_path, dst_path, src_stat); - - return copy_file(dst_path, src_stat, source); -} - -bool copy_directory(const String& src_path, const String& dst_path, const struct stat& src_stat) -{ - int rc = mkdir(dst_path.characters(), 0755); - if (rc < 0) { - return false; - } - Core::DirIterator di(src_path, Core::DirIterator::SkipDots); - if (di.has_error()) { - return false; - } - while (di.has_next()) { - String filename = di.next_path(); - bool is_copied = copy_file_or_directory( - String::formatted("{}/{}", src_path, filename), - String::formatted("{}/{}", dst_path, filename)); - if (!is_copied) { - return false; - } - } - - auto my_umask = umask(0); - umask(my_umask); - rc = chmod(dst_path.characters(), src_stat.st_mode & ~my_umask); - if (rc < 0) { - return false; - } - return true; -} - -bool copy_file(const String& dst_path, const struct stat& src_stat, Core::File& source) -{ - int dst_fd = creat(dst_path.characters(), 0666); - if (dst_fd < 0) { - if (errno != EISDIR) { - return false; - } - auto dst_dir_path = String::formatted("{}/{}", dst_path, LexicalPath(source.filename()).basename()); - dst_fd = creat(dst_dir_path.characters(), 0666); - if (dst_fd < 0) { - return false; - } - } - - ScopeGuard close_fd_guard([dst_fd]() { close(dst_fd); }); - - if (src_stat.st_size > 0) { - if (ftruncate(dst_fd, src_stat.st_size) < 0) { - perror("cp: ftruncate"); - return false; - } - } - - for (;;) { - char buffer[32768]; - ssize_t nread = read(source.fd(), buffer, sizeof(buffer)); - if (nread < 0) { - return false; - } - if (nread == 0) - break; - ssize_t remaining_to_write = nread; - char* bufptr = buffer; - while (remaining_to_write) { - ssize_t nwritten = write(dst_fd, bufptr, remaining_to_write); - if (nwritten < 0) { - return false; - } - assert(nwritten > 0); - remaining_to_write -= nwritten; - bufptr += nwritten; - } - } - - auto my_umask = umask(0); - umask(my_umask); - int rc = fchmod(dst_fd, src_stat.st_mode & ~my_umask); - if (rc < 0) { - return false; - } - - return true; -} - -bool link_file(const String& src_path, const String& dst_path) -{ - int duplicate_count = 0; - while (access(get_duplicate_name(dst_path, duplicate_count).characters(), F_OK) == 0) { - ++duplicate_count; - } - if (duplicate_count != 0) { - return link_file(src_path, get_duplicate_name(dst_path, duplicate_count)); - } - int rc = symlink(src_path.characters(), dst_path.characters()); - if (rc < 0) { - return false; - } - - return true; -} - -String get_duplicate_name(const String& path, int duplicate_count) -{ - if (duplicate_count == 0) { - return path; - } - LexicalPath lexical_path(path); - StringBuilder duplicated_name; - duplicated_name.append('/'); - for (size_t i = 0; i < lexical_path.parts().size() - 1; ++i) { - duplicated_name.appendff("{}/", lexical_path.parts()[i]); - } - auto prev_duplicate_tag = String::formatted("({})", duplicate_count); - auto title = lexical_path.title(); - if (title.ends_with(prev_duplicate_tag)) { - // remove the previous duplicate tag "(n)" so we can add a new tag. - title = title.substring(0, title.length() - prev_duplicate_tag.length()); - } - duplicated_name.appendff("{} ({})", lexical_path.title(), duplicate_count); - if (!lexical_path.extension().is_empty()) { - duplicated_name.appendff(".{}", lexical_path.extension()); - } - return duplicated_name.build(); -} -} diff --git a/Applications/FileManager/FileUtils.h b/Applications/FileManager/FileUtils.h deleted file mode 100644 index fcf6b483ac..0000000000 --- a/Applications/FileManager/FileUtils.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 <AK/String.h> -#include <LibCore/Forward.h> -#include <LibGUI/Forward.h> -#include <sys/stat.h> - -namespace FileUtils { - -enum class FileOperation { - Copy = 0, - Cut -}; - -void delete_path(const String&, GUI::Window*); -void delete_paths(const Vector<String>&, bool should_confirm, GUI::Window*); -int delete_directory(String directory, String& file_that_caused_error); -bool copy_file_or_directory(const String& src_path, const String& dst_path); -String get_duplicate_name(const String& path, int duplicate_count); -bool copy_file(const String& dst_path, const struct stat& src_stat, Core::File&); -bool copy_directory(const String& src_path, const String& dst_path, const struct stat& src_stat); -bool link_file(const String& src_path, const String& dst_path); - -} diff --git a/Applications/FileManager/PropertiesWindow.cpp b/Applications/FileManager/PropertiesWindow.cpp deleted file mode 100644 index 8c0ff9f74e..0000000000 --- a/Applications/FileManager/PropertiesWindow.cpp +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "PropertiesWindow.h" -#include <AK/LexicalPath.h> -#include <AK/StringBuilder.h> -#include <LibDesktop/Launcher.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/CheckBox.h> -#include <LibGUI/FileIconProvider.h> -#include <LibGUI/FilePicker.h> -#include <LibGUI/LinkLabel.h> -#include <LibGUI/MessageBox.h> -#include <LibGUI/SeparatorWidget.h> -#include <LibGUI/TabWidget.h> -#include <grp.h> -#include <limits.h> -#include <pwd.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> - -PropertiesWindow::PropertiesWindow(const String& path, bool disable_rename, Window* parent_window) - : Window(parent_window) -{ - auto lexical_path = LexicalPath(path); - ASSERT(lexical_path.is_valid()); - - auto& main_widget = set_main_widget<GUI::Widget>(); - main_widget.set_layout<GUI::VerticalBoxLayout>(); - main_widget.layout()->set_margins({ 4, 4, 4, 4 }); - main_widget.set_fill_with_background_color(true); - - set_rect({ 0, 0, 360, 420 }); - set_resizable(false); - - auto& tab_widget = main_widget.add<GUI::TabWidget>(); - - auto& general_tab = tab_widget.add_tab<GUI::Widget>("General"); - general_tab.set_layout<GUI::VerticalBoxLayout>(); - general_tab.layout()->set_margins({ 12, 8, 12, 8 }); - general_tab.layout()->set_spacing(10); - - auto& file_container = general_tab.add<GUI::Widget>(); - file_container.set_layout<GUI::HorizontalBoxLayout>(); - file_container.layout()->set_spacing(20); - file_container.set_fixed_height(34); - - m_icon = file_container.add<GUI::ImageWidget>(); - m_icon->set_fixed_size(32, 32); - - m_name = lexical_path.basename(); - m_path = lexical_path.string(); - m_parent_path = lexical_path.dirname(); - - m_name_box = file_container.add<GUI::TextBox>(); - m_name_box->set_text(m_name); - m_name_box->set_mode(disable_rename ? GUI::TextBox::Mode::DisplayOnly : GUI::TextBox::Mode::Editable); - m_name_box->on_change = [&]() { - m_name_dirty = m_name != m_name_box->text(); - m_apply_button->set_enabled(m_name_dirty || m_permissions_dirty); - }; - - set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/properties.png")); - general_tab.add<GUI::SeparatorWidget>(Gfx::Orientation::Horizontal); - - struct stat st; - if (lstat(path.characters(), &st)) { - perror("stat"); - return; - } - - String owner_name; - String group_name; - - if (auto* pw = getpwuid(st.st_uid)) { - owner_name = pw->pw_name; - } else { - owner_name = "n/a"; - } - - if (auto* gr = getgrgid(st.st_gid)) { - group_name = gr->gr_name; - } else { - group_name = "n/a"; - } - - m_mode = st.st_mode; - m_old_mode = st.st_mode; - - auto properties = Vector<PropertyValuePair>(); - properties.append({ "Type:", get_description(m_mode) }); - auto parent_link = URL::create_with_file_protocol(m_parent_path); - properties.append(PropertyValuePair { "Location:", path, Optional(parent_link) }); - - if (S_ISLNK(m_mode)) { - auto link_destination = Core::File::read_link(path); - if (link_destination.is_null()) { - perror("readlink"); - } else { - auto link_directory = LexicalPath(link_destination); - ASSERT(link_directory.is_valid()); - auto link_parent = URL::create_with_file_protocol(link_directory.dirname()); - properties.append({ "Link target:", link_destination, Optional(link_parent) }); - } - } - - properties.append({ "Size:", String::formatted("{} bytes", st.st_size) }); - properties.append({ "Owner:", String::formatted("{} ({})", owner_name, st.st_uid) }); - properties.append({ "Group:", String::formatted("{} ({})", group_name, st.st_gid) }); - properties.append({ "Created at:", GUI::FileSystemModel::timestamp_string(st.st_ctime) }); - properties.append({ "Last modified:", GUI::FileSystemModel::timestamp_string(st.st_mtime) }); - - make_property_value_pairs(properties, general_tab); - - general_tab.add<GUI::SeparatorWidget>(Gfx::Orientation::Horizontal); - - make_permission_checkboxes(general_tab, { S_IRUSR, S_IWUSR, S_IXUSR }, "Owner:", m_mode); - make_permission_checkboxes(general_tab, { S_IRGRP, S_IWGRP, S_IXGRP }, "Group:", m_mode); - make_permission_checkboxes(general_tab, { S_IROTH, S_IWOTH, S_IXOTH }, "Others:", m_mode); - - general_tab.layout()->add_spacer(); - - auto& button_widget = main_widget.add<GUI::Widget>(); - button_widget.set_layout<GUI::HorizontalBoxLayout>(); - button_widget.set_fixed_height(24); - button_widget.layout()->set_spacing(5); - - button_widget.layout()->add_spacer(); - - make_button("OK", button_widget).on_click = [this](auto) { - if (apply_changes()) - close(); - }; - make_button("Cancel", button_widget).on_click = [this](auto) { - close(); - }; - - m_apply_button = make_button("Apply", button_widget); - m_apply_button->on_click = [this](auto) { apply_changes(); }; - m_apply_button->set_enabled(false); - - update(); -} - -PropertiesWindow::~PropertiesWindow() -{ -} - -void PropertiesWindow::update() -{ - m_icon->set_bitmap(GUI::FileIconProvider::icon_for_path(make_full_path(m_name), m_mode).bitmap_for_size(32)); - set_title(String::formatted("{} - Properties", m_name)); -} - -void PropertiesWindow::permission_changed(mode_t mask, bool set) -{ - if (set) { - m_mode |= mask; - } else { - m_mode &= ~mask; - } - - m_permissions_dirty = m_mode != m_old_mode; - m_apply_button->set_enabled(m_name_dirty || m_permissions_dirty); -} - -String PropertiesWindow::make_full_path(const String& name) -{ - return String::formatted("{}/{}", m_parent_path, name); -} - -bool PropertiesWindow::apply_changes() -{ - if (m_name_dirty) { - String new_name = m_name_box->text(); - String new_file = make_full_path(new_name).characters(); - - if (GUI::FilePicker::file_exists(new_file)) { - GUI::MessageBox::show(this, String::formatted("A file \"{}\" already exists!", new_name), "Error", GUI::MessageBox::Type::Error); - return false; - } - - if (rename(make_full_path(m_name).characters(), new_file.characters())) { - GUI::MessageBox::show(this, String::formatted("Could not rename file: {}!", strerror(errno)), "Error", GUI::MessageBox::Type::Error); - return false; - } - - m_name = new_name; - m_name_dirty = false; - update(); - } - - if (m_permissions_dirty) { - if (chmod(make_full_path(m_name).characters(), m_mode)) { - GUI::MessageBox::show(this, String::formatted("Could not update permissions: {}!", strerror(errno)), "Error", GUI::MessageBox::Type::Error); - return false; - } - - m_old_mode = m_mode; - m_permissions_dirty = false; - } - - update(); - m_apply_button->set_enabled(false); - return true; -} - -void PropertiesWindow::make_permission_checkboxes(GUI::Widget& parent, PermissionMasks masks, String label_string, mode_t mode) -{ - auto& widget = parent.add<GUI::Widget>(); - widget.set_layout<GUI::HorizontalBoxLayout>(); - widget.set_fixed_height(16); - widget.layout()->set_spacing(10); - - auto& label = widget.add<GUI::Label>(label_string); - label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - - struct stat st; - if (lstat(m_path.characters(), &st)) { - perror("stat"); - return; - } - - auto can_edit_checkboxes = st.st_uid == getuid(); - - auto& box_read = widget.add<GUI::CheckBox>("Read"); - box_read.set_checked(mode & masks.read); - box_read.on_checked = [&, masks](bool checked) { permission_changed(masks.read, checked); }; - box_read.set_enabled(can_edit_checkboxes); - - auto& box_write = widget.add<GUI::CheckBox>("Write"); - box_write.set_checked(mode & masks.write); - box_write.on_checked = [&, masks](bool checked) { permission_changed(masks.write, checked); }; - box_write.set_enabled(can_edit_checkboxes); - - auto& box_execute = widget.add<GUI::CheckBox>("Execute"); - box_execute.set_checked(mode & masks.execute); - box_execute.on_checked = [&, masks](bool checked) { permission_changed(masks.execute, checked); }; - box_execute.set_enabled(can_edit_checkboxes); -} - -void PropertiesWindow::make_property_value_pairs(const Vector<PropertyValuePair>& pairs, GUI::Widget& parent) -{ - int max_width = 0; - Vector<NonnullRefPtr<GUI::Label>> property_labels; - - property_labels.ensure_capacity(pairs.size()); - for (auto pair : pairs) { - auto& label_container = parent.add<GUI::Widget>(); - label_container.set_layout<GUI::HorizontalBoxLayout>(); - label_container.set_fixed_height(14); - label_container.layout()->set_spacing(12); - - auto& label_property = label_container.add<GUI::Label>(pair.property); - label_property.set_text_alignment(Gfx::TextAlignment::CenterLeft); - - if (!pair.link.has_value()) { - label_container.add<GUI::Label>(pair.value).set_text_alignment(Gfx::TextAlignment::CenterLeft); - } else { - auto& link = label_container.add<GUI::LinkLabel>(pair.value); - link.set_text_alignment(Gfx::TextAlignment::CenterLeft); - link.on_click = [pair]() { - Desktop::Launcher::open(pair.link.value()); - }; - } - - max_width = max(max_width, label_property.font().width(pair.property)); - property_labels.append(label_property); - } - - for (auto label : property_labels) - label->set_fixed_width(max_width); -} - -GUI::Button& PropertiesWindow::make_button(String text, GUI::Widget& parent) -{ - auto& button = parent.add<GUI::Button>(text); - button.set_fixed_size(70, 22); - return button; -} diff --git a/Applications/FileManager/PropertiesWindow.h b/Applications/FileManager/PropertiesWindow.h deleted file mode 100644 index d2801f8049..0000000000 --- a/Applications/FileManager/PropertiesWindow.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 <LibCore/File.h> -#include <LibGUI/Button.h> -#include <LibGUI/Dialog.h> -#include <LibGUI/FileSystemModel.h> -#include <LibGUI/ImageWidget.h> -#include <LibGUI/Label.h> -#include <LibGUI/TextBox.h> - -class PropertiesWindow final : public GUI::Window { - C_OBJECT(PropertiesWindow); - -public: - virtual ~PropertiesWindow() override; - -private: - PropertiesWindow(const String& path, bool disable_rename, Window* parent = nullptr); - - struct PropertyValuePair { - String property; - String value; - Optional<URL> link = {}; - }; - - struct PermissionMasks { - mode_t read; - mode_t write; - mode_t execute; - }; - - static const String get_description(const mode_t mode) - { - if (S_ISREG(mode)) - return "File"; - if (S_ISDIR(mode)) - return "Directory"; - if (S_ISLNK(mode)) - return "Symbolic link"; - if (S_ISCHR(mode)) - return "Character device"; - if (S_ISBLK(mode)) - return "Block device"; - if (S_ISFIFO(mode)) - return "FIFO (named pipe)"; - if (S_ISSOCK(mode)) - return "Socket"; - if (mode & S_IXUSR) - return "Executable"; - - return "Unknown"; - } - - GUI::Button& make_button(String, GUI::Widget& parent); - void make_property_value_pairs(const Vector<PropertyValuePair>& pairs, GUI::Widget& parent); - void make_permission_checkboxes(GUI::Widget& parent, PermissionMasks, String label_string, mode_t mode); - void permission_changed(mode_t mask, bool set); - bool apply_changes(); - void update(); - String make_full_path(const String& name); - - RefPtr<GUI::Button> m_apply_button; - RefPtr<GUI::TextBox> m_name_box; - RefPtr<GUI::ImageWidget> m_icon; - String m_name; - String m_parent_path; - String m_path; - mode_t m_mode { 0 }; - mode_t m_old_mode { 0 }; - bool m_permissions_dirty { false }; - bool m_name_dirty { false }; -}; diff --git a/Applications/FileManager/main.cpp b/Applications/FileManager/main.cpp deleted file mode 100644 index 9766876f21..0000000000 --- a/Applications/FileManager/main.cpp +++ /dev/null @@ -1,974 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "DesktopWidget.h" -#include "DirectoryView.h" -#include "FileUtils.h" -#include "PropertiesWindow.h" -#include <AK/LexicalPath.h> -#include <AK/StringBuilder.h> -#include <AK/URL.h> -#include <Applications/FileManager/FileManagerWindowGML.h> -#include <LibCore/ConfigFile.h> -#include <LibCore/MimeData.h> -#include <LibCore/StandardPaths.h> -#include <LibDesktop/Launcher.h> -#include <LibGUI/Action.h> -#include <LibGUI/ActionGroup.h> -#include <LibGUI/Application.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/BreadcrumbBar.h> -#include <LibGUI/Clipboard.h> -#include <LibGUI/Desktop.h> -#include <LibGUI/FileIconProvider.h> -#include <LibGUI/FileSystemModel.h> -#include <LibGUI/InputBox.h> -#include <LibGUI/Label.h> -#include <LibGUI/Menu.h> -#include <LibGUI/MenuBar.h> -#include <LibGUI/MessageBox.h> -#include <LibGUI/Painter.h> -#include <LibGUI/ProgressBar.h> -#include <LibGUI/Splitter.h> -#include <LibGUI/StatusBar.h> -#include <LibGUI/TextEditor.h> -#include <LibGUI/ToolBar.h> -#include <LibGUI/ToolBarContainer.h> -#include <LibGUI/TreeView.h> -#include <LibGUI/Widget.h> -#include <LibGUI/Window.h> -#include <LibGfx/Palette.h> -#include <pthread.h> -#include <serenity.h> -#include <signal.h> -#include <spawn.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> - -using namespace FileManager; - -static int run_in_desktop_mode(RefPtr<Core::ConfigFile>); -static int run_in_windowed_mode(RefPtr<Core::ConfigFile>, String initial_location); -static void do_copy(const Vector<String>& selected_file_paths, FileUtils::FileOperation file_operation); -static void do_paste(const String& target_directory, GUI::Window* window); -static void do_create_link(const Vector<String>& selected_file_paths, GUI::Window* window); -static void show_properties(const String& container_dir_path, const String& path, const Vector<String>& selected, GUI::Window* window); - -int main(int argc, char** argv) -{ - if (pledge("stdio thread shared_buffer accept unix cpath rpath wpath fattr proc exec sigaction", nullptr) < 0) { - perror("pledge"); - return 1; - } - - struct sigaction act; - memset(&act, 0, sizeof(act)); - act.sa_flags = SA_NOCLDWAIT; - act.sa_handler = SIG_IGN; - int rc = sigaction(SIGCHLD, &act, nullptr); - if (rc < 0) { - perror("sigaction"); - return 1; - } - - RefPtr<Core::ConfigFile> config = Core::ConfigFile::get_for_app("FileManager"); - - auto app = GUI::Application::construct(argc, argv); - - if (pledge("stdio thread shared_buffer accept cpath rpath wpath fattr proc exec unix", nullptr) < 0) { - perror("pledge"); - return 1; - } - - if (app->args().contains_slow("--desktop") || app->args().contains_slow("-d")) - return run_in_desktop_mode(move(config)); - - // our initial location is defined as, in order of precedence: - // 1. the first command-line argument (e.g. FileManager /bin) - // 2. the user's home directory - // 3. the root directory - String initial_location; - - if (argc >= 2) { - char* buffer = realpath(argv[1], nullptr); - initial_location = buffer; - free(buffer); - } - - if (initial_location.is_empty()) - initial_location = Core::StandardPaths::home_directory(); - - if (initial_location.is_empty()) - initial_location = "/"; - - return run_in_windowed_mode(move(config), initial_location); -} - -void do_copy(const Vector<String>& selected_file_paths, FileUtils::FileOperation file_operation) -{ - if (selected_file_paths.is_empty()) - ASSERT_NOT_REACHED(); - - StringBuilder copy_text; - if (file_operation == FileUtils::FileOperation::Cut) { - copy_text.append("#cut\n"); // This exploits the comment lines in the text/uri-list specification, which might be a bit hackish - } - for (auto& path : selected_file_paths) { - auto url = URL::create_with_file_protocol(path); - copy_text.appendff("{}\n", url); - } - GUI::Clipboard::the().set_data(copy_text.build().bytes(), "text/uri-list"); -} - -void do_paste(const String& target_directory, GUI::Window* window) -{ - auto data_and_type = GUI::Clipboard::the().data_and_type(); - if (data_and_type.mime_type != "text/uri-list") { - dbgln("Cannot paste clipboard type {}", data_and_type.mime_type); - return; - } - auto copied_lines = String::copy(data_and_type.data).split('\n'); - if (copied_lines.is_empty()) { - dbgln("No files to paste"); - return; - } - - bool should_delete_src = false; - if (copied_lines[0] == "#cut") { // cut operation encoded as a text/uri-list commen - should_delete_src = true; - copied_lines.remove(0); - } - - for (auto& uri_as_string : copied_lines) { - if (uri_as_string.is_empty()) - continue; - URL url = uri_as_string; - if (!url.is_valid() || url.protocol() != "file") { - dbgln("Cannot paste URI {}", uri_as_string); - continue; - } - - auto new_path = String::formatted("{}/{}", target_directory, url.basename()); - if (!FileUtils::copy_file_or_directory(url.path(), new_path)) { - auto error_message = String::formatted("Could not paste {}.", url.path()); - GUI::MessageBox::show(window, error_message, "File Manager", GUI::MessageBox::Type::Error); - } else if (should_delete_src) { - FileUtils::delete_path(url.path(), window); - } - } -} - -void do_create_link(const Vector<String>& selected_file_paths, GUI::Window* window) -{ - auto path = selected_file_paths.first(); - auto destination = String::formatted("{}/{}", Core::StandardPaths::desktop_directory(), LexicalPath { path }.basename()); - if (!FileUtils::link_file(path, destination)) { - GUI::MessageBox::show(window, "Could not create desktop shortcut", "File Manager", - GUI::MessageBox::Type::Error); - } -} - -void show_properties(const String& container_dir_path, const String& path, const Vector<String>& selected, GUI::Window* window) -{ - RefPtr<PropertiesWindow> properties; - if (selected.is_empty()) { - properties = window->add<PropertiesWindow>(path, true); - } else { - properties = window->add<PropertiesWindow>(selected.first(), access(container_dir_path.characters(), W_OK) != 0); - } - properties->on_close = [properties = properties.ptr()] { - properties->remove_from_parent(); - }; - properties->center_on_screen(); - properties->show(); -} - -int run_in_desktop_mode([[maybe_unused]] RefPtr<Core::ConfigFile> config) -{ - static constexpr const char* process_name = "FileManager (Desktop)"; - set_process_name(process_name, strlen(process_name)); - pthread_setname_np(pthread_self(), process_name); - - auto window = GUI::Window::construct(); - window->set_title("Desktop Manager"); - window->set_window_type(GUI::WindowType::Desktop); - window->set_has_alpha_channel(true); - - auto& desktop_widget = window->set_main_widget<FileManager::DesktopWidget>(); - desktop_widget.set_layout<GUI::VerticalBoxLayout>(); - - [[maybe_unused]] auto& directory_view = desktop_widget.add<DirectoryView>(DirectoryView::Mode::Desktop); - - auto copy_action = GUI::CommonActions::make_copy_action( - [&](auto&) { - auto paths = directory_view.selected_file_paths(); - - if (paths.is_empty()) - ASSERT_NOT_REACHED(); - - do_copy(paths, FileUtils::FileOperation::Copy); - }, - window); - copy_action->set_enabled(false); - - auto cut_action = GUI::CommonActions::make_cut_action( - [&](auto&) { - auto paths = directory_view.selected_file_paths(); - - if (paths.is_empty()) - ASSERT_NOT_REACHED(); - - do_copy(paths, FileUtils::FileOperation::Cut); - }, - window); - cut_action->set_enabled(false); - - directory_view.on_selection_change = [&](const GUI::AbstractView& view) { - copy_action->set_enabled(!view.selection().is_empty()); - cut_action->set_enabled(!view.selection().is_empty()); - }; - - auto properties_action - = GUI::Action::create( - "Properties", { Mod_Alt, Key_Return }, Gfx::Bitmap::load_from_file("/res/icons/16x16/properties.png"), [&](const GUI::Action&) { - String path = directory_view.path(); - Vector<String> selected = directory_view.selected_file_paths(); - - show_properties(path, path, selected, directory_view.window()); - }, - window); - - auto paste_action = GUI::CommonActions::make_paste_action( - [&](const GUI::Action&) { - do_paste(directory_view.path(), directory_view.window()); - }, - window); - paste_action->set_enabled(GUI::Clipboard::the().mime_type() == "text/uri-list" && access(directory_view.path().characters(), W_OK) == 0); - - GUI::Clipboard::the().on_change = [&](const String& data_type) { - paste_action->set_enabled(data_type == "text/uri-list" && access(directory_view.path().characters(), W_OK) == 0); - }; - - auto desktop_view_context_menu = GUI::Menu::construct("Directory View"); - - auto file_manager_action = GUI::Action::create("Show in File Manager", {}, Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-folder.png"), [&](const GUI::Action&) { - Desktop::Launcher::open(URL::create_with_file_protocol(directory_view.path())); - }); - - auto display_properties_action = GUI::Action::create("Display Settings", {}, Gfx::Bitmap::load_from_file("/res/icons/16x16/app-display-settings.png"), [&](const GUI::Action&) { - Desktop::Launcher::open(URL::create_with_file_protocol("/bin/DisplaySettings")); - }); - - desktop_view_context_menu->add_action(directory_view.mkdir_action()); - desktop_view_context_menu->add_action(directory_view.touch_action()); - desktop_view_context_menu->add_action(paste_action); - desktop_view_context_menu->add_separator(); - desktop_view_context_menu->add_action(file_manager_action); - desktop_view_context_menu->add_action(directory_view.open_terminal_action()); - desktop_view_context_menu->add_separator(); - desktop_view_context_menu->add_action(display_properties_action); - - auto desktop_context_menu = GUI::Menu::construct("Directory View Directory"); - desktop_context_menu->add_action(copy_action); - desktop_context_menu->add_action(cut_action); - desktop_context_menu->add_action(paste_action); - desktop_context_menu->add_action(directory_view.delete_action()); - desktop_context_menu->add_separator(); - desktop_context_menu->add_action(properties_action); - - directory_view.on_context_menu_request = [&](const GUI::ModelIndex& index, const GUI::ContextMenuEvent& event) { - if (!index.is_valid()) - desktop_view_context_menu->popup(event.screen_position()); - else - desktop_context_menu->popup(event.screen_position()); - }; - - auto wm_config = Core::ConfigFile::get_for_app("WindowManager"); - auto selected_wallpaper = wm_config->read_entry("Background", "Wallpaper", ""); - if (!selected_wallpaper.is_empty()) { - GUI::Desktop::the().set_wallpaper(selected_wallpaper, false); - } - - window->show(); - return GUI::Application::the()->exec(); -} - -int run_in_windowed_mode(RefPtr<Core::ConfigFile> config, String initial_location) -{ - auto window = GUI::Window::construct(); - window->set_title("File Manager"); - - auto left = config->read_num_entry("Window", "Left", 150); - auto top = config->read_num_entry("Window", "Top", 75); - auto width = config->read_num_entry("Window", "Width", 640); - auto height = config->read_num_entry("Window", "Height", 480); - window->set_rect({ left, top, width, height }); - - auto& widget = window->set_main_widget<GUI::Widget>(); - - widget.load_from_gml(file_manager_window_gml); - - auto& main_toolbar = *widget.find_descendant_of_type_named<GUI::ToolBar>("main_toolbar"); - auto& location_toolbar = *widget.find_descendant_of_type_named<GUI::ToolBar>("location_toolbar"); - location_toolbar.layout()->set_margins({ 6, 3, 6, 3 }); - - auto& location_textbox = *widget.find_descendant_of_type_named<GUI::TextBox>("location_textbox"); - - auto& breadcrumb_toolbar = *widget.find_descendant_of_type_named<GUI::ToolBar>("breadcrumb_toolbar"); - breadcrumb_toolbar.layout()->set_margins({}); - auto& breadcrumb_bar = *widget.find_descendant_of_type_named<GUI::BreadcrumbBar>("breadcrumb_bar"); - - location_textbox.on_focusout = [&] { - location_toolbar.set_visible(false); - breadcrumb_toolbar.set_visible(true); - }; - - auto& splitter = *widget.find_descendant_of_type_named<GUI::HorizontalSplitter>("splitter"); - auto& tree_view = *widget.find_descendant_of_type_named<GUI::TreeView>("tree_view"); - - auto directories_model = GUI::FileSystemModel::create({}, GUI::FileSystemModel::Mode::DirectoriesOnly); - tree_view.set_model(directories_model); - tree_view.set_column_hidden(GUI::FileSystemModel::Column::Icon, true); - tree_view.set_column_hidden(GUI::FileSystemModel::Column::Size, true); - tree_view.set_column_hidden(GUI::FileSystemModel::Column::Owner, true); - tree_view.set_column_hidden(GUI::FileSystemModel::Column::Group, true); - tree_view.set_column_hidden(GUI::FileSystemModel::Column::Permissions, true); - tree_view.set_column_hidden(GUI::FileSystemModel::Column::ModificationTime, true); - tree_view.set_column_hidden(GUI::FileSystemModel::Column::Inode, true); - tree_view.set_column_hidden(GUI::FileSystemModel::Column::SymlinkTarget, true); - bool is_reacting_to_tree_view_selection_change = false; - - auto& directory_view = splitter.add<DirectoryView>(DirectoryView::Mode::Normal); - - location_textbox.on_escape_pressed = [&] { - directory_view.set_focus(true); - }; - - // Open the root directory. FIXME: This is awkward. - tree_view.toggle_index(directories_model->index(0, 0, {})); - - auto& statusbar = *widget.find_descendant_of_type_named<GUI::StatusBar>("statusbar"); - - auto& progressbar = *widget.find_descendant_of_type_named<GUI::ProgressBar>("progressbar"); - progressbar.set_format(GUI::ProgressBar::Format::ValueSlashMax); - progressbar.set_frame_shape(Gfx::FrameShape::Panel); - progressbar.set_frame_shadow(Gfx::FrameShadow::Sunken); - progressbar.set_frame_thickness(1); - - location_textbox.on_return_pressed = [&] { - directory_view.open(location_textbox.text()); - }; - - auto refresh_tree_view = [&] { - directories_model->update(); - - auto current_path = directory_view.path(); - - struct stat st; - // If the directory no longer exists, we find a parent that does. - while (stat(current_path.characters(), &st) != 0) { - directory_view.open_parent_directory(); - current_path = directory_view.path(); - if (current_path == directories_model->root_path()) { - break; - } - } - - // Reselect the existing folder in the tree. - auto new_index = directories_model->index(current_path, GUI::FileSystemModel::Column::Name); - if (new_index.is_valid()) { - tree_view.expand_all_parents_of(new_index); - tree_view.set_cursor(new_index, GUI::AbstractView::SelectionUpdate::Set, true); - } - - directory_view.refresh(); - }; - - auto directory_context_menu = GUI::Menu::construct("Directory View Directory"); - auto directory_view_context_menu = GUI::Menu::construct("Directory View"); - auto tree_view_directory_context_menu = GUI::Menu::construct("Tree View Directory"); - auto tree_view_context_menu = GUI::Menu::construct("Tree View"); - - auto open_parent_directory_action = GUI::Action::create("Open parent directory", { Mod_Alt, Key_Up }, Gfx::Bitmap::load_from_file("/res/icons/16x16/open-parent-directory.png"), [&](const GUI::Action&) { - directory_view.open_parent_directory(); - }); - - RefPtr<GUI::Action> view_as_table_action; - RefPtr<GUI::Action> view_as_icons_action; - RefPtr<GUI::Action> view_as_columns_action; - - view_as_icons_action = GUI::Action::create_checkable( - "Icon view", { Mod_Ctrl, KeyCode::Key_1 }, Gfx::Bitmap::load_from_file("/res/icons/16x16/icon-view.png"), [&](const GUI::Action&) { - directory_view.set_view_mode(DirectoryView::ViewMode::Icon); - config->write_entry("DirectoryView", "ViewMode", "Icon"); - config->sync(); - }, - window); - - view_as_table_action = GUI::Action::create_checkable( - "Table view", { Mod_Ctrl, KeyCode::Key_2 }, Gfx::Bitmap::load_from_file("/res/icons/16x16/table-view.png"), [&](const GUI::Action&) { - directory_view.set_view_mode(DirectoryView::ViewMode::Table); - config->write_entry("DirectoryView", "ViewMode", "Table"); - config->sync(); - }, - window); - - view_as_columns_action = GUI::Action::create_checkable( - "Columns view", { Mod_Ctrl, KeyCode::Key_3 }, Gfx::Bitmap::load_from_file("/res/icons/16x16/columns-view.png"), [&](const GUI::Action&) { - directory_view.set_view_mode(DirectoryView::ViewMode::Columns); - config->write_entry("DirectoryView", "ViewMode", "Columns"); - config->sync(); - }, - window); - - auto view_type_action_group = make<GUI::ActionGroup>(); - view_type_action_group->set_exclusive(true); - view_type_action_group->add_action(*view_as_icons_action); - view_type_action_group->add_action(*view_as_table_action); - view_type_action_group->add_action(*view_as_columns_action); - - auto tree_view_selected_file_paths = [&] { - Vector<String> paths; - auto& view = tree_view; - view.selection().for_each_index([&](const GUI::ModelIndex& index) { - paths.append(directories_model->full_path(index)); - }); - return paths; - }; - - auto select_all_action = GUI::Action::create("Select all", { Mod_Ctrl, KeyCode::Key_A }, [&](const GUI::Action&) { - directory_view.current_view().select_all(); - }); - - auto copy_action = GUI::CommonActions::make_copy_action( - [&](auto&) { - auto paths = directory_view.selected_file_paths(); - - if (paths.is_empty()) - paths = tree_view_selected_file_paths(); - - if (paths.is_empty()) - ASSERT_NOT_REACHED(); - - do_copy(paths, FileUtils::FileOperation::Copy); - refresh_tree_view(); - }, - window); - copy_action->set_enabled(false); - - auto cut_action = GUI::CommonActions::make_cut_action( - [&](auto&) { - auto paths = directory_view.selected_file_paths(); - - if (paths.is_empty()) - paths = tree_view_selected_file_paths(); - - if (paths.is_empty()) - ASSERT_NOT_REACHED(); - - do_copy(paths, FileUtils::FileOperation::Cut); - refresh_tree_view(); - }, - window); - cut_action->set_enabled(false); - - auto shortcut_action - = GUI::Action::create( - "Create desktop shortcut", - {}, - Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-symlink.png"), - [&](const GUI::Action&) { - auto paths = directory_view.selected_file_paths(); - if (paths.is_empty()) { - return; - } - do_create_link(paths, directory_view.window()); - }, - window); - - auto properties_action - = GUI::Action::create( - "Properties", { Mod_Alt, Key_Return }, Gfx::Bitmap::load_from_file("/res/icons/16x16/properties.png"), [&](const GUI::Action& action) { - String container_dir_path; - String path; - Vector<String> selected; - if (action.activator() == directory_context_menu || directory_view.active_widget()->is_focused()) { - path = directory_view.path(); - container_dir_path = path; - selected = directory_view.selected_file_paths(); - } else { - path = directories_model->full_path(tree_view.selection().first()); - container_dir_path = LexicalPath(path).basename(); - selected = tree_view_selected_file_paths(); - } - - show_properties(container_dir_path, path, selected, directory_view.window()); - }, - window); - - auto paste_action = GUI::CommonActions::make_paste_action( - [&](const GUI::Action& action) { - String target_directory; - if (action.activator() == directory_context_menu) - target_directory = directory_view.selected_file_paths()[0]; - else - target_directory = directory_view.path(); - do_paste(target_directory, directory_view.window()); - refresh_tree_view(); - }, - window); - - auto folder_specific_paste_action = GUI::CommonActions::make_paste_action( - [&](const GUI::Action& action) { - String target_directory; - if (action.activator() == directory_context_menu) - target_directory = directory_view.selected_file_paths()[0]; - else - target_directory = directory_view.path(); - do_paste(target_directory, directory_view.window()); - refresh_tree_view(); - }, - window); - - auto go_back_action = GUI::CommonActions::make_go_back_action( - [&](auto&) { - directory_view.open_previous_directory(); - }, - window); - - auto go_forward_action = GUI::CommonActions::make_go_forward_action( - [&](auto&) { - directory_view.open_next_directory(); - }, - window); - - auto go_home_action = GUI::CommonActions::make_go_home_action( - [&](auto&) { - directory_view.open(Core::StandardPaths::home_directory()); - }, - window); - - GUI::Clipboard::the().on_change = [&](const String& data_type) { - auto current_location = directory_view.path(); - paste_action->set_enabled(data_type == "text/uri-list" && access(current_location.characters(), W_OK) == 0); - }; - - auto tree_view_delete_action = GUI::CommonActions::make_delete_action( - [&](auto&) { - FileUtils::delete_paths(tree_view_selected_file_paths(), true, window); - refresh_tree_view(); - }, - &tree_view); - - // This is a little awkward. The menu action does something different depending on which view has focus. - // It would be nice to find a good abstraction for this instead of creating a branching action like this. - auto focus_dependent_delete_action = GUI::CommonActions::make_delete_action([&](auto&) { - if (tree_view.is_focused()) - tree_view_delete_action->activate(); - else - directory_view.delete_action().activate(); - refresh_tree_view(); - }); - focus_dependent_delete_action->set_enabled(false); - - auto mkdir_action = GUI::Action::create("New directory...", { Mod_Ctrl | Mod_Shift, Key_N }, Gfx::Bitmap::load_from_file("/res/icons/16x16/mkdir.png"), [&](const GUI::Action&) { - directory_view.mkdir_action().activate(); - refresh_tree_view(); - }); - - auto touch_action = GUI::Action::create("New file...", { Mod_Ctrl | Mod_Shift, Key_F }, Gfx::Bitmap::load_from_file("/res/icons/16x16/new.png"), [&](const GUI::Action&) { - directory_view.touch_action().activate(); - refresh_tree_view(); - }); - - auto menubar = GUI::MenuBar::construct(); - - auto& app_menu = menubar->add_menu("File Manager"); - app_menu.add_action(mkdir_action); - app_menu.add_action(touch_action); - app_menu.add_action(copy_action); - app_menu.add_action(cut_action); - app_menu.add_action(paste_action); - app_menu.add_action(focus_dependent_delete_action); - app_menu.add_action(directory_view.open_terminal_action()); - app_menu.add_separator(); - app_menu.add_action(properties_action); - app_menu.add_separator(); - app_menu.add_action(GUI::CommonActions::make_quit_action([](auto&) { - GUI::Application::the()->quit(); - })); - - auto action_show_dotfiles = GUI::Action::create_checkable("Show dotfiles", { Mod_Ctrl, Key_H }, [&](auto& action) { - directory_view.set_should_show_dotfiles(action.is_checked()); - refresh_tree_view(); - }); - - auto& view_menu = menubar->add_menu("View"); - view_menu.add_action(*view_as_icons_action); - view_menu.add_action(*view_as_table_action); - view_menu.add_action(*view_as_columns_action); - view_menu.add_separator(); - view_menu.add_action(action_show_dotfiles); - - auto go_to_location_action = GUI::Action::create("Go to location...", { Mod_Ctrl, Key_L }, [&](auto&) { - location_toolbar.set_visible(true); - breadcrumb_toolbar.set_visible(false); - location_textbox.select_all(); - location_textbox.set_focus(true); - }); - - auto& go_menu = menubar->add_menu("Go"); - go_menu.add_action(go_back_action); - go_menu.add_action(go_forward_action); - go_menu.add_action(open_parent_directory_action); - go_menu.add_action(go_home_action); - go_menu.add_action(go_to_location_action); - - auto& help_menu = menubar->add_menu("Help"); - help_menu.add_action(GUI::CommonActions::make_about_action("File Manager", GUI::Icon::default_icon("filetype-folder"))); - - GUI::Application::the()->set_menubar(move(menubar)); - - main_toolbar.add_action(go_back_action); - main_toolbar.add_action(go_forward_action); - main_toolbar.add_action(open_parent_directory_action); - main_toolbar.add_action(go_home_action); - - main_toolbar.add_separator(); - main_toolbar.add_action(mkdir_action); - main_toolbar.add_action(touch_action); - main_toolbar.add_action(copy_action); - main_toolbar.add_action(cut_action); - main_toolbar.add_action(paste_action); - main_toolbar.add_action(focus_dependent_delete_action); - main_toolbar.add_action(directory_view.open_terminal_action()); - - main_toolbar.add_separator(); - main_toolbar.add_action(*view_as_icons_action); - main_toolbar.add_action(*view_as_table_action); - main_toolbar.add_action(*view_as_columns_action); - - directory_view.on_path_change = [&](const String& new_path, bool can_write_in_path) { - auto icon = GUI::FileIconProvider::icon_for_path(new_path); - auto* bitmap = icon.bitmap_for_size(16); - window->set_icon(bitmap); - location_textbox.set_icon(bitmap); - - window->set_title(String::formatted("{} - File Manager", new_path)); - location_textbox.set_text(new_path); - - { - LexicalPath lexical_path(new_path); - - auto segment_index_of_new_path_in_breadcrumb_bar = [&]() -> Optional<size_t> { - for (size_t i = 0; i < breadcrumb_bar.segment_count(); ++i) { - if (breadcrumb_bar.segment_data(i) == new_path) - return i; - } - return {}; - }(); - - if (segment_index_of_new_path_in_breadcrumb_bar.has_value()) { - breadcrumb_bar.set_selected_segment(segment_index_of_new_path_in_breadcrumb_bar.value()); - } else { - breadcrumb_bar.clear_segments(); - - breadcrumb_bar.append_segment("/", GUI::FileIconProvider::icon_for_path("/").bitmap_for_size(16), "/"); - StringBuilder builder; - - for (auto& part : lexical_path.parts()) { - // NOTE: We rebuild the path as we go, so we have something to pass to GUI::FileIconProvider. - builder.append('/'); - builder.append(part); - - breadcrumb_bar.append_segment(part, GUI::FileIconProvider::icon_for_path(builder.string_view()).bitmap_for_size(16), builder.string_view()); - } - - breadcrumb_bar.set_selected_segment(breadcrumb_bar.segment_count() - 1); - - breadcrumb_bar.on_segment_click = [&](size_t segment_index) { - directory_view.open(breadcrumb_bar.segment_data(segment_index)); - }; - } - } - - if (!is_reacting_to_tree_view_selection_change) { - auto new_index = directories_model->index(new_path, GUI::FileSystemModel::Column::Name); - if (new_index.is_valid()) { - tree_view.expand_all_parents_of(new_index); - tree_view.set_cursor(new_index, GUI::AbstractView::SelectionUpdate::Set); - } - } - - struct stat st; - if (lstat(new_path.characters(), &st)) { - perror("stat"); - return; - } - - paste_action->set_enabled(can_write_in_path && GUI::Clipboard::the().mime_type() == "text/uri-list"); - go_forward_action->set_enabled(directory_view.path_history_position() < directory_view.path_history_size() - 1); - go_back_action->set_enabled(directory_view.path_history_position() > 0); - open_parent_directory_action->set_enabled(new_path != "/"); - }; - - directory_view.on_accepted_drop = [&]() { - refresh_tree_view(); - }; - - directory_view.on_status_message = [&](const StringView& message) { - statusbar.set_text(message); - }; - - directory_view.on_thumbnail_progress = [&](int done, int total) { - if (done == total) { - progressbar.set_visible(false); - return; - } - progressbar.set_range(0, total); - progressbar.set_value(done); - progressbar.set_visible(true); - }; - - directory_view.on_selection_change = [&](GUI::AbstractView& view) { - auto& selection = view.selection(); - copy_action->set_enabled(!selection.is_empty()); - cut_action->set_enabled(!selection.is_empty()); - focus_dependent_delete_action->set_enabled((!tree_view.selection().is_empty() && tree_view.is_focused()) - || !directory_view.current_view().selection().is_empty()); - }; - - directory_context_menu->add_action(copy_action); - directory_context_menu->add_action(cut_action); - directory_context_menu->add_action(folder_specific_paste_action); - directory_context_menu->add_action(directory_view.delete_action()); - directory_context_menu->add_action(shortcut_action); - directory_context_menu->add_separator(); - directory_context_menu->add_action(properties_action); - - directory_view_context_menu->add_action(mkdir_action); - directory_view_context_menu->add_action(touch_action); - directory_view_context_menu->add_action(paste_action); - directory_view_context_menu->add_action(directory_view.open_terminal_action()); - directory_view_context_menu->add_separator(); - directory_view_context_menu->add_action(action_show_dotfiles); - directory_view_context_menu->add_separator(); - directory_view_context_menu->add_action(properties_action); - - tree_view_directory_context_menu->add_action(copy_action); - tree_view_directory_context_menu->add_action(cut_action); - tree_view_directory_context_menu->add_action(paste_action); - tree_view_directory_context_menu->add_action(tree_view_delete_action); - tree_view_directory_context_menu->add_separator(); - tree_view_directory_context_menu->add_action(properties_action); - tree_view_directory_context_menu->add_separator(); - tree_view_directory_context_menu->add_action(mkdir_action); - tree_view_directory_context_menu->add_action(touch_action); - - RefPtr<GUI::Menu> file_context_menu; - NonnullRefPtrVector<LauncherHandler> current_file_handlers; - RefPtr<GUI::Action> file_context_menu_action_default_action; - - directory_view.on_context_menu_request = [&](const GUI::ModelIndex& index, const GUI::ContextMenuEvent& event) { - if (index.is_valid()) { - auto& node = directory_view.node(index); - - if (node.is_directory()) { - auto should_get_enabled = access(node.full_path().characters(), W_OK) == 0 && GUI::Clipboard::the().mime_type() == "text/uri-list"; - folder_specific_paste_action->set_enabled(should_get_enabled); - directory_context_menu->popup(event.screen_position()); - } else { - auto full_path = node.full_path(); - current_file_handlers = directory_view.get_launch_handlers(full_path); - - file_context_menu = GUI::Menu::construct("Directory View File"); - file_context_menu->add_action(copy_action); - file_context_menu->add_action(cut_action); - file_context_menu->add_action(paste_action); - file_context_menu->add_action(directory_view.delete_action()); - file_context_menu->add_action(shortcut_action); - - file_context_menu->add_separator(); - bool added_open_menu_items = false; - auto default_file_handler = directory_view.get_default_launch_handler(current_file_handlers); - if (default_file_handler) { - auto file_open_action = default_file_handler->create_launch_action([&, full_path = move(full_path)](auto& launcher_handler) { - directory_view.launch(URL::create_with_file_protocol(full_path), launcher_handler); - }); - if (default_file_handler->details().launcher_type == Desktop::Launcher::LauncherType::Application) - file_open_action->set_text(String::formatted("Run {}", file_open_action->text())); - else - file_open_action->set_text(String::formatted("Open in {}", file_open_action->text())); - - file_context_menu_action_default_action = file_open_action; - - file_context_menu->add_action(move(file_open_action)); - added_open_menu_items = true; - } else { - file_context_menu_action_default_action.clear(); - } - - if (current_file_handlers.size() > 1) { - added_open_menu_items = true; - auto& file_open_with_menu = file_context_menu->add_submenu("Open with"); - for (auto& handler : current_file_handlers) { - if (&handler == default_file_handler.ptr()) - continue; - file_open_with_menu.add_action(handler.create_launch_action([&, full_path = move(full_path)](auto& launcher_handler) { - directory_view.launch(URL::create_with_file_protocol(full_path), launcher_handler); - })); - } - } - - if (added_open_menu_items) - file_context_menu->add_separator(); - - file_context_menu->add_action(properties_action); - file_context_menu->popup(event.screen_position(), file_context_menu_action_default_action); - } - } else { - directory_view_context_menu->popup(event.screen_position()); - } - }; - - tree_view.on_selection = [&](const GUI::ModelIndex& index) { - if (directories_model->m_previously_selected_index.is_valid()) - directories_model->update_node_on_selection(directories_model->m_previously_selected_index, false); - - directories_model->update_node_on_selection(index, true); - directories_model->m_previously_selected_index = index; - }; - - tree_view.on_selection_change = [&] { - focus_dependent_delete_action->set_enabled((!tree_view.selection().is_empty() && tree_view.is_focused()) - || !directory_view.current_view().selection().is_empty()); - - if (tree_view.selection().is_empty()) - return; - auto path = directories_model->full_path(tree_view.selection().first()); - if (directory_view.path() == path) - return; - TemporaryChange change(is_reacting_to_tree_view_selection_change, true); - directory_view.open(path); - copy_action->set_enabled(!tree_view.selection().is_empty()); - cut_action->set_enabled(!tree_view.selection().is_empty()); - directory_view.delete_action().set_enabled(!tree_view.selection().is_empty()); - }; - - tree_view.on_focus_change = [&]([[maybe_unused]] const bool has_focus, [[maybe_unused]] const GUI::FocusSource source) { - focus_dependent_delete_action->set_enabled((!tree_view.selection().is_empty() && has_focus) - || !directory_view.current_view().selection().is_empty()); - }; - - tree_view.on_context_menu_request = [&](const GUI::ModelIndex& index, const GUI::ContextMenuEvent& event) { - if (index.is_valid()) { - tree_view_directory_context_menu->popup(event.screen_position()); - } - }; - - auto copy_urls_to_directory = [&](const Vector<URL>& urls, const String& directory) { - if (urls.is_empty()) { - dbgln("No files to copy"); - return; - } - bool had_accepted_copy = false; - for (auto& url_to_copy : urls) { - if (!url_to_copy.is_valid() || url_to_copy.path() == directory) - continue; - auto new_path = String::formatted("{}/{}", directory, LexicalPath(url_to_copy.path()).basename()); - if (url_to_copy.path() == new_path) - continue; - - if (!FileUtils::copy_file_or_directory(url_to_copy.path(), new_path)) { - auto error_message = String::formatted("Could not copy {} into {}.", url_to_copy.to_string(), new_path); - GUI::MessageBox::show(window, error_message, "File Manager", GUI::MessageBox::Type::Error); - } else { - had_accepted_copy = true; - } - } - if (had_accepted_copy) - refresh_tree_view(); - }; - - breadcrumb_bar.on_segment_drop = [&](size_t segment_index, const GUI::DropEvent& event) { - if (!event.mime_data().has_urls()) - return; - copy_urls_to_directory(event.mime_data().urls(), breadcrumb_bar.segment_data(segment_index)); - }; - - breadcrumb_bar.on_segment_drag_enter = [&](size_t, GUI::DragEvent& event) { - if (event.mime_types().contains_slow("text/uri-list")) - event.accept(); - }; - - breadcrumb_bar.on_doubleclick = [&](const GUI::MouseEvent&) { - go_to_location_action->activate(); - }; - - tree_view.on_drop = [&](const GUI::ModelIndex& index, const GUI::DropEvent& event) { - if (!event.mime_data().has_urls()) - return; - auto& target_node = directories_model->node(index); - if (!target_node.is_directory()) - return; - copy_urls_to_directory(event.mime_data().urls(), target_node.full_path()); - }; - - directory_view.open(initial_location); - directory_view.set_focus(true); - - paste_action->set_enabled(GUI::Clipboard::the().mime_type() == "text/uri-list" && access(initial_location.characters(), W_OK) == 0); - - window->show(); - - // Read directory read mode from config. - auto dir_view_mode = config->read_entry("DirectoryView", "ViewMode", "Icon"); - - if (dir_view_mode.contains("Table")) { - directory_view.set_view_mode(DirectoryView::ViewMode::Table); - view_as_table_action->set_checked(true); - } else if (dir_view_mode.contains("Columns")) { - directory_view.set_view_mode(DirectoryView::ViewMode::Columns); - view_as_columns_action->set_checked(true); - } else { - directory_view.set_view_mode(DirectoryView::ViewMode::Icon); - view_as_icons_action->set_checked(true); - } - - // Write window position to config file on close request. - window->on_close_request = [&] { - config->write_num_entry("Window", "Left", window->x()); - config->write_num_entry("Window", "Top", window->y()); - config->write_num_entry("Window", "Width", window->width()); - config->write_num_entry("Window", "Height", window->height()); - config->sync(); - - return GUI::Window::CloseRequestDecision::Close; - }; - - return GUI::Application::the()->exec(); -} diff --git a/Applications/FontEditor/.gitignore b/Applications/FontEditor/.gitignore deleted file mode 100644 index 0e7af5b54f..0000000000 --- a/Applications/FontEditor/.gitignore +++ /dev/null @@ -1 +0,0 @@ -UI_*.h diff --git a/Applications/FontEditor/CMakeLists.txt b/Applications/FontEditor/CMakeLists.txt deleted file mode 100644 index 569ab9a9bd..0000000000 --- a/Applications/FontEditor/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -include_directories(${CMAKE_CURRENT_BINARY_DIR}) - -set(SOURCES - FontEditor.cpp - GlyphEditorWidget.cpp - GlyphMapWidget.cpp - main.cpp -) - -serenity_app(FontEditor ICON app-font-editor) -target_link_libraries(FontEditor LibGUI LibDesktop LibGfx) diff --git a/Applications/FontEditor/FontEditor.cpp b/Applications/FontEditor/FontEditor.cpp deleted file mode 100644 index 56cbf3e06b..0000000000 --- a/Applications/FontEditor/FontEditor.cpp +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "FontEditor.h" -#include "GlyphEditorWidget.h" -#include "GlyphMapWidget.h" -#include <AK/StringBuilder.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Button.h> -#include <LibGUI/CheckBox.h> -#include <LibGUI/GroupBox.h> -#include <LibGUI/Label.h> -#include <LibGUI/MessageBox.h> -#include <LibGUI/Painter.h> -#include <LibGUI/SpinBox.h> -#include <LibGUI/TextBox.h> -#include <LibGUI/Window.h> -#include <LibGfx/BitmapFont.h> -#include <LibGfx/Palette.h> -#include <stdlib.h> - -FontEditorWidget::FontEditorWidget(const String& path, RefPtr<Gfx::BitmapFont>&& edited_font) - : m_edited_font(move(edited_font)) - , m_path(path) -{ - set_fill_with_background_color(true); - set_layout<GUI::VerticalBoxLayout>(); - - // Top - auto& main_container = add<GUI::Widget>(); - main_container.set_layout<GUI::HorizontalBoxLayout>(); - main_container.layout()->set_margins({ 4, 4, 4, 4 }); - main_container.set_background_role(Gfx::ColorRole::SyntaxKeyword); - - // Top-Left Glyph Editor and info - auto& editor_container = main_container.add<GUI::Widget>(); - editor_container.set_layout<GUI::VerticalBoxLayout>(); - editor_container.layout()->set_margins({ 4, 4, 4, 4 }); - editor_container.set_background_role(Gfx::ColorRole::SyntaxKeyword); - - m_glyph_editor_widget = editor_container.add<GlyphEditorWidget>(*m_edited_font); - m_glyph_editor_widget->set_fixed_size(m_glyph_editor_widget->preferred_width(), m_glyph_editor_widget->preferred_height()); - - editor_container.set_fixed_width(m_glyph_editor_widget->preferred_width()); - - auto& glyph_width_label = editor_container.add<GUI::Label>(); - glyph_width_label.set_fixed_height(22); - glyph_width_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - glyph_width_label.set_text("Glyph width:"); - - auto& glyph_width_spinbox = editor_container.add<GUI::SpinBox>(); - glyph_width_spinbox.set_min(0); - glyph_width_spinbox.set_max(32); - glyph_width_spinbox.set_value(0); - glyph_width_spinbox.set_enabled(!m_edited_font->is_fixed_width()); - - auto& info_label = editor_container.add<GUI::Label>(); - info_label.set_fixed_height(22); - info_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - info_label.set_text("info_label"); - - /// Top-Right glyph map and font meta data - - auto& map_and_test_container = main_container.add<GUI::Widget>(); - map_and_test_container.set_layout<GUI::VerticalBoxLayout>(); - map_and_test_container.layout()->set_margins({ 4, 4, 4, 4 }); - - m_glyph_map_widget = map_and_test_container.add<GlyphMapWidget>(*m_edited_font); - m_glyph_map_widget->set_fixed_size(m_glyph_map_widget->preferred_width(), m_glyph_map_widget->preferred_height()); - - auto& font_mtest_group_box = map_and_test_container.add<GUI::GroupBox>(); - font_mtest_group_box.set_layout<GUI::VerticalBoxLayout>(); - font_mtest_group_box.layout()->set_margins({ 5, 15, 5, 5 }); - font_mtest_group_box.set_fixed_height(2 * m_edited_font->glyph_height() + 50); - font_mtest_group_box.set_title("Test"); - - auto& demo_label_1 = font_mtest_group_box.add<GUI::Label>(); - demo_label_1.set_font(m_edited_font); - demo_label_1.set_text("quick fox jumps nightly above wizard."); - - auto& demo_label_2 = font_mtest_group_box.add<GUI::Label>(); - demo_label_2.set_font(m_edited_font); - demo_label_2.set_text("QUICK FOX JUMPS NIGHTLY ABOVE WIZARD!"); - - auto& font_metadata_group_box = map_and_test_container.add<GUI::GroupBox>(); - font_metadata_group_box.set_layout<GUI::VerticalBoxLayout>(); - font_metadata_group_box.layout()->set_margins({ 5, 15, 5, 5 }); - font_metadata_group_box.set_fixed_height(275); - font_metadata_group_box.set_title("Font metadata"); - - //// Name Row - auto& namecontainer = font_metadata_group_box.add<GUI::Widget>(); - namecontainer.set_layout<GUI::HorizontalBoxLayout>(); - namecontainer.set_fixed_height(22); - - auto& name_label = namecontainer.add<GUI::Label>(); - name_label.set_fixed_width(100); - name_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - name_label.set_text("Name:"); - - auto& name_textbox = namecontainer.add<GUI::TextBox>(); - name_textbox.set_text(m_edited_font->name()); - name_textbox.on_change = [&] { - m_edited_font->set_name(name_textbox.text()); - }; - - //// Family Row - auto& family_container = font_metadata_group_box.add<GUI::Widget>(); - family_container.set_layout<GUI::HorizontalBoxLayout>(); - family_container.set_fixed_height(22); - - auto& family_label = family_container.add<GUI::Label>(); - family_label.set_fixed_width(100); - family_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - family_label.set_text("Family:"); - - auto& family_textbox = family_container.add<GUI::TextBox>(); - family_textbox.set_text(m_edited_font->family()); - family_textbox.on_change = [&] { - m_edited_font->set_family(family_textbox.text()); - }; - - //// Presentation size Row - auto& presentation_size_container = font_metadata_group_box.add<GUI::Widget>(); - presentation_size_container.set_layout<GUI::HorizontalBoxLayout>(); - presentation_size_container.set_fixed_height(22); - - auto& presentation_size_label = presentation_size_container.add<GUI::Label>(); - presentation_size_label.set_fixed_width(100); - presentation_size_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - presentation_size_label.set_text("Presentation size:"); - - auto& presentation_size_spinbox = presentation_size_container.add<GUI::SpinBox>(); - presentation_size_spinbox.set_min(0); - presentation_size_spinbox.set_max(255); - presentation_size_spinbox.set_value(m_edited_font->presentation_size()); - - //// Weight Row - auto& weight_container = font_metadata_group_box.add<GUI::Widget>(); - weight_container.set_layout<GUI::HorizontalBoxLayout>(); - weight_container.set_fixed_height(22); - - auto& weight_label = weight_container.add<GUI::Label>(); - weight_label.set_fixed_width(100); - weight_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - weight_label.set_text("Weight:"); - - auto& weight_spinbox = weight_container.add<GUI::SpinBox>(); - weight_spinbox.set_min(0); - weight_spinbox.set_max(65535); - weight_spinbox.set_value(m_edited_font->weight()); - - //// Glyph spacing Row - auto& glyph_spacing_container = font_metadata_group_box.add<GUI::Widget>(); - glyph_spacing_container.set_layout<GUI::HorizontalBoxLayout>(); - glyph_spacing_container.set_fixed_height(22); - - auto& glyph_spacing = glyph_spacing_container.add<GUI::Label>(); - glyph_spacing.set_fixed_width(100); - glyph_spacing.set_text_alignment(Gfx::TextAlignment::CenterLeft); - glyph_spacing.set_text("Glyph spacing:"); - - auto& spacing_spinbox = glyph_spacing_container.add<GUI::SpinBox>(); - spacing_spinbox.set_min(0); - spacing_spinbox.set_max(255); - spacing_spinbox.set_value(m_edited_font->glyph_spacing()); - - //// Glyph Height Row - auto& glyph_height_container = font_metadata_group_box.add<GUI::Widget>(); - glyph_height_container.set_layout<GUI::HorizontalBoxLayout>(); - glyph_height_container.set_fixed_height(22); - - auto& glyph_height = glyph_height_container.add<GUI::Label>(); - glyph_height.set_fixed_width(100); - glyph_height.set_text_alignment(Gfx::TextAlignment::CenterLeft); - glyph_height.set_text("Glyph height:"); - - auto& glyph_height_spinbox = glyph_height_container.add<GUI::SpinBox>(); - glyph_height_spinbox.set_min(0); - glyph_height_spinbox.set_max(255); - glyph_height_spinbox.set_value(m_edited_font->glyph_height()); - glyph_height_spinbox.set_enabled(false); - - //// Glyph width Row - auto& glyph_weight_container = font_metadata_group_box.add<GUI::Widget>(); - glyph_weight_container.set_layout<GUI::HorizontalBoxLayout>(); - glyph_weight_container.set_fixed_height(22); - - auto& glyph_header_width_label = glyph_weight_container.add<GUI::Label>(); - glyph_header_width_label.set_fixed_width(100); - glyph_header_width_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - glyph_header_width_label.set_text("Glyph width:"); - - auto& glyph_header_width_spinbox = glyph_weight_container.add<GUI::SpinBox>(); - glyph_header_width_spinbox.set_min(0); - glyph_header_width_spinbox.set_max(255); - glyph_header_width_spinbox.set_value(m_edited_font->glyph_fixed_width()); - glyph_header_width_spinbox.set_enabled(false); - - //// Baseline Row - auto& baseline_container = font_metadata_group_box.add<GUI::Widget>(); - baseline_container.set_layout<GUI::HorizontalBoxLayout>(); - baseline_container.set_fixed_height(22); - - auto& baseline_label = baseline_container.add<GUI::Label>(); - baseline_label.set_fixed_width(100); - baseline_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - baseline_label.set_text("Baseline:"); - - auto& baseline_spinbox = baseline_container.add<GUI::SpinBox>(); - baseline_spinbox.set_min(0); - baseline_spinbox.set_max(m_edited_font->glyph_height() - 1); - baseline_spinbox.set_value(m_edited_font->baseline()); - - //// Mean line Row - auto& mean_line_container = font_metadata_group_box.add<GUI::Widget>(); - mean_line_container.set_layout<GUI::HorizontalBoxLayout>(); - mean_line_container.set_fixed_height(22); - - auto& mean_line_label = mean_line_container.add<GUI::Label>(); - mean_line_label.set_fixed_width(100); - mean_line_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - mean_line_label.set_text("Mean Line:"); - - auto& mean_line_spinbox = mean_line_container.add<GUI::SpinBox>(); - mean_line_spinbox.set_min(0); - mean_line_spinbox.set_max(m_edited_font->glyph_height() - 1); - mean_line_spinbox.set_value(m_edited_font->mean_line()); - - //// Fixed checkbox Row - auto& fixed_width_checkbox = font_metadata_group_box.add<GUI::CheckBox>(); - fixed_width_checkbox.set_text("Fixed width"); - fixed_width_checkbox.set_checked(m_edited_font->is_fixed_width()); - - // Bottom - auto& bottom_container = add<GUI::Widget>(); - bottom_container.set_layout<GUI::HorizontalBoxLayout>(); - bottom_container.layout()->set_margins({ 8, 0, 8, 8 }); - bottom_container.set_fixed_height(32); - - bottom_container.layout()->add_spacer(); - - auto& save_button = bottom_container.add<GUI::Button>(); - save_button.set_fixed_size(80, 22); - save_button.set_text("Save"); - save_button.on_click = [this](auto) { save_as(m_path); }; - - auto& quit_button = bottom_container.add<GUI::Button>(); - quit_button.set_fixed_size(80, 22); - quit_button.set_text("Quit"); - quit_button.on_click = [](auto) { - exit(0); - }; - - // Event hanglers - auto update_demo = [&] { - demo_label_1.update(); - demo_label_2.update(); - }; - - auto calculate_prefed_sizes = [&] { - int right_site_width = m_edited_font->width("QUICK FOX JUMPS NIGHTLY ABOVE WIZARD!") + 20; - right_site_width = max(right_site_width, m_glyph_map_widget->preferred_width()); - - m_preferred_width = m_glyph_editor_widget->width() + right_site_width + 20; - m_preferred_height = m_glyph_map_widget->relative_rect().height() + 2 * m_edited_font->glyph_height() + 380; - }; - - m_glyph_editor_widget->on_glyph_altered = [this, update_demo](u8 glyph) { - m_glyph_map_widget->update_glyph(glyph); - update_demo(); - }; - - m_glyph_map_widget->on_glyph_selected = [&](size_t glyph) { - m_glyph_editor_widget->set_glyph(glyph); - glyph_width_spinbox.set_value(m_edited_font->glyph_width(m_glyph_map_widget->selected_glyph())); - StringBuilder builder; - builder.appendff("{:#02x} (", glyph); - if (glyph < 128) { - builder.append(glyph); - } else { - builder.append(128 | 64 | (glyph / 64)); - builder.append(128 | (glyph % 64)); - } - builder.append(')'); - info_label.set_text(builder.to_string()); - }; - - fixed_width_checkbox.on_checked = [&, update_demo](bool checked) { - m_edited_font->set_fixed_width(checked); - glyph_width_spinbox.set_enabled(!m_edited_font->is_fixed_width()); - glyph_width_spinbox.set_value(m_edited_font->glyph_width(m_glyph_map_widget->selected_glyph())); - m_glyph_editor_widget->update(); - update_demo(); - }; - - glyph_width_spinbox.on_change = [this, update_demo](int value) { - m_edited_font->set_glyph_width(m_glyph_map_widget->selected_glyph(), value); - m_glyph_editor_widget->update(); - m_glyph_map_widget->update_glyph(m_glyph_map_widget->selected_glyph()); - update_demo(); - }; - - weight_spinbox.on_change = [this, update_demo](int value) { - m_edited_font->set_weight(value); - update_demo(); - }; - - presentation_size_spinbox.on_change = [this, update_demo](int value) { - m_edited_font->set_presentation_size(value); - update_demo(); - }; - - spacing_spinbox.on_change = [this, update_demo](int value) { - m_edited_font->set_glyph_spacing(value); - update_demo(); - }; - - baseline_spinbox.on_change = [this, update_demo](int value) { - m_edited_font->set_baseline(value); - m_glyph_editor_widget->update(); - update_demo(); - }; - - mean_line_spinbox.on_change = [this, update_demo](int value) { - m_edited_font->set_mean_line(value); - m_glyph_editor_widget->update(); - update_demo(); - }; - - // init widget - calculate_prefed_sizes(); - m_glyph_map_widget->set_selected_glyph('A'); -} - -FontEditorWidget::~FontEditorWidget() -{ -} - -bool FontEditorWidget::save_as(const String& path) -{ - auto ret_val = m_edited_font->write_to_file(path); - if (!ret_val) { - GUI::MessageBox::show(window(), "The font file could not be saved.", "Save failed", GUI::MessageBox::Type::Error); - return false; - } - m_path = path; - return true; -} diff --git a/Applications/FontEditor/FontEditor.h b/Applications/FontEditor/FontEditor.h deleted file mode 100644 index ed904e8e7d..0000000000 --- a/Applications/FontEditor/FontEditor.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 <AK/Function.h> -#include <LibGUI/Widget.h> -#include <LibGfx/BitmapFont.h> - -class GlyphEditorWidget; -class GlyphMapWidget; - -class FontEditorWidget final : public GUI::Widget { - C_OBJECT(FontEditorWidget) -public: - virtual ~FontEditorWidget() override; - - int preferred_width() { return m_preferred_width; } - int preferred_height() { return m_preferred_height; } - - bool save_as(const String&); - - const String& path() { return m_path; } - -private: - FontEditorWidget(const String& path, RefPtr<Gfx::BitmapFont>&&); - RefPtr<Gfx::BitmapFont> m_edited_font; - - RefPtr<GlyphMapWidget> m_glyph_map_widget; - RefPtr<GlyphEditorWidget> m_glyph_editor_widget; - - String m_path; - int m_preferred_width; - int m_preferred_height; -}; diff --git a/Applications/FontEditor/GlyphEditorWidget.cpp b/Applications/FontEditor/GlyphEditorWidget.cpp deleted file mode 100644 index 6f545c1ccd..0000000000 --- a/Applications/FontEditor/GlyphEditorWidget.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "GlyphEditorWidget.h" -#include <LibGUI/Painter.h> -#include <LibGfx/BitmapFont.h> -#include <LibGfx/Palette.h> - -GlyphEditorWidget::GlyphEditorWidget(Gfx::BitmapFont& mutable_font) - : m_font(mutable_font) -{ - set_relative_rect({ 0, 0, preferred_width(), preferred_height() }); -} - -GlyphEditorWidget::~GlyphEditorWidget() -{ -} - -void GlyphEditorWidget::set_glyph(int glyph) -{ - if (m_glyph == glyph) - return; - m_glyph = glyph; - update(); -} - -void GlyphEditorWidget::paint_event(GUI::PaintEvent& event) -{ - GUI::Frame::paint_event(event); - - GUI::Painter painter(*this); - painter.add_clip_rect(frame_inner_rect()); - painter.add_clip_rect(event.rect()); - painter.fill_rect(frame_inner_rect(), palette().base()); - painter.translate(frame_thickness(), frame_thickness()); - - painter.translate(-1, -1); - for (int y = 1; y < font().glyph_height(); ++y) { - int y_below = y - 1; - bool bold_line = y_below == font().baseline() || y_below == font().mean_line(); - painter.draw_line({ 0, y * m_scale }, { font().max_glyph_width() * m_scale, y * m_scale }, palette().threed_shadow2(), bold_line ? 2 : 1); - } - - for (int x = 1; x < font().max_glyph_width(); ++x) - painter.draw_line({ x * m_scale, 0 }, { x * m_scale, font().glyph_height() * m_scale }, palette().threed_shadow2()); - - auto bitmap = font().glyph_bitmap(m_glyph); - - for (int y = 0; y < font().glyph_height(); ++y) { - for (int x = 0; x < font().max_glyph_width(); ++x) { - Gfx::IntRect rect { x * m_scale, y * m_scale, m_scale, m_scale }; - if (x >= font().glyph_width(m_glyph)) { - painter.fill_rect(rect, palette().threed_shadow1()); - } else { - if (bitmap.bit_at(x, y)) - painter.fill_rect(rect, palette().base_text()); - } - } - } -} - -void GlyphEditorWidget::mousedown_event(GUI::MouseEvent& event) -{ - draw_at_mouse(event); -} - -void GlyphEditorWidget::mousemove_event(GUI::MouseEvent& event) -{ - if (event.buttons() & (GUI::MouseButton::Left | GUI::MouseButton::Right)) - draw_at_mouse(event); -} - -void GlyphEditorWidget::draw_at_mouse(const GUI::MouseEvent& event) -{ - bool set = event.buttons() & GUI::MouseButton::Left; - bool unset = event.buttons() & GUI::MouseButton::Right; - if (!(set ^ unset)) - return; - int x = (event.x() - 1) / m_scale; - int y = (event.y() - 1) / m_scale; - auto bitmap = font().glyph_bitmap(m_glyph); - if (x < 0 || x >= bitmap.width()) - return; - if (y < 0 || y >= bitmap.height()) - return; - if (bitmap.bit_at(x, y) == set) - return; - bitmap.set_bit_at(x, y, set); - if (on_glyph_altered) - on_glyph_altered(m_glyph); - update(); -} - -int GlyphEditorWidget::preferred_width() const -{ - return frame_thickness() * 2 + font().max_glyph_width() * m_scale - 1; -} - -int GlyphEditorWidget::preferred_height() const -{ - return frame_thickness() * 2 + font().glyph_height() * m_scale - 1; -} diff --git a/Applications/FontEditor/GlyphEditorWidget.h b/Applications/FontEditor/GlyphEditorWidget.h deleted file mode 100644 index a54c551642..0000000000 --- a/Applications/FontEditor/GlyphEditorWidget.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 <AK/Function.h> -#include <LibGUI/Frame.h> -#include <LibGfx/BitmapFont.h> - -class GlyphEditorWidget final : public GUI::Frame { - C_OBJECT(GlyphEditorWidget) -public: - virtual ~GlyphEditorWidget() override; - - int glyph() const { return m_glyph; } - void set_glyph(int); - - int preferred_width() const; - int preferred_height() const; - - Gfx::BitmapFont& font() { return *m_font; } - const Gfx::BitmapFont& font() const { return *m_font; } - - Function<void(u8)> on_glyph_altered; - -private: - GlyphEditorWidget(Gfx::BitmapFont&); - virtual void paint_event(GUI::PaintEvent&) override; - virtual void mousedown_event(GUI::MouseEvent&) override; - virtual void mousemove_event(GUI::MouseEvent&) override; - - void draw_at_mouse(const GUI::MouseEvent&); - - RefPtr<Gfx::BitmapFont> m_font; - int m_glyph { 0 }; - int m_scale { 10 }; -}; diff --git a/Applications/FontEditor/GlyphMapWidget.cpp b/Applications/FontEditor/GlyphMapWidget.cpp deleted file mode 100644 index ed6a65493f..0000000000 --- a/Applications/FontEditor/GlyphMapWidget.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "GlyphMapWidget.h" -#include <LibGUI/Painter.h> -#include <LibGfx/BitmapFont.h> -#include <LibGfx/Palette.h> - -GlyphMapWidget::GlyphMapWidget(Gfx::BitmapFont& mutable_font) - : m_font(mutable_font) -{ - m_glyph_count = mutable_font.glyph_count(); - set_relative_rect({ 0, 0, preferred_width(), preferred_height() }); - set_focus_policy(GUI::FocusPolicy::StrongFocus); -} - -GlyphMapWidget::~GlyphMapWidget() -{ -} - -int GlyphMapWidget::preferred_width() const -{ - return columns() * (font().max_glyph_width() + m_horizontal_spacing) + 2 + frame_thickness() * 2; -} - -int GlyphMapWidget::preferred_height() const -{ - return rows() * (font().glyph_height() + m_vertical_spacing) + 2 + frame_thickness() * 2; -} - -void GlyphMapWidget::set_selected_glyph(int glyph) -{ - if (m_selected_glyph == glyph) - return; - m_selected_glyph = glyph; - if (on_glyph_selected) - on_glyph_selected(glyph); - update(); -} - -Gfx::IntRect GlyphMapWidget::get_outer_rect(int glyph) const -{ - int row = glyph / columns(); - int column = glyph % columns(); - return Gfx::IntRect { - column * (font().max_glyph_width() + m_horizontal_spacing) + 1, - row * (font().glyph_height() + m_vertical_spacing) + 1, - font().max_glyph_width() + m_horizontal_spacing, - font().glyph_height() + m_horizontal_spacing - } - .translated(frame_thickness(), frame_thickness()); -} - -void GlyphMapWidget::update_glyph(int glyph) -{ - update(get_outer_rect(glyph)); -} - -void GlyphMapWidget::paint_event(GUI::PaintEvent& event) -{ - GUI::Frame::paint_event(event); - - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - - painter.set_font(font()); - painter.fill_rect(frame_inner_rect(), palette().base()); - - for (int glyph = 0; glyph < m_glyph_count; ++glyph) { - Gfx::IntRect outer_rect = get_outer_rect(glyph); - Gfx::IntRect inner_rect( - outer_rect.x() + m_horizontal_spacing / 2, - outer_rect.y() + m_vertical_spacing / 2, - font().max_glyph_width(), - font().glyph_height()); - if (glyph == m_selected_glyph) { - painter.fill_rect(outer_rect, is_focused() ? palette().selection() : palette().inactive_selection()); - painter.draw_glyph(inner_rect.location(), glyph, is_focused() ? palette().selection_text() : palette().inactive_selection_text()); - } else { - painter.draw_glyph(inner_rect.location(), glyph, palette().base_text()); - } - } -} - -void GlyphMapWidget::mousedown_event(GUI::MouseEvent& event) -{ - GUI::Frame::mousedown_event(event); - - // FIXME: This is a silly loop. - for (int glyph = 0; glyph < m_glyph_count; ++glyph) { - if (get_outer_rect(glyph).contains(event.position())) { - set_selected_glyph(glyph); - break; - } - } -} - -void GlyphMapWidget::keydown_event(GUI::KeyEvent& event) -{ - GUI::Frame::keydown_event(event); - - if (event.key() == KeyCode::Key_Up) { - if (selected_glyph() >= m_columns) { - set_selected_glyph(selected_glyph() - m_columns); - return; - } - } - if (event.key() == KeyCode::Key_Down) { - if (selected_glyph() < m_glyph_count - m_columns) { - set_selected_glyph(selected_glyph() + m_columns); - return; - } - } - if (event.key() == KeyCode::Key_Left) { - if (selected_glyph() > 0) { - set_selected_glyph(selected_glyph() - 1); - return; - } - } - if (event.key() == KeyCode::Key_Right) { - if (selected_glyph() < m_glyph_count - 1) { - set_selected_glyph(selected_glyph() + 1); - return; - } - } - if (event.ctrl() && event.key() == KeyCode::Key_Home) { - set_selected_glyph(0); - return; - } - if (event.ctrl() && event.key() == KeyCode::Key_End) { - set_selected_glyph(m_glyph_count - 1); - return; - } - if (!event.ctrl() && event.key() == KeyCode::Key_Home) { - set_selected_glyph(selected_glyph() / m_columns * m_columns); - return; - } - if (!event.ctrl() && event.key() == KeyCode::Key_End) { - int new_selection = selected_glyph() / m_columns * m_columns + (m_columns - 1); - int max = m_glyph_count - 1; - new_selection = clamp(new_selection, 0, max); - set_selected_glyph(new_selection); - return; - } -} diff --git a/Applications/FontEditor/GlyphMapWidget.h b/Applications/FontEditor/GlyphMapWidget.h deleted file mode 100644 index 2c8443a251..0000000000 --- a/Applications/FontEditor/GlyphMapWidget.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 <AK/Function.h> -#include <AK/StdLibExtras.h> -#include <LibGUI/Frame.h> -#include <LibGfx/BitmapFont.h> - -class GlyphMapWidget final : public GUI::Frame { - C_OBJECT(GlyphMapWidget) -public: - virtual ~GlyphMapWidget() override; - - int selected_glyph() const { return m_selected_glyph; } - void set_selected_glyph(int); - - int rows() const { return ceil_div(m_glyph_count, m_columns); } - int columns() const { return m_columns; } - - int preferred_width() const; - int preferred_height() const; - - Gfx::BitmapFont& font() { return *m_font; } - const Gfx::BitmapFont& font() const { return *m_font; } - - void update_glyph(int); - - Function<void(int)> on_glyph_selected; - -private: - explicit GlyphMapWidget(Gfx::BitmapFont&); - virtual void paint_event(GUI::PaintEvent&) override; - virtual void mousedown_event(GUI::MouseEvent&) override; - virtual void keydown_event(GUI::KeyEvent&) override; - - Gfx::IntRect get_outer_rect(int glyph) const; - - RefPtr<Gfx::BitmapFont> m_font; - int m_glyph_count; - int m_columns { 32 }; - int m_horizontal_spacing { 2 }; - int m_vertical_spacing { 2 }; - int m_selected_glyph { 0 }; -}; diff --git a/Applications/FontEditor/main.cpp b/Applications/FontEditor/main.cpp deleted file mode 100644 index f0a72d1d29..0000000000 --- a/Applications/FontEditor/main.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "FontEditor.h" -#include <AK/URL.h> -#include <LibCore/ArgsParser.h> -#include <LibDesktop/Launcher.h> -#include <LibGUI/Action.h> -#include <LibGUI/Application.h> -#include <LibGUI/FilePicker.h> -#include <LibGUI/Icon.h> -#include <LibGUI/Menu.h> -#include <LibGUI/MenuBar.h> -#include <LibGUI/MessageBox.h> -#include <LibGUI/Window.h> -#include <LibGfx/Bitmap.h> -#include <LibGfx/BitmapFont.h> -#include <LibGfx/FontDatabase.h> -#include <LibGfx/Point.h> -#include <stdio.h> - -int main(int argc, char** argv) -{ - if (pledge("stdio shared_buffer thread rpath accept unix cpath wpath fattr unix", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto app = GUI::Application::construct(argc, argv); - - if (pledge("stdio shared_buffer thread rpath accept cpath wpath unix", nullptr) < 0) { - perror("pledge"); - return 1; - } - - if (!Desktop::Launcher::add_allowed_handler_with_only_specific_urls( - "/bin/Help", - { URL::create_with_file_protocol("/usr/share/man/man1/FontEditor.md") }) - || !Desktop::Launcher::seal_allowlist()) { - warnln("Failed to set up allowed launch URLs"); - return 1; - } - - if (pledge("stdio shared_buffer thread rpath accept cpath wpath", nullptr) < 0) { - perror("pledge"); - return 1; - } - - const char* path = nullptr; - Core::ArgsParser args_parser; - args_parser.add_positional_argument(path, "The font file for editing.", "file", Core::ArgsParser::Required::No); - args_parser.parse(argc, argv); - - RefPtr<Gfx::BitmapFont> edited_font; - if (path == nullptr) { - path = "/tmp/saved.font"; - edited_font = static_ptr_cast<Gfx::BitmapFont>(Gfx::FontDatabase::default_font().clone()); - } else { - edited_font = static_ptr_cast<Gfx::BitmapFont>(Gfx::Font::load_from_file(path)->clone()); - if (!edited_font) { - String message = String::formatted("Couldn't load font: {}\n", path); - GUI::MessageBox::show(nullptr, message, "Font Editor", GUI::MessageBox::Type::Error); - return 1; - } - } - - auto app_icon = GUI::Icon::default_icon("app-font-editor"); - - auto window = GUI::Window::construct(); - window->set_icon(app_icon.bitmap_for_size(16)); - - auto set_edited_font = [&](const String& path, RefPtr<Gfx::BitmapFont>&& font, Gfx::IntPoint point) { - // Convert 256 char font to 384 char font. - if (font->type() == Gfx::FontTypes::Default) - font->set_type(Gfx::FontTypes::LatinExtendedA); - - window->set_title(String::formatted("{} - Font Editor", path)); - auto& font_editor_widget = window->set_main_widget<FontEditorWidget>(path, move(font)); - window->set_rect({ point, { font_editor_widget.preferred_width(), font_editor_widget.preferred_height() } }); - }; - set_edited_font(path, move(edited_font), window->position()); - - auto menubar = GUI::MenuBar::construct(); - - auto& app_menu = menubar->add_menu("Font Editor"); - app_menu.add_action(GUI::CommonActions::make_open_action([&](auto&) { - Optional<String> open_path = GUI::FilePicker::get_open_filepath(window); - if (!open_path.has_value()) - return; - - RefPtr<Gfx::BitmapFont> new_font = static_ptr_cast<Gfx::BitmapFont>(Gfx::Font::load_from_file(open_path.value())->clone()); - if (!new_font) { - String message = String::formatted("Couldn't load font: {}\n", open_path.value()); - GUI::MessageBox::show(window, message, "Font Editor", GUI::MessageBox::Type::Error); - return; - } - - set_edited_font(open_path.value(), move(new_font), window->position()); - })); - app_menu.add_action(GUI::CommonActions::make_save_action([&](auto&) { - FontEditorWidget* editor = static_cast<FontEditorWidget*>(window->main_widget()); - editor->save_as(editor->path()); - })); - app_menu.add_action(GUI::CommonActions::make_save_as_action([&](auto&) { - FontEditorWidget* editor = static_cast<FontEditorWidget*>(window->main_widget()); - LexicalPath lexical_path(editor->path()); - Optional<String> save_path = GUI::FilePicker::get_save_filepath(window, lexical_path.title(), lexical_path.extension()); - if (!save_path.has_value()) - return; - - if (editor->save_as(save_path.value())) - window->set_title(String::formatted("{} - Font Editor", save_path.value())); - })); - app_menu.add_separator(); - app_menu.add_action(GUI::CommonActions::make_quit_action([&](auto&) { - app->quit(); - return; - })); - - auto& help_menu = menubar->add_menu("Help"); - help_menu.add_action(GUI::CommonActions::make_help_action([](auto&) { - Desktop::Launcher::open(URL::create_with_file_protocol("/usr/share/man/man1/FontEditor.md"), "/bin/Help"); - })); - - help_menu.add_action(GUI::CommonActions::make_about_action("Font Editor", app_icon, window)); - - app->set_menubar(move(menubar)); - - window->show(); - - return app->exec(); -} diff --git a/Applications/Help/CMakeLists.txt b/Applications/Help/CMakeLists.txt deleted file mode 100644 index 46a832c044..0000000000 --- a/Applications/Help/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -set(SOURCES - History.cpp - main.cpp - ManualModel.cpp - ManualPageNode.cpp - ManualSectionNode.cpp -) - -serenity_app(Help ICON app-help) -target_link_libraries(Help LibWeb LibMarkdown LibGUI LibDesktop) diff --git a/Applications/Help/History.cpp b/Applications/Help/History.cpp deleted file mode 100644 index bb4c822dcb..0000000000 --- a/Applications/Help/History.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@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 "History.h" - -void History::push(const StringView& history_item) -{ - m_items.shrink(m_current_history_item + 1); - m_items.append(history_item); - m_current_history_item++; -} - -String History::current() -{ - if (m_current_history_item == -1) - return {}; - return m_items[m_current_history_item]; -} - -void History::go_back() -{ - ASSERT(can_go_back()); - m_current_history_item--; -} - -void History::go_forward() -{ - ASSERT(can_go_forward()); - m_current_history_item++; -} - -void History::clear() -{ - m_items = {}; - m_current_history_item = -1; -} diff --git a/Applications/Help/History.h b/Applications/Help/History.h deleted file mode 100644 index c4058fabfc..0000000000 --- a/Applications/Help/History.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@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 <AK/String.h> -#include <AK/Vector.h> - -class History final { -public: - void push(const StringView& history_item); - String current(); - - void go_back(); - void go_forward(); - - bool can_go_back() { return m_current_history_item > 0; } - bool can_go_forward() { return m_current_history_item + 1 < static_cast<int>(m_items.size()); } - - void clear(); - -private: - Vector<String> m_items; - int m_current_history_item { -1 }; -}; diff --git a/Applications/Help/ManualModel.cpp b/Applications/Help/ManualModel.cpp deleted file mode 100644 index 484b1b22c1..0000000000 --- a/Applications/Help/ManualModel.cpp +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@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 "ManualModel.h" -#include "ManualNode.h" -#include "ManualPageNode.h" -#include "ManualSectionNode.h" -#include <AK/ByteBuffer.h> -#include <LibCore/File.h> -#include <LibGUI/FilteringProxyModel.h> - -static ManualSectionNode s_sections[] = { - { "1", "User programs" }, - { "2", "System calls" }, - { "3", "Libraries" }, - { "4", "Special files" }, - { "5", "File formats" }, - { "6", "Games" }, - { "7", "Miscellanea" }, - { "8", "Sysadmin tools" } -}; - -ManualModel::ManualModel() -{ - m_section_open_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/book-open.png")); - m_section_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/book.png")); - m_page_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-unknown.png")); -} - -Optional<GUI::ModelIndex> ManualModel::index_from_path(const StringView& path) const -{ - for (int section = 0; section < row_count(); ++section) { - auto parent_index = index(section, 0); - for (int row = 0; row < row_count(parent_index); ++row) { - auto child_index = index(row, 0, parent_index); - auto* node = static_cast<const ManualNode*>(child_index.internal_data()); - if (!node->is_page()) - continue; - auto* page = static_cast<const ManualPageNode*>(node); - if (page->path() != path) - continue; - return child_index; - } - } - return {}; -} - -String ManualModel::page_path(const GUI::ModelIndex& index) const -{ - if (!index.is_valid()) - return {}; - auto* node = static_cast<const ManualNode*>(index.internal_data()); - if (!node->is_page()) - return {}; - auto* page = static_cast<const ManualPageNode*>(node); - return page->path(); -} - -Result<StringView, OSError> ManualModel::page_view(const String& path) const -{ - if (path.is_empty()) - return StringView {}; - - { - // Check if we've got it cached already. - auto mapped_file = m_mapped_files.get(path); - if (mapped_file.has_value()) - return StringView { mapped_file.value()->bytes() }; - } - - auto file_or_error = MappedFile::map(path); - if (file_or_error.is_error()) - return file_or_error.error(); - - StringView view { file_or_error.value()->bytes() }; - m_mapped_files.set(path, file_or_error.release_value()); - return view; -} - -String ManualModel::page_and_section(const GUI::ModelIndex& index) const -{ - if (!index.is_valid()) - return {}; - auto* node = static_cast<const ManualNode*>(index.internal_data()); - if (!node->is_page()) - return {}; - auto* page = static_cast<const ManualPageNode*>(node); - auto* section = static_cast<const ManualSectionNode*>(page->parent()); - return String::formatted("{}({})", page->name(), section->section_name()); -} - -GUI::ModelIndex ManualModel::index(int row, int column, const GUI::ModelIndex& parent_index) const -{ - if (!parent_index.is_valid()) - return create_index(row, column, &s_sections[row]); - auto* parent = static_cast<const ManualNode*>(parent_index.internal_data()); - auto* child = &parent->children()[row]; - return create_index(row, column, child); -} - -GUI::ModelIndex ManualModel::parent_index(const GUI::ModelIndex& index) const -{ - if (!index.is_valid()) - return {}; - auto* child = static_cast<const ManualNode*>(index.internal_data()); - auto* parent = child->parent(); - if (parent == nullptr) - return {}; - - if (parent->parent() == nullptr) { - for (size_t row = 0; row < sizeof(s_sections) / sizeof(s_sections[0]); row++) - if (&s_sections[row] == parent) - return create_index(row, 0, parent); - ASSERT_NOT_REACHED(); - } - for (size_t row = 0; row < parent->parent()->children().size(); row++) { - ManualNode* child_at_row = &parent->parent()->children()[row]; - if (child_at_row == parent) - return create_index(row, 0, parent); - } - ASSERT_NOT_REACHED(); -} - -int ManualModel::row_count(const GUI::ModelIndex& index) const -{ - if (!index.is_valid()) - return sizeof(s_sections) / sizeof(s_sections[0]); - auto* node = static_cast<const ManualNode*>(index.internal_data()); - return node->children().size(); -} - -int ManualModel::column_count(const GUI::ModelIndex&) const -{ - return 1; -} - -GUI::Variant ManualModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const -{ - auto* node = static_cast<const ManualNode*>(index.internal_data()); - switch (role) { - case GUI::ModelRole::Search: - if (!node->is_page()) - return {}; - return String(page_view(page_path(index)).value()); - case GUI::ModelRole::Display: - return node->name(); - case GUI::ModelRole::Icon: - if (node->is_page()) - return m_page_icon; - if (node->is_open()) - return m_section_open_icon; - return m_section_icon; - default: - return {}; - } -} - -void ManualModel::update_section_node_on_toggle(const GUI::ModelIndex& index, const bool open) -{ - auto* node = static_cast<ManualSectionNode*>(index.internal_data()); - node->set_open(open); -} - -TriState ManualModel::data_matches(const GUI::ModelIndex& index, GUI::Variant term) const -{ - auto view_result = page_view(page_path(index)); - if (view_result.is_error() || view_result.value().is_empty()) - return TriState::False; - - return view_result.value().contains(term.as_string()) ? TriState::True : TriState::False; -} - -void ManualModel::update() -{ - did_update(); -} diff --git a/Applications/Help/ManualModel.h b/Applications/Help/ManualModel.h deleted file mode 100644 index 02efecaff0..0000000000 --- a/Applications/Help/ManualModel.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@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 <AK/NonnullRefPtr.h> -#include <AK/Optional.h> -#include <AK/Result.h> -#include <AK/String.h> -#include <LibGUI/Model.h> - -class ManualModel final : public GUI::Model { -public: - static NonnullRefPtr<ManualModel> create() - { - return adopt(*new ManualModel); - } - - virtual ~ManualModel() override {}; - - Optional<GUI::ModelIndex> index_from_path(const StringView&) const; - - String page_path(const GUI::ModelIndex&) const; - String page_and_section(const GUI::ModelIndex&) const; - Result<StringView, OSError> page_view(const String& path) const; - - void update_section_node_on_toggle(const GUI::ModelIndex&, const bool); - virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override; - virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override; - virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override; - virtual TriState data_matches(const GUI::ModelIndex&, GUI::Variant) const override; - virtual void update() override; - virtual GUI::ModelIndex parent_index(const GUI::ModelIndex&) const override; - virtual GUI::ModelIndex index(int row, int column = 0, const GUI::ModelIndex& parent = GUI::ModelIndex()) const override; - -private: - ManualModel(); - - GUI::Icon m_section_open_icon; - GUI::Icon m_section_icon; - GUI::Icon m_page_icon; - mutable HashMap<String, NonnullRefPtr<MappedFile>> m_mapped_files; -}; diff --git a/Applications/Help/ManualNode.h b/Applications/Help/ManualNode.h deleted file mode 100644 index b5901f9787..0000000000 --- a/Applications/Help/ManualNode.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@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 <AK/NonnullOwnPtrVector.h> -#include <AK/String.h> - -class ManualNode { -public: - virtual ~ManualNode() { } - - virtual NonnullOwnPtrVector<ManualNode>& children() const = 0; - virtual const ManualNode* parent() const = 0; - virtual String name() const = 0; - virtual bool is_page() const { return false; } - virtual bool is_open() const { return false; } -}; diff --git a/Applications/Help/ManualPageNode.cpp b/Applications/Help/ManualPageNode.cpp deleted file mode 100644 index 4b30c4dca4..0000000000 --- a/Applications/Help/ManualPageNode.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@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 "ManualPageNode.h" -#include "ManualSectionNode.h" - -const ManualNode* ManualPageNode::parent() const -{ - return &m_section; -} - -NonnullOwnPtrVector<ManualNode>& ManualPageNode::children() const -{ - static NonnullOwnPtrVector<ManualNode> empty_vector; - return empty_vector; -} - -String ManualPageNode::path() const -{ - return String::formatted("{}/{}.md", m_section.path(), m_page); -} diff --git a/Applications/Help/ManualPageNode.h b/Applications/Help/ManualPageNode.h deleted file mode 100644 index 6de744e0e4..0000000000 --- a/Applications/Help/ManualPageNode.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@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 "ManualNode.h" - -class ManualSectionNode; - -class ManualPageNode : public ManualNode { -public: - virtual ~ManualPageNode() override { } - - ManualPageNode(const ManualSectionNode& section, const StringView& page) - : m_section(section) - , m_page(page) - { - } - - virtual NonnullOwnPtrVector<ManualNode>& children() const override; - virtual const ManualNode* parent() const override; - virtual String name() const override { return m_page; }; - virtual bool is_page() const override { return true; } - - String path() const; - -private: - const ManualSectionNode& m_section; - String m_page; -}; diff --git a/Applications/Help/ManualSectionNode.cpp b/Applications/Help/ManualSectionNode.cpp deleted file mode 100644 index ce34eb3286..0000000000 --- a/Applications/Help/ManualSectionNode.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@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 "ManualSectionNode.h" -#include "ManualPageNode.h" -#include <AK/LexicalPath.h> -#include <AK/QuickSort.h> -#include <AK/String.h> -#include <LibCore/DirIterator.h> - -String ManualSectionNode::path() const -{ - return String::formatted("/usr/share/man/man{}", m_section); -} - -void ManualSectionNode::reify_if_needed() const -{ - if (m_reified) - return; - m_reified = true; - - Core::DirIterator dir_iter { path(), Core::DirIterator::Flags::SkipDots }; - - Vector<String> page_names; - while (dir_iter.has_next()) { - LexicalPath lexical_path(dir_iter.next_path()); - if (lexical_path.extension() != "md") - continue; - page_names.append(lexical_path.title()); - } - - quick_sort(page_names); - - for (auto& page_name : page_names) - m_children.append(make<ManualPageNode>(*this, move(page_name))); -} - -void ManualSectionNode::set_open(bool open) -{ - if (m_open == open) - return; - m_open = open; -} diff --git a/Applications/Help/ManualSectionNode.h b/Applications/Help/ManualSectionNode.h deleted file mode 100644 index 9b70ea9812..0000000000 --- a/Applications/Help/ManualSectionNode.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@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 "ManualNode.h" - -class ManualSectionNode : public ManualNode { -public: - virtual ~ManualSectionNode() override { } - - ManualSectionNode(String section, String name) - : m_section(section) - , m_full_name(String::formatted("{}. {}", section, name)) - { - } - - virtual NonnullOwnPtrVector<ManualNode>& children() const override - { - reify_if_needed(); - return m_children; - } - - virtual const ManualNode* parent() const override { return nullptr; } - virtual String name() const override { return m_full_name; } - virtual bool is_open() const override { return m_open; } - void set_open(bool open); - - const String& section_name() const { return m_section; } - String path() const; - -private: - void reify_if_needed() const; - - String m_section; - String m_full_name; - mutable NonnullOwnPtrVector<ManualNode> m_children; - mutable bool m_reified { false }; - bool m_open { false }; -}; diff --git a/Applications/Help/main.cpp b/Applications/Help/main.cpp deleted file mode 100644 index d5a6405fbf..0000000000 --- a/Applications/Help/main.cpp +++ /dev/null @@ -1,312 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@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 "History.h" -#include "ManualModel.h" -#include <AK/URL.h> -#include <LibCore/ArgsParser.h> -#include <LibCore/File.h> -#include <LibDesktop/Launcher.h> -#include <LibGUI/Action.h> -#include <LibGUI/Application.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/FilteringProxyModel.h> -#include <LibGUI/ListView.h> -#include <LibGUI/Menu.h> -#include <LibGUI/MenuBar.h> -#include <LibGUI/MessageBox.h> -#include <LibGUI/Splitter.h> -#include <LibGUI/TabWidget.h> -#include <LibGUI/TextBox.h> -#include <LibGUI/ToolBar.h> -#include <LibGUI/ToolBarContainer.h> -#include <LibGUI/TreeView.h> -#include <LibGUI/Window.h> -#include <LibMarkdown/Document.h> -#include <LibWeb/OutOfProcessWebView.h> -#include <libgen.h> -#include <stdio.h> -#include <string.h> - -int main(int argc, char* argv[]) -{ - if (pledge("stdio shared_buffer accept rpath unix cpath fattr", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto app = GUI::Application::construct(argc, argv); - - if (pledge("stdio shared_buffer accept rpath unix", nullptr) < 0) { - perror("pledge"); - return 1; - } - - if (unveil("/res", "r") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/usr/share/man", "r") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/tmp/portal/launch", "rw") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/tmp/portal/webcontent", "rw") < 0) { - perror("unveil"); - return 1; - } - - unveil(nullptr, nullptr); - - const char* start_page = nullptr; - - Core::ArgsParser args_parser; - args_parser.add_positional_argument(start_page, "Page to open at launch", "page", Core::ArgsParser::Required::No); - - args_parser.parse(argc, argv); - - auto app_icon = GUI::Icon::default_icon("app-help"); - - auto window = GUI::Window::construct(); - window->set_icon(app_icon.bitmap_for_size(16)); - window->set_title("Help"); - window->resize(570, 500); - - auto& widget = window->set_main_widget<GUI::Widget>(); - widget.set_layout<GUI::VerticalBoxLayout>(); - widget.set_fill_with_background_color(true); - widget.layout()->set_spacing(2); - - auto& toolbar_container = widget.add<GUI::ToolBarContainer>(); - auto& toolbar = toolbar_container.add<GUI::ToolBar>(); - - auto& splitter = widget.add<GUI::HorizontalSplitter>(); - - auto model = ManualModel::create(); - - auto& left_tab_bar = splitter.add<GUI::TabWidget>(); - auto& tree_view_container = left_tab_bar.add_tab<GUI::Widget>("Browse"); - tree_view_container.set_layout<GUI::VerticalBoxLayout>(); - tree_view_container.layout()->set_margins({ 4, 4, 4, 4 }); - auto& tree_view = tree_view_container.add<GUI::TreeView>(); - auto& search_view = left_tab_bar.add_tab<GUI::Widget>("Search"); - search_view.set_layout<GUI::VerticalBoxLayout>(); - search_view.layout()->set_margins({ 4, 4, 4, 4 }); - auto& search_box = search_view.add<GUI::TextBox>(); - auto& search_list_view = search_view.add<GUI::ListView>(); - search_box.set_fixed_height(20); - search_box.set_placeholder("Search..."); - search_box.on_change = [&] { - if (auto model = search_list_view.model()) { - auto& search_model = *static_cast<GUI::FilteringProxyModel*>(model); - search_model.set_filter_term(search_box.text()); - search_model.update(); - } - }; - search_list_view.set_model(GUI::FilteringProxyModel::construct(model)); - search_list_view.model()->update(); - - tree_view.set_model(model); - left_tab_bar.set_fixed_width(200); - - auto& page_view = splitter.add<Web::OutOfProcessWebView>(); - - History history; - - RefPtr<GUI::Action> go_back_action; - RefPtr<GUI::Action> go_forward_action; - - auto update_actions = [&]() { - go_back_action->set_enabled(history.can_go_back()); - go_forward_action->set_enabled(history.can_go_forward()); - }; - - auto open_page = [&](const String& path) { - if (path.is_null()) { - window->set_title("Help"); - page_view.load_empty_document(); - return; - } - - auto source_result = model->page_view(path); - if (source_result.is_error()) { - GUI::MessageBox::show(window, source_result.error().string(), "Failed to open man page", GUI::MessageBox::Type::Error); - return; - } - - auto source = source_result.value(); - String html; - { - auto md_document = Markdown::Document::parse(source); - ASSERT(md_document); - html = md_document->render_to_html(); - } - - auto url = URL::create_with_file_protocol(path); - page_view.load_html(html, url); - - auto tree_view_index = model->index_from_path(path); - if (tree_view_index.has_value()) - tree_view.expand_tree(tree_view_index.value().parent()); - - String page_and_section = model->page_and_section(tree_view_index.value()); - window->set_title(String::formatted("{} - Help", page_and_section)); - }; - - tree_view.on_selection_change = [&] { - String path = model->page_path(tree_view.selection().first()); - history.push(path); - update_actions(); - open_page(path); - }; - - tree_view.on_toggle = [&](const GUI::ModelIndex& index, const bool open) { - model->update_section_node_on_toggle(index, open); - }; - - auto open_external = [&](auto& url) { - if (!Desktop::Launcher::open(url)) { - GUI::MessageBox::show(window, - String::formatted("The link to '{}' could not be opened.", url), - "Failed to open link", - GUI::MessageBox::Type::Error); - } - }; - search_list_view.on_selection = [&](auto index) { - if (!index.is_valid()) - return; - - if (auto model = search_list_view.model()) { - auto& search_model = *static_cast<GUI::FilteringProxyModel*>(model); - index = search_model.map(index); - } else { - page_view.load_empty_document(); - return; - } - String path = model->page_path(index); - if (path.is_null()) { - page_view.load_empty_document(); - return; - } - tree_view.selection().clear(); - tree_view.selection().add(index); - history.push(path); - update_actions(); - open_page(path); - }; - - page_view.on_link_click = [&](auto& url, auto&, unsigned) { - if (url.protocol() != "file") { - open_external(url); - return; - } - auto path = Core::File::real_path_for(url.path()); - if (!path.starts_with("/usr/share/man/")) { - open_external(url); - return; - } - auto tree_view_index = model->index_from_path(path); - if (tree_view_index.has_value()) { - dbgln("Found path _{}_ in model at index {}", path, tree_view_index.value()); - tree_view.selection().set(tree_view_index.value()); - return; - } - history.push(path); - update_actions(); - open_page(path); - }; - - go_back_action = GUI::CommonActions::make_go_back_action([&](auto&) { - history.go_back(); - update_actions(); - open_page(history.current()); - }); - - go_forward_action = GUI::CommonActions::make_go_forward_action([&](auto&) { - history.go_forward(); - update_actions(); - open_page(history.current()); - }); - - go_back_action->set_enabled(false); - go_forward_action->set_enabled(false); - - auto go_home_action = GUI::CommonActions::make_go_home_action([&](auto&) { - String path = "/usr/share/man/man7/Help-index.md"; - history.push(path); - update_actions(); - open_page(path); - }); - - toolbar.add_action(*go_back_action); - toolbar.add_action(*go_forward_action); - toolbar.add_action(*go_home_action); - - auto menubar = GUI::MenuBar::construct(); - - auto& app_menu = menubar->add_menu("Help"); - app_menu.add_action(GUI::CommonActions::make_about_action("Help", app_icon, window)); - app_menu.add_separator(); - app_menu.add_action(GUI::CommonActions::make_quit_action([](auto&) { - GUI::Application::the()->quit(); - })); - - auto& go_menu = menubar->add_menu("Go"); - go_menu.add_action(*go_back_action); - go_menu.add_action(*go_forward_action); - go_menu.add_action(*go_home_action); - - app->set_menubar(move(menubar)); - - if (start_page) { - URL url = URL::create_with_url_or_path(start_page); - if (url.is_valid() && url.path().ends_with(".md")) { - history.push(url.path()); - update_actions(); - open_page(url.path()); - } else { - left_tab_bar.set_active_widget(&search_view); - search_box.set_text(start_page); - if (auto model = search_list_view.model()) { - auto& search_model = *static_cast<GUI::FilteringProxyModel*>(model); - search_model.set_filter_term(search_box.text()); - } - } - } else { - go_home_action->activate(); - } - - window->set_focused_widget(&left_tab_bar); - window->show(); - - return app->exec(); -} diff --git a/Applications/HexEditor/CMakeLists.txt b/Applications/HexEditor/CMakeLists.txt deleted file mode 100644 index ebe134863f..0000000000 --- a/Applications/HexEditor/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -set(SOURCES - HexEditor.cpp - HexEditorWidget.cpp - main.cpp -) - -serenity_app(HexEditor ICON app-hexeditor) -target_link_libraries(HexEditor LibGUI) diff --git a/Applications/HexEditor/HexEditor.cpp b/Applications/HexEditor/HexEditor.cpp deleted file mode 100644 index f4999661f8..0000000000 --- a/Applications/HexEditor/HexEditor.cpp +++ /dev/null @@ -1,583 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "HexEditor.h" -#include <AK/StringBuilder.h> -#include <LibGUI/Action.h> -#include <LibGUI/Clipboard.h> -#include <LibGUI/Menu.h> -#include <LibGUI/Painter.h> -#include <LibGUI/ScrollBar.h> -#include <LibGUI/TextEditor.h> -#include <LibGUI/Window.h> -#include <LibGfx/FontDatabase.h> -#include <LibGfx/Palette.h> -#include <ctype.h> -#include <fcntl.h> -#include <stdio.h> -#include <unistd.h> - -HexEditor::HexEditor() -{ - set_focus_policy(GUI::FocusPolicy::StrongFocus); - set_scrollbars_enabled(true); - set_font(Gfx::FontDatabase::default_fixed_width_font()); - set_background_role(ColorRole::Base); - set_foreground_role(ColorRole::BaseText); - vertical_scrollbar().set_step(line_height()); -} - -HexEditor::~HexEditor() -{ -} - -void HexEditor::set_readonly(bool readonly) -{ - if (m_readonly == readonly) - return; - m_readonly = readonly; -} - -void HexEditor::set_buffer(const ByteBuffer& buffer) -{ - m_buffer = buffer; - set_content_length(buffer.size()); - m_tracked_changes.clear(); - m_position = 0; - m_byte_position = 0; - update(); - update_status(); -} - -void HexEditor::fill_selection(u8 fill_byte) -{ - if (!has_selection()) - return; - - for (int i = m_selection_start; i <= m_selection_end; i++) { - m_tracked_changes.set(i, m_buffer.data()[i]); - m_buffer.data()[i] = fill_byte; - } - - update(); - did_change(); -} - -void HexEditor::set_position(int position) -{ - if (position > static_cast<int>(m_buffer.size())) - return; - - m_position = position; - m_byte_position = 0; - scroll_position_into_view(position); - update_status(); -} - -bool HexEditor::write_to_file(const StringView& path) -{ - if (m_buffer.is_empty()) - return true; - - int fd = open_with_path_length(path.characters_without_null_termination(), path.length(), O_WRONLY | O_CREAT | O_TRUNC, 0666); - if (fd < 0) { - perror("open"); - return false; - } - - int rc = ftruncate(fd, m_buffer.size()); - if (rc < 0) { - perror("ftruncate"); - return false; - } - - ssize_t nwritten = write(fd, m_buffer.data(), m_buffer.size()); - if (nwritten < 0) { - perror("write"); - close(fd); - return false; - } - - if (static_cast<size_t>(nwritten) == m_buffer.size()) { - m_tracked_changes.clear(); - update(); - } - - close(fd); - return true; -} - -bool HexEditor::copy_selected_hex_to_clipboard() -{ - if (!has_selection()) - return false; - - StringBuilder output_string_builder; - for (int i = m_selection_start; i <= m_selection_end; i++) - output_string_builder.appendff("{:02X} ", m_buffer.data()[i]); - - GUI::Clipboard::the().set_plain_text(output_string_builder.to_string()); - return true; -} - -bool HexEditor::copy_selected_text_to_clipboard() -{ - if (!has_selection()) - return false; - - StringBuilder output_string_builder; - for (int i = m_selection_start; i <= m_selection_end; i++) - output_string_builder.append(isprint(m_buffer.data()[i]) ? m_buffer[i] : '.'); - - GUI::Clipboard::the().set_plain_text(output_string_builder.to_string()); - return true; -} - -bool HexEditor::copy_selected_hex_to_clipboard_as_c_code() -{ - if (!has_selection()) - return false; - - StringBuilder output_string_builder; - output_string_builder.appendff("unsigned char raw_data[{}] = {{\n", (m_selection_end - m_selection_start) + 1); - output_string_builder.append(" "); - for (int i = m_selection_start, j = 1; i <= m_selection_end; i++, j++) { - output_string_builder.appendff("{:#02X}", m_buffer.data()[i]); - if (i != m_selection_end) - output_string_builder.append(", "); - if ((j % 12) == 0) { - output_string_builder.append("\n"); - output_string_builder.append(" "); - } - } - output_string_builder.append("\n};\n"); - - GUI::Clipboard::the().set_plain_text(output_string_builder.to_string()); - return true; -} - -void HexEditor::set_bytes_per_row(int bytes_per_row) -{ - m_bytes_per_row = bytes_per_row; - set_content_size({ offset_margin_width() + (m_bytes_per_row * (character_width() * 3)) + 10 + (m_bytes_per_row * character_width()) + 20, total_rows() * line_height() + 10 }); - update(); -} - -void HexEditor::set_content_length(int length) -{ - if (length == m_content_length) - return; - m_content_length = length; - set_content_size({ offset_margin_width() + (m_bytes_per_row * (character_width() * 3)) + 10 + (m_bytes_per_row * character_width()) + 20, total_rows() * line_height() + 10 }); -} - -void HexEditor::mousedown_event(GUI::MouseEvent& event) -{ - if (event.button() != GUI::MouseButton::Left) { - return; - } - - auto absolute_x = horizontal_scrollbar().value() + event.x(); - auto absolute_y = vertical_scrollbar().value() + event.y(); - - auto hex_start_x = frame_thickness() + 90; - auto hex_start_y = frame_thickness() + 5; - auto hex_end_x = hex_start_x + (bytes_per_row() * (character_width() * 3)); - auto hex_end_y = hex_start_y + 5 + (total_rows() * line_height()); - - auto text_start_x = frame_thickness() + 100 + (bytes_per_row() * (character_width() * 3)); - auto text_start_y = frame_thickness() + 5; - auto text_end_x = text_start_x + (bytes_per_row() * character_width()); - auto text_end_y = text_start_y + 5 + (total_rows() * line_height()); - - if (absolute_x >= hex_start_x && absolute_x <= hex_end_x && absolute_y >= hex_start_y && absolute_y <= hex_end_y) { - auto byte_x = (absolute_x - hex_start_x) / (character_width() * 3); - auto byte_y = (absolute_y - hex_start_y) / line_height(); - auto offset = (byte_y * m_bytes_per_row) + byte_x; - - if (offset < 0 || offset >= static_cast<int>(m_buffer.size())) - return; - -#ifdef HEX_DEBUG - outln("HexEditor::mousedown_event(hex): offset={}", offset); -#endif - - m_edit_mode = EditMode::Hex; - m_byte_position = 0; - m_position = offset; - m_in_drag_select = true; - m_selection_start = offset; - m_selection_end = offset; - update(); - update_status(); - } - - if (absolute_x >= text_start_x && absolute_x <= text_end_x && absolute_y >= text_start_y && absolute_y <= text_end_y) { - auto byte_x = (absolute_x - text_start_x) / character_width(); - auto byte_y = (absolute_y - text_start_y) / line_height(); - auto offset = (byte_y * m_bytes_per_row) + byte_x; - - if (offset < 0 || offset >= static_cast<int>(m_buffer.size())) - return; - -#ifdef HEX_DEBUG - outln("HexEditor::mousedown_event(text): offset={}", offset); -#endif - - m_position = offset; - m_byte_position = 0; - m_in_drag_select = true; - m_selection_start = offset; - m_selection_end = offset; - m_edit_mode = EditMode::Text; - update(); - update_status(); - } -} - -void HexEditor::mousemove_event(GUI::MouseEvent& event) -{ - auto absolute_x = horizontal_scrollbar().value() + event.x(); - auto absolute_y = vertical_scrollbar().value() + event.y(); - - auto hex_start_x = frame_thickness() + 90; - auto hex_start_y = frame_thickness() + 5; - auto hex_end_x = hex_start_x + (bytes_per_row() * (character_width() * 3)); - auto hex_end_y = hex_start_y + 5 + (total_rows() * line_height()); - - auto text_start_x = frame_thickness() + 100 + (bytes_per_row() * (character_width() * 3)); - auto text_start_y = frame_thickness() + 5; - auto text_end_x = text_start_x + (bytes_per_row() * character_width()); - auto text_end_y = text_start_y + 5 + (total_rows() * line_height()); - - if ((absolute_x >= hex_start_x && absolute_x <= hex_end_x - && absolute_y >= hex_start_y && absolute_y <= hex_end_y) - || (absolute_x >= text_start_x && absolute_x <= text_end_x - && absolute_y >= text_start_y && absolute_y <= text_end_y)) { - set_override_cursor(Gfx::StandardCursor::IBeam); - } else { - set_override_cursor(Gfx::StandardCursor::None); - } - - if (m_in_drag_select) { - if (absolute_x >= hex_start_x && absolute_x <= hex_end_x && absolute_y >= hex_start_y && absolute_y <= hex_end_y) { - auto byte_x = (absolute_x - hex_start_x) / (character_width() * 3); - auto byte_y = (absolute_y - hex_start_y) / line_height(); - auto offset = (byte_y * m_bytes_per_row) + byte_x; - - if (offset < 0 || offset > static_cast<int>(m_buffer.size())) - return; - - m_selection_end = offset; - scroll_position_into_view(offset); - } - - if (absolute_x >= text_start_x && absolute_x <= text_end_x && absolute_y >= text_start_y && absolute_y <= text_end_y) { - auto byte_x = (absolute_x - text_start_x) / character_width(); - auto byte_y = (absolute_y - text_start_y) / line_height(); - auto offset = (byte_y * m_bytes_per_row) + byte_x; - if (offset < 0 || offset > static_cast<int>(m_buffer.size())) - return; - - m_selection_end = offset; - scroll_position_into_view(offset); - } - update_status(); - update(); - return; - } -} - -void HexEditor::mouseup_event(GUI::MouseEvent& event) -{ - if (event.button() == GUI::MouseButton::Left) { - if (m_in_drag_select) { - if (m_selection_end < m_selection_start) { - // lets flip these around - auto start = m_selection_end; - m_selection_end = m_selection_start; - m_selection_start = start; - } - m_in_drag_select = false; - } - update(); - update_status(); - } -} - -void HexEditor::scroll_position_into_view(int position) -{ - int y = position / bytes_per_row(); - int x = position % bytes_per_row(); - Gfx::IntRect rect { - frame_thickness() + offset_margin_width() + (x * (character_width() * 3)) + 10, - frame_thickness() + 5 + (y * line_height()), - (character_width() * 3), - line_height() - m_line_spacing - }; - scroll_into_view(rect, true, true); -} - -void HexEditor::keydown_event(GUI::KeyEvent& event) -{ -#ifdef HEX_DEBUG - outln("HexEditor::keydown_event key={}", static_cast<u8>(event.key())); -#endif - - if (event.key() == KeyCode::Key_Up) { - if (m_position - bytes_per_row() >= 0) { - m_position -= bytes_per_row(); - m_byte_position = 0; - scroll_position_into_view(m_position); - update(); - update_status(); - } - return; - } - - if (event.key() == KeyCode::Key_Down) { - if (m_position + bytes_per_row() < static_cast<int>(m_buffer.size())) { - m_position += bytes_per_row(); - m_byte_position = 0; - scroll_position_into_view(m_position); - update(); - update_status(); - } - return; - } - - if (event.key() == KeyCode::Key_Left) { - if (m_position - 1 >= 0) { - m_position--; - m_byte_position = 0; - scroll_position_into_view(m_position); - update(); - update_status(); - } - return; - } - - if (event.key() == KeyCode::Key_Right) { - if (m_position + 1 < static_cast<int>(m_buffer.size())) { - m_position++; - m_byte_position = 0; - scroll_position_into_view(m_position); - update(); - update_status(); - } - return; - } - - if (event.key() == KeyCode::Key_Backspace) { - if (m_position > 0) { - m_position--; - m_byte_position = 0; - scroll_position_into_view(m_position); - update(); - update_status(); - } - return; - } - - if (!is_readonly() && !event.ctrl() && !event.alt() && !event.text().is_empty()) { - if (m_edit_mode == EditMode::Hex) { - hex_mode_keydown_event(event); - } else { - text_mode_keydown_event(event); - } - } -} - -void HexEditor::hex_mode_keydown_event(GUI::KeyEvent& event) -{ - if ((event.key() >= KeyCode::Key_0 && event.key() <= KeyCode::Key_9) || (event.key() >= KeyCode::Key_A && event.key() <= KeyCode::Key_F)) { - if (m_buffer.is_empty()) - return; - ASSERT(m_position >= 0); - ASSERT(m_position < static_cast<int>(m_buffer.size())); - - // yes, this is terrible... but it works. - auto value = (event.key() >= KeyCode::Key_0 && event.key() <= KeyCode::Key_9) - ? event.key() - KeyCode::Key_0 - : (event.key() - KeyCode::Key_A) + 0xA; - - if (m_byte_position == 0) { - m_tracked_changes.set(m_position, m_buffer.data()[m_position]); - m_buffer.data()[m_position] = value << 4 | (m_buffer.data()[m_position] & 0xF); // shift new value left 4 bits, OR with existing last 4 bits - m_byte_position++; - } else { - m_buffer.data()[m_position] = (m_buffer.data()[m_position] & 0xF0) | value; // save the first 4 bits, OR the new value in the last 4 - if (m_position + 1 < static_cast<int>(m_buffer.size())) - m_position++; - m_byte_position = 0; - } - - update(); - update_status(); - did_change(); - } -} - -void HexEditor::text_mode_keydown_event(GUI::KeyEvent& event) -{ - if (m_buffer.is_empty()) - return; - ASSERT(m_position >= 0); - ASSERT(m_position < static_cast<int>(m_buffer.size())); - - if (event.code_point() == 0) // This is a control key - return; - - m_tracked_changes.set(m_position, m_buffer.data()[m_position]); - m_buffer.data()[m_position] = event.code_point(); - if (m_position + 1 < static_cast<int>(m_buffer.size())) - m_position++; - m_byte_position = 0; - - update(); - update_status(); - did_change(); -} - -void HexEditor::update_status() -{ - if (on_status_change) - on_status_change(m_position, m_edit_mode, m_selection_start, m_selection_end); -} - -void HexEditor::did_change() -{ - if (on_change) - on_change(); -} - -void HexEditor::paint_event(GUI::PaintEvent& event) -{ - GUI::Frame::paint_event(event); - - GUI::Painter painter(*this); - painter.add_clip_rect(widget_inner_rect()); - painter.add_clip_rect(event.rect()); - painter.fill_rect(event.rect(), palette().color(background_role())); - - if (m_buffer.is_empty()) - return; - - painter.translate(frame_thickness(), frame_thickness()); - painter.translate(-horizontal_scrollbar().value(), -vertical_scrollbar().value()); - - Gfx::IntRect offset_clip_rect { - 0, - vertical_scrollbar().value(), - 85, - height() - height_occupied_by_horizontal_scrollbar() //(total_rows() * line_height()) + 5 - }; - painter.fill_rect(offset_clip_rect, palette().ruler()); - painter.draw_line(offset_clip_rect.top_right(), offset_clip_rect.bottom_right(), palette().ruler_border()); - - auto margin_and_hex_width = offset_margin_width() + (m_bytes_per_row * (character_width() * 3)) + 15; - painter.draw_line({ margin_and_hex_width, 0 }, - { margin_and_hex_width, vertical_scrollbar().value() + (height() - height_occupied_by_horizontal_scrollbar()) }, - palette().ruler_border()); - - auto view_height = (height() - height_occupied_by_horizontal_scrollbar()); - auto min_row = max(0, vertical_scrollbar().value() / line_height()); // if below 0 then use 0 - auto max_row = min(total_rows(), min_row + ceil_div(view_height, line_height())); // if above calculated rows, use calculated rows - - // paint offsets - for (int i = min_row; i < max_row; i++) { - Gfx::IntRect side_offset_rect { - frame_thickness() + 5, - frame_thickness() + 5 + (i * line_height()), - width() - width_occupied_by_vertical_scrollbar(), - height() - height_occupied_by_horizontal_scrollbar() - }; - - bool is_current_line = (m_position / bytes_per_row()) == i; - auto line = String::formatted("{:#08X}", i * bytes_per_row()); - painter.draw_text( - side_offset_rect, - line, - is_current_line ? Gfx::FontDatabase::default_bold_font() : font(), - Gfx::TextAlignment::TopLeft, - is_current_line ? palette().ruler_active_text() : palette().ruler_inactive_text()); - } - - for (int i = min_row; i < max_row; i++) { - for (int j = 0; j < bytes_per_row(); j++) { - auto byte_position = (i * bytes_per_row()) + j; - if (byte_position >= static_cast<int>(m_buffer.size())) - return; - - Color text_color = palette().color(foreground_role()); - if (m_tracked_changes.contains(byte_position)) { - text_color = Color::Red; - } - - auto highlight_flag = false; - if (m_selection_start > -1 && m_selection_end > -1) { - if (byte_position >= m_selection_start && byte_position <= m_selection_end) { - highlight_flag = true; - } - if (byte_position >= m_selection_end && byte_position <= m_selection_start) { - highlight_flag = true; - } - } - - Gfx::IntRect hex_display_rect { - frame_thickness() + offset_margin_width() + (j * (character_width() * 3)) + 10, - frame_thickness() + 5 + (i * line_height()), - (character_width() * 3), - line_height() - m_line_spacing - }; - if (highlight_flag) { - painter.fill_rect(hex_display_rect, palette().selection()); - text_color = text_color == Color::Red ? Color::from_rgb(0xFFC0CB) : palette().selection_text(); - } else if (byte_position == m_position) { - painter.fill_rect(hex_display_rect, palette().inactive_selection()); - text_color = palette().inactive_selection_text(); - } - - auto line = String::formatted("{:02X}", m_buffer[byte_position]); - painter.draw_text(hex_display_rect, line, Gfx::TextAlignment::TopLeft, text_color); - - Gfx::IntRect text_display_rect { - frame_thickness() + offset_margin_width() + (bytes_per_row() * (character_width() * 3)) + (j * character_width()) + 20, - frame_thickness() + 5 + (i * line_height()), - character_width(), - line_height() - m_line_spacing - }; - // selection highlighting. - if (highlight_flag) { - painter.fill_rect(text_display_rect, palette().selection()); - } else if (byte_position == m_position) { - painter.fill_rect(text_display_rect, palette().inactive_selection()); - } - - painter.draw_text(text_display_rect, String::formatted("{:c}", isprint(m_buffer[byte_position]) ? m_buffer[byte_position] : '.'), Gfx::TextAlignment::TopLeft, text_color); - } - } -} diff --git a/Applications/HexEditor/HexEditor.h b/Applications/HexEditor/HexEditor.h deleted file mode 100644 index e47eedeec3..0000000000 --- a/Applications/HexEditor/HexEditor.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 <AK/ByteBuffer.h> -#include <AK/Function.h> -#include <AK/HashMap.h> -#include <AK/NonnullOwnPtrVector.h> -#include <AK/NonnullRefPtrVector.h> -#include <AK/StdLibExtras.h> -#include <LibGUI/ScrollableWidget.h> -#include <LibGfx/Font.h> -#include <LibGfx/TextAlignment.h> - -class HexEditor : public GUI::ScrollableWidget { - C_OBJECT(HexEditor) -public: - enum EditMode { - Hex, - Text - }; - - virtual ~HexEditor() override; - - bool is_readonly() const { return m_readonly; } - void set_readonly(bool); - - void set_buffer(const ByteBuffer&); - void fill_selection(u8 fill_byte); - bool write_to_file(const StringView& path); - - bool has_selection() const { return !(m_selection_start == -1 || m_selection_end == -1 || (m_selection_end - m_selection_start) < 0 || m_buffer.is_empty()); } - bool copy_selected_text_to_clipboard(); - bool copy_selected_hex_to_clipboard(); - bool copy_selected_hex_to_clipboard_as_c_code(); - - int bytes_per_row() const { return m_bytes_per_row; } - void set_bytes_per_row(int); - - void set_position(int position); - - Function<void(int, EditMode, int, int)> on_status_change; // position, edit mode, selection start, selection end - Function<void()> on_change; - -protected: - HexEditor(); - - virtual void paint_event(GUI::PaintEvent&) override; - virtual void mousedown_event(GUI::MouseEvent&) override; - virtual void mouseup_event(GUI::MouseEvent&) override; - virtual void mousemove_event(GUI::MouseEvent&) override; - virtual void keydown_event(GUI::KeyEvent&) override; - -private: - bool m_readonly { false }; - int m_line_spacing { 4 }; - int m_content_length { 0 }; - int m_bytes_per_row { 16 }; - ByteBuffer m_buffer; - bool m_in_drag_select { false }; - int m_selection_start { 0 }; - int m_selection_end { 0 }; - HashMap<int, u8> m_tracked_changes; - int m_position { 0 }; - int m_byte_position { 0 }; // 0 or 1 - EditMode m_edit_mode { Hex }; - - void scroll_position_into_view(int position); - - int total_rows() const { return ceil_div(m_content_length, m_bytes_per_row); } - int line_height() const { return font().glyph_height() + m_line_spacing; } - int character_width() const { return font().glyph_width('W'); } - int offset_margin_width() const { return 80; } - - void hex_mode_keydown_event(GUI::KeyEvent&); - void text_mode_keydown_event(GUI::KeyEvent&); - - void set_content_length(int); // I might make this public if I add fetching data on demand. - void update_status(); - void did_change(); -}; diff --git a/Applications/HexEditor/HexEditorWidget.cpp b/Applications/HexEditor/HexEditorWidget.cpp deleted file mode 100644 index e6f6711884..0000000000 --- a/Applications/HexEditor/HexEditorWidget.cpp +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "HexEditorWidget.h" -#include <AK/Optional.h> -#include <AK/StringBuilder.h> -#include <LibCore/File.h> -#include <LibGUI/Action.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Button.h> -#include <LibGUI/FilePicker.h> -#include <LibGUI/InputBox.h> -#include <LibGUI/Menu.h> -#include <LibGUI/MenuBar.h> -#include <LibGUI/MessageBox.h> -#include <LibGUI/StatusBar.h> -#include <LibGUI/TextBox.h> -#include <LibGUI/TextEditor.h> -#include <LibGUI/ToolBar.h> -#include <stdio.h> -#include <string.h> - -HexEditorWidget::HexEditorWidget() -{ - set_fill_with_background_color(true); - set_layout<GUI::VerticalBoxLayout>(); - layout()->set_spacing(2); - - m_editor = add<HexEditor>(); - - m_editor->on_status_change = [this](int position, HexEditor::EditMode edit_mode, int selection_start, int selection_end) { - m_statusbar->set_text(0, String::formatted("Offset: {:#08X}", position)); - m_statusbar->set_text(1, String::formatted("Edit Mode: {}", edit_mode == HexEditor::EditMode::Hex ? "Hex" : "Text")); - m_statusbar->set_text(2, String::formatted("Selection Start: {}", selection_start)); - m_statusbar->set_text(3, String::formatted("Selection End: {}", selection_end)); - m_statusbar->set_text(4, String::formatted("Selected Bytes: {}", abs(selection_end - selection_start) + 1)); - }; - - m_editor->on_change = [this] { - bool was_dirty = m_document_dirty; - m_document_dirty = true; - if (!was_dirty) - update_title(); - }; - - m_statusbar = add<GUI::StatusBar>(5); - - m_new_action = GUI::Action::create("New", { Mod_Ctrl, Key_N }, Gfx::Bitmap::load_from_file("/res/icons/16x16/new.png"), [this](const GUI::Action&) { - if (m_document_dirty) { - if (GUI::MessageBox::show(window(), "Save changes to current file first?", "Warning", GUI::MessageBox::Type::Warning, GUI::MessageBox::InputType::OKCancel) != GUI::Dialog::ExecResult::ExecOK) - return; - m_save_action->activate(); - } - - String value; - if (GUI::InputBox::show(value, window(), "Enter new file size:", "New file size") == GUI::InputBox::ExecOK && !value.is_empty()) { - auto file_size = value.to_int(); - if (file_size.has_value() && file_size.value() > 0) { - m_document_dirty = false; - m_editor->set_buffer(ByteBuffer::create_zeroed(file_size.value())); - set_path(LexicalPath()); - update_title(); - } else { - GUI::MessageBox::show(window(), "Invalid file size entered.", "Error", GUI::MessageBox::Type::Error); - } - } - }); - - m_open_action = GUI::CommonActions::make_open_action([this](auto&) { - Optional<String> open_path = GUI::FilePicker::get_open_filepath(window()); - - if (!open_path.has_value()) - return; - - open_file(open_path.value()); - }); - - m_save_action = GUI::CommonActions::make_save_action([&](auto&) { - if (!m_path.is_empty()) { - if (!m_editor->write_to_file(m_path)) { - GUI::MessageBox::show(window(), "Unable to save file.\n", "Error", GUI::MessageBox::Type::Error); - } else { - m_document_dirty = false; - update_title(); - } - return; - } - - m_save_as_action->activate(); - }); - - m_save_as_action = GUI::CommonActions::make_save_as_action([&](auto&) { - Optional<String> save_path = GUI::FilePicker::get_save_filepath(window(), m_name.is_null() ? "Untitled" : m_name, m_extension.is_null() ? "bin" : m_extension); - if (!save_path.has_value()) - return; - - if (!m_editor->write_to_file(save_path.value())) { - GUI::MessageBox::show(window(), "Unable to save file.\n", "Error", GUI::MessageBox::Type::Error); - return; - } - - m_document_dirty = false; - set_path(LexicalPath(save_path.value())); - dbgln("Wrote document to {}", save_path.value()); - }); - - auto menubar = GUI::MenuBar::construct(); - auto& app_menu = menubar->add_menu("Hex Editor"); - app_menu.add_action(*m_new_action); - app_menu.add_action(*m_open_action); - app_menu.add_action(*m_save_action); - app_menu.add_action(*m_save_as_action); - app_menu.add_separator(); - app_menu.add_action(GUI::CommonActions::make_quit_action([this](auto&) { - if (!request_close()) - return; - GUI::Application::the()->quit(); - })); - - m_goto_decimal_offset_action = GUI::Action::create("Go To Offset (Decimal)...", { Mod_Ctrl | Mod_Shift, Key_G }, Gfx::Bitmap::load_from_file("/res/icons/16x16/go-forward.png"), [this](const GUI::Action&) { - String value; - if (GUI::InputBox::show(value, window(), "Enter Decimal offset:", "Go To") == GUI::InputBox::ExecOK && !value.is_empty()) { - auto new_offset = value.to_int(); - if (new_offset.has_value()) - m_editor->set_position(new_offset.value()); - } - }); - - m_goto_hex_offset_action = GUI::Action::create("Go To Offset (Hex)...", { Mod_Ctrl, Key_G }, Gfx::Bitmap::load_from_file("/res/icons/16x16/go-forward.png"), [this](const GUI::Action&) { - String value; - if (GUI::InputBox::show(value, window(), "Enter Hex offset:", "Go To") == GUI::InputBox::ExecOK && !value.is_empty()) { - auto new_offset = strtol(value.characters(), nullptr, 16); - m_editor->set_position(new_offset); - } - }); - - auto& edit_menu = menubar->add_menu("Edit"); - edit_menu.add_action(GUI::Action::create("Fill selection...", { Mod_Ctrl, Key_B }, [&](const GUI::Action&) { - String value; - if (GUI::InputBox::show(value, window(), "Fill byte (hex):", "Fill Selection") == GUI::InputBox::ExecOK && !value.is_empty()) { - auto fill_byte = strtol(value.characters(), nullptr, 16); - m_editor->fill_selection(fill_byte); - } - })); - edit_menu.add_separator(); - edit_menu.add_action(*m_goto_decimal_offset_action); - edit_menu.add_action(*m_goto_hex_offset_action); - edit_menu.add_separator(); - edit_menu.add_action(GUI::Action::create("Copy Hex", { Mod_Ctrl, Key_C }, [&](const GUI::Action&) { - m_editor->copy_selected_hex_to_clipboard(); - })); - edit_menu.add_action(GUI::Action::create("Copy Text", { Mod_Ctrl | Mod_Shift, Key_C }, [&](const GUI::Action&) { - m_editor->copy_selected_text_to_clipboard(); - })); - edit_menu.add_separator(); - edit_menu.add_action(GUI::Action::create("Copy As C Code", { Mod_Alt | Mod_Shift, Key_C }, [&](const GUI::Action&) { - m_editor->copy_selected_hex_to_clipboard_as_c_code(); - })); - - auto& view_menu = menubar->add_menu("View"); - auto& bytes_per_row_menu = view_menu.add_submenu("Bytes per row"); - for (int i = 8; i <= 32; i += 8) { - bytes_per_row_menu.add_action(GUI::Action::create(String::number(i), [this, i](auto&) { - m_editor->set_bytes_per_row(i); - m_editor->update(); - })); - } - - auto& help_menu = menubar->add_menu("Help"); - help_menu.add_action(GUI::CommonActions::make_about_action("Hex Editor", GUI::Icon::default_icon("Hex Editor"), window())); - - GUI::Application::the()->set_menubar(move(menubar)); - - m_editor->set_focus(true); -} - -HexEditorWidget::~HexEditorWidget() -{ -} - -void HexEditorWidget::set_path(const LexicalPath& lexical_path) -{ - m_path = lexical_path.string(); - m_name = lexical_path.title(); - m_extension = lexical_path.extension(); - update_title(); -} - -void HexEditorWidget::update_title() -{ - StringBuilder builder; - builder.append(m_path); - if (m_document_dirty) - builder.append(" (*)"); - builder.append(" - Hex Editor"); - window()->set_title(builder.to_string()); -} - -void HexEditorWidget::open_file(const String& path) -{ - auto file = Core::File::construct(path); - if (!file->open(Core::IODevice::ReadOnly)) { - GUI::MessageBox::show(window(), String::formatted("Opening \"{}\" failed: {}", path, strerror(errno)), "Error", GUI::MessageBox::Type::Error); - return; - } - - m_document_dirty = false; - m_editor->set_buffer(file->read_all()); // FIXME: On really huge files, this is never going to work. Should really create a framework to fetch data from the file on-demand. - set_path(LexicalPath(path)); -} - -bool HexEditorWidget::request_close() -{ - if (!m_document_dirty) - return true; - auto result = GUI::MessageBox::show(window(), "The file has been modified. Quit without saving?", "Quit without saving?", GUI::MessageBox::Type::Warning, GUI::MessageBox::InputType::OKCancel); - return result == GUI::MessageBox::ExecOK; -} diff --git a/Applications/HexEditor/HexEditorWidget.h b/Applications/HexEditor/HexEditorWidget.h deleted file mode 100644 index 55152518ab..0000000000 --- a/Applications/HexEditor/HexEditorWidget.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "HexEditor.h" -#include <AK/Function.h> -#include <AK/LexicalPath.h> -#include <LibGUI/Application.h> -#include <LibGUI/TextEditor.h> -#include <LibGUI/Widget.h> -#include <LibGUI/Window.h> - -class HexEditor; - -class HexEditorWidget final : public GUI::Widget { - C_OBJECT(HexEditorWidget) -public: - virtual ~HexEditorWidget() override; - void open_file(const String& path); - bool request_close(); - -private: - HexEditorWidget(); - void set_path(const LexicalPath& file); - void update_title(); - - RefPtr<HexEditor> m_editor; - String m_path; - String m_name; - String m_extension; - RefPtr<GUI::Action> m_new_action; - RefPtr<GUI::Action> m_open_action; - RefPtr<GUI::Action> m_save_action; - RefPtr<GUI::Action> m_save_as_action; - RefPtr<GUI::Action> m_goto_decimal_offset_action; - RefPtr<GUI::Action> m_goto_hex_offset_action; - - RefPtr<GUI::StatusBar> m_statusbar; - - bool m_document_dirty { false }; -}; diff --git a/Applications/HexEditor/main.cpp b/Applications/HexEditor/main.cpp deleted file mode 100644 index 65e358726e..0000000000 --- a/Applications/HexEditor/main.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "HexEditorWidget.h" -#include <LibGUI/Icon.h> -#include <LibGfx/Bitmap.h> -#include <stdio.h> - -int main(int argc, char** argv) -{ - if (pledge("stdio shared_buffer accept rpath unix cpath wpath fattr thread", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto app = GUI::Application::construct(argc, argv); - - if (pledge("stdio shared_buffer accept rpath cpath wpath thread", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto app_icon = GUI::Icon::default_icon("app-hexeditor"); - - auto window = GUI::Window::construct(); - window->set_title("Hex Editor"); - window->resize(640, 400); - - auto& hex_editor_widget = window->set_main_widget<HexEditorWidget>(); - - window->on_close_request = [&]() -> GUI::Window::CloseRequestDecision { - if (hex_editor_widget.request_close()) - return GUI::Window::CloseRequestDecision::Close; - return GUI::Window::CloseRequestDecision::StayOpen; - }; - - window->show(); - window->set_icon(app_icon.bitmap_for_size(16)); - - if (argc >= 2) - hex_editor_widget.open_file(argv[1]); - - return app->exec(); -} diff --git a/Applications/IRCClient/CMakeLists.txt b/Applications/IRCClient/CMakeLists.txt deleted file mode 100644 index 3926bbd5dc..0000000000 --- a/Applications/IRCClient/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -set(SOURCES - IRCAppWindow.cpp - IRCChannel.cpp - IRCChannelMemberListModel.cpp - IRCClient.cpp - IRCLogBuffer.cpp - IRCQuery.cpp - IRCWindow.cpp - IRCWindowListModel.cpp - main.cpp -) - -serenity_app(IRCClient ICON app-irc-client) -target_link_libraries(IRCClient LibWeb LibGUI) diff --git a/Applications/IRCClient/IRCAppWindow.cpp b/Applications/IRCClient/IRCAppWindow.cpp deleted file mode 100644 index 8d4f931025..0000000000 --- a/Applications/IRCClient/IRCAppWindow.cpp +++ /dev/null @@ -1,375 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "IRCAppWindow.h" -#include "IRCChannel.h" -#include "IRCWindow.h" -#include "IRCWindowListModel.h" -#include <LibGUI/Action.h> -#include <LibGUI/Application.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/InputBox.h> -#include <LibGUI/Menu.h> -#include <LibGUI/MenuBar.h> -#include <LibGUI/Splitter.h> -#include <LibGUI/StackWidget.h> -#include <LibGUI/TableView.h> -#include <LibGUI/ToolBar.h> -#include <LibGUI/ToolBarContainer.h> - -static IRCAppWindow* s_the; - -IRCAppWindow& IRCAppWindow::the() -{ - return *s_the; -} - -IRCAppWindow::IRCAppWindow(String server, int port) - : m_client(IRCClient::construct(server, port)) -{ - ASSERT(!s_the); - s_the = this; - - set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-irc-client.png")); - - update_title(); - resize(600, 400); - setup_actions(); - setup_menus(); - setup_widgets(); - - setup_client(); -} - -IRCAppWindow::~IRCAppWindow() -{ -} - -void IRCAppWindow::update_title() -{ - set_title(String::formatted("{}@{}:{} - IRC Client", m_client->nickname(), m_client->hostname(), m_client->port())); -} - -void IRCAppWindow::setup_client() -{ - m_client->aid_create_window = [this](void* owner, IRCWindow::Type type, const String& name) { - return create_window(owner, type, name); - }; - m_client->aid_get_active_window = [this] { - return static_cast<IRCWindow*>(m_container->active_widget()); - }; - m_client->aid_update_window_list = [this] { - m_window_list->model()->update(); - }; - m_client->on_nickname_changed = [this](const String&) { - update_title(); - }; - m_client->on_part_from_channel = [this](auto&) { - update_gui_actions(); - }; - - if (m_client->hostname().is_empty()) { - String value; - if (GUI::InputBox::show(value, this, "Enter server:", "Connect to server") == GUI::InputBox::ExecCancel) - ::exit(0); - - m_client->set_server(value, 6667); - } - update_title(); - bool success = m_client->connect(); - ASSERT(success); -} - -void IRCAppWindow::setup_actions() -{ - m_join_action = GUI::Action::create("Join channel", { Mod_Ctrl, Key_J }, Gfx::Bitmap::load_from_file("/res/icons/16x16/irc-join.png"), [&](auto&) { - String value; - if (GUI::InputBox::show(value, this, "Enter channel name:", "Join channel") == GUI::InputBox::ExecOK && !value.is_empty()) - m_client->handle_join_action(value); - }); - - m_list_channels_action = GUI::Action::create("List channels", Gfx::Bitmap::load_from_file("/res/icons/16x16/irc-list.png"), [&](auto&) { - m_client->handle_list_channels_action(); - }); - - m_part_action = GUI::Action::create("Part from channel", { Mod_Ctrl, Key_P }, Gfx::Bitmap::load_from_file("/res/icons/16x16/irc-part.png"), [this](auto&) { - auto* window = m_client->current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) { - return; - } - m_client->handle_part_action(window->channel().name()); - }); - - m_whois_action = GUI::Action::create("Whois user", Gfx::Bitmap::load_from_file("/res/icons/16x16/irc-whois.png"), [&](auto&) { - String value; - if (GUI::InputBox::show(value, this, "Enter nickname:", "IRC WHOIS lookup") == GUI::InputBox::ExecOK && !value.is_empty()) - m_client->handle_whois_action(value); - }); - - m_open_query_action = GUI::Action::create("Open query", { Mod_Ctrl, Key_O }, Gfx::Bitmap::load_from_file("/res/icons/16x16/irc-open-query.png"), [&](auto&) { - String value; - if (GUI::InputBox::show(value, this, "Enter nickname:", "Open IRC query with...") == GUI::InputBox::ExecOK && !value.is_empty()) - m_client->handle_open_query_action(value); - }); - - m_close_query_action = GUI::Action::create("Close query", { Mod_Ctrl, Key_D }, Gfx::Bitmap::load_from_file("/res/icons/16x16/irc-close-query.png"), [](auto&) { - outln("FIXME: Implement close-query action"); - }); - - m_change_nick_action = GUI::Action::create("Change nickname", Gfx::Bitmap::load_from_file("/res/icons/16x16/irc-nick.png"), [this](auto&) { - String value; - if (GUI::InputBox::show(value, this, "Enter nickname:", "Change nickname") == GUI::InputBox::ExecOK && !value.is_empty()) - m_client->handle_change_nick_action(value); - }); - - m_change_topic_action = GUI::Action::create("Change topic", Gfx::Bitmap::load_from_file("/res/icons/16x16/irc-topic.png"), [this](auto&) { - auto* window = m_client->current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) { - return; - } - String value; - if (GUI::InputBox::show(value, this, "Enter topic:", "Change topic") == GUI::InputBox::ExecOK && !value.is_empty()) - m_client->handle_change_topic_action(window->channel().name(), value); - }); - - m_invite_user_action = GUI::Action::create("Invite user", Gfx::Bitmap::load_from_file("/res/icons/16x16/irc-invite.png"), [this](auto&) { - auto* window = m_client->current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) { - return; - } - String value; - if (GUI::InputBox::show(value, this, "Enter nick:", "Invite user") == GUI::InputBox::ExecOK && !value.is_empty()) - m_client->handle_invite_user_action(window->channel().name(), value); - }); - - m_banlist_action = GUI::Action::create("Ban list", [this](auto&) { - auto* window = m_client->current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) { - return; - } - m_client->handle_banlist_action(window->channel().name()); - }); - - m_voice_user_action = GUI::Action::create("Voice user", [this](auto&) { - auto* window = m_client->current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) { - return; - } - String value; - if (GUI::InputBox::show(value, this, "Enter nick:", "Voice user") == GUI::InputBox::ExecOK && !value.is_empty()) - m_client->handle_voice_user_action(window->channel().name(), value); - }); - - m_devoice_user_action = GUI::Action::create("DeVoice user", [this](auto&) { - auto* window = m_client->current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) { - return; - } - String value; - if (GUI::InputBox::show(value, this, "Enter nick:", "DeVoice user") == GUI::InputBox::ExecOK && !value.is_empty()) - m_client->handle_devoice_user_action(window->channel().name(), value); - }); - - m_hop_user_action = GUI::Action::create("Hop user", [this](auto&) { - auto* window = m_client->current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) { - return; - } - String value; - if (GUI::InputBox::show(value, this, "Enter nick:", "Hop user") == GUI::InputBox::ExecOK && !value.is_empty()) - m_client->handle_hop_user_action(window->channel().name(), value); - }); - - m_dehop_user_action = GUI::Action::create("DeHop user", [this](auto&) { - auto* window = m_client->current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) { - return; - } - String value; - if (GUI::InputBox::show(value, this, "Enter nick:", "DeHop user") == GUI::InputBox::ExecOK && !value.is_empty()) - m_client->handle_dehop_user_action(window->channel().name(), value); - }); - - m_op_user_action = GUI::Action::create("Op user", [this](auto&) { - auto* window = m_client->current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) { - return; - } - String value; - if (GUI::InputBox::show(value, this, "Enter nick:", "Op user") == GUI::InputBox::ExecOK && !value.is_empty()) - m_client->handle_op_user_action(window->channel().name(), value); - }); - - m_deop_user_action = GUI::Action::create("DeOp user", [this](auto&) { - auto* window = m_client->current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) { - return; - } - String value; - if (GUI::InputBox::show(value, this, "Enter nick:", "DeOp user") == GUI::InputBox::ExecOK && !value.is_empty()) - m_client->handle_deop_user_action(window->channel().name(), value); - }); - - m_kick_user_action = GUI::Action::create("Kick user", [this](auto&) { - auto* window = m_client->current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) { - return; - } - String nick_value; - if (GUI::InputBox::show(nick_value, this, "Enter nick:", "Kick user") != GUI::InputBox::ExecOK || nick_value.is_empty()) - return; - String reason_value; - if (GUI::InputBox::show(reason_value, this, "Enter reason:", "Reason") == GUI::InputBox::ExecOK) - m_client->handle_kick_user_action(window->channel().name(), nick_value, reason_value.characters()); - }); - - m_cycle_channel_action = GUI::Action::create("Cycle channel", [this](auto&) { - auto* window = m_client->current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) { - return; - } - m_client->handle_cycle_channel_action(window->channel().name()); - }); -} - -void IRCAppWindow::setup_menus() -{ - auto menubar = GUI::MenuBar::construct(); - auto& app_menu = menubar->add_menu("IRC Client"); - app_menu.add_action(GUI::CommonActions::make_quit_action([](auto&) { - dbgln("Terminal: Quit menu activated!"); - GUI::Application::the()->quit(); - return; - })); - - auto& server_menu = menubar->add_menu("Server"); - server_menu.add_action(*m_change_nick_action); - server_menu.add_separator(); - server_menu.add_action(*m_join_action); - server_menu.add_action(*m_list_channels_action); - server_menu.add_separator(); - server_menu.add_action(*m_whois_action); - server_menu.add_action(*m_open_query_action); - server_menu.add_action(*m_close_query_action); - - auto& channel_menu = menubar->add_menu("Channel"); - channel_menu.add_action(*m_change_topic_action); - channel_menu.add_action(*m_invite_user_action); - channel_menu.add_action(*m_banlist_action); - - auto& channel_control_menu = channel_menu.add_submenu("Control"); - channel_control_menu.add_action(*m_voice_user_action); - channel_control_menu.add_action(*m_devoice_user_action); - channel_control_menu.add_action(*m_hop_user_action); - channel_control_menu.add_action(*m_dehop_user_action); - channel_control_menu.add_action(*m_op_user_action); - channel_control_menu.add_action(*m_deop_user_action); - channel_control_menu.add_separator(); - channel_control_menu.add_action(*m_kick_user_action); - - channel_menu.add_separator(); - channel_menu.add_action(*m_cycle_channel_action); - channel_menu.add_action(*m_part_action); - - auto& help_menu = menubar->add_menu("Help"); - help_menu.add_action(GUI::CommonActions::make_about_action("IRC Client", GUI::Icon::default_icon("app-irc-client"), this)); - - GUI::Application::the()->set_menubar(move(menubar)); -} - -void IRCAppWindow::setup_widgets() -{ - auto& widget = set_main_widget<GUI::Widget>(); - widget.set_fill_with_background_color(true); - widget.set_layout<GUI::VerticalBoxLayout>(); - widget.layout()->set_spacing(0); - - auto& toolbar_container = widget.add<GUI::ToolBarContainer>(); - auto& toolbar = toolbar_container.add<GUI::ToolBar>(); - toolbar.set_has_frame(false); - toolbar.add_action(*m_change_nick_action); - toolbar.add_separator(); - toolbar.add_action(*m_join_action); - toolbar.add_action(*m_part_action); - toolbar.add_separator(); - toolbar.add_action(*m_whois_action); - toolbar.add_action(*m_open_query_action); - toolbar.add_action(*m_close_query_action); - - auto& outer_container = widget.add<GUI::Widget>(); - outer_container.set_layout<GUI::VerticalBoxLayout>(); - outer_container.layout()->set_margins({ 2, 0, 2, 2 }); - - auto& horizontal_container = outer_container.add<GUI::HorizontalSplitter>(); - - m_window_list = horizontal_container.add<GUI::TableView>(); - m_window_list->set_column_headers_visible(false); - m_window_list->set_alternating_row_colors(false); - m_window_list->set_model(m_client->client_window_list_model()); - m_window_list->set_activates_on_selection(true); - m_window_list->set_fixed_width(100); - m_window_list->on_activation = [this](auto& index) { - set_active_window(m_client->window_at(index.row())); - }; - - m_container = horizontal_container.add<GUI::StackWidget>(); - m_container->on_active_widget_change = [this](auto*) { - update_gui_actions(); - }; - - create_window(&m_client, IRCWindow::Server, "Server"); -} - -void IRCAppWindow::set_active_window(IRCWindow& window) -{ - m_container->set_active_widget(&window); - window.clear_unread_count(); - auto index = m_window_list->model()->index(m_client->window_index(window)); - m_window_list->selection().set(index); -} - -void IRCAppWindow::update_gui_actions() -{ - auto* window = static_cast<IRCWindow*>(m_container->active_widget()); - bool is_open_channel = window && window->type() == IRCWindow::Type::Channel && window->channel().is_open(); - m_change_topic_action->set_enabled(is_open_channel); - m_invite_user_action->set_enabled(is_open_channel); - m_banlist_action->set_enabled(is_open_channel); - m_voice_user_action->set_enabled(is_open_channel); - m_devoice_user_action->set_enabled(is_open_channel); - m_hop_user_action->set_enabled(is_open_channel); - m_dehop_user_action->set_enabled(is_open_channel); - m_op_user_action->set_enabled(is_open_channel); - m_deop_user_action->set_enabled(is_open_channel); - m_kick_user_action->set_enabled(is_open_channel); - m_cycle_channel_action->set_enabled(is_open_channel); - m_part_action->set_enabled(is_open_channel); -} - -NonnullRefPtr<IRCWindow> IRCAppWindow::create_window(void* owner, IRCWindow::Type type, const String& name) -{ - return m_container->add<IRCWindow>(m_client, owner, type, name); -} diff --git a/Applications/IRCClient/IRCAppWindow.h b/Applications/IRCClient/IRCAppWindow.h deleted file mode 100644 index 10f0f5a4ee..0000000000 --- a/Applications/IRCClient/IRCAppWindow.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "IRCClient.h" -#include "IRCWindow.h" -#include <LibGUI/Widget.h> -#include <LibGUI/Window.h> - -class IRCAppWindow : public GUI::Window { - C_OBJECT(IRCAppWindow); - -public: - virtual ~IRCAppWindow() override; - - static IRCAppWindow& the(); - - void set_active_window(IRCWindow&); - -private: - IRCAppWindow(String server, int port); - - void setup_client(); - void setup_actions(); - void setup_menus(); - void setup_widgets(); - void update_title(); - void update_gui_actions(); - - NonnullRefPtr<IRCWindow> create_window(void* owner, IRCWindow::Type, const String& name); - NonnullRefPtr<IRCClient> m_client; - RefPtr<GUI::StackWidget> m_container; - RefPtr<GUI::TableView> m_window_list; - RefPtr<GUI::Action> m_join_action; - RefPtr<GUI::Action> m_list_channels_action; - RefPtr<GUI::Action> m_part_action; - RefPtr<GUI::Action> m_cycle_channel_action; - RefPtr<GUI::Action> m_whois_action; - RefPtr<GUI::Action> m_open_query_action; - RefPtr<GUI::Action> m_close_query_action; - RefPtr<GUI::Action> m_change_nick_action; - RefPtr<GUI::Action> m_change_topic_action; - RefPtr<GUI::Action> m_invite_user_action; - RefPtr<GUI::Action> m_banlist_action; - RefPtr<GUI::Action> m_voice_user_action; - RefPtr<GUI::Action> m_devoice_user_action; - RefPtr<GUI::Action> m_hop_user_action; - RefPtr<GUI::Action> m_dehop_user_action; - RefPtr<GUI::Action> m_op_user_action; - RefPtr<GUI::Action> m_deop_user_action; - RefPtr<GUI::Action> m_kick_user_action; -}; diff --git a/Applications/IRCClient/IRCChannel.cpp b/Applications/IRCClient/IRCChannel.cpp deleted file mode 100644 index 81d9173e31..0000000000 --- a/Applications/IRCClient/IRCChannel.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "IRCChannel.h" -#include "IRCChannelMemberListModel.h" -#include "IRCClient.h" -#include <stdio.h> - -IRCChannel::IRCChannel(IRCClient& client, const String& name) - : m_client(client) - , m_name(name) - , m_log(IRCLogBuffer::create()) - , m_member_model(IRCChannelMemberListModel::create(*this)) -{ - m_window = m_client.aid_create_window(this, IRCWindow::Channel, m_name); - m_window->set_log_buffer(*m_log); -} - -IRCChannel::~IRCChannel() -{ -} - -NonnullRefPtr<IRCChannel> IRCChannel::create(IRCClient& client, const String& name) -{ - return adopt(*new IRCChannel(client, name)); -} - -void IRCChannel::add_member(const String& name, char prefix) -{ - for (auto& member : m_members) { - if (member.name == name) { - member.prefix = prefix; - return; - } - } - m_members.append({ name, prefix }); - m_member_model->update(); -} - -void IRCChannel::remove_member(const String& name) -{ - m_members.remove_first_matching([&](auto& member) { return name == member.name; }); -} - -void IRCChannel::add_message(char prefix, const String& name, const String& text, Color color) -{ - log().add_message(prefix, name, text, color); - window().did_add_message(name, text); -} - -void IRCChannel::add_message(const String& text, Color color) -{ - log().add_message(text, color); - window().did_add_message(); -} - -void IRCChannel::say(const String& text) -{ - m_client.send_privmsg(m_name, text); - add_message(' ', m_client.nickname(), text); -} - -void IRCChannel::handle_join(const String& nick, const String& hostmask) -{ - if (nick == m_client.nickname()) { - m_open = true; - return; - } - add_member(nick, (char)0); - m_member_model->update(); - if (m_client.show_join_part_messages()) - add_message(String::formatted("*** {} [{}] has joined {}", nick, hostmask, m_name), Color::MidGreen); -} - -void IRCChannel::handle_part(const String& nick, const String& hostmask) -{ - if (nick == m_client.nickname()) { - m_open = false; - m_members.clear(); - m_client.did_part_from_channel({}, *this); - } else { - remove_member(nick); - } - m_member_model->update(); - if (m_client.show_join_part_messages()) - add_message(String::formatted("*** {} [{}] has parted from {}", nick, hostmask, m_name), Color::MidGreen); -} - -void IRCChannel::handle_quit(const String& nick, const String& hostmask, const String& message) -{ - if (nick == m_client.nickname()) { - m_open = false; - m_members.clear(); - m_client.did_part_from_channel({}, *this); - } else { - remove_member(nick); - } - m_member_model->update(); - add_message(String::formatted("*** {} [{}] has quit ({})", nick, hostmask, message), Color::MidGreen); -} - -void IRCChannel::handle_topic(const String& nick, const String& topic) -{ - if (nick.is_null()) - add_message(String::formatted("*** Topic is \"{}\"", topic), Color::MidBlue); - else - add_message(String::formatted("*** {} set topic to \"{}\"", nick, topic), Color::MidBlue); -} - -void IRCChannel::notify_nick_changed(const String& old_nick, const String& new_nick) -{ - for (auto& member : m_members) { - if (member.name == old_nick) { - member.name = new_nick; - m_member_model->update(); - if (m_client.show_nick_change_messages()) - add_message(String::formatted("~ {} changed nickname to {}", old_nick, new_nick), Color::MidMagenta); - return; - } - } -} diff --git a/Applications/IRCClient/IRCChannel.h b/Applications/IRCClient/IRCChannel.h deleted file mode 100644 index 1d0eb8657b..0000000000 --- a/Applications/IRCClient/IRCChannel.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "IRCLogBuffer.h" -#include <AK/RefCounted.h> -#include <AK/RefPtr.h> -#include <AK/String.h> -#include <AK/Vector.h> - -class IRCClient; -class IRCChannelMemberListModel; -class IRCWindow; - -class IRCChannel : public RefCounted<IRCChannel> { -public: - static NonnullRefPtr<IRCChannel> create(IRCClient&, const String&); - ~IRCChannel(); - - bool is_open() const { return m_open; } - void set_open(bool b) { m_open = b; } - - String name() const { return m_name; } - - void add_member(const String& name, char prefix); - void remove_member(const String& name); - - void add_message(char prefix, const String& name, const String& text, Color = Color::Black); - void add_message(const String& text, Color = Color::Black); - - void say(const String&); - - const IRCLogBuffer& log() const { return *m_log; } - IRCLogBuffer& log() { return *m_log; } - - IRCChannelMemberListModel* member_model() { return m_member_model.ptr(); } - const IRCChannelMemberListModel* member_model() const { return m_member_model.ptr(); } - - int member_count() const { return m_members.size(); } - String member_at(int i) { return m_members[i].name; } - - void handle_join(const String& nick, const String& hostmask); - void handle_part(const String& nick, const String& hostmask); - void handle_quit(const String& nick, const String& hostmask, const String& message); - void handle_topic(const String& nick, const String& topic); - - IRCWindow& window() { return *m_window; } - const IRCWindow& window() const { return *m_window; } - - String topic() const { return m_topic; } - - void notify_nick_changed(const String& old_nick, const String& new_nick); - -private: - IRCChannel(IRCClient&, const String&); - - IRCClient& m_client; - String m_name; - String m_topic; - struct Member { - String name; - char prefix { 0 }; - }; - Vector<Member> m_members; - bool m_open { false }; - - NonnullRefPtr<IRCLogBuffer> m_log; - NonnullRefPtr<IRCChannelMemberListModel> m_member_model; - IRCWindow* m_window { nullptr }; -}; diff --git a/Applications/IRCClient/IRCChannelMemberListModel.cpp b/Applications/IRCClient/IRCChannelMemberListModel.cpp deleted file mode 100644 index 22cdcd710f..0000000000 --- a/Applications/IRCClient/IRCChannelMemberListModel.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "IRCChannelMemberListModel.h" -#include "IRCChannel.h" -#include <stdio.h> -#include <time.h> - -IRCChannelMemberListModel::IRCChannelMemberListModel(IRCChannel& channel) - : m_channel(channel) -{ -} - -IRCChannelMemberListModel::~IRCChannelMemberListModel() -{ -} - -int IRCChannelMemberListModel::row_count(const GUI::ModelIndex&) const -{ - return m_channel.member_count(); -} - -int IRCChannelMemberListModel::column_count(const GUI::ModelIndex&) const -{ - return 1; -} - -String IRCChannelMemberListModel::column_name(int column) const -{ - switch (column) { - case Column::Name: - return "Name"; - } - ASSERT_NOT_REACHED(); -} - -GUI::Variant IRCChannelMemberListModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const -{ - if (role == GUI::ModelRole::TextAlignment) - return Gfx::TextAlignment::CenterLeft; - if (role == GUI::ModelRole::Display) { - switch (index.column()) { - case Column::Name: - return m_channel.member_at(index.row()); - } - } - return {}; -} - -void IRCChannelMemberListModel::update() -{ - did_update(); -} - -String IRCChannelMemberListModel::nick_at(const GUI::ModelIndex& index) const -{ - return data(index, GUI::ModelRole::Display).to_string(); -} diff --git a/Applications/IRCClient/IRCChannelMemberListModel.h b/Applications/IRCClient/IRCChannelMemberListModel.h deleted file mode 100644 index 99e4128fc6..0000000000 --- a/Applications/IRCClient/IRCChannelMemberListModel.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 <AK/Function.h> -#include <LibGUI/Model.h> - -class IRCChannel; - -class IRCChannelMemberListModel final : public GUI::Model { -public: - enum Column { - Name - }; - static NonnullRefPtr<IRCChannelMemberListModel> create(IRCChannel& channel) { return adopt(*new IRCChannelMemberListModel(channel)); } - virtual ~IRCChannelMemberListModel() override; - - virtual int row_count(const GUI::ModelIndex&) const override; - virtual int column_count(const GUI::ModelIndex&) const override; - virtual String column_name(int column) const override; - virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override; - virtual void update() override; - virtual String nick_at(const GUI::ModelIndex& index) const; - -private: - explicit IRCChannelMemberListModel(IRCChannel&); - - IRCChannel& m_channel; -}; diff --git a/Applications/IRCClient/IRCClient.cpp b/Applications/IRCClient/IRCClient.cpp deleted file mode 100644 index c0da630b8f..0000000000 --- a/Applications/IRCClient/IRCClient.cpp +++ /dev/null @@ -1,1188 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "IRCClient.h" -#include "IRCAppWindow.h" -#include "IRCChannel.h" -#include "IRCLogBuffer.h" -#include "IRCQuery.h" -#include "IRCWindow.h" -#include "IRCWindowListModel.h" -#include <AK/QuickSort.h> -#include <AK/StringBuilder.h> -#include <LibCore/DateTime.h> -#include <LibCore/Notifier.h> -#include <pwd.h> -#include <stdio.h> -#include <strings.h> - -#ifndef IRC_DEBUG -# define IRC_DEBUG -#endif - -enum IRCNumeric { - RPL_WELCOME = 1, - RPL_WHOISUSER = 311, - RPL_WHOISSERVER = 312, - RPL_WHOISOPERATOR = 313, - RPL_ENDOFWHO = 315, - RPL_WHOISIDLE = 317, - RPL_ENDOFWHOIS = 318, - RPL_WHOISCHANNELS = 319, - RPL_TOPIC = 332, - RPL_TOPICWHOTIME = 333, - RPL_NAMREPLY = 353, - RPL_ENDOFNAMES = 366, - RPL_BANLIST = 367, - RPL_ENDOFBANLIST = 368, - RPL_ENDOFWHOWAS = 369, - RPL_ENDOFMOTD = 376, - ERR_NOSUCHNICK = 401, - ERR_UNKNOWNCOMMAND = 421, - ERR_NICKNAMEINUSE = 433, -}; - -IRCClient::IRCClient(String server, int port) - : m_nickname("seren1ty") - , m_client_window_list_model(IRCWindowListModel::create(*this)) - , m_log(IRCLogBuffer::create()) - , m_config(Core::ConfigFile::get_for_app("IRCClient")) -{ - struct passwd* user_pw = getpwuid(getuid()); - m_socket = Core::TCPSocket::construct(this); - m_nickname = m_config->read_entry("User", "Nickname", String::formatted("{}_seren1ty", user_pw->pw_name)); - - if (server.is_empty()) { - m_hostname = m_config->read_entry("Connection", "Server", ""); - m_port = m_config->read_num_entry("Connection", "Port", 6667); - } else { - m_hostname = server; - m_port = port ? port : 6667; - } - - m_show_join_part_messages = m_config->read_bool_entry("Messaging", "ShowJoinPartMessages", 1); - m_show_nick_change_messages = m_config->read_bool_entry("Messaging", "ShowNickChangeMessages", 1); - - m_notify_on_message = m_config->read_bool_entry("Notifications", "NotifyOnMessage", 1); - m_notify_on_mention = m_config->read_bool_entry("Notifications", "NotifyOnMention", 1); - - m_ctcp_version_reply = m_config->read_entry("CTCP", "VersionReply", "IRC Client [x86] / Serenity OS"); - m_ctcp_userinfo_reply = m_config->read_entry("CTCP", "UserInfoReply", user_pw->pw_name); - m_ctcp_finger_reply = m_config->read_entry("CTCP", "FingerReply", user_pw->pw_name); -} - -IRCClient::~IRCClient() -{ -} - -void IRCClient::set_server(const String& hostname, int port) -{ - m_hostname = hostname; - m_port = port; - m_config->write_entry("Connection", "Server", hostname); - m_config->write_num_entry("Connection", "Port", port); - m_config->sync(); -} - -void IRCClient::on_socket_connected() -{ - m_notifier = Core::Notifier::construct(m_socket->fd(), Core::Notifier::Read); - m_notifier->on_ready_to_read = [this] { receive_from_server(); }; - - send_user(); - send_nick(); -} - -bool IRCClient::connect() -{ - if (m_socket->is_connected()) - ASSERT_NOT_REACHED(); - - m_socket->on_connected = [this] { on_socket_connected(); }; - - return m_socket->connect(m_hostname, m_port); -} - -void IRCClient::receive_from_server() -{ - while (m_socket->can_read_line()) { - auto line = m_socket->read_line(); - if (line.is_null()) { - if (!m_socket->is_connected()) { - outln("IRCClient: Connection closed!"); - exit(1); - } - ASSERT_NOT_REACHED(); - } - process_line(line); - } -} - -void IRCClient::process_line(const String& line) -{ - Message msg; - Vector<char, 32> prefix; - Vector<char, 32> command; - Vector<char, 256> current_parameter; - enum { - Start, - InPrefix, - InCommand, - InStartOfParameter, - InParameter, - InTrailingParameter, - } state - = Start; - - for (size_t i = 0; i < line.length(); ++i) { - char ch = line[i]; - if (ch == '\r') - continue; - if (ch == '\n') - break; - switch (state) { - case Start: - if (ch == ':') { - state = InPrefix; - continue; - } - state = InCommand; - [[fallthrough]]; - case InCommand: - if (ch == ' ') { - state = InStartOfParameter; - continue; - } - command.append(ch); - continue; - case InPrefix: - if (ch == ' ') { - state = InCommand; - continue; - } - prefix.append(ch); - continue; - case InStartOfParameter: - if (ch == ':') { - state = InTrailingParameter; - continue; - } - state = InParameter; - [[fallthrough]]; - case InParameter: - if (ch == ' ') { - if (!current_parameter.is_empty()) - msg.arguments.append(String(current_parameter.data(), current_parameter.size())); - current_parameter.clear_with_capacity(); - state = InStartOfParameter; - continue; - } - current_parameter.append(ch); - continue; - case InTrailingParameter: - current_parameter.append(ch); - continue; - } - } - if (!current_parameter.is_empty()) - msg.arguments.append(String::copy(current_parameter)); - msg.prefix = String::copy(prefix); - msg.command = String::copy(command); - handle(msg); -} - -void IRCClient::send(const String& text) -{ - if (!m_socket->send(text.bytes())) { - perror("send"); - exit(1); - } -} - -void IRCClient::send_user() -{ - send(String::formatted("USER {} 0 * :{}\r\n", m_nickname, m_nickname)); -} - -void IRCClient::send_nick() -{ - send(String::formatted("NICK {}\r\n", m_nickname)); -} - -void IRCClient::send_pong(const String& server) -{ - send(String::formatted("PONG {}\r\n", server)); - sleep(1); -} - -void IRCClient::join_channel(const String& channel_name) -{ - send(String::formatted("JOIN {}\r\n", channel_name)); -} - -void IRCClient::part_channel(const String& channel_name) -{ - send(String::formatted("PART {}\r\n", channel_name)); -} - -void IRCClient::send_whois(const String& nick) -{ - send(String::formatted("WHOIS {}\r\n", nick)); -} - -void IRCClient::handle(const Message& msg) -{ -#ifdef IRC_DEBUG - outln("IRCClient::execute: prefix='{}', command='{}', arguments={}", - msg.prefix, - msg.command, - msg.arguments.size()); - - size_t index = 0; - for (auto& arg : msg.arguments) - outln(" [{}]: {}", index++, arg); -#endif - - auto numeric = msg.command.to_uint(); - - if (numeric.has_value()) { - switch (numeric.value()) { - case RPL_WELCOME: - return handle_rpl_welcome(msg); - case RPL_WHOISCHANNELS: - return handle_rpl_whoischannels(msg); - case RPL_ENDOFWHO: - return handle_rpl_endofwho(msg); - case RPL_ENDOFWHOIS: - return handle_rpl_endofwhois(msg); - case RPL_ENDOFWHOWAS: - return handle_rpl_endofwhowas(msg); - case RPL_ENDOFMOTD: - return handle_rpl_endofmotd(msg); - case RPL_WHOISOPERATOR: - return handle_rpl_whoisoperator(msg); - case RPL_WHOISSERVER: - return handle_rpl_whoisserver(msg); - case RPL_WHOISUSER: - return handle_rpl_whoisuser(msg); - case RPL_WHOISIDLE: - return handle_rpl_whoisidle(msg); - case RPL_TOPICWHOTIME: - return handle_rpl_topicwhotime(msg); - case RPL_TOPIC: - return handle_rpl_topic(msg); - case RPL_NAMREPLY: - return handle_rpl_namreply(msg); - case RPL_ENDOFNAMES: - return handle_rpl_endofnames(msg); - case RPL_BANLIST: - return handle_rpl_banlist(msg); - case RPL_ENDOFBANLIST: - return handle_rpl_endofbanlist(msg); - case ERR_NOSUCHNICK: - return handle_err_nosuchnick(msg); - case ERR_UNKNOWNCOMMAND: - return handle_err_unknowncommand(msg); - case ERR_NICKNAMEINUSE: - return handle_err_nicknameinuse(msg); - } - } - - if (msg.command == "PING") - return handle_ping(msg); - - if (msg.command == "JOIN") - return handle_join(msg); - - if (msg.command == "PART") - return handle_part(msg); - - if (msg.command == "QUIT") - return handle_quit(msg); - - if (msg.command == "TOPIC") - return handle_topic(msg); - - if (msg.command == "PRIVMSG") - return handle_privmsg_or_notice(msg, PrivmsgOrNotice::Privmsg); - - if (msg.command == "NOTICE") - return handle_privmsg_or_notice(msg, PrivmsgOrNotice::Notice); - - if (msg.command == "NICK") - return handle_nick(msg); - - if (msg.arguments.size() >= 2) - add_server_message(String::formatted("[{}] {}", msg.command, msg.arguments[1])); -} - -void IRCClient::add_server_message(const String& text, Color color) -{ - m_log->add_message(0, "", text, color); - m_server_subwindow->did_add_message(); -} - -void IRCClient::send_topic(const String& channel_name, const String& text) -{ - send(String::formatted("TOPIC {} :{}\r\n", channel_name, text)); -} - -void IRCClient::send_invite(const String& channel_name, const String& nick) -{ - send(String::formatted("INVITE {} {}\r\n", nick, channel_name)); -} - -void IRCClient::send_banlist(const String& channel_name) -{ - send(String::formatted("MODE {} +b\r\n", channel_name)); -} - -void IRCClient::send_voice_user(const String& channel_name, const String& nick) -{ - send(String::formatted("MODE {} +v {}\r\n", channel_name, nick)); -} - -void IRCClient::send_devoice_user(const String& channel_name, const String& nick) -{ - send(String::formatted("MODE {} -v {}\r\n", channel_name, nick)); -} - -void IRCClient::send_hop_user(const String& channel_name, const String& nick) -{ - send(String::formatted("MODE {} +h {}\r\n", channel_name, nick)); -} - -void IRCClient::send_dehop_user(const String& channel_name, const String& nick) -{ - send(String::formatted("MODE {} -h {}\r\n", channel_name, nick)); -} - -void IRCClient::send_op_user(const String& channel_name, const String& nick) -{ - send(String::formatted("MODE {} +o {}\r\n", channel_name, nick)); -} - -void IRCClient::send_deop_user(const String& channel_name, const String& nick) -{ - send(String::formatted("MODE {} -o {}\r\n", channel_name, nick)); -} - -void IRCClient::send_kick(const String& channel_name, const String& nick, const String& comment) -{ - send(String::formatted("KICK {} {} :{}\r\n", channel_name, nick, comment)); -} - -void IRCClient::send_list() -{ - send("LIST\r\n"); -} - -void IRCClient::send_privmsg(const String& target, const String& text) -{ - send(String::formatted("PRIVMSG {} :{}\r\n", target, text)); -} - -void IRCClient::send_notice(const String& target, const String& text) -{ - send(String::formatted("NOTICE {} :{}\r\n", target, text)); -} - -void IRCClient::handle_user_input_in_channel(const String& channel_name, const String& input) -{ - if (input.is_empty()) - return; - if (input[0] == '/') - return handle_user_command(input); - ensure_channel(channel_name).say(input); -} - -void IRCClient::handle_user_input_in_query(const String& query_name, const String& input) -{ - if (input.is_empty()) - return; - if (input[0] == '/') - return handle_user_command(input); - ensure_query(query_name).say(input); -} - -void IRCClient::handle_user_input_in_server(const String& input) -{ - if (input.is_empty()) - return; - if (input[0] == '/') - return handle_user_command(input); -} - -String IRCClient::nick_without_prefix(const String& nick) -{ - assert(!nick.is_empty()); - if (IRCClient::is_nick_prefix(nick[0])) - return nick.substring(1, nick.length() - 1); - return nick; -} - -bool IRCClient::is_nick_prefix(char ch) -{ - switch (ch) { - case '@': - case '+': - case '~': - case '&': - case '%': - return true; - } - return false; -} - -bool IRCClient::is_channel_prefix(char ch) -{ - switch (ch) { - case '&': - case '#': - case '+': - case '!': - return true; - } - return false; -} - -static bool has_ctcp_payload(const StringView& string) -{ - return string.length() >= 2 && string[0] == 0x01 && string[string.length() - 1] == 0x01; -} - -void IRCClient::handle_privmsg_or_notice(const Message& msg, PrivmsgOrNotice type) -{ - if (msg.arguments.size() < 2) - return; - if (msg.prefix.is_empty()) - return; - auto parts = msg.prefix.split('!'); - auto sender_nick = parts[0]; - auto target = msg.arguments[0]; - - bool is_ctcp = has_ctcp_payload(msg.arguments[1]); - -#ifdef IRC_DEBUG - outln("handle_privmsg_or_notice: type='{}'{}, sender_nick='{}', target='{}'", - type == PrivmsgOrNotice::Privmsg ? "privmsg" : "notice", - is_ctcp ? " (ctcp)" : "", - sender_nick, - target); -#endif - - if (sender_nick.is_empty()) - return; - - char sender_prefix = 0; - if (is_nick_prefix(sender_nick[0])) { - sender_prefix = sender_nick[0]; - sender_nick = sender_nick.substring(1, sender_nick.length() - 1); - } - - String message_text = msg.arguments[1]; - auto message_color = Color::Black; - - bool insert_as_raw_message = false; - - if (is_ctcp) { - auto ctcp_payload = msg.arguments[1].substring_view(1, msg.arguments[1].length() - 2); - if (type == PrivmsgOrNotice::Privmsg) - handle_ctcp_request(sender_nick, ctcp_payload); - else - handle_ctcp_response(sender_nick, ctcp_payload); - - if (ctcp_payload.starts_with("ACTION")) { - insert_as_raw_message = true; - message_text = String::formatted("* {}{}", sender_nick, ctcp_payload.substring_view(6, ctcp_payload.length() - 6)); - message_color = Color::Magenta; - } else { - message_text = String::formatted("(CTCP) {}", ctcp_payload); - message_color = Color::Blue; - } - } - - { - auto it = m_channels.find(target); - if (it != m_channels.end()) { - if (insert_as_raw_message) - (*it).value->add_message(message_text, message_color); - else - (*it).value->add_message(sender_prefix, sender_nick, message_text, message_color); - return; - } - } - - // For NOTICE or CTCP messages, only put them in query if one already exists. - // Otherwise, put them in the server window. This seems to match other clients. - IRCQuery* query = nullptr; - if (is_ctcp || type == PrivmsgOrNotice::Notice) { - query = query_with_name(sender_nick); - } else { - query = &ensure_query(sender_nick); - } - if (query) { - if (insert_as_raw_message) - query->add_message(message_text, message_color); - else - query->add_message(sender_prefix, sender_nick, message_text, message_color); - } else { - add_server_message(String::formatted("<{}> {}", sender_nick, message_text), message_color); - } -} - -IRCQuery* IRCClient::query_with_name(const String& name) -{ - return const_cast<IRCQuery*>(m_queries.get(name).value_or(nullptr)); -} - -IRCQuery& IRCClient::ensure_query(const String& name) -{ - auto it = m_queries.find(name); - if (it != m_queries.end()) - return *(*it).value; - auto query = IRCQuery::create(*this, name); - auto& query_reference = *query; - m_queries.set(name, query); - return query_reference; -} - -IRCChannel& IRCClient::ensure_channel(const String& name) -{ - auto it = m_channels.find(name); - if (it != m_channels.end()) - return *(*it).value; - auto channel = IRCChannel::create(*this, name); - auto& channel_reference = *channel; - m_channels.set(name, channel); - return channel_reference; -} - -void IRCClient::handle_ping(const Message& msg) -{ - if (msg.arguments.size() < 1) - return; - m_log->add_message(0, "", "Ping? Pong!"); - send_pong(msg.arguments[0]); -} - -void IRCClient::handle_join(const Message& msg) -{ - if (msg.arguments.size() != 1) - return; - auto prefix_parts = msg.prefix.split('!'); - if (prefix_parts.size() < 1) - return; - auto nick = prefix_parts[0]; - auto& channel_name = msg.arguments[0]; - ensure_channel(channel_name).handle_join(nick, msg.prefix); -} - -void IRCClient::handle_part(const Message& msg) -{ - if (msg.arguments.size() < 1) - return; - auto prefix_parts = msg.prefix.split('!'); - if (prefix_parts.size() < 1) - return; - auto nick = prefix_parts[0]; - auto& channel_name = msg.arguments[0]; - ensure_channel(channel_name).handle_part(nick, msg.prefix); -} - -void IRCClient::handle_quit(const Message& msg) -{ - if (msg.arguments.size() < 1) - return; - auto prefix_parts = msg.prefix.split('!'); - if (prefix_parts.size() < 1) - return; - auto nick = prefix_parts[0]; - auto& message = msg.arguments[0]; - for (auto& it : m_channels) { - it.value->handle_quit(nick, msg.prefix, message); - } -} - -void IRCClient::handle_nick(const Message& msg) -{ - auto prefix_parts = msg.prefix.split('!'); - if (prefix_parts.size() < 1) - return; - auto old_nick = prefix_parts[0]; - if (msg.arguments.size() != 1) - return; - auto& new_nick = msg.arguments[0]; - if (old_nick == m_nickname) - m_nickname = new_nick; - if (m_show_nick_change_messages) - add_server_message(String::formatted("~ {} changed nickname to {}", old_nick, new_nick)); - if (on_nickname_changed) - on_nickname_changed(new_nick); - for (auto& it : m_channels) { - it.value->notify_nick_changed(old_nick, new_nick); - } -} - -void IRCClient::handle_topic(const Message& msg) -{ - if (msg.arguments.size() != 2) - return; - auto prefix_parts = msg.prefix.split('!'); - if (prefix_parts.size() < 1) - return; - auto nick = prefix_parts[0]; - auto& channel_name = msg.arguments[0]; - ensure_channel(channel_name).handle_topic(nick, msg.arguments[1]); -} - -void IRCClient::handle_rpl_welcome(const Message& msg) -{ - if (msg.arguments.size() < 2) - return; - auto& welcome_message = msg.arguments[1]; - add_server_message(welcome_message); - - auto channel_str = m_config->read_entry("Connection", "AutoJoinChannels", ""); - if (channel_str.is_empty()) - return; - dbgln("IRCClient: Channels to autojoin: {}", channel_str); - auto channels = channel_str.split(','); - for (auto& channel : channels) { - join_channel(channel); - dbgln("IRCClient: Auto joining channel: {}", channel); - } -} - -void IRCClient::handle_rpl_topic(const Message& msg) -{ - if (msg.arguments.size() < 3) - return; - auto& channel_name = msg.arguments[1]; - auto& topic = msg.arguments[2]; - ensure_channel(channel_name).handle_topic({}, topic); -} - -void IRCClient::handle_rpl_namreply(const Message& msg) -{ - if (msg.arguments.size() < 4) - return; - auto& channel_name = msg.arguments[2]; - auto& channel = ensure_channel(channel_name); - auto members = msg.arguments[3].split(' '); - - quick_sort(members, [](auto& a, auto& b) { - return strcasecmp(a.characters(), b.characters()) < 0; - }); - - for (auto& member : members) { - if (member.is_empty()) - continue; - char prefix = 0; - if (is_nick_prefix(member[0])) - prefix = member[0]; - channel.add_member(member, prefix); - } -} - -void IRCClient::handle_rpl_endofnames(const Message&) -{ - add_server_message("// End of NAMES"); -} - -void IRCClient::handle_rpl_banlist(const Message& msg) -{ - if (msg.arguments.size() < 5) - return; - auto& channel = msg.arguments[1]; - auto& mask = msg.arguments[2]; - auto& user = msg.arguments[3]; - auto& datestamp = msg.arguments[4]; - add_server_message(String::formatted("* {}: {} on {} by {}", channel, mask, datestamp, user)); -} - -void IRCClient::handle_rpl_endofbanlist(const Message&) -{ - add_server_message("// End of BANLIST"); -} - -void IRCClient::handle_rpl_endofwho(const Message&) -{ - add_server_message("// End of WHO"); -} - -void IRCClient::handle_rpl_endofwhois(const Message&) -{ - add_server_message("// End of WHOIS"); -} - -void IRCClient::handle_rpl_endofwhowas(const Message&) -{ - add_server_message("// End of WHOWAS"); -} - -void IRCClient::handle_rpl_endofmotd(const Message&) -{ - add_server_message("// End of MOTD"); -} - -void IRCClient::handle_rpl_whoisoperator(const Message& msg) -{ - if (msg.arguments.size() < 2) - return; - auto& nick = msg.arguments[1]; - add_server_message(String::formatted("* {} is an IRC operator", nick)); -} - -void IRCClient::handle_rpl_whoisserver(const Message& msg) -{ - if (msg.arguments.size() < 3) - return; - auto& nick = msg.arguments[1]; - auto& server = msg.arguments[2]; - add_server_message(String::formatted("* {} is using server {}", nick, server)); -} - -void IRCClient::handle_rpl_whoisuser(const Message& msg) -{ - if (msg.arguments.size() < 6) - return; - auto& nick = msg.arguments[1]; - auto& username = msg.arguments[2]; - auto& host = msg.arguments[3]; - [[maybe_unused]] auto& asterisk = msg.arguments[4]; - auto& realname = msg.arguments[5]; - add_server_message(String::formatted("* {} is {}@{}, real name: {}", nick, username, host, realname)); -} - -void IRCClient::handle_rpl_whoisidle(const Message& msg) -{ - if (msg.arguments.size() < 3) - return; - auto& nick = msg.arguments[1]; - auto& secs = msg.arguments[2]; - add_server_message(String::formatted("* {} is {} seconds idle", nick, secs)); -} - -void IRCClient::handle_rpl_whoischannels(const Message& msg) -{ - if (msg.arguments.size() < 3) - return; - auto& nick = msg.arguments[1]; - auto& channel_list = msg.arguments[2]; - add_server_message(String::formatted("* {} is in channels {}", nick, channel_list)); -} - -void IRCClient::handle_rpl_topicwhotime(const Message& msg) -{ - if (msg.arguments.size() < 4) - return; - auto& channel_name = msg.arguments[1]; - auto& nick = msg.arguments[2]; - auto setat = msg.arguments[3]; - auto setat_time = setat.to_uint(); - if (setat_time.has_value()) - setat = Core::DateTime::from_timestamp(setat_time.value()).to_string(); - ensure_channel(channel_name).add_message(String::formatted("*** (set by {} at {})", nick, setat), Color::Blue); -} - -void IRCClient::handle_err_nosuchnick(const Message& msg) -{ - if (msg.arguments.size() < 3) - return; - auto& nick = msg.arguments[1]; - auto& message = msg.arguments[2]; - add_server_message(String::formatted("* {} :{}", nick, message)); -} - -void IRCClient::handle_err_unknowncommand(const Message& msg) -{ - if (msg.arguments.size() < 2) - return; - auto& cmd = msg.arguments[1]; - add_server_message(String::formatted("* Unknown command: {}", cmd)); -} - -void IRCClient::handle_err_nicknameinuse(const Message& msg) -{ - if (msg.arguments.size() < 2) - return; - auto& nick = msg.arguments[1]; - add_server_message(String::formatted("* {} :Nickname in use", nick)); -} - -void IRCClient::register_subwindow(IRCWindow& subwindow) -{ - if (subwindow.type() == IRCWindow::Server) { - m_server_subwindow = &subwindow; - subwindow.set_log_buffer(*m_log); - } - m_windows.append(&subwindow); - m_client_window_list_model->update(); -} - -void IRCClient::unregister_subwindow(IRCWindow& subwindow) -{ - if (subwindow.type() == IRCWindow::Server) { - m_server_subwindow = &subwindow; - } - for (size_t i = 0; i < m_windows.size(); ++i) { - if (m_windows.at(i) == &subwindow) { - m_windows.remove(i); - break; - } - } - m_client_window_list_model->update(); -} - -void IRCClient::handle_user_command(const String& input) -{ - auto parts = input.split_view(' '); - if (parts.is_empty()) - return; - auto command = String(parts[0]).to_uppercase(); - if (command == "/RAW") { - if (parts.size() <= 1) - return; - int command_length = command.length() + 1; - StringView raw_message = input.view().substring_view(command_length, input.view().length() - command_length); - send(String::formatted("{}\r\n", raw_message)); - return; - } - if (command == "/NICK") { - if (parts.size() >= 2) - change_nick(parts[1]); - return; - } - if (command == "/JOIN") { - if (parts.size() >= 2) - join_channel(parts[1]); - return; - } - if (command == "/PART") { - if (parts.size() >= 2) { - auto channel = parts[1]; - part_channel(channel); - } else { - auto* window = current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) - return; - auto channel = window->channel().name(); - join_channel(channel); - } - return; - } - if (command == "/CYCLE") { - if (parts.size() >= 2) { - auto channel = parts[1]; - part_channel(channel); - join_channel(channel); - } else { - auto* window = current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) - return; - auto channel = window->channel().name(); - part_channel(channel); - join_channel(channel); - } - return; - } - if (command == "/BANLIST") { - if (parts.size() >= 2) { - auto channel = parts[1]; - send_banlist(channel); - } else { - auto* window = current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) - return; - auto channel = window->channel().name(); - send_banlist(channel); - } - return; - } - if (command == "/ME") { - if (parts.size() < 2) - return; - - auto* window = current_window(); - if (!window) - return; - - auto emote = input.view().substring_view_starting_after_substring(parts[0]); - auto action_string = String::formatted("ACTION{}", emote); - String peer; - if (window->type() == IRCWindow::Type::Channel) { - peer = window->channel().name(); - window->channel().add_message(String::formatted("* {}{}", m_nickname, emote), Gfx::Color::Magenta); - } else if (window->type() == IRCWindow::Type::Query) { - peer = window->query().name(); - window->query().add_message(String::formatted("* {}{}", m_nickname, emote), Gfx::Color::Magenta); - } else { - return; - } - - send_ctcp_request(peer, action_string); - return; - } - if (command == "/TOPIC") { - if (parts.size() < 2) - return; - if (parts[1].is_empty()) - return; - - if (is_channel_prefix(parts[1][0])) { - if (parts.size() < 3) - return; - auto channel = parts[1]; - auto topic = input.view().substring_view_starting_after_substring(channel); - send_topic(channel, topic); - } else { - auto* window = current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) - return; - auto channel = window->channel().name(); - auto topic = input.view().substring_view_starting_after_substring(parts[0]); - send_topic(channel, topic); - } - return; - } - if (command == "/KICK") { - if (parts.size() < 2) - return; - if (parts[1].is_empty()) - return; - - if (is_channel_prefix(parts[1][0])) { - if (parts.size() < 3) - return; - auto channel = parts[1]; - auto nick = parts[2]; - auto reason = input.view().substring_view_starting_after_substring(nick); - send_kick(channel, nick, reason); - } else { - auto* window = current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) - return; - auto channel = window->channel().name(); - auto nick = parts[1]; - auto reason = input.view().substring_view_starting_after_substring(nick); - send_kick(channel, nick, reason); - } - return; - } - if (command == "/LIST") { - send_list(); - return; - } - if (command == "/QUERY") { - if (parts.size() >= 2) { - auto& query = ensure_query(parts[1]); - IRCAppWindow::the().set_active_window(query.window()); - } - return; - } - if (command == "/MSG") { - if (parts.size() < 3) - return; - auto nick = parts[1]; - auto& query = ensure_query(nick); - IRCAppWindow::the().set_active_window(query.window()); - query.say(input.view().substring_view_starting_after_substring(nick)); - return; - } - if (command == "/WHOIS") { - if (parts.size() >= 2) - send_whois(parts[1]); - return; - } -} - -void IRCClient::change_nick(const String& nick) -{ - send(String::formatted("NICK {}\r\n", nick)); -} - -void IRCClient::handle_list_channels_action() -{ - send_list(); -} - -void IRCClient::handle_whois_action(const String& nick) -{ - send_whois(nick); -} - -void IRCClient::handle_ctcp_user_action(const String& nick, const String& message) -{ - send_ctcp_request(nick, message); -} - -void IRCClient::handle_open_query_action(const String& nick) -{ - ensure_query(nick); -} - -void IRCClient::handle_change_nick_action(const String& nick) -{ - change_nick(nick); -} - -void IRCClient::handle_change_topic_action(const String& channel, const String& topic) -{ - send_topic(channel, topic); -} - -void IRCClient::handle_invite_user_action(const String& channel, const String& nick) -{ - send_invite(channel, nick); -} - -void IRCClient::handle_banlist_action(const String& channel) -{ - send_banlist(channel); -} - -void IRCClient::handle_voice_user_action(const String& channel, const String& nick) -{ - send_voice_user(channel, nick); -} - -void IRCClient::handle_devoice_user_action(const String& channel, const String& nick) -{ - send_devoice_user(channel, nick); -} - -void IRCClient::handle_hop_user_action(const String& channel, const String& nick) -{ - send_hop_user(channel, nick); -} - -void IRCClient::handle_dehop_user_action(const String& channel, const String& nick) -{ - send_dehop_user(channel, nick); -} - -void IRCClient::handle_op_user_action(const String& channel, const String& nick) -{ - send_op_user(channel, nick); -} - -void IRCClient::handle_deop_user_action(const String& channel, const String& nick) -{ - send_deop_user(channel, nick); -} - -void IRCClient::handle_kick_user_action(const String& channel, const String& nick, const String& message) -{ - send_kick(channel, nick, message); -} - -void IRCClient::handle_close_query_action(const String& nick) -{ - m_queries.remove(nick); - m_client_window_list_model->update(); -} - -void IRCClient::handle_join_action(const String& channel) -{ - join_channel(channel); -} - -void IRCClient::handle_part_action(const String& channel) -{ - part_channel(channel); -} - -void IRCClient::handle_cycle_channel_action(const String& channel) -{ - part_channel(channel); - join_channel(channel); -} - -void IRCClient::did_part_from_channel(Badge<IRCChannel>, IRCChannel& channel) -{ - if (on_part_from_channel) - on_part_from_channel(channel); -} - -void IRCClient::send_ctcp_response(const StringView& peer, const StringView& payload) -{ - StringBuilder builder; - builder.append(0x01); - builder.append(payload); - builder.append(0x01); - auto message = builder.to_string(); - send_notice(peer, message); -} - -void IRCClient::send_ctcp_request(const StringView& peer, const StringView& payload) -{ - StringBuilder builder; - builder.append(0x01); - builder.append(payload); - builder.append(0x01); - auto message = builder.to_string(); - send_privmsg(peer, message); -} - -void IRCClient::handle_ctcp_request(const StringView& peer, const StringView& payload) -{ - dbgln("handle_ctcp_request: {}", payload); - - if (payload == "VERSION") { - auto version = ctcp_version_reply(); - if (version.is_empty()) - return; - send_ctcp_response(peer, String::formatted("VERSION {}", version)); - return; - } - - if (payload == "USERINFO") { - auto userinfo = ctcp_userinfo_reply(); - if (userinfo.is_empty()) - return; - send_ctcp_response(peer, String::formatted("USERINFO {}", userinfo)); - return; - } - - if (payload == "FINGER") { - auto finger = ctcp_finger_reply(); - if (finger.is_empty()) - return; - send_ctcp_response(peer, String::formatted("FINGER {}", finger)); - return; - } - - if (payload.starts_with("PING")) { - send_ctcp_response(peer, payload); - return; - } -} - -void IRCClient::handle_ctcp_response(const StringView& peer, const StringView& payload) -{ - dbgln("handle_ctcp_response({}): {}", peer, payload); -} diff --git a/Applications/IRCClient/IRCClient.h b/Applications/IRCClient/IRCClient.h deleted file mode 100644 index 06f7e59289..0000000000 --- a/Applications/IRCClient/IRCClient.h +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "IRCLogBuffer.h" -#include "IRCWindow.h" -#include <AK/CircularQueue.h> -#include <AK/Function.h> -#include <AK/HashMap.h> -#include <AK/String.h> -#include <LibCore/ConfigFile.h> -#include <LibCore/TCPSocket.h> - -class IRCChannel; -class IRCQuery; -class IRCWindowListModel; - -class IRCClient final : public Core::Object { - C_OBJECT(IRCClient) - friend class IRCChannel; - friend class IRCQuery; - -public: - virtual ~IRCClient() override; - - void set_server(const String& hostname, int port = 6667); - - bool connect(); - - String hostname() const { return m_hostname; } - int port() const { return m_port; } - - String nickname() const { return m_nickname; } - - String ctcp_version_reply() const { return m_ctcp_version_reply; } - String ctcp_userinfo_reply() const { return m_ctcp_userinfo_reply; } - String ctcp_finger_reply() const { return m_ctcp_finger_reply; } - - bool show_join_part_messages() const { return m_show_join_part_messages; } - bool show_nick_change_messages() const { return m_show_nick_change_messages; } - - bool notify_on_message() const { return m_notify_on_message; } - bool notify_on_mention() const { return m_notify_on_mention; } - - void join_channel(const String&); - void part_channel(const String&); - void change_nick(const String&); - - static bool is_nick_prefix(char); - static bool is_channel_prefix(char); - String nick_without_prefix(const String& nick); - - IRCWindow* current_window() { return aid_get_active_window(); } - const IRCWindow* current_window() const { return aid_get_active_window(); } - - Function<void()> on_disconnect; - Function<void()> on_server_message; - Function<void(const String&)> on_nickname_changed; - Function<void(IRCChannel&)> on_part_from_channel; - - Function<NonnullRefPtr<IRCWindow>(void*, IRCWindow::Type, const String&)> aid_create_window; - Function<IRCWindow*()> aid_get_active_window; - Function<void()> aid_update_window_list; - - void register_subwindow(IRCWindow&); - void unregister_subwindow(IRCWindow&); - - IRCWindowListModel* client_window_list_model() { return m_client_window_list_model.ptr(); } - const IRCWindowListModel* client_window_list_model() const { return m_client_window_list_model.ptr(); } - - int window_count() const { return m_windows.size(); } - const IRCWindow& window_at(int index) const { return *m_windows.at(index); } - IRCWindow& window_at(int index) { return *m_windows.at(index); } - - size_t window_index(const IRCWindow& window) const - { - for (size_t i = 0; i < m_windows.size(); ++i) { - if (m_windows[i] == &window) - return i; - } - ASSERT_NOT_REACHED(); - } - - void did_part_from_channel(Badge<IRCChannel>, IRCChannel&); - - void handle_user_input_in_channel(const String& channel_name, const String&); - void handle_user_input_in_query(const String& query_name, const String&); - void handle_user_input_in_server(const String&); - - void handle_list_channels_action(); - void handle_whois_action(const String& nick); - void handle_ctcp_user_action(const String& nick, const String& message); - void handle_open_query_action(const String&); - void handle_close_query_action(const String&); - void handle_join_action(const String& channel_name); - void handle_part_action(const String& channel_name); - void handle_cycle_channel_action(const String& channel_name); - void handle_change_nick_action(const String& nick); - void handle_change_topic_action(const String& channel_name, const String&); - void handle_invite_user_action(const String& channel_name, const String& nick); - void handle_banlist_action(const String& channel_name); - void handle_voice_user_action(const String& channel_name, const String& nick); - void handle_devoice_user_action(const String& channel_name, const String& nick); - void handle_hop_user_action(const String& channel_name, const String& nick); - void handle_dehop_user_action(const String& channel_name, const String& nick); - void handle_op_user_action(const String& channel_name, const String& nick); - void handle_deop_user_action(const String& channel_name, const String& nick); - void handle_kick_user_action(const String& channel_name, const String& nick, const String&); - - IRCQuery* query_with_name(const String&); - IRCQuery& ensure_query(const String& name); - IRCChannel& ensure_channel(const String& name); - - void add_server_message(const String&, Color = Color::Black); - -private: - IRCClient(String server, int port); - - struct Message { - String prefix; - String command; - Vector<String> arguments; - }; - - enum class PrivmsgOrNotice { - Privmsg, - Notice, - }; - - void receive_from_server(); - void send(const String&); - void send_user(); - void send_nick(); - void send_pong(const String& server); - void send_privmsg(const String& target, const String&); - void send_notice(const String& target, const String&); - void send_topic(const String& channel_name, const String&); - void send_invite(const String& channel_name, const String& nick); - void send_banlist(const String& channel_name); - void send_voice_user(const String& channel_name, const String& nick); - void send_devoice_user(const String& channel_name, const String& nick); - void send_hop_user(const String& channel_name, const String& nick); - void send_dehop_user(const String& channel_name, const String& nick); - void send_op_user(const String& channel_name, const String& nick); - void send_deop_user(const String& channel_name, const String& nick); - void send_kick(const String& channel_name, const String& nick, const String&); - void send_list(); - void send_whois(const String&); - void process_line(const String&); - void handle_join(const Message&); - void handle_part(const Message&); - void handle_quit(const Message&); - void handle_ping(const Message&); - void handle_topic(const Message&); - void handle_rpl_welcome(const Message&); - void handle_rpl_topic(const Message&); - void handle_rpl_whoisuser(const Message&); - void handle_rpl_whoisserver(const Message&); - void handle_rpl_whoisoperator(const Message&); - void handle_rpl_whoisidle(const Message&); - void handle_rpl_endofwho(const Message&); - void handle_rpl_endofwhois(const Message&); - void handle_rpl_endofwhowas(const Message&); - void handle_rpl_endofmotd(const Message&); - void handle_rpl_whoischannels(const Message&); - void handle_rpl_topicwhotime(const Message&); - void handle_rpl_endofnames(const Message&); - void handle_rpl_endofbanlist(const Message&); - void handle_rpl_namreply(const Message&); - void handle_rpl_banlist(const Message&); - void handle_err_nosuchnick(const Message&); - void handle_err_unknowncommand(const Message&); - void handle_err_nicknameinuse(const Message&); - void handle_privmsg_or_notice(const Message&, PrivmsgOrNotice); - void handle_nick(const Message&); - void handle(const Message&); - void handle_user_command(const String&); - void handle_ctcp_request(const StringView& peer, const StringView& payload); - void handle_ctcp_response(const StringView& peer, const StringView& payload); - void send_ctcp_request(const StringView& peer, const StringView& payload); - void send_ctcp_response(const StringView& peer, const StringView& payload); - - void on_socket_connected(); - - String m_hostname; - int m_port { 6667 }; - - RefPtr<Core::TCPSocket> m_socket; - - String m_nickname; - RefPtr<Core::Notifier> m_notifier; - HashMap<String, RefPtr<IRCChannel>, CaseInsensitiveStringTraits> m_channels; - HashMap<String, RefPtr<IRCQuery>, CaseInsensitiveStringTraits> m_queries; - - bool m_show_join_part_messages { 1 }; - bool m_show_nick_change_messages { 1 }; - - bool m_notify_on_message { 1 }; - bool m_notify_on_mention { 1 }; - - String m_ctcp_version_reply; - String m_ctcp_userinfo_reply; - String m_ctcp_finger_reply; - - Vector<IRCWindow*> m_windows; - - IRCWindow* m_server_subwindow { nullptr }; - - NonnullRefPtr<IRCWindowListModel> m_client_window_list_model; - NonnullRefPtr<IRCLogBuffer> m_log; - NonnullRefPtr<Core::ConfigFile> m_config; -}; diff --git a/Applications/IRCClient/IRCLogBuffer.cpp b/Applications/IRCClient/IRCLogBuffer.cpp deleted file mode 100644 index b292e0a4ee..0000000000 --- a/Applications/IRCClient/IRCLogBuffer.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "IRCLogBuffer.h" -#include <AK/StringBuilder.h> -#include <LibWeb/DOM/DocumentFragment.h> -#include <LibWeb/DOM/DocumentType.h> -#include <LibWeb/DOM/ElementFactory.h> -#include <LibWeb/DOM/Text.h> -#include <LibWeb/HTML/HTMLBodyElement.h> -#include <time.h> - -NonnullRefPtr<IRCLogBuffer> IRCLogBuffer::create() -{ - return adopt(*new IRCLogBuffer); -} - -IRCLogBuffer::IRCLogBuffer() -{ - m_document = Web::DOM::Document::create(); - m_document->append_child(adopt(*new Web::DOM::DocumentType(document()))); - auto html_element = m_document->create_element("html"); - m_document->append_child(html_element); - auto head_element = m_document->create_element("head"); - html_element->append_child(head_element); - auto style_element = m_document->create_element("style"); - style_element->append_child(adopt(*new Web::DOM::Text(document(), "div { font-family: Csilla; font-weight: lighter; }"))); - head_element->append_child(style_element); - auto body_element = m_document->create_element("body"); - html_element->append_child(body_element); - m_container_element = body_element; -} - -IRCLogBuffer::~IRCLogBuffer() -{ -} - -static String timestamp_string() -{ - auto now = time(nullptr); - auto* tm = localtime(&now); - return String::formatted("{:02}:{:02}:{:02} ", tm->tm_hour, tm->tm_min, tm->tm_sec); -} - -void IRCLogBuffer::add_message(char prefix, const String& name, const String& text, Color color) -{ - auto nick_string = String::formatted("<{}{}> ", prefix ? prefix : ' ', name.characters()); - auto html = String::formatted( - "<span>{}</span>" - "<b>{}</b>" - "<span>{}</span>", - timestamp_string(), - escape_html_entities(nick_string), - escape_html_entities(text)); - - auto wrapper = m_document->create_element(Web::HTML::TagNames::div); - wrapper->set_attribute(Web::HTML::AttributeNames::style, String::formatted("color: {}", color.to_string())); - wrapper->set_inner_html(html); - m_container_element->append_child(wrapper); - m_document->force_layout(); -} - -void IRCLogBuffer::add_message(const String& text, Color color) -{ - auto html = String::formatted( - "<span>{}</span>" - "<span>{}</span>", - timestamp_string(), - escape_html_entities(text)); - auto wrapper = m_document->create_element(Web::HTML::TagNames::div); - wrapper->set_attribute(Web::HTML::AttributeNames::style, String::formatted("color: {}", color.to_string())); - wrapper->set_inner_html(html); - m_container_element->append_child(wrapper); - m_document->force_layout(); -} diff --git a/Applications/IRCClient/IRCLogBuffer.h b/Applications/IRCClient/IRCLogBuffer.h deleted file mode 100644 index db4636f0ee..0000000000 --- a/Applications/IRCClient/IRCLogBuffer.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 <AK/RefCounted.h> -#include <AK/RefPtr.h> -#include <AK/String.h> -#include <LibGfx/Color.h> -#include <LibWeb/DOM/Document.h> - -class IRCLogBuffer : public RefCounted<IRCLogBuffer> { -public: - static NonnullRefPtr<IRCLogBuffer> create(); - ~IRCLogBuffer(); - - struct Message { - time_t timestamp { 0 }; - char prefix { 0 }; - String sender; - String text; - Color color { Color::Black }; - }; - - void add_message(char prefix, const String& name, const String& text, Color = Color::Black); - void add_message(const String& text, Color = Color::Black); - - const Web::DOM::Document& document() const { return *m_document; } - Web::DOM::Document& document() { return *m_document; } - -private: - IRCLogBuffer(); - RefPtr<Web::DOM::Document> m_document; - RefPtr<Web::DOM::Element> m_container_element; -}; diff --git a/Applications/IRCClient/IRCQuery.cpp b/Applications/IRCClient/IRCQuery.cpp deleted file mode 100644 index e2795f05d8..0000000000 --- a/Applications/IRCClient/IRCQuery.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "IRCQuery.h" -#include "IRCClient.h" -#include <stdio.h> - -IRCQuery::IRCQuery(IRCClient& client, const String& name) - : m_client(client) - , m_name(name) - , m_log(IRCLogBuffer::create()) -{ - m_window = m_client->aid_create_window(this, IRCWindow::Query, m_name); - m_window->set_log_buffer(*m_log); -} - -IRCQuery::~IRCQuery() -{ -} - -NonnullRefPtr<IRCQuery> IRCQuery::create(IRCClient& client, const String& name) -{ - return adopt(*new IRCQuery(client, name)); -} - -void IRCQuery::add_message(char prefix, const String& name, const String& text, Color color) -{ - log().add_message(prefix, name, text, color); - window().did_add_message(name, text); -} - -void IRCQuery::add_message(const String& text, Color color) -{ - log().add_message(text, color); - window().did_add_message(); -} - -void IRCQuery::say(const String& text) -{ - m_client->send_privmsg(m_name, text); - add_message(' ', m_client->nickname(), text); -} diff --git a/Applications/IRCClient/IRCQuery.h b/Applications/IRCClient/IRCQuery.h deleted file mode 100644 index 379d9d6492..0000000000 --- a/Applications/IRCClient/IRCQuery.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "IRCLogBuffer.h" -#include <AK/CircularQueue.h> -#include <AK/RefCounted.h> -#include <AK/RefPtr.h> -#include <AK/String.h> -#include <AK/Vector.h> - -class IRCClient; -class IRCWindow; - -class IRCQuery : public RefCounted<IRCQuery> { -public: - static NonnullRefPtr<IRCQuery> create(IRCClient&, const String& name); - ~IRCQuery(); - - String name() const { return m_name; } - void add_message(char prefix, const String& name, const String& text, Color = Color::Black); - void add_message(const String& text, Color = Color::Black); - - const IRCLogBuffer& log() const { return *m_log; } - IRCLogBuffer& log() { return *m_log; } - - void say(const String&); - - IRCWindow& window() { return *m_window; } - const IRCWindow& window() const { return *m_window; } - -private: - IRCQuery(IRCClient&, const String& name); - - NonnullRefPtr<IRCClient> m_client; - String m_name; - RefPtr<IRCWindow> m_window; - - NonnullRefPtr<IRCLogBuffer> m_log; -}; diff --git a/Applications/IRCClient/IRCWindow.cpp b/Applications/IRCClient/IRCWindow.cpp deleted file mode 100644 index 7a84795b8f..0000000000 --- a/Applications/IRCClient/IRCWindow.cpp +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "IRCWindow.h" -#include "IRCChannel.h" -#include "IRCChannelMemberListModel.h" -#include "IRCClient.h" -#include <AK/StringBuilder.h> -#include <LibGUI/Action.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/InputBox.h> -#include <LibGUI/Menu.h> -#include <LibGUI/Notification.h> -#include <LibGUI/Splitter.h> -#include <LibGUI/TableView.h> -#include <LibGUI/TextBox.h> -#include <LibGUI/TextEditor.h> -#include <LibGUI/Window.h> -#include <LibWeb/InProcessWebView.h> - -IRCWindow::IRCWindow(IRCClient& client, void* owner, Type type, const String& name) - : m_client(client) - , m_owner(owner) - , m_type(type) - , m_name(name) -{ - set_layout<GUI::VerticalBoxLayout>(); - - // Make a container for the log buffer view + (optional) member list. - auto& container = add<GUI::HorizontalSplitter>(); - - m_page_view = container.add<Web::InProcessWebView>(); - - if (m_type == Channel) { - auto& member_view = container.add<GUI::TableView>(); - member_view.set_column_headers_visible(false); - member_view.set_fixed_width(100); - member_view.set_alternating_row_colors(false); - member_view.set_model(channel().member_model()); - member_view.set_activates_on_selection(true); - member_view.on_activation = [&](auto& index) { - if (!index.is_valid()) - return; - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - m_client->handle_open_query_action(m_client->nick_without_prefix(nick.characters())); - }; - member_view.on_context_menu_request = [&](const GUI::ModelIndex& index, const GUI::ContextMenuEvent& event) { - if (!index.is_valid()) - return; - - m_context_menu = GUI::Menu::construct(); - - m_context_menu->add_action(GUI::Action::create("Open query", Gfx::Bitmap::load_from_file("/res/icons/16x16/irc-open-query.png"), [&](const GUI::Action&) { - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - m_client->handle_open_query_action(m_client->nick_without_prefix(nick.characters())); - })); - - m_context_menu->add_action(GUI::Action::create("Whois", Gfx::Bitmap::load_from_file("/res/icons/16x16/irc-whois.png"), [&](const GUI::Action&) { - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - m_client->handle_whois_action(m_client->nick_without_prefix(nick.characters())); - })); - - auto& context_control_menu = m_context_menu->add_submenu("Control"); - - context_control_menu.add_action(GUI::Action::create("Voice", [&](const GUI::Action&) { - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - m_client->handle_voice_user_action(m_name.characters(), m_client->nick_without_prefix(nick.characters())); - })); - - context_control_menu.add_action(GUI::Action::create("DeVoice", [&](const GUI::Action&) { - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - m_client->handle_devoice_user_action(m_name.characters(), m_client->nick_without_prefix(nick.characters())); - })); - - context_control_menu.add_action(GUI::Action::create("Hop", [&](const GUI::Action&) { - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - m_client->handle_hop_user_action(m_name.characters(), m_client->nick_without_prefix(nick.characters())); - })); - - context_control_menu.add_action(GUI::Action::create("DeHop", [&](const GUI::Action&) { - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - m_client->handle_dehop_user_action(m_name.characters(), m_client->nick_without_prefix(nick.characters())); - })); - - context_control_menu.add_action(GUI::Action::create("Op", [&](const GUI::Action&) { - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - m_client->handle_op_user_action(m_name.characters(), m_client->nick_without_prefix(nick.characters())); - })); - - context_control_menu.add_action(GUI::Action::create("DeOp", [&](const GUI::Action&) { - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - m_client->handle_deop_user_action(m_name.characters(), m_client->nick_without_prefix(nick.characters())); - })); - - context_control_menu.add_separator(); - - context_control_menu.add_action(GUI::Action::create("Kick", [&](const GUI::Action&) { - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - if (IRCClient::is_nick_prefix(nick[0])) - nick = nick.substring(1, nick.length() - 1); - String value; - if (GUI::InputBox::show(value, window(), "Enter reason:", "Reason") == GUI::InputBox::ExecOK) - m_client->handle_kick_user_action(m_name.characters(), m_client->nick_without_prefix(nick.characters()), value); - })); - - auto& context_ctcp_menu = m_context_menu->add_submenu("CTCP"); - - context_ctcp_menu.add_action(GUI::Action::create("User info", [&](const GUI::Action&) { - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - m_client->handle_ctcp_user_action(m_client->nick_without_prefix(nick.characters()), "USERINFO"); - })); - - context_ctcp_menu.add_action(GUI::Action::create("Finger", [&](const GUI::Action&) { - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - m_client->handle_ctcp_user_action(m_client->nick_without_prefix(nick.characters()), "FINGER"); - })); - - context_ctcp_menu.add_action(GUI::Action::create("Time", [&](const GUI::Action&) { - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - m_client->handle_ctcp_user_action(m_client->nick_without_prefix(nick.characters()), "TIME"); - })); - - context_ctcp_menu.add_action(GUI::Action::create("Version", [&](const GUI::Action&) { - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - m_client->handle_ctcp_user_action(m_client->nick_without_prefix(nick.characters()), "VERSION"); - })); - - context_ctcp_menu.add_action(GUI::Action::create("Client info", [&](const GUI::Action&) { - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - m_client->handle_ctcp_user_action(m_client->nick_without_prefix(nick.characters()), "CLIENTINFO"); - })); - - m_context_menu->popup(event.screen_position()); - }; - } - - m_text_box = add<GUI::TextBox>(); - m_text_box->set_fixed_height(19); - m_text_box->on_return_pressed = [this] { - if (m_type == Channel) - m_client->handle_user_input_in_channel(m_name, m_text_box->text()); - else if (m_type == Query) - m_client->handle_user_input_in_query(m_name, m_text_box->text()); - else if (m_type == Server) - m_client->handle_user_input_in_server(m_text_box->text()); - m_text_box->add_current_text_to_history(); - m_text_box->clear(); - }; - m_text_box->set_history_enabled(true); - m_text_box->set_placeholder("Message"); - - m_client->register_subwindow(*this); -} - -IRCWindow::~IRCWindow() -{ - m_client->unregister_subwindow(*this); -} - -void IRCWindow::set_log_buffer(const IRCLogBuffer& log_buffer) -{ - m_log_buffer = &log_buffer; - m_page_view->set_document(const_cast<Web::DOM::Document*>(&log_buffer.document())); -} - -bool IRCWindow::is_active() const -{ - return m_client->current_window() == this; -} - -void IRCWindow::post_notification_if_needed(const String& name, const String& message) -{ - if (name.is_null() || message.is_null()) - return; - if (is_active() && window()->is_active()) - return; - - auto notification = GUI::Notification::construct(); - - if (type() == Type::Channel) { - if (!m_client->notify_on_mention()) - return; - if (!message.contains(m_client->nickname())) - return; - - StringBuilder builder; - builder.append(name); - builder.append(" in "); - builder.append(m_name); - notification->set_title(builder.to_string()); - } else { - if (!m_client->notify_on_message()) - return; - notification->set_title(name); - } - - notification->set_icon(Gfx::Bitmap::load_from_file("/res/icons/32x32/app-irc-client.png")); - notification->set_text(message); - notification->show(); -} - -void IRCWindow::did_add_message(const String& name, const String& message) -{ - post_notification_if_needed(name, message); - - if (!is_active()) { - ++m_unread_count; - m_client->aid_update_window_list(); - return; - } - m_page_view->scroll_to_bottom(); -} - -void IRCWindow::clear_unread_count() -{ - if (!m_unread_count) - return; - m_unread_count = 0; - m_client->aid_update_window_list(); -} - -int IRCWindow::unread_count() const -{ - return m_unread_count; -} diff --git a/Applications/IRCClient/IRCWindow.h b/Applications/IRCClient/IRCWindow.h deleted file mode 100644 index a26e00d65e..0000000000 --- a/Applications/IRCClient/IRCWindow.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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> -#include <LibWeb/Forward.h> - -class IRCChannel; -class IRCClient; -class IRCQuery; -class IRCLogBuffer; - -class IRCWindow : public GUI::Widget { - C_OBJECT(IRCWindow) -public: - enum Type { - Server, - Channel, - Query, - }; - - virtual ~IRCWindow() override; - - String name() const { return m_name; } - void set_name(const String& name) { m_name = name; } - - Type type() const { return m_type; } - - void set_log_buffer(const IRCLogBuffer&); - - bool is_active() const; - - int unread_count() const; - void clear_unread_count(); - - void did_add_message(const String& name = {}, const String& message = {}); - - IRCChannel& channel() { return *(IRCChannel*)m_owner; } - const IRCChannel& channel() const { return *(const IRCChannel*)m_owner; } - - IRCQuery& query() { return *(IRCQuery*)m_owner; } - const IRCQuery& query() const { return *(const IRCQuery*)m_owner; } - -private: - IRCWindow(IRCClient&, void* owner, Type, const String& name); - - void post_notification_if_needed(const String& name, const String& message); - - NonnullRefPtr<IRCClient> m_client; - void* m_owner { nullptr }; - Type m_type; - String m_name; - RefPtr<Web::InProcessWebView> m_page_view; - RefPtr<GUI::TextBox> m_text_box; - RefPtr<IRCLogBuffer> m_log_buffer; - RefPtr<GUI::Menu> m_context_menu; - int m_unread_count { 0 }; -}; diff --git a/Applications/IRCClient/IRCWindowListModel.cpp b/Applications/IRCClient/IRCWindowListModel.cpp deleted file mode 100644 index 7ee68c429e..0000000000 --- a/Applications/IRCClient/IRCWindowListModel.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "IRCWindowListModel.h" -#include "IRCChannel.h" -#include "IRCClient.h" - -IRCWindowListModel::IRCWindowListModel(IRCClient& client) - : m_client(client) -{ -} - -IRCWindowListModel::~IRCWindowListModel() -{ -} - -int IRCWindowListModel::row_count(const GUI::ModelIndex&) const -{ - return m_client->window_count(); -} - -int IRCWindowListModel::column_count(const GUI::ModelIndex&) const -{ - return 1; -} - -String IRCWindowListModel::column_name(int column) const -{ - switch (column) { - case Column::Name: - return "Name"; - } - ASSERT_NOT_REACHED(); -} - -GUI::Variant IRCWindowListModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const -{ - if (role == GUI::ModelRole::TextAlignment) - return Gfx::TextAlignment::CenterLeft; - if (role == GUI::ModelRole::Display) { - switch (index.column()) { - case Column::Name: { - auto& window = m_client->window_at(index.row()); - if (window.unread_count()) - return String::formatted("{} ({})", window.name(), window.unread_count()); - return window.name(); - } - } - } - if (role == GUI::ModelRole::ForegroundColor) { - switch (index.column()) { - case Column::Name: { - auto& window = m_client->window_at(index.row()); - if (window.unread_count()) - return Color(Color::Red); - if (!window.channel().is_open()) - return Color(Color::WarmGray); - return Color(Color::Black); - } - } - } - return {}; -} - -void IRCWindowListModel::update() -{ - did_update(); -} diff --git a/Applications/IRCClient/IRCWindowListModel.h b/Applications/IRCClient/IRCWindowListModel.h deleted file mode 100644 index 88e5463b82..0000000000 --- a/Applications/IRCClient/IRCWindowListModel.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 <AK/Function.h> -#include <LibGUI/Model.h> - -class IRCClient; -class IRCWindow; - -class IRCWindowListModel final : public GUI::Model { -public: - enum Column { - Name, - }; - - static NonnullRefPtr<IRCWindowListModel> create(IRCClient& client) { return adopt(*new IRCWindowListModel(client)); } - virtual ~IRCWindowListModel() override; - - virtual int row_count(const GUI::ModelIndex&) const override; - virtual int column_count(const GUI::ModelIndex&) const override; - virtual String column_name(int column) const override; - virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override; - virtual void update() override; - -private: - explicit IRCWindowListModel(IRCClient&); - - NonnullRefPtr<IRCClient> m_client; -}; diff --git a/Applications/IRCClient/main.cpp b/Applications/IRCClient/main.cpp deleted file mode 100644 index a0adf92c0d..0000000000 --- a/Applications/IRCClient/main.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "IRCAppWindow.h" -#include "IRCClient.h" -#include <LibCore/StandardPaths.h> -#include <LibGUI/Application.h> -#include <LibGUI/MessageBox.h> -#include <stdio.h> - -int main(int argc, char** argv) -{ - if (pledge("stdio inet dns unix shared_buffer cpath rpath fattr wpath cpath", nullptr) < 0) { - perror("pledge"); - return 1; - } - - if (getuid() == 0) { - warnln("Refusing to run as root"); - return 1; - } - - auto app = GUI::Application::construct(argc, argv); - - if (pledge("stdio inet dns unix shared_buffer rpath wpath cpath", nullptr) < 0) { - perror("pledge"); - return 1; - } - - if (unveil("/tmp/portal/lookup", "rw") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/tmp/portal/notify", "rw") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/etc/passwd", "r") < 0) { - perror("unveil"); - return 1; - } - - if (unveil(Core::StandardPaths::home_directory().characters(), "rwc") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/res", "r") < 0) { - perror("unveil"); - return 1; - } - - if (unveil(nullptr, nullptr) < 0) { - perror("unveil"); - return 1; - } - - URL url = ""; - if (app->args().size() >= 1) { - url = URL::create_with_url_or_path(app->args()[0]); - - if (url.protocol().to_lowercase() == "ircs") { - warnln("Secure IRC over SSL/TLS (ircs) is not supported"); - return 1; - } - - if (url.protocol().to_lowercase() != "irc") { - warnln("Unsupported protocol"); - return 1; - } - - if (url.host().is_empty()) { - warnln("Invalid URL"); - return 1; - } - - if (!url.port() || url.port() == 80) - url.set_port(6667); - } - - auto app_window = IRCAppWindow::construct(url.host(), url.port()); - app_window->show(); - return app->exec(); -} diff --git a/Applications/KeyboardMapper/CMakeLists.txt b/Applications/KeyboardMapper/CMakeLists.txt deleted file mode 100644 index fe24376eb1..0000000000 --- a/Applications/KeyboardMapper/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -set(SOURCES - KeyboardMapperWidget.cpp - KeyButton.cpp - main.cpp -) - -serenity_bin(KeyboardMapper) -target_link_libraries(KeyboardMapper LibGUI LibKeyboard) diff --git a/Applications/KeyboardMapper/KeyButton.cpp b/Applications/KeyboardMapper/KeyButton.cpp deleted file mode 100644 index 58a687faca..0000000000 --- a/Applications/KeyboardMapper/KeyButton.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2020, Hüseyin Aslıtürk <asliturk@hotmail.com> - * 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 "KeyButton.h" -#include <LibGUI/Button.h> -#include <LibGUI/Painter.h> -#include <LibGUI/Window.h> -#include <LibGfx/Font.h> -#include <LibGfx/Palette.h> - -KeyButton::~KeyButton() -{ -} - -void KeyButton::paint_event(GUI::PaintEvent& event) -{ - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - - auto cont_rect = rect(); - auto& font = this->font(); - - Color color; - if (m_pressed) { - color = Color::Cyan; - } else if (!is_enabled()) { - color = Color::LightGray; - } else { - color = Color::White; - } - - painter.fill_rect(cont_rect, Color::Black); - painter.fill_rect({ cont_rect.x() + 1, cont_rect.y() + 1, cont_rect.width() - 2, cont_rect.height() - 2 }, Color::from_rgb(0x999999)); - painter.fill_rect({ cont_rect.x() + 6, cont_rect.y() + 3, cont_rect.width() - 12, cont_rect.height() - 12 }, Color::from_rgb(0x8C7272)); - painter.fill_rect({ cont_rect.x() + 7, cont_rect.y() + 4, cont_rect.width() - 14, cont_rect.height() - 14 }, color); - - if (!text().is_empty()) { - Gfx::IntRect text_rect { 0, 0, font.width(text()), font.glyph_height() }; - text_rect.align_within({ cont_rect.x() + 7, cont_rect.y() + 4, cont_rect.width() - 14, cont_rect.height() - 14 }, Gfx::TextAlignment::Center); - - painter.draw_text(text_rect, text(), font, Gfx::TextAlignment::Center, palette().button_text(), Gfx::TextElision::Right); - if (is_focused()) - painter.draw_rect(text_rect.inflated(6, 4), palette().focus_outline()); - } -} - -void KeyButton::click(unsigned) -{ - if (on_click) - on_click(); -} - -void KeyButton::mousemove_event(GUI::MouseEvent& event) -{ - if (!is_enabled()) - return; - - Gfx::IntRect c = { rect().x() + 7, rect().y() + 4, rect().width() - 14, rect().height() - 14 }; - - if (c.contains(event.position())) { - window()->set_cursor(Gfx::StandardCursor::Hand); - return; - } - window()->set_cursor(Gfx::StandardCursor::Arrow); - - AbstractButton::mousemove_event(event); -} - -void KeyButton::leave_event(Core::Event& event) -{ - window()->set_cursor(Gfx::StandardCursor::Arrow); - AbstractButton::leave_event(event); -} diff --git a/Applications/KeyboardMapper/KeyButton.h b/Applications/KeyboardMapper/KeyButton.h deleted file mode 100644 index afb57033b3..0000000000 --- a/Applications/KeyboardMapper/KeyButton.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2020, Hüseyin Aslıtürk <asliturk@hotmail.com> - * 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/AbstractButton.h> - -class KeyButton : public GUI::AbstractButton { - C_OBJECT(KeyButton) - -public: - virtual ~KeyButton() override; - - void set_pressed(bool value) { m_pressed = value; } - - Function<void()> on_click; - -protected: - virtual void click(unsigned modifiers = 0) override; - virtual void leave_event(Core::Event&) override; - virtual void mousemove_event(GUI::MouseEvent&) override; - virtual void paint_event(GUI::PaintEvent&) override; - -private: - bool m_pressed { false }; -}; diff --git a/Applications/KeyboardMapper/KeyPositions.h b/Applications/KeyboardMapper/KeyPositions.h deleted file mode 100644 index 3cd9dbaaf7..0000000000 --- a/Applications/KeyboardMapper/KeyPositions.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) 2020, Hüseyin Aslıtürk <asliturk@hotmail.com> - * 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 <AK/String.h> - -struct KeyPosition { - u32 scancode; - int x; - int y; - int width; - int height; - bool enabled; - int map_index; - AK::String name; -}; - -#define KEY_COUNT 63 - -struct KeyPosition keys[KEY_COUNT] = { - // clang-format off - [ 0] = { 0, 0, 0, 0, 0, false, 0, ""}, - - [ 1] = { 0x29, 0, 0, 50, 50, true, 41, "`"}, - [ 2] = { 0x02, 51, 0, 50, 50, true, 2, "1"}, - [ 3] = { 0x03, 102, 0, 50, 50, true, 3, "2"}, - [ 4] = { 0x04, 153, 0, 50, 50, true, 4, "3"}, - [ 5] = { 0x05, 204, 0, 50, 50, true, 5, "4"}, - [ 6] = { 0x06, 255, 0, 50, 50, true, 6, "5"}, - [ 7] = { 0x07, 306, 0, 50, 50, true, 7, "6"}, - [ 8] = { 0x08, 357, 0, 50, 50, true, 8, "7"}, - [ 9] = { 0x09, 408, 0, 50, 50, true, 9, "8"}, - [10] = { 0x0A, 459, 0, 50, 50, true, 10, "9"}, - [11] = { 0x0B, 510, 0, 50, 50, true, 11, "0"}, - [12] = { 0x0C, 561, 0, 50, 50, true, 12, "-"}, - [13] = { 0x0D, 612, 0, 50, 50, true, 13, "="}, - [14] = { 0x0E, 663, 0, 100, 50, false, 0, "back space"}, - - [15] = { 0x0F, 0, 52, 76, 50, false, 0, "tab"}, - [16] = { 0x10, 77, 52, 50, 50, true, 16, "q"}, - [17] = { 0x11, 128, 52, 50, 50, true, 17, "w"}, - [18] = { 0x12, 179, 52, 50, 50, true, 18, "e"}, - [19] = { 0x13, 230, 52, 50, 50, true, 19, "r"}, - [20] = { 0x14, 281, 52, 50, 50, true, 20, "t"}, - [21] = { 0x15, 332, 52, 50, 50, true, 21, "y"}, - [22] = { 0x16, 383, 52, 50, 50, true, 22, "u"}, - [23] = { 0x17, 434, 52, 50, 50, true, 23, "ı"}, - [24] = { 0x18, 485, 52, 50, 50, true, 24, "o"}, - [25] = { 0x19, 536, 52, 50, 50, true, 25, "p"}, - [26] = { 0x1A, 587, 52, 50, 50, true, 26, "["}, - [27] = { 0x1B, 638, 52, 50, 50, true, 27, "]"}, - [28] = { 0x1C, 689, 52, 74, 50, false, 0, "enter"}, - - [29] = { 0x3A, 0, 104, 101, 50, false, 0, "caps lock"}, - [30] = { 0x1E, 103, 104, 50, 50, true, 30, "a"}, - [31] = { 0x1F, 154, 104, 50, 50, true, 31, "s"}, - [32] = { 0x20, 205, 104, 50, 50, true, 32, "d"}, - [33] = { 0x21, 256, 104, 50, 50, true, 33, "f"}, - [34] = { 0x22, 307, 104, 50, 50, true, 34, "g"}, - [35] = { 0x23, 358, 104, 50, 50, true, 35, "h"}, - [36] = { 0x24, 409, 104, 50, 50, true, 36, "j"}, - [37] = { 0x25, 460, 104, 50, 50, true, 37, "k"}, - [38] = { 0x26, 511, 104, 50, 50, true, 38, "l"}, - [39] = { 0x27, 562, 104, 50, 50, true, 39, ";"}, - [40] = { 0x28, 614, 104, 50, 50, true, 40, "\""}, - [41] = { 0x2B, 665, 104, 50, 50, true, 43, "\\"}, - - [42] = { 0x2A, 0, 156, 76, 50, false, 0, "left shift"}, - [43] = { 0x56, 77, 156, 50, 50, true, 86, "\\"}, - [44] = { 0x2C, 128, 156, 50, 50, true, 44, "z"}, - [45] = { 0x2D, 179, 156, 50, 50, true, 45, "x"}, - [46] = { 0x2E, 230, 156, 50, 50, true, 46, "c"}, - [47] = { 0x2F, 281, 156, 50, 50, true, 47, "v"}, - [48] = { 0x30, 332, 156, 50, 50, true, 48, "b"}, - [49] = { 0x31, 383, 156, 50, 50, true, 49, "n"}, - [50] = { 0x32, 434, 156, 50, 50, true, 50, "m"}, - [51] = { 0x33, 485, 156, 50, 50, true, 51, ","}, - [52] = { 0x34, 536, 156, 50, 50, true, 52, "."}, - [53] = { 0x35, 587, 156, 50, 50, true, 53, "/"}, - [54] = { 0x36, 638, 156, 125, 50, false, 0, "right shift"}, - - [55] = { 0x1D, 0, 208, 76, 50, false, 0, "left ctrl"}, - [56] = {0xE05B, 77, 208, 50, 50, false, 0, "left\nsuper"}, - [57] = { 0x38, 128, 208, 50, 50, false, 0, "alt"}, - [58] = { 0x39, 179, 208, 356, 50, false, 0, "space"}, - [59] = {0xE038, 536, 208, 50, 50, false, 0, "alt gr"}, - [60] = {0xE05C, 587, 208, 50, 50, false, 0, "right\nsuper"}, - [61] = {0xE05D, 638, 208, 50, 50, false, 0, "menu"}, - [62] = {0xE01D, 689, 208, 74, 50, false, 0, "right ctrl"} - // clang-format on -}; diff --git a/Applications/KeyboardMapper/KeyboardMapperWidget.cpp b/Applications/KeyboardMapper/KeyboardMapperWidget.cpp deleted file mode 100644 index 24df141a01..0000000000 --- a/Applications/KeyboardMapper/KeyboardMapperWidget.cpp +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Copyright (c) 2020, Hüseyin Aslıtürk <asliturk@hotmail.com> - * 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 "KeyboardMapperWidget.h" -#include "KeyPositions.h" -#include <LibCore/File.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/InputBox.h> -#include <LibGUI/MessageBox.h> -#include <LibGUI/RadioButton.h> -#include <LibKeyboard/CharacterMapFile.h> -#include <fcntl.h> -#include <stdio.h> -#include <string.h> - -KeyboardMapperWidget::KeyboardMapperWidget() -{ - create_frame(); -} - -KeyboardMapperWidget::~KeyboardMapperWidget() -{ -} - -void KeyboardMapperWidget::create_frame() -{ - set_fill_with_background_color(true); - set_layout<GUI::VerticalBoxLayout>(); - layout()->set_margins({ 4, 4, 4, 4 }); - - auto& main_widget = add<GUI::Widget>(); - main_widget.set_relative_rect(0, 0, 200, 200); - - m_keys.resize(KEY_COUNT); - - for (unsigned i = 0; i < KEY_COUNT; i++) { - Gfx::IntRect rect = { keys[i].x, keys[i].y, keys[i].width, keys[i].height }; - - auto& tmp_button = main_widget.add<KeyButton>(); - tmp_button.set_relative_rect(rect); - tmp_button.set_text(keys[i].name); - tmp_button.set_enabled(keys[i].enabled); - - tmp_button.on_click = [this, &tmp_button]() { - String value; - if (GUI::InputBox::show(value, window(), "New Character:", "Select Character") == GUI::InputBox::ExecOK) { - int i = m_keys.find_first_index(&tmp_button).value_or(0); - ASSERT(i > 0); - - auto index = keys[i].map_index; - ASSERT(index > 0); - - tmp_button.set_text(value); - u32* map; - - if (m_current_map_name == "map") { - map = m_character_map.map; - } else if (m_current_map_name == "shift_map") { - map = m_character_map.shift_map; - } else if (m_current_map_name == "alt_map") { - map = m_character_map.alt_map; - } else if (m_current_map_name == "altgr_map") { - map = m_character_map.altgr_map; - } else if (m_current_map_name == "shift_altgr_map") { - map = m_character_map.shift_altgr_map; - } else { - ASSERT_NOT_REACHED(); - } - - if (value.length() == 0) - map[index] = '\0'; // Empty string - else - map[index] = value[0]; - - m_modified = true; - update_window_title(); - } - }; - - m_keys.insert(i, &tmp_button); - } - - // Action Buttons - auto& bottom_widget = add<GUI::Widget>(); - bottom_widget.set_layout<GUI::HorizontalBoxLayout>(); - bottom_widget.set_fixed_height(40); - - // Map Selection - m_map_group = bottom_widget.add<GUI::Widget>(); - m_map_group->set_layout<GUI::HorizontalBoxLayout>(); - m_map_group->set_fixed_width(250); - - auto& radio_map = m_map_group->add<GUI::RadioButton>("Default"); - radio_map.set_name("map"); - radio_map.on_checked = [&](bool) { - set_current_map("map"); - }; - auto& radio_shift = m_map_group->add<GUI::RadioButton>("Shift"); - radio_shift.set_name("shift_map"); - radio_shift.on_checked = [this](bool) { - set_current_map("shift_map"); - }; - auto& radio_altgr = m_map_group->add<GUI::RadioButton>("AltGr"); - radio_altgr.set_name("altgr_map"); - radio_altgr.on_checked = [this](bool) { - set_current_map("altgr_map"); - }; - auto& radio_alt = m_map_group->add<GUI::RadioButton>("Alt"); - radio_alt.set_name("alt_map"); - radio_alt.on_checked = [this](bool) { - set_current_map("alt_map"); - }; - auto& radio_shift_altgr = m_map_group->add<GUI::RadioButton>("Shift+AltGr"); - radio_shift_altgr.set_name("shift_altgr_map"); - radio_shift_altgr.on_checked = [this](bool) { - set_current_map("shift_altgr_map"); - }; - - bottom_widget.layout()->add_spacer(); - - auto& ok_button = bottom_widget.add<GUI::Button>(); - ok_button.set_text("Save"); - ok_button.set_fixed_width(80); - ok_button.on_click = [this](auto) { - save(); - }; -} - -void KeyboardMapperWidget::load_from_file(String file_name) -{ - auto result = Keyboard::CharacterMapFile::load_from_file(file_name); - if (!result.has_value()) { - ASSERT_NOT_REACHED(); - } - - m_file_name = file_name; - m_character_map = result.value(); - set_current_map("map"); - - for (Widget* widget : m_map_group->child_widgets()) { - auto radio_button = (GUI::RadioButton*)widget; - radio_button->set_checked(radio_button->name() == "map"); - } - - update_window_title(); -} - -void KeyboardMapperWidget::save() -{ - save_to_file(m_file_name); -} - -void KeyboardMapperWidget::save_to_file(const StringView& file_name) -{ - JsonObject map_json; - - auto add_array = [&](String name, u32* values) { - JsonArray items; - for (int i = 0; i < 90; i++) { - AK::StringBuilder sb; - sb.append(values[i]); - - JsonValue val(sb.to_string()); - items.append(move(val)); - } - map_json.set(name, move(items)); - }; - - add_array("map", m_character_map.map); - add_array("shift_map", m_character_map.shift_map); - add_array("alt_map", m_character_map.alt_map); - add_array("altgr_map", m_character_map.altgr_map); - add_array("shift_altgr_map", m_character_map.shift_altgr_map); - - // Write to file. - String file_content = map_json.to_string(); - - auto file = Core::File::construct(file_name); - file->open(Core::IODevice::WriteOnly); - if (!file->is_open()) { - StringBuilder sb; - sb.append("Failed to open "); - sb.append(file_name); - sb.append(" for write. Error: "); - sb.append(file->error_string()); - - GUI::MessageBox::show(window(), sb.to_string(), "Error", GUI::MessageBox::Type::Error); - return; - } - - bool result = file->write(file_content); - if (!result) { - int error_number = errno; - StringBuilder sb; - sb.append("Unable to save file. Error: "); - sb.append(strerror(error_number)); - - GUI::MessageBox::show(window(), sb.to_string(), "Error", GUI::MessageBox::Type::Error); - return; - } - - m_modified = false; - m_file_name = file_name; - update_window_title(); -} - -void KeyboardMapperWidget::keydown_event(GUI::KeyEvent& event) -{ - for (int i = 0; i < KEY_COUNT; i++) { - auto& tmp_button = m_keys.at(i); - tmp_button->set_pressed(keys[i].scancode == event.scancode()); - tmp_button->update(); - } -} - -void KeyboardMapperWidget::keyup_event(GUI::KeyEvent& event) -{ - for (int i = 0; i < KEY_COUNT; i++) { - if (keys[i].scancode == event.scancode()) { - auto& tmp_button = m_keys.at(i); - tmp_button->set_pressed(false); - tmp_button->update(); - break; - } - } -} - -void KeyboardMapperWidget::set_current_map(const String current_map) -{ - m_current_map_name = current_map; - u32* map; - - if (m_current_map_name == "map") { - map = m_character_map.map; - } else if (m_current_map_name == "shift_map") { - map = m_character_map.shift_map; - } else if (m_current_map_name == "alt_map") { - map = m_character_map.alt_map; - } else if (m_current_map_name == "altgr_map") { - map = m_character_map.altgr_map; - } else if (m_current_map_name == "shift_altgr_map") { - map = m_character_map.shift_altgr_map; - } else { - ASSERT_NOT_REACHED(); - } - - for (unsigned k = 0; k < KEY_COUNT; k++) { - auto index = keys[k].map_index; - if (index == 0) - continue; - - AK::StringBuilder sb; - sb.append_code_point(map[index]); - - m_keys.at(k)->set_text(sb.to_string()); - } - - this->update(); -} - -void KeyboardMapperWidget::update_window_title() -{ - StringBuilder sb; - sb.append(m_file_name); - if (m_modified) - sb.append(" (*)"); - sb.append(" - KeyboardMapper"); - - window()->set_title(sb.to_string()); -} diff --git a/Applications/KeyboardMapper/KeyboardMapperWidget.h b/Applications/KeyboardMapper/KeyboardMapperWidget.h deleted file mode 100644 index e0df34ed78..0000000000 --- a/Applications/KeyboardMapper/KeyboardMapperWidget.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2020, Hüseyin Aslıtürk <asliturk@hotmail.com> - * 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 "KeyButton.h" -#include <LibGUI/Button.h> -#include <LibKeyboard/CharacterMapData.h> - -class KeyboardMapperWidget : public GUI::Widget { - C_OBJECT(KeyboardMapperWidget) - -public: - KeyboardMapperWidget(); - virtual ~KeyboardMapperWidget() override; - - void create_frame(); - void load_from_file(const String); - void save(); - void save_to_file(const StringView&); - -protected: - virtual void keydown_event(GUI::KeyEvent&) override; - virtual void keyup_event(GUI::KeyEvent&) override; - - void set_current_map(const String); - void update_window_title(); - -private: - Vector<KeyButton*> m_keys; - RefPtr<GUI::Widget> m_map_group; - - String m_file_name; - Keyboard::CharacterMapData m_character_map; - String m_current_map_name; - bool m_modified { false }; -}; diff --git a/Applications/KeyboardMapper/main.cpp b/Applications/KeyboardMapper/main.cpp deleted file mode 100644 index 0edeef72fe..0000000000 --- a/Applications/KeyboardMapper/main.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2020, Hüseyin Aslıtürk <asliturk@hotmail.com> - * 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 "KeyboardMapperWidget.h" -#include <LibCore/ArgsParser.h> -#include <LibGUI/Action.h> -#include <LibGUI/Application.h> -#include <LibGUI/FilePicker.h> -#include <LibGUI/Icon.h> -#include <LibGUI/Menu.h> -#include <LibGUI/MenuBar.h> - -int main(int argc, char** argv) -{ - const char* path = nullptr; - Core::ArgsParser args_parser; - args_parser.add_positional_argument(path, "Keyboard character mapping file.", "file", Core::ArgsParser::Required::No); - args_parser.parse(argc, argv); - - if (pledge("stdio thread rpath accept cpath wpath shared_buffer unix fattr", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto app = GUI::Application::construct(argc, argv); - - if (pledge("stdio thread rpath accept cpath wpath shared_buffer", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto app_icon = GUI::Icon::default_icon("app-keyboard-mapper"); - - auto window = GUI::Window::construct(); - window->set_title("Keyboard Mapper"); - window->set_icon(app_icon.bitmap_for_size(16)); - window->set_main_widget<KeyboardMapperWidget>(); - window->resize(775, 315); - window->set_resizable(false); - window->show(); - - auto keyboard_mapper_widget = (KeyboardMapperWidget*)window->main_widget(); - if (path != nullptr) { - keyboard_mapper_widget->load_from_file(path); - } else { - keyboard_mapper_widget->load_from_file("/res/keymaps/en.json"); - } - - // Actions - auto open_action = GUI::CommonActions::make_open_action( - [&](auto&) { - Optional<String> path = GUI::FilePicker::get_open_filepath(window, "Open"); - if (path.has_value()) { - keyboard_mapper_widget->load_from_file(path.value()); - } - }); - - auto save_action = GUI::CommonActions::make_save_action( - [&](auto&) { - keyboard_mapper_widget->save(); - }); - - auto save_as_action = GUI::CommonActions::make_save_as_action([&](auto&) { - String m_name = "Unnamed"; - Optional<String> save_path = GUI::FilePicker::get_save_filepath(window, m_name, "json"); - if (!save_path.has_value()) - return; - - keyboard_mapper_widget->save_to_file(save_path.value()); - }); - - auto quit_action = GUI::CommonActions::make_quit_action( - [&](auto&) { - app->quit(); - }); - - // Menu - auto menubar = GUI::MenuBar::construct(); - - auto& app_menu = menubar->add_menu("Keyboard Mapper"); - app_menu.add_action(open_action); - app_menu.add_action(save_action); - app_menu.add_action(save_as_action); - app_menu.add_separator(); - app_menu.add_action(quit_action); - - auto& help_menu = menubar->add_menu("Help"); - help_menu.add_action(GUI::CommonActions::make_about_action("Keyboard Mapper", app_icon, window)); - - app->set_menubar(move(menubar)); - - return app->exec(); -} diff --git a/Applications/KeyboardSettings/CMakeLists.txt b/Applications/KeyboardSettings/CMakeLists.txt deleted file mode 100644 index 62212cda36..0000000000 --- a/Applications/KeyboardSettings/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -set(SOURCES - main.cpp -) - -serenity_app(KeyboardSettings ICON app-keyboard-settings) -target_link_libraries(KeyboardSettings LibGUI LibKeyboard) diff --git a/Applications/KeyboardSettings/CharacterMapFileListModel.h b/Applications/KeyboardSettings/CharacterMapFileListModel.h deleted file mode 100644 index 4fcd1d8123..0000000000 --- a/Applications/KeyboardSettings/CharacterMapFileListModel.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2020, Hüseyin Aslıtürk <asliturk@hotmail.com> - * 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 <AK/Vector.h> -#include <LibGUI/Model.h> - -class CharacterMapFileListModel final : public GUI::Model { -public: - static NonnullRefPtr<CharacterMapFileListModel> create(Vector<String>& file_names) - { - return adopt(*new CharacterMapFileListModel(file_names)); - } - - virtual ~CharacterMapFileListModel() override { } - - virtual int row_count(const GUI::ModelIndex&) const override - { - return m_file_names.size(); - } - - virtual int column_count(const GUI::ModelIndex&) const override - { - return 1; - } - - virtual GUI::Variant data(const GUI::ModelIndex& index, GUI::ModelRole role) const override - { - ASSERT(index.is_valid()); - ASSERT(index.column() == 0); - - if (role == GUI::ModelRole::Display) - return m_file_names.at(index.row()); - - return {}; - } - - virtual void update() override - { - did_update(); - } - -private: - explicit CharacterMapFileListModel(Vector<String>& file_names) - : m_file_names(file_names) - { - } - - Vector<String>& m_file_names; -}; diff --git a/Applications/KeyboardSettings/main.cpp b/Applications/KeyboardSettings/main.cpp deleted file mode 100644 index 8b515d815e..0000000000 --- a/Applications/KeyboardSettings/main.cpp +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (c) 2020, Hüseyin Aslıtürk <asliturk@hotmail.com> - * 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 "CharacterMapFileListModel.h" -#include <AK/JsonObject.h> -#include <AK/QuickSort.h> -#include <LibCore/ArgsParser.h> -#include <LibCore/DirIterator.h> -#include <LibCore/File.h> -#include <LibGUI/Action.h> -#include <LibGUI/Application.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Button.h> -#include <LibGUI/ComboBox.h> -#include <LibGUI/Label.h> -#include <LibGUI/Menu.h> -#include <LibGUI/MenuBar.h> -#include <LibGUI/MessageBox.h> -#include <LibGUI/WindowServerConnection.h> -#include <LibKeyboard/CharacterMap.h> -#include <spawn.h> - -int main(int argc, char** argv) -{ - if (pledge("stdio rpath accept cpath wpath shared_buffer unix fattr proc exec", nullptr) < 0) { - perror("pledge"); - return 1; - } - - // If there is no command line parameter go for GUI. - auto app = GUI::Application::construct(argc, argv); - - if (pledge("stdio rpath accept shared_buffer proc exec", nullptr) < 0) { - perror("pledge"); - return 1; - } - - if (unveil("/res", "r") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/bin/keymap", "x") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/proc/keymap", "r") < 0) { - perror("unveil"); - return 1; - } - - if (unveil(nullptr, nullptr)) { - perror("unveil"); - return 1; - } - - auto app_icon = GUI::Icon::default_icon("app-keyboard-settings"); - - auto proc_keymap = Core::File::construct("/proc/keymap"); - if (!proc_keymap->open(Core::IODevice::OpenMode::ReadOnly)) - ASSERT_NOT_REACHED(); - - auto json = JsonValue::from_string(proc_keymap->read_all()); - ASSERT(json.has_value()); - JsonObject keymap_object = json.value().as_object(); - ASSERT(keymap_object.has("keymap")); - String current_keymap = keymap_object.get("keymap").to_string(); - dbgln("KeyboardSettings thinks the current keymap is: {}", current_keymap); - - Vector<String> character_map_files; - Core::DirIterator iterator("/res/keymaps/", Core::DirIterator::Flags::SkipDots); - if (iterator.has_error()) { - GUI::MessageBox::show(nullptr, String::formatted("Error on reading mapping file list: {}", iterator.error_string()), "Keyboard settings", GUI::MessageBox::Type::Error); - return -1; - } - - while (iterator.has_next()) { - auto name = iterator.next_path(); - name.replace(".json", ""); - character_map_files.append(name); - } - quick_sort(character_map_files); - - size_t initial_keymap_index = SIZE_MAX; - for (size_t i = 0; i < character_map_files.size(); ++i) { - if (character_map_files[i].equals_ignoring_case(current_keymap)) - initial_keymap_index = i; - } - ASSERT(initial_keymap_index < character_map_files.size()); - - auto window = GUI::Window::construct(); - window->set_title("Keyboard Settings"); - window->resize(300, 70); - window->set_icon(app_icon.bitmap_for_size(16)); - - auto& root_widget = window->set_main_widget<GUI::Widget>(); - root_widget.set_layout<GUI::VerticalBoxLayout>(); - root_widget.set_fill_with_background_color(true); - root_widget.layout()->set_spacing(0); - root_widget.layout()->set_margins({ 4, 4, 4, 4 }); - - auto& character_map_file_selection_container = root_widget.add<GUI::Widget>(); - character_map_file_selection_container.set_layout<GUI::HorizontalBoxLayout>(); - character_map_file_selection_container.set_fixed_height(22); - - auto& character_map_file_label = character_map_file_selection_container.add<GUI::Label>(); - character_map_file_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - character_map_file_label.set_fixed_width(130); - character_map_file_label.set_text("Character Mapping File:"); - - auto& character_map_file_combo = character_map_file_selection_container.add<GUI::ComboBox>(); - character_map_file_combo.set_only_allow_values_from_model(true); - character_map_file_combo.set_model(*CharacterMapFileListModel::create(character_map_files)); - character_map_file_combo.set_selected_index(initial_keymap_index); - - root_widget.layout()->add_spacer(); - - auto apply_settings = [&](bool quit) { - String character_map_file = character_map_file_combo.text(); - if (character_map_file.is_empty()) { - GUI::MessageBox::show(window, "Please select character mapping file.", "Keyboard settings", GUI::MessageBox::Type::Error); - return; - } - pid_t child_pid; - const char* argv[] = { "/bin/keymap", character_map_file.characters(), nullptr }; - if ((errno = posix_spawn(&child_pid, "/bin/keymap", nullptr, nullptr, const_cast<char**>(argv), environ))) { - perror("posix_spawn"); - exit(1); - } - if (quit) - app->quit(); - }; - - auto& bottom_widget = root_widget.add<GUI::Widget>(); - bottom_widget.set_layout<GUI::HorizontalBoxLayout>(); - bottom_widget.layout()->add_spacer(); - bottom_widget.set_fixed_height(22); - - auto& apply_button = bottom_widget.add<GUI::Button>(); - apply_button.set_text("Apply"); - apply_button.set_fixed_width(60); - apply_button.on_click = [&](auto) { - apply_settings(false); - }; - - auto& ok_button = bottom_widget.add<GUI::Button>(); - ok_button.set_text("OK"); - ok_button.set_fixed_width(60); - ok_button.on_click = [&](auto) { - apply_settings(true); - }; - - auto& cancel_button = bottom_widget.add<GUI::Button>(); - cancel_button.set_text("Cancel"); - cancel_button.set_fixed_width(60); - cancel_button.on_click = [&](auto) { - app->quit(); - }; - - auto quit_action = GUI::CommonActions::make_quit_action( - [&](auto&) { - app->quit(); - }); - - auto menubar = GUI::MenuBar::construct(); - - auto& app_menu = menubar->add_menu("Keyboard Settings"); - app_menu.add_action(quit_action); - - auto& help_menu = menubar->add_menu("Help"); - help_menu.add_action(GUI::CommonActions::make_about_action("Keyboard Settings", app_icon, window)); - - app->set_menubar(move(menubar)); - - window->show(); - - return app->exec(); -} diff --git a/Applications/MouseSettings/CMakeLists.txt b/Applications/MouseSettings/CMakeLists.txt deleted file mode 100644 index f52df8d76c..0000000000 --- a/Applications/MouseSettings/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -set(SOURCES - main.cpp -) - -serenity_app(MouseSettings ICON app-mouse) -target_link_libraries(MouseSettings LibGUI) diff --git a/Applications/MouseSettings/main.cpp b/Applications/MouseSettings/main.cpp deleted file mode 100644 index 74b07d0b0a..0000000000 --- a/Applications/MouseSettings/main.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2020, Idan Horowitz <idan.horowitz@gmail.com> - * 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 <LibGUI/Action.h> -#include <LibGUI/Application.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Button.h> -#include <LibGUI/GroupBox.h> -#include <LibGUI/Icon.h> -#include <LibGUI/Menu.h> -#include <LibGUI/MenuBar.h> -#include <LibGUI/Slider.h> -#include <LibGUI/SpinBox.h> -#include <LibGUI/Widget.h> -#include <LibGUI/Window.h> -#include <LibGUI/WindowServerConnection.h> -#include <LibGfx/SystemTheme.h> -#include <WindowServer/Screen.h> - -int main(int argc, char** argv) -{ - if (pledge("stdio cpath rpath shared_buffer unix fattr", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto app = GUI::Application::construct(argc, argv); - - if (pledge("stdio cpath rpath shared_buffer", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto app_icon = GUI::Icon::default_icon("app-mouse"); - auto window = GUI::Window::construct(); - window->set_title("Mouse Settings"); - window->resize(200, 130); - window->set_resizable(false); - window->set_icon(app_icon.bitmap_for_size(16)); - - auto& settings = window->set_main_widget<GUI::Widget>(); - settings.set_fill_with_background_color(true); - settings.set_background_role(ColorRole::Button); - settings.set_layout<GUI::VerticalBoxLayout>(); - settings.layout()->set_margins({ 4, 4, 4, 4 }); - - auto& speed_container = settings.add<GUI::GroupBox>("Mouse speed"); - speed_container.set_layout<GUI::VerticalBoxLayout>(); - speed_container.layout()->set_margins({ 6, 16, 6, 6 }); - speed_container.set_fixed_height(50); - - auto& speed_slider = speed_container.add<GUI::HorizontalSlider>(); - const auto scalar = 1000.0; - speed_slider.set_range(WindowServer::mouse_accel_min * scalar, WindowServer::mouse_accel_max * scalar); // These values are scaled down (by a factor of 1000) to get fractional values - int current_value = GUI::WindowServerConnection::the().send_sync<Messages::WindowServer::GetMouseAcceleration>()->factor() * scalar; - speed_slider.set_value(current_value); - - auto& scroll_container = settings.add<GUI::GroupBox>("Scroll length"); - scroll_container.set_layout<GUI::VerticalBoxLayout>(); - scroll_container.layout()->set_margins({ 6, 16, 6, 6 }); - scroll_container.set_fixed_height(46); - - auto& scroll_spinbox = scroll_container.add<GUI::SpinBox>(); - scroll_spinbox.set_min(WindowServer::scroll_step_size_min); - scroll_spinbox.set_value(GUI::WindowServerConnection::the().send_sync<Messages::WindowServer::GetScrollStepSize>()->step_size()); - - auto update_window_server = [&]() { - float factor = speed_slider.value() / scalar; - GUI::WindowServerConnection::the().send_sync<Messages::WindowServer::SetMouseAcceleration>(factor); - GUI::WindowServerConnection::the().send_sync<Messages::WindowServer::SetScrollStepSize>(scroll_spinbox.value()); - }; - - auto& prompt_buttons = settings.add<GUI::Widget>(); - prompt_buttons.set_layout<GUI::HorizontalBoxLayout>(); - prompt_buttons.set_fixed_height(22); - - auto& ok_button = prompt_buttons.add<GUI::Button>(); - ok_button.set_text("OK"); - prompt_buttons.set_fixed_height(22); - ok_button.on_click = [&](auto) { - update_window_server(); - app->quit(); - }; - auto& apply_button = prompt_buttons.add<GUI::Button>(); - apply_button.set_text("Apply"); - prompt_buttons.set_fixed_height(22); - apply_button.on_click = [&](auto) { - update_window_server(); - }; - auto& reset_button = prompt_buttons.add<GUI::Button>(); - reset_button.set_text("Reset"); - prompt_buttons.set_fixed_height(22); - reset_button.on_click = [&](auto) { - speed_slider.set_value(scalar); - scroll_spinbox.set_value(4); - update_window_server(); - }; - - auto menubar = GUI::MenuBar::construct(); - auto& app_menu = menubar->add_menu("Mouse Settings"); - app_menu.add_action(GUI::CommonActions::make_quit_action([&](auto&) { - app->quit(); - })); - auto& help_menu = menubar->add_menu("Help"); - help_menu.add_action(GUI::CommonActions::make_about_action("Mouse Settings", app_icon, window)); - app->set_menubar(move(menubar)); - - window->show(); - return app->exec(); -} diff --git a/Applications/Piano/CMakeLists.txt b/Applications/Piano/CMakeLists.txt deleted file mode 100644 index 382e023c3b..0000000000 --- a/Applications/Piano/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -set(SOURCES - Track.cpp - TrackManager.cpp - KeysWidget.cpp - KnobsWidget.cpp - main.cpp - MainWidget.cpp - RollWidget.cpp - SamplerWidget.cpp - WaveWidget.cpp -) - -serenity_app(Piano ICON app-piano) -target_link_libraries(Piano LibAudio LibGUI) diff --git a/Applications/Piano/KeysWidget.cpp b/Applications/Piano/KeysWidget.cpp deleted file mode 100644 index 65fcdc39b5..0000000000 --- a/Applications/Piano/KeysWidget.cpp +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * Copyright (c) 2019-2020, William McPherson <willmcpherson2@gmail.com> - * 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 "KeysWidget.h" -#include "TrackManager.h" -#include <LibGUI/Painter.h> - -KeysWidget::KeysWidget(TrackManager& track_manager) - : m_track_manager(track_manager) -{ - set_fill_with_background_color(true); -} - -KeysWidget::~KeysWidget() -{ -} - -int KeysWidget::mouse_note() const -{ - if (m_mouse_down && m_mouse_note + m_track_manager.octave_base() < note_count) - return m_mouse_note; // Can be -1. - else - return -1; -} - -void KeysWidget::set_key(int key, Switch switch_key) -{ - if (key == -1 || key + m_track_manager.octave_base() >= note_count) - return; - - if (switch_key == On) { - ++m_key_on[key]; - } else { - if (m_key_on[key] >= 1) - --m_key_on[key]; - } - ASSERT(m_key_on[key] <= 2); - - m_track_manager.set_note_current_octave(key, switch_key); -} - -bool KeysWidget::note_is_set(int note) const -{ - if (note < m_track_manager.octave_base()) - return false; - - if (note >= m_track_manager.octave_base() + note_count) - return false; - - return m_key_on[note - m_track_manager.octave_base()] != 0; -} - -int KeysWidget::key_code_to_key(int key_code) const -{ - switch (key_code) { - case Key_A: - return 0; - case Key_W: - return 1; - case Key_S: - return 2; - case Key_E: - return 3; - case Key_D: - return 4; - case Key_F: - return 5; - case Key_T: - return 6; - case Key_G: - return 7; - case Key_Y: - return 8; - case Key_H: - return 9; - case Key_U: - return 10; - case Key_J: - return 11; - case Key_K: - return 12; - case Key_O: - return 13; - case Key_L: - return 14; - case Key_P: - return 15; - case Key_Semicolon: - return 16; - case Key_Apostrophe: - return 17; - case Key_RightBracket: - return 18; - case Key_Return: - return 19; - default: - return -1; - } -} - -constexpr int white_key_width = 24; -constexpr int black_key_width = 16; -constexpr int black_key_x_offset = black_key_width / 2; -constexpr int black_key_height = 60; - -constexpr char white_key_labels[] = { - 'A', - 'S', - 'D', - 'F', - 'G', - 'H', - 'J', - 'K', - 'L', - ';', - '\'', - 'r', -}; -constexpr int white_key_labels_count = sizeof(white_key_labels) / sizeof(char); - -constexpr char black_key_labels[] = { - 'W', - 'E', - 'T', - 'Y', - 'U', - 'O', - 'P', - ']', -}; -constexpr int black_key_labels_count = sizeof(black_key_labels) / sizeof(char); - -constexpr int black_key_offsets[] = { - white_key_width, - white_key_width * 2, - white_key_width, - white_key_width, - white_key_width * 2, -}; - -constexpr int white_key_note_accumulator[] = { - 2, - 2, - 1, - 2, - 2, - 2, - 1, -}; - -constexpr int black_key_note_accumulator[] = { - 2, - 3, - 2, - 2, - 3, -}; - -void KeysWidget::paint_event(GUI::PaintEvent& event) -{ - GUI::Painter painter(*this); - painter.translate(frame_thickness(), frame_thickness()); - - int note = 0; - int x = 0; - int i = 0; - for (;;) { - Gfx::IntRect rect(x, 0, white_key_width, frame_inner_rect().height()); - painter.fill_rect(rect, m_key_on[note] ? note_pressed_color : Color::White); - painter.draw_rect(rect, Color::Black); - if (i < white_key_labels_count) { - rect.set_height(rect.height() * 1.5); - painter.draw_text(rect, StringView(&white_key_labels[i], 1), Gfx::TextAlignment::Center, Color::Black); - } - - note += white_key_note_accumulator[i % white_keys_per_octave]; - x += white_key_width; - ++i; - - if (note + m_track_manager.octave_base() >= note_count) - break; - if (x >= frame_inner_rect().width()) - break; - } - - note = 1; - x = white_key_width - black_key_x_offset; - i = 0; - for (;;) { - Gfx::IntRect rect(x, 0, black_key_width, black_key_height); - painter.fill_rect(rect, m_key_on[note] ? note_pressed_color : Color::Black); - painter.draw_rect(rect, Color::Black); - if (i < black_key_labels_count) { - rect.set_height(rect.height() * 1.5); - painter.draw_text(rect, StringView(&black_key_labels[i], 1), Gfx::TextAlignment::Center, Color::White); - } - - note += black_key_note_accumulator[i % black_keys_per_octave]; - x += black_key_offsets[i % black_keys_per_octave]; - ++i; - - if (note + m_track_manager.octave_base() >= note_count) - break; - if (x >= frame_inner_rect().width()) - break; - } - - GUI::Frame::paint_event(event); -} - -constexpr int notes_per_white_key[] = { - 1, - 3, - 5, - 6, - 8, - 10, - 12, -}; - -// Keep in mind that in any of these functions a note value can be out of -// bounds. Bounds checking is done in set_key(). - -static inline int note_from_white_keys(int white_keys) -{ - int octaves = white_keys / white_keys_per_octave; - int remainder = white_keys % white_keys_per_octave; - int notes_from_octaves = octaves * notes_per_octave; - int notes_from_remainder = notes_per_white_key[remainder]; - int note = (notes_from_octaves + notes_from_remainder) - 1; - return note; -} - -int KeysWidget::note_for_event_position(const Gfx::IntPoint& a_point) const -{ - if (!frame_inner_rect().contains(a_point)) - return -1; - - auto point = a_point; - point.move_by(-frame_thickness(), -frame_thickness()); - - int white_keys = point.x() / white_key_width; - int note = note_from_white_keys(white_keys); - - bool black_key_on_left = note != 0 && key_pattern[(note - 1) % notes_per_octave] == Black; - if (black_key_on_left) { - int black_key_x = (white_keys * white_key_width) - black_key_x_offset; - Gfx::IntRect black_key(black_key_x, 0, black_key_width, black_key_height); - if (black_key.contains(point)) - return note - 1; - } - - bool black_key_on_right = key_pattern[(note + 1) % notes_per_octave] == Black; - if (black_key_on_right) { - int black_key_x = ((white_keys + 1) * white_key_width) - black_key_x_offset; - Gfx::IntRect black_key(black_key_x, 0, black_key_width, black_key_height); - if (black_key.contains(point)) - return note + 1; - } - - return note; -} - -void KeysWidget::mousedown_event(GUI::MouseEvent& event) -{ - if (event.button() != GUI::MouseButton::Left) - return; - - m_mouse_down = true; - - m_mouse_note = note_for_event_position(event.position()); - - set_key(m_mouse_note, On); - update(); -} - -void KeysWidget::mouseup_event(GUI::MouseEvent& event) -{ - if (event.button() != GUI::MouseButton::Left) - return; - - m_mouse_down = false; - - set_key(m_mouse_note, Off); - update(); -} - -void KeysWidget::mousemove_event(GUI::MouseEvent& event) -{ - if (!m_mouse_down) - return; - - int new_mouse_note = note_for_event_position(event.position()); - - if (m_mouse_note == new_mouse_note) - return; - - set_key(m_mouse_note, Off); - set_key(new_mouse_note, On); - update(); - - m_mouse_note = new_mouse_note; -} diff --git a/Applications/Piano/KeysWidget.h b/Applications/Piano/KeysWidget.h deleted file mode 100644 index d0b5a833ce..0000000000 --- a/Applications/Piano/KeysWidget.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * Copyright (c) 2019-2020, William McPherson <willmcpherson2@gmail.com> - * 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 "Music.h" -#include <LibGUI/Frame.h> - -class TrackManager; - -class KeysWidget final : public GUI::Frame { - C_OBJECT(KeysWidget) -public: - virtual ~KeysWidget() override; - - int key_code_to_key(int key_code) const; - int mouse_note() const; - - void set_key(int key, Switch); - bool note_is_set(int note) const; - -private: - explicit KeysWidget(TrackManager&); - - virtual void paint_event(GUI::PaintEvent&) override; - virtual void mousedown_event(GUI::MouseEvent&) override; - virtual void mouseup_event(GUI::MouseEvent&) override; - virtual void mousemove_event(GUI::MouseEvent&) override; - - int note_for_event_position(const Gfx::IntPoint&) const; - - TrackManager& m_track_manager; - - u8 m_key_on[note_count] { 0 }; - - bool m_mouse_down { false }; - int m_mouse_note { -1 }; -}; diff --git a/Applications/Piano/KnobsWidget.cpp b/Applications/Piano/KnobsWidget.cpp deleted file mode 100644 index 4b9e4360c2..0000000000 --- a/Applications/Piano/KnobsWidget.cpp +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * Copyright (c) 2019-2020, William McPherson <willmcpherson2@gmail.com> - * 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 "KnobsWidget.h" -#include "MainWidget.h" -#include "TrackManager.h" -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Label.h> -#include <LibGUI/Slider.h> - -constexpr int max_attack = 1000; -constexpr int max_decay = 1000; -constexpr int max_sustain = 1000; -constexpr int max_release = 1000; -constexpr int max_delay = 8; - -KnobsWidget::KnobsWidget(TrackManager& track_manager, MainWidget& main_widget) - : m_track_manager(track_manager) - , m_main_widget(main_widget) -{ - set_layout<GUI::VerticalBoxLayout>(); - set_fill_with_background_color(true); - - m_labels_container = add<GUI::Widget>(); - m_labels_container->set_layout<GUI::HorizontalBoxLayout>(); - m_labels_container->set_fixed_height(20); - - m_octave_label = m_labels_container->add<GUI::Label>("Octave"); - m_wave_label = m_labels_container->add<GUI::Label>("Wave"); - m_attack_label = m_labels_container->add<GUI::Label>("Attack"); - m_decay_label = m_labels_container->add<GUI::Label>("Decay"); - m_sustain_label = m_labels_container->add<GUI::Label>("Sustain"); - m_release_label = m_labels_container->add<GUI::Label>("Release"); - m_delay_label = m_labels_container->add<GUI::Label>("Delay"); - - m_values_container = add<GUI::Widget>(); - m_values_container->set_layout<GUI::HorizontalBoxLayout>(); - m_values_container->set_fixed_height(10); - - m_octave_value = m_values_container->add<GUI::Label>(String::number(m_track_manager.octave())); - m_wave_value = m_values_container->add<GUI::Label>(wave_strings[m_track_manager.current_track().wave()]); - m_attack_value = m_values_container->add<GUI::Label>(String::number(m_track_manager.current_track().attack())); - m_decay_value = m_values_container->add<GUI::Label>(String::number(m_track_manager.current_track().decay())); - m_sustain_value = m_values_container->add<GUI::Label>(String::number(m_track_manager.current_track().sustain())); - m_release_value = m_values_container->add<GUI::Label>(String::number(m_track_manager.current_track().release())); - m_delay_value = m_values_container->add<GUI::Label>(String::number(m_track_manager.current_track().delay())); - - m_knobs_container = add<GUI::Widget>(); - m_knobs_container->set_layout<GUI::HorizontalBoxLayout>(); - - // FIXME: Implement vertical flipping in GUI::Slider, not here. - - m_octave_knob = m_knobs_container->add<GUI::VerticalSlider>(); - m_octave_knob->set_tooltip("Z: octave down, X: octave up"); - m_octave_knob->set_range(octave_min - 1, octave_max - 1); - m_octave_knob->set_value((octave_max - 1) - (m_track_manager.octave() - 1)); - m_octave_knob->on_change = [this](int value) { - int new_octave = octave_max - value; - if (m_change_underlying) - m_main_widget.set_octave_and_ensure_note_change(new_octave); - ASSERT(new_octave == m_track_manager.octave()); - m_octave_value->set_text(String::number(new_octave)); - }; - - m_wave_knob = m_knobs_container->add<GUI::VerticalSlider>(); - m_wave_knob->set_tooltip("C: cycle through waveforms"); - m_wave_knob->set_range(0, last_wave); - m_wave_knob->set_value(last_wave - m_track_manager.current_track().wave()); - m_wave_knob->on_change = [this](int value) { - int new_wave = last_wave - value; - if (m_change_underlying) - m_track_manager.current_track().set_wave(new_wave); - ASSERT(new_wave == m_track_manager.current_track().wave()); - m_wave_value->set_text(wave_strings[new_wave]); - }; - - m_attack_knob = m_knobs_container->add<GUI::VerticalSlider>(); - m_attack_knob->set_range(0, max_attack); - m_attack_knob->set_value(max_attack - m_track_manager.current_track().attack()); - m_attack_knob->set_step(100); - m_attack_knob->on_change = [this](int value) { - int new_attack = max_attack - value; - if (m_change_underlying) - m_track_manager.current_track().set_attack(new_attack); - ASSERT(new_attack == m_track_manager.current_track().attack()); - m_attack_value->set_text(String::number(new_attack)); - }; - - m_decay_knob = m_knobs_container->add<GUI::VerticalSlider>(); - m_decay_knob->set_range(0, max_decay); - m_decay_knob->set_value(max_decay - m_track_manager.current_track().decay()); - m_decay_knob->set_step(100); - m_decay_knob->on_change = [this](int value) { - int new_decay = max_decay - value; - if (m_change_underlying) - m_track_manager.current_track().set_decay(new_decay); - ASSERT(new_decay == m_track_manager.current_track().decay()); - m_decay_value->set_text(String::number(new_decay)); - }; - - m_sustain_knob = m_knobs_container->add<GUI::VerticalSlider>(); - m_sustain_knob->set_range(0, max_sustain); - m_sustain_knob->set_value(max_sustain - m_track_manager.current_track().sustain()); - m_sustain_knob->set_step(100); - m_sustain_knob->on_change = [this](int value) { - int new_sustain = max_sustain - value; - if (m_change_underlying) - m_track_manager.current_track().set_sustain(new_sustain); - ASSERT(new_sustain == m_track_manager.current_track().sustain()); - m_sustain_value->set_text(String::number(new_sustain)); - }; - - m_release_knob = m_knobs_container->add<GUI::VerticalSlider>(); - m_release_knob->set_range(0, max_release); - m_release_knob->set_value(max_release - m_track_manager.current_track().release()); - m_release_knob->set_step(100); - m_release_knob->on_change = [this](int value) { - int new_release = max_release - value; - if (m_change_underlying) - m_track_manager.current_track().set_release(new_release); - ASSERT(new_release == m_track_manager.current_track().release()); - m_release_value->set_text(String::number(new_release)); - }; - - m_delay_knob = m_knobs_container->add<GUI::VerticalSlider>(); - m_delay_knob->set_range(0, max_delay); - m_delay_knob->set_value(max_delay - m_track_manager.current_track().delay()); - m_delay_knob->on_change = [this](int value) { - int new_delay = max_delay - value; - if (m_change_underlying) - m_track_manager.current_track().set_delay(new_delay); - ASSERT(new_delay == m_track_manager.current_track().delay()); - m_delay_value->set_text(String::number(new_delay)); - }; -} - -KnobsWidget::~KnobsWidget() -{ -} - -void KnobsWidget::update_knobs() -{ - m_wave_knob->set_value(last_wave - m_track_manager.current_track().wave()); - - // FIXME: This is needed because when the slider is changed normally, we - // need to change the underlying value, but if the keyboard was used, we - // need to change the slider without changing the underlying value. - m_change_underlying = false; - - m_octave_knob->set_value(octave_max - m_track_manager.octave()); - m_wave_knob->set_value(last_wave - m_track_manager.current_track().wave()); - m_attack_knob->set_value(max_attack - m_track_manager.current_track().attack()); - m_decay_knob->set_value(max_decay - m_track_manager.current_track().decay()); - m_sustain_knob->set_value(max_sustain - m_track_manager.current_track().sustain()); - m_release_knob->set_value(max_release - m_track_manager.current_track().release()); - m_delay_knob->set_value(max_delay - m_track_manager.current_track().delay()); - - m_change_underlying = true; -} diff --git a/Applications/Piano/KnobsWidget.h b/Applications/Piano/KnobsWidget.h deleted file mode 100644 index d5abea5be0..0000000000 --- a/Applications/Piano/KnobsWidget.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * Copyright (c) 2019-2020, William McPherson <willmcpherson2@gmail.com> - * 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/Frame.h> - -class TrackManager; -class MainWidget; - -class KnobsWidget final : public GUI::Frame { - C_OBJECT(KnobsWidget) -public: - virtual ~KnobsWidget() override; - - void update_knobs(); - -private: - KnobsWidget(TrackManager&, MainWidget&); - - TrackManager& m_track_manager; - MainWidget& m_main_widget; - - RefPtr<GUI::Widget> m_labels_container; - RefPtr<GUI::Label> m_octave_label; - RefPtr<GUI::Label> m_wave_label; - RefPtr<GUI::Label> m_attack_label; - RefPtr<GUI::Label> m_decay_label; - RefPtr<GUI::Label> m_sustain_label; - RefPtr<GUI::Label> m_release_label; - RefPtr<GUI::Label> m_delay_label; - - RefPtr<GUI::Widget> m_values_container; - RefPtr<GUI::Label> m_octave_value; - RefPtr<GUI::Label> m_wave_value; - RefPtr<GUI::Label> m_attack_value; - RefPtr<GUI::Label> m_decay_value; - RefPtr<GUI::Label> m_sustain_value; - RefPtr<GUI::Label> m_release_value; - RefPtr<GUI::Label> m_delay_value; - - RefPtr<GUI::Widget> m_knobs_container; - RefPtr<GUI::Slider> m_octave_knob; - RefPtr<GUI::Slider> m_wave_knob; - RefPtr<GUI::Slider> m_attack_knob; - RefPtr<GUI::Slider> m_decay_knob; - RefPtr<GUI::Slider> m_sustain_knob; - RefPtr<GUI::Slider> m_release_knob; - RefPtr<GUI::Slider> m_delay_knob; - - bool m_change_underlying { true }; -}; diff --git a/Applications/Piano/MainWidget.cpp b/Applications/Piano/MainWidget.cpp deleted file mode 100644 index ef40a4fd1a..0000000000 --- a/Applications/Piano/MainWidget.cpp +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * Copyright (c) 2019-2020, William McPherson <willmcpherson2@gmail.com> - * 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 "MainWidget.h" -#include "KeysWidget.h" -#include "KnobsWidget.h" -#include "RollWidget.h" -#include "SamplerWidget.h" -#include "TrackManager.h" -#include "WaveWidget.h" -#include <LibGUI/Action.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Menu.h> -#include <LibGUI/TabWidget.h> - -MainWidget::MainWidget(TrackManager& track_manager) - : m_track_manager(track_manager) -{ - set_layout<GUI::VerticalBoxLayout>(); - layout()->set_spacing(2); - layout()->set_margins({ 2, 2, 2, 2 }); - set_fill_with_background_color(true); - - m_wave_widget = add<WaveWidget>(track_manager); - m_wave_widget->set_fixed_height(100); - - m_tab_widget = add<GUI::TabWidget>(); - m_roll_widget = m_tab_widget->add_tab<RollWidget>("Piano Roll", track_manager); - - m_roll_widget->set_fixed_height(300); - - m_tab_widget->add_tab<SamplerWidget>("Sampler", track_manager); - - m_keys_and_knobs_container = add<GUI::Widget>(); - m_keys_and_knobs_container->set_layout<GUI::HorizontalBoxLayout>(); - m_keys_and_knobs_container->layout()->set_spacing(2); - m_keys_and_knobs_container->set_fixed_height(100); - m_keys_and_knobs_container->set_fill_with_background_color(true); - - m_keys_widget = m_keys_and_knobs_container->add<KeysWidget>(track_manager); - - m_knobs_widget = m_keys_and_knobs_container->add<KnobsWidget>(track_manager, *this); - m_knobs_widget->set_fixed_width(350); - - m_roll_widget->set_keys_widget(m_keys_widget); -} - -MainWidget::~MainWidget() -{ -} - -void MainWidget::add_actions(GUI::Menu& menu) -{ - menu.add_action(GUI::Action::create("Add track", { Mod_Ctrl, Key_T }, [&](auto&) { - m_track_manager.add_track(); - })); - - menu.add_action(GUI::Action::create("Next track", { Mod_Ctrl, Key_N }, [&](auto&) { - turn_off_pressed_keys(); - m_track_manager.next_track(); - turn_on_pressed_keys(); - - m_knobs_widget->update_knobs(); - })); -} - -// FIXME: There are some unnecessary calls to update() throughout this program, -// which are an easy target for optimization. - -void MainWidget::custom_event(Core::CustomEvent&) -{ - m_wave_widget->update(); - m_roll_widget->update(); -} - -void MainWidget::keydown_event(GUI::KeyEvent& event) -{ - // This is to stop held-down keys from creating multiple events. - if (m_keys_pressed[event.key()]) - return; - else - m_keys_pressed[event.key()] = true; - - note_key_action(event.key(), On); - special_key_action(event.key()); - m_keys_widget->update(); -} - -void MainWidget::keyup_event(GUI::KeyEvent& event) -{ - m_keys_pressed[event.key()] = false; - - note_key_action(event.key(), Off); - m_keys_widget->update(); -} - -void MainWidget::note_key_action(int key_code, Switch switch_note) -{ - int key = m_keys_widget->key_code_to_key(key_code); - m_keys_widget->set_key(key, switch_note); -} - -void MainWidget::special_key_action(int key_code) -{ - switch (key_code) { - case Key_Z: - set_octave_and_ensure_note_change(Down); - break; - case Key_X: - set_octave_and_ensure_note_change(Up); - break; - case Key_C: - m_track_manager.current_track().set_wave(Up); - m_knobs_widget->update_knobs(); - break; - } -} - -void MainWidget::turn_off_pressed_keys() -{ - m_keys_widget->set_key(m_keys_widget->mouse_note(), Off); - for (int i = 0; i < key_code_count; ++i) { - if (m_keys_pressed[i]) - note_key_action(i, Off); - } -} - -void MainWidget::turn_on_pressed_keys() -{ - m_keys_widget->set_key(m_keys_widget->mouse_note(), On); - for (int i = 0; i < key_code_count; ++i) { - if (m_keys_pressed[i]) - note_key_action(i, On); - } -} - -void MainWidget::set_octave_and_ensure_note_change(int octave) -{ - turn_off_pressed_keys(); - m_track_manager.set_octave(octave); - turn_on_pressed_keys(); - - m_knobs_widget->update_knobs(); - m_keys_widget->update(); -} - -void MainWidget::set_octave_and_ensure_note_change(Direction direction) -{ - turn_off_pressed_keys(); - m_track_manager.set_octave(direction); - turn_on_pressed_keys(); - - m_knobs_widget->update_knobs(); - m_keys_widget->update(); -} diff --git a/Applications/Piano/MainWidget.h b/Applications/Piano/MainWidget.h deleted file mode 100644 index a2a6a35a0c..0000000000 --- a/Applications/Piano/MainWidget.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * Copyright (c) 2019-2020, William McPherson <willmcpherson2@gmail.com> - * 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 "Music.h" -#include <LibGUI/Widget.h> - -class TrackManager; -class WaveWidget; -class RollWidget; -class SamplerWidget; -class KeysWidget; -class KnobsWidget; - -class MainWidget final : public GUI::Widget { - C_OBJECT(MainWidget) -public: - virtual ~MainWidget() override; - - void add_actions(GUI::Menu&); - - void set_octave_and_ensure_note_change(Direction); - void set_octave_and_ensure_note_change(int); - -private: - explicit MainWidget(TrackManager&); - - virtual void keydown_event(GUI::KeyEvent&) override; - virtual void keyup_event(GUI::KeyEvent&) override; - virtual void custom_event(Core::CustomEvent&) override; - - void note_key_action(int key_code, Switch); - void special_key_action(int key_code); - - void turn_off_pressed_keys(); - void turn_on_pressed_keys(); - - TrackManager& m_track_manager; - - RefPtr<WaveWidget> m_wave_widget; - RefPtr<RollWidget> m_roll_widget; - RefPtr<SamplerWidget> m_sampler_widget; - RefPtr<GUI::TabWidget> m_tab_widget; - RefPtr<GUI::Widget> m_keys_and_knobs_container; - RefPtr<KeysWidget> m_keys_widget; - RefPtr<KnobsWidget> m_knobs_widget; - - bool m_keys_pressed[key_code_count] { false }; -}; diff --git a/Applications/Piano/Music.h b/Applications/Piano/Music.h deleted file mode 100644 index 3a150944ec..0000000000 --- a/Applications/Piano/Music.h +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * Copyright (c) 2019-2020, William McPherson <willmcpherson2@gmail.com> - * 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 <AK/Types.h> -#include <LibGfx/Color.h> - -namespace Music { - -// CD quality -// - Stereo -// - 16 bit -// - 44,100 samples/sec -// - 1,411.2 kbps - -struct Sample { - i16 left; - i16 right; -}; - -constexpr int sample_count = 1024; - -constexpr int buffer_size = sample_count * sizeof(Sample); - -constexpr double sample_rate = 44100; - -constexpr double volume = 1800; - -enum Switch { - Off, - On, -}; - -struct RollNote { - u32 length() const { return (off_sample - on_sample) + 1; } - - u32 on_sample; - u32 off_sample; -}; - -enum Direction { - Down, - Up, -}; - -enum Wave { - Sine, - Triangle, - Square, - Saw, - Noise, - RecordedSample, -}; - -constexpr const char* wave_strings[] = { - "Sine", - "Triangle", - "Square", - "Saw", - "Noise", - "Sample", -}; - -constexpr int first_wave = Sine; -constexpr int last_wave = RecordedSample; - -enum Envelope { - Done, - Attack, - Decay, - Release, -}; - -enum KeyColor { - White, - Black, -}; - -constexpr KeyColor key_pattern[] = { - White, - Black, - White, - Black, - White, - White, - Black, - White, - Black, - White, - Black, - White, -}; - -const Color note_pressed_color(64, 64, 255); -const Color column_playing_color(128, 128, 255); - -const Color left_wave_colors[] = { - // Sine - { - 255, - 192, - 0, - }, - // Triangle - { - 35, - 171, - 35, - }, - // Square - { - 128, - 160, - 255, - }, - // Saw - { - 240, - 100, - 128, - }, - // Noise - { - 197, - 214, - 225, - }, - // RecordedSample - { - 227, - 39, - 39, - }, -}; - -const Color right_wave_colors[] = { - // Sine - { - 255, - 223, - 0, - }, - // Triangle - { - 35, - 171, - 90, - }, - // Square - { - 139, - 128, - 255, - }, - // Saw - { - 240, - 100, - 220, - }, - // Noise - { - 197, - 223, - 225, - }, - // RecordedSample - { - 227, - 105, - 39, - }, -}; - -constexpr int notes_per_octave = 12; -constexpr int white_keys_per_octave = 7; -constexpr int black_keys_per_octave = 5; -constexpr int octave_min = 1; -constexpr int octave_max = 7; - -constexpr double beats_per_minute = 60; -constexpr int beats_per_bar = 4; -constexpr int notes_per_beat = 4; -constexpr int roll_length = (sample_rate / (beats_per_minute / 60)) * beats_per_bar; - -constexpr const char* note_names[] = { - "C", - "C#", - "D", - "D#", - "E", - "F", - "F#", - "G", - "G#", - "A", - "A#", - "B", -}; - -// Equal temperament, A = 440Hz -// We calculate note frequencies relative to A4: -// 440.0 * pow(pow(2.0, 1.0 / 12.0), N) -// Where N is the note distance from A. -constexpr double note_frequencies[] = { - // Octave 1 - 32.703195662574764, - 34.647828872108946, - 36.708095989675876, - 38.890872965260044, - 41.203444614108669, - 43.653528929125407, - 46.249302838954222, - 48.99942949771858, - 51.913087197493056, - 54.999999999999915, - 58.270470189761156, - 61.735412657015416, - // Octave 2 - 65.406391325149571, - 69.295657744217934, - 73.416191979351794, - 77.781745930520117, - 82.406889228217381, - 87.307057858250872, - 92.4986056779085, - 97.998858995437217, - 103.82617439498618, - 109.99999999999989, - 116.54094037952237, - 123.4708253140309, - // Octave 3 - 130.8127826502992, - 138.59131548843592, - 146.83238395870364, - 155.56349186104035, - 164.81377845643485, - 174.61411571650183, - 184.99721135581709, - 195.99771799087452, - 207.65234878997245, - 219.99999999999989, - 233.08188075904488, - 246.94165062806198, - // Octave 4 - 261.62556530059851, - 277.18263097687202, - 293.66476791740746, - 311.12698372208081, - 329.62755691286986, - 349.22823143300383, - 369.99442271163434, - 391.99543598174927, - 415.30469757994513, - 440, - 466.16376151808993, - 493.88330125612413, - // Octave 5 - 523.25113060119736, - 554.36526195374427, - 587.32953583481526, - 622.25396744416196, - 659.25511382574007, - 698.456462866008, - 739.98884542326903, - 783.99087196349899, - 830.60939515989071, - 880.00000000000034, - 932.32752303618031, - 987.76660251224882, - // Octave 6 - 1046.5022612023952, - 1108.7305239074892, - 1174.659071669631, - 1244.5079348883246, - 1318.5102276514808, - 1396.9129257320169, - 1479.977690846539, - 1567.9817439269987, - 1661.2187903197821, - 1760.000000000002, - 1864.6550460723618, - 1975.5332050244986, - // Octave 7 - 2093.0045224047913, - 2217.4610478149793, - 2349.3181433392633, - 2489.0158697766506, - 2637.020455302963, - 2793.8258514640347, - 2959.9553816930793, - 3135.9634878539991, - 3322.437580639566, - 3520.0000000000055, - 3729.3100921447249, - 3951.0664100489994, -}; -constexpr int note_count = sizeof(note_frequencies) / sizeof(double); - -constexpr double middle_c = note_frequencies[36]; - -} - -using namespace Music; diff --git a/Applications/Piano/RollWidget.cpp b/Applications/Piano/RollWidget.cpp deleted file mode 100644 index 2677fed384..0000000000 --- a/Applications/Piano/RollWidget.cpp +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * Copyright (c) 2019-2020, William McPherson <willmcpherson2@gmail.com> - * 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 "RollWidget.h" -#include "TrackManager.h" -#include <LibGUI/Painter.h> -#include <LibGUI/ScrollBar.h> -#include <LibGfx/Font.h> -#include <LibGfx/FontDatabase.h> -#include <math.h> - -constexpr int note_height = 20; -constexpr int max_note_width = note_height * 2; -constexpr int roll_height = note_count * note_height; -constexpr int horizontal_scroll_sensitivity = 20; -constexpr int max_zoom = 1 << 8; - -RollWidget::RollWidget(TrackManager& track_manager) - : m_track_manager(track_manager) -{ - set_should_hide_unnecessary_scrollbars(true); - set_content_size({ 0, roll_height }); - vertical_scrollbar().set_value(roll_height / 2); -} - -RollWidget::~RollWidget() -{ -} - -void RollWidget::paint_event(GUI::PaintEvent& event) -{ - m_roll_width = widget_inner_rect().width() * m_zoom_level; - set_content_size({ m_roll_width, roll_height }); - - // Divide the roll by the maximum note width. If we get fewer notes than - // our time signature requires, round up. Otherwise, round down to the - // nearest x*(2^y), where x is the base number of notes of our time - // signature. In other words, find a number that is a double of our time - // signature. For 4/4 that would be 16, 32, 64, 128 ... - m_num_notes = m_roll_width / max_note_width; - int time_signature_notes = beats_per_bar * notes_per_beat; - if (m_num_notes < time_signature_notes) - m_num_notes = time_signature_notes; - else - m_num_notes = time_signature_notes * pow(2, static_cast<int>(log2(m_num_notes / time_signature_notes))); - m_note_width = static_cast<double>(m_roll_width) / m_num_notes; - - // This calculates the minimum number of rows needed. We account for a - // partial row at the top and/or bottom. - int y_offset = vertical_scrollbar().value(); - int note_offset = y_offset / note_height; - int note_offset_remainder = y_offset % note_height; - int paint_area = widget_inner_rect().height() + note_offset_remainder; - if (paint_area % note_height != 0) - paint_area += note_height; - int notes_to_paint = paint_area / note_height; - int key_pattern_index = (notes_per_octave - 1) - (note_offset % notes_per_octave); - - int x_offset = horizontal_scrollbar().value(); - int horizontal_note_offset_remainder = fmod(x_offset, m_note_width); - int horizontal_paint_area = widget_inner_rect().width() + horizontal_note_offset_remainder; - if (fmod(horizontal_paint_area, m_note_width) != 0) - horizontal_paint_area += m_note_width; - int horizontal_notes_to_paint = horizontal_paint_area / m_note_width; - - GUI::Painter painter(*this); - painter.translate(frame_thickness(), frame_thickness()); - painter.add_clip_rect(event.rect()); - painter.translate(-horizontal_note_offset_remainder, -note_offset_remainder); - - for (int y = 0; y < notes_to_paint; ++y) { - int y_pos = y * note_height; - - int note = (note_count - note_offset - 1) - y; - for (int x = 0; x < horizontal_notes_to_paint; ++x) { - // This is needed to avoid rounding errors. You can't just use - // m_note_width as the width. - int x_pos = x * m_note_width; - int next_x_pos = (x + 1) * m_note_width; - int distance_to_next_x = next_x_pos - x_pos; - Gfx::IntRect rect(x_pos, y_pos, distance_to_next_x, note_height); - - if (key_pattern[key_pattern_index] == Black) - painter.fill_rect(rect, Color::LightGray); - else - painter.fill_rect(rect, Color::White); - - if (keys_widget() && keys_widget()->note_is_set(note)) - painter.fill_rect(rect, note_pressed_color.with_alpha(128)); - - painter.draw_line(rect.top_right(), rect.bottom_right(), Color::Black); - painter.draw_line(rect.bottom_left(), rect.bottom_right(), Color::Black); - } - - if (--key_pattern_index == -1) - key_pattern_index = notes_per_octave - 1; - } - - painter.translate(-x_offset, -y_offset); - painter.translate(horizontal_note_offset_remainder, note_offset_remainder); - - for (int note = note_count - (note_offset + notes_to_paint); note <= (note_count - 1) - note_offset; ++note) { - int y = ((note_count - 1) - note) * note_height; - for (auto roll_note : m_track_manager.current_track().roll_notes(note)) { - int x = m_roll_width * (static_cast<double>(roll_note.on_sample) / roll_length); - int width = m_roll_width * (static_cast<double>(roll_note.length()) / roll_length); - if (x + width < x_offset || x > x_offset + widget_inner_rect().width()) - continue; - if (width < 2) - width = 2; - - int height = note_height; - - Gfx::IntRect rect(x, y, width, height); - painter.fill_rect(rect, note_pressed_color); - painter.draw_rect(rect, Color::Black); - } - Gfx::IntRect note_name_rect(3, y, 1, note_height); - const char* note_name = note_names[note % notes_per_octave]; - - painter.draw_text(note_name_rect, note_name, Gfx::TextAlignment::CenterLeft); - note_name_rect.move_by(Gfx::FontDatabase::default_font().width(note_name) + 2, 0); - if (note % notes_per_octave == 0) - painter.draw_text(note_name_rect, String::formatted("{}", note / notes_per_octave + 1), Gfx::TextAlignment::CenterLeft); - } - - int x = m_roll_width * (static_cast<double>(m_track_manager.time()) / roll_length); - if (x > x_offset && x <= x_offset + widget_inner_rect().width()) - painter.draw_line({ x, 0 }, { x, roll_height }, Gfx::Color::Black); - - GUI::Frame::paint_event(event); -} - -void RollWidget::mousedown_event(GUI::MouseEvent& event) -{ - if (!widget_inner_rect().contains(event.x(), event.y())) - return; - - m_note_drag_start = event.position(); - - int y = (m_note_drag_start.value().y() + vertical_scrollbar().value()) - frame_thickness(); - y /= note_height; - m_drag_note = (note_count - 1) - y; - - mousemove_event(event); -} - -void RollWidget::mousemove_event(GUI::MouseEvent& event) -{ - if (!m_note_drag_start.has_value()) - return; - - if (m_note_drag_location.has_value()) { - // Clear previous note - m_track_manager.current_track().set_roll_note(m_drag_note, m_note_drag_location.value().on_sample, m_note_drag_location.value().off_sample); - } - - auto get_note_x = [&](int x0) { - // There's a case where we can't just use x / m_note_width. For example, if - // your m_note_width is 3.1 you will have a rect starting at 3. When that - // leftmost pixel of the rect is clicked you will do 3 / 3.1 which is 0 - // and not 1. We can avoid that case by shifting x by 1 if m_note_width is - // fractional, being careful not to shift out of bounds. - int x = (x0 + horizontal_scrollbar().value()) - frame_thickness(); - bool note_width_is_fractional = m_note_width - static_cast<int>(m_note_width) != 0; - bool x_is_not_last = x != widget_inner_rect().width() - 1; - if (note_width_is_fractional && x_is_not_last) - ++x; - x /= m_note_width; - return x; - }; - - int x0 = get_note_x(m_note_drag_start.value().x()); - int x1 = get_note_x(event.x()); - - u32 on_sample = roll_length * (static_cast<double>(min(x0, x1)) / m_num_notes); - u32 off_sample = (roll_length * (static_cast<double>(max(x0, x1) + 1) / m_num_notes)) - 1; - m_track_manager.current_track().set_roll_note(m_drag_note, on_sample, off_sample); - m_note_drag_location = RollNote({ on_sample, off_sample }); - - update(); -} - -void RollWidget::mouseup_event([[maybe_unused]] GUI::MouseEvent& event) -{ - m_note_drag_start = {}; - m_note_drag_location = {}; -} - -// FIXME: Implement zoom and horizontal scroll events in LibGUI, not here. -void RollWidget::mousewheel_event(GUI::MouseEvent& event) -{ - if (event.modifiers() & KeyModifier::Mod_Shift) { - horizontal_scrollbar().set_value(horizontal_scrollbar().value() + (event.wheel_delta() * horizontal_scroll_sensitivity)); - return; - } - - if (!(event.modifiers() & KeyModifier::Mod_Ctrl)) { - GUI::ScrollableWidget::mousewheel_event(event); - return; - } - - double multiplier = event.wheel_delta() >= 0 ? 0.5 : 2; - - if (m_zoom_level * multiplier > max_zoom) - return; - - if (m_zoom_level * multiplier < 1) { - if (m_zoom_level == 1) - return; - m_zoom_level = 1; - } else { - m_zoom_level *= multiplier; - } - - int absolute_x_of_pixel_at_cursor = horizontal_scrollbar().value() + event.position().x(); - int absolute_x_of_pixel_at_cursor_after_resize = absolute_x_of_pixel_at_cursor * multiplier; - int new_scrollbar = absolute_x_of_pixel_at_cursor_after_resize - event.position().x(); - - m_roll_width = widget_inner_rect().width() * m_zoom_level; - set_content_size({ m_roll_width, roll_height }); - - horizontal_scrollbar().set_value(new_scrollbar); -} diff --git a/Applications/Piano/RollWidget.h b/Applications/Piano/RollWidget.h deleted file mode 100644 index bbfcc8bb62..0000000000 --- a/Applications/Piano/RollWidget.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * Copyright (c) 2019-2020, William McPherson <willmcpherson2@gmail.com> - * 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 "KeysWidget.h" -#include "Music.h" -#include <LibGUI/ScrollableWidget.h> - -class TrackManager; - -class RollWidget final : public GUI::ScrollableWidget { - C_OBJECT(RollWidget) -public: - virtual ~RollWidget() override; - - const KeysWidget* keys_widget() const { return m_keys_widget; } - void set_keys_widget(const KeysWidget* widget) { m_keys_widget = widget; } - -private: - explicit RollWidget(TrackManager&); - - virtual void paint_event(GUI::PaintEvent&) override; - virtual void mousedown_event(GUI::MouseEvent& event) override; - virtual void mousemove_event(GUI::MouseEvent& event) override; - virtual void mouseup_event(GUI::MouseEvent& event) override; - virtual void mousewheel_event(GUI::MouseEvent&) override; - - TrackManager& m_track_manager; - const KeysWidget* m_keys_widget; - - int m_roll_width { 0 }; - int m_num_notes { 0 }; - double m_note_width { 0.0 }; - int m_zoom_level { 1 }; - - Optional<Gfx::IntPoint> m_note_drag_start; - Optional<RollNote> m_note_drag_location; - int m_drag_note; -}; diff --git a/Applications/Piano/SamplerWidget.cpp b/Applications/Piano/SamplerWidget.cpp deleted file mode 100644 index 72704f28c7..0000000000 --- a/Applications/Piano/SamplerWidget.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2020, William McPherson <willmcpherson2@gmail.com> - * 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 "SamplerWidget.h" -#include "TrackManager.h" -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Button.h> -#include <LibGUI/FilePicker.h> -#include <LibGUI/Label.h> -#include <LibGUI/MessageBox.h> -#include <LibGUI/Painter.h> - -WaveEditor::WaveEditor(TrackManager& track_manager) - : m_track_manager(track_manager) -{ -} - -WaveEditor::~WaveEditor() -{ -} - -int WaveEditor::sample_to_y(double percentage) const -{ - double portion_of_half_height = percentage * ((frame_inner_rect().height() - 1) / 2.0); - double y = (frame_inner_rect().height() / 2.0) + portion_of_half_height; - return y; -} - -void WaveEditor::paint_event(GUI::PaintEvent& event) -{ - GUI::Frame::paint_event(event); - - GUI::Painter painter(*this); - painter.fill_rect(frame_inner_rect(), Color::Black); - - auto recorded_sample = m_track_manager.current_track().recorded_sample(); - if (recorded_sample.is_empty()) - return; - - double width_scale = static_cast<double>(frame_inner_rect().width()) / recorded_sample.size(); - - painter.translate(frame_thickness(), frame_thickness()); - - int prev_x = 0; - int left_prev_y = sample_to_y(recorded_sample[0].left); - int right_prev_y = sample_to_y(recorded_sample[0].right); - painter.set_pixel({ prev_x, left_prev_y }, left_wave_colors[RecordedSample]); - painter.set_pixel({ prev_x, right_prev_y }, right_wave_colors[RecordedSample]); - - for (size_t x = 1; x < recorded_sample.size(); ++x) { - int left_y = sample_to_y(recorded_sample[x].left); - int right_y = sample_to_y(recorded_sample[x].right); - - Gfx::IntPoint left_point1(prev_x * width_scale, left_prev_y); - Gfx::IntPoint left_point2(x * width_scale, left_y); - painter.draw_line(left_point1, left_point2, left_wave_colors[RecordedSample]); - - Gfx::IntPoint right_point1(prev_x * width_scale, right_prev_y); - Gfx::IntPoint right_point2(x * width_scale, right_y); - painter.draw_line(right_point1, right_point2, right_wave_colors[RecordedSample]); - - prev_x = x; - left_prev_y = left_y; - right_prev_y = right_y; - } -} - -SamplerWidget::SamplerWidget(TrackManager& track_manager) - : m_track_manager(track_manager) -{ - set_layout<GUI::VerticalBoxLayout>(); - layout()->set_margins({ 10, 10, 10, 10 }); - layout()->set_spacing(10); - set_fill_with_background_color(true); - - m_open_button_and_recorded_sample_name_container = add<GUI::Widget>(); - m_open_button_and_recorded_sample_name_container->set_layout<GUI::HorizontalBoxLayout>(); - m_open_button_and_recorded_sample_name_container->layout()->set_spacing(10); - m_open_button_and_recorded_sample_name_container->set_fixed_height(24); - - m_open_button = m_open_button_and_recorded_sample_name_container->add<GUI::Button>(); - m_open_button->set_fixed_size(24, 24); - m_open_button->set_focus_policy(GUI::FocusPolicy::TabFocus); - m_open_button->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/open.png")); - m_open_button->on_click = [this](auto) { - Optional<String> open_path = GUI::FilePicker::get_open_filepath(window()); - if (!open_path.has_value()) - return; - String error_string = m_track_manager.current_track().set_recorded_sample(open_path.value()); - if (!error_string.is_empty()) { - GUI::MessageBox::show(window(), String::formatted("Failed to load WAV file: {}", error_string.characters()), "Error", GUI::MessageBox::Type::Error); - return; - } - m_recorded_sample_name->set_text(open_path.value()); - m_wave_editor->update(); - }; - - m_recorded_sample_name = m_open_button_and_recorded_sample_name_container->add<GUI::Label>("No sample loaded"); - m_recorded_sample_name->set_text_alignment(Gfx::TextAlignment::CenterLeft); - - m_wave_editor = add<WaveEditor>(m_track_manager); - m_wave_editor->set_fixed_height(100); -} - -SamplerWidget::~SamplerWidget() -{ -} diff --git a/Applications/Piano/SamplerWidget.h b/Applications/Piano/SamplerWidget.h deleted file mode 100644 index b99dbcbe02..0000000000 --- a/Applications/Piano/SamplerWidget.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2020, William McPherson <willmcpherson2@gmail.com> - * 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/Frame.h> - -class TrackManager; - -class WaveEditor final : public GUI::Frame { - C_OBJECT(WaveEditor) -public: - virtual ~WaveEditor() override; - -private: - explicit WaveEditor(TrackManager&); - - virtual void paint_event(GUI::PaintEvent&) override; - - int sample_to_y(double percentage) const; - - TrackManager& m_track_manager; -}; - -class SamplerWidget final : public GUI::Frame { - C_OBJECT(SamplerWidget) -public: - virtual ~SamplerWidget() override; - -private: - explicit SamplerWidget(TrackManager&); - - TrackManager& m_track_manager; - - RefPtr<GUI::Widget> m_open_button_and_recorded_sample_name_container; - RefPtr<GUI::Button> m_open_button; - RefPtr<GUI::Label> m_recorded_sample_name; - RefPtr<WaveEditor> m_wave_editor; -}; diff --git a/Applications/Piano/Track.cpp b/Applications/Piano/Track.cpp deleted file mode 100644 index 61d4351d06..0000000000 --- a/Applications/Piano/Track.cpp +++ /dev/null @@ -1,369 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * Copyright (c) 2019-2020, William McPherson <willmcpherson2@gmail.com> - * 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 "Track.h" -#include <AK/NumericLimits.h> -#include <LibAudio/Loader.h> -#include <math.h> - -Track::Track(const u32& time) - : m_time(time) -{ - set_sustain_impl(1000); - set_attack(5); - set_decay(1000); - set_release(5); -} - -Track::~Track() -{ -} - -void Track::fill_sample(Sample& sample) -{ - Audio::Sample new_sample; - - for (size_t note = 0; note < note_count; ++note) { - if (!m_roll_iters[note].is_end()) { - if (m_roll_iters[note]->on_sample == m_time) { - set_note(note, On); - } else if (m_roll_iters[note]->off_sample == m_time) { - set_note(note, Off); - ++m_roll_iters[note]; - if (m_roll_iters[note].is_end()) - m_roll_iters[note] = m_roll_notes[note].begin(); - } - } - - switch (m_envelope[note]) { - case Done: - continue; - case Attack: - m_power[note] += m_attack_step; - if (m_power[note] >= 1) { - m_power[note] = 1; - m_envelope[note] = Decay; - } - break; - case Decay: - m_power[note] -= m_decay_step; - if (m_power[note] < m_sustain_level) - m_power[note] = m_sustain_level; - break; - case Release: - m_power[note] -= m_release_step[note]; - if (m_power[note] <= 0) { - m_power[note] = 0; - m_envelope[note] = Done; - continue; - } - break; - default: - ASSERT_NOT_REACHED(); - } - - Audio::Sample note_sample; - switch (m_wave) { - case Wave::Sine: - note_sample = sine(note); - break; - case Wave::Saw: - note_sample = saw(note); - break; - case Wave::Square: - note_sample = square(note); - break; - case Wave::Triangle: - note_sample = triangle(note); - break; - case Wave::Noise: - note_sample = noise(); - break; - case Wave::RecordedSample: - note_sample = recorded_sample(note); - break; - default: - ASSERT_NOT_REACHED(); - } - new_sample.left += note_sample.left * m_power[note] * volume; - new_sample.right += note_sample.right * m_power[note] * volume; - } - - if (m_delay) { - new_sample.left += m_delay_buffer[m_delay_index].left * 0.333333; - new_sample.right += m_delay_buffer[m_delay_index].right * 0.333333; - m_delay_buffer[m_delay_index].left = new_sample.left; - m_delay_buffer[m_delay_index].right = new_sample.right; - if (++m_delay_index >= m_delay_samples) - m_delay_index = 0; - } - - sample.left += new_sample.left; - sample.right += new_sample.right; -} - -void Track::reset() -{ - memset(m_delay_buffer.data(), 0, m_delay_buffer.size() * sizeof(Sample)); - m_delay_index = 0; - - memset(m_note_on, 0, sizeof(m_note_on)); - memset(m_power, 0, sizeof(m_power)); - memset(m_envelope, 0, sizeof(m_envelope)); -} - -String Track::set_recorded_sample(const StringView& path) -{ - NonnullRefPtr<Audio::Loader> loader = Audio::Loader::create(path); - if (loader->has_error()) - return String(loader->error_string()); - auto buffer = loader->get_more_samples(60 * sample_rate * sizeof(Sample)); // 1 minute maximum - - if (!m_recorded_sample.is_empty()) - m_recorded_sample.clear(); - m_recorded_sample.resize(buffer->sample_count()); - - double peak = 0; - for (int i = 0; i < buffer->sample_count(); ++i) { - double left_abs = fabs(buffer->samples()[i].left); - double right_abs = fabs(buffer->samples()[i].right); - if (left_abs > peak) - peak = left_abs; - if (right_abs > peak) - peak = right_abs; - } - - if (peak) { - for (int i = 0; i < buffer->sample_count(); ++i) { - m_recorded_sample[i].left = buffer->samples()[i].left / peak; - m_recorded_sample[i].right = buffer->samples()[i].right / peak; - } - } - - return String::empty(); -} - -// All of the information for these waves is on Wikipedia. - -Audio::Sample Track::sine(size_t note) -{ - double pos = note_frequencies[note] / sample_rate; - double sin_step = pos * 2 * M_PI; - double w = sin(m_pos[note]); - m_pos[note] += sin_step; - return w; -} - -Audio::Sample Track::saw(size_t note) -{ - double saw_step = note_frequencies[note] / sample_rate; - double t = m_pos[note]; - double w = (0.5 - (t - floor(t))) * 2; - m_pos[note] += saw_step; - return w; -} - -Audio::Sample Track::square(size_t note) -{ - double pos = note_frequencies[note] / sample_rate; - double square_step = pos * 2 * M_PI; - double w = sin(m_pos[note]) >= 0 ? 1 : -1; - m_pos[note] += square_step; - return w; -} - -Audio::Sample Track::triangle(size_t note) -{ - double triangle_step = note_frequencies[note] / sample_rate; - double t = m_pos[note]; - double w = fabs(fmod((4 * t) + 1, 4) - 2) - 1; - m_pos[note] += triangle_step; - return w; -} - -Audio::Sample Track::noise() const -{ - double random_percentage = static_cast<double>(rand()) / RAND_MAX; - double w = (random_percentage * 2) - 1; - return w; -} - -Audio::Sample Track::recorded_sample(size_t note) -{ - int t = m_pos[note]; - if (t >= static_cast<int>(m_recorded_sample.size())) - return 0; - double w_left = m_recorded_sample[t].left; - double w_right = m_recorded_sample[t].right; - if (t + 1 < static_cast<int>(m_recorded_sample.size())) { - double t_fraction = m_pos[note] - t; - w_left += (m_recorded_sample[t + 1].left - m_recorded_sample[t].left) * t_fraction; - w_right += (m_recorded_sample[t + 1].right - m_recorded_sample[t].right) * t_fraction; - } - double recorded_sample_step = note_frequencies[note] / middle_c; - m_pos[note] += recorded_sample_step; - return { w_left, w_right }; -} - -static inline double calculate_step(double distance, int milliseconds) -{ - if (milliseconds == 0) - return distance; - - constexpr double samples_per_millisecond = sample_rate / 1000.0; - double samples = milliseconds * samples_per_millisecond; - double step = distance / samples; - return step; -} - -void Track::set_note(int note, Switch switch_note) -{ - ASSERT(note >= 0 && note < note_count); - - if (switch_note == On) { - if (m_note_on[note] == 0) { - m_pos[note] = 0; - m_envelope[note] = Attack; - } - ++m_note_on[note]; - } else { - if (m_note_on[note] >= 1) { - if (m_note_on[note] == 1) { - m_release_step[note] = calculate_step(m_power[note], m_release); - m_envelope[note] = Release; - } - --m_note_on[note]; - } - } - - ASSERT(m_note_on[note] != NumericLimits<u8>::max()); - ASSERT(m_power[note] >= 0); -} - -void Track::sync_roll(int note) -{ - auto it = m_roll_notes[note].find_if([&](auto& roll_note) { return roll_note.off_sample > m_time; }); - if (it.is_end()) - m_roll_iters[note] = m_roll_notes[note].begin(); - else - m_roll_iters[note] = it; -} - -void Track::set_roll_note(int note, u32 on_sample, u32 off_sample) -{ - RollNote new_roll_note = { on_sample, off_sample }; - - ASSERT(note >= 0 && note < note_count); - ASSERT(new_roll_note.off_sample < roll_length); - ASSERT(new_roll_note.length() >= 2); - - for (auto it = m_roll_notes[note].begin(); !it.is_end();) { - if (it->on_sample > new_roll_note.off_sample) { - m_roll_notes[note].insert_before(it, new_roll_note); - sync_roll(note); - return; - } - if (it->on_sample <= new_roll_note.on_sample && it->off_sample >= new_roll_note.on_sample) { - if (m_time >= it->on_sample && m_time <= it->off_sample) - set_note(note, Off); - m_roll_notes[note].remove(it); - sync_roll(note); - return; - } - if ((new_roll_note.on_sample == 0 || it->on_sample >= new_roll_note.on_sample - 1) && it->on_sample <= new_roll_note.off_sample) { - if (m_time >= new_roll_note.off_sample && m_time <= it->off_sample) - set_note(note, Off); - m_roll_notes[note].remove(it); - it = m_roll_notes[note].begin(); - continue; - } - ++it; - } - - m_roll_notes[note].append(new_roll_note); - sync_roll(note); -} - -void Track::set_wave(int wave) -{ - ASSERT(wave >= first_wave && wave <= last_wave); - m_wave = wave; -} - -void Track::set_wave(Direction direction) -{ - if (direction == Up) { - if (++m_wave > last_wave) - m_wave = first_wave; - } else { - if (--m_wave < first_wave) - m_wave = last_wave; - } -} - -void Track::set_attack(int attack) -{ - ASSERT(attack >= 0); - m_attack = attack; - m_attack_step = calculate_step(1, m_attack); -} - -void Track::set_decay(int decay) -{ - ASSERT(decay >= 0); - m_decay = decay; - m_decay_step = calculate_step(1 - m_sustain_level, m_decay); -} - -void Track::set_sustain_impl(int sustain) -{ - ASSERT(sustain >= 0); - m_sustain = sustain; - m_sustain_level = sustain / 1000.0; -} - -void Track::set_sustain(int sustain) -{ - set_sustain_impl(sustain); - set_decay(m_decay); -} - -void Track::set_release(int release) -{ - ASSERT(release >= 0); - m_release = release; -} - -void Track::set_delay(int delay) -{ - ASSERT(delay >= 0); - m_delay = delay; - m_delay_samples = m_delay == 0 ? 0 : (sample_rate / (beats_per_minute / 60)) / m_delay; - m_delay_buffer.resize(m_delay_samples); - memset(m_delay_buffer.data(), 0, m_delay_buffer.size() * sizeof(Sample)); - m_delay_index = 0; -} diff --git a/Applications/Piano/Track.h b/Applications/Piano/Track.h deleted file mode 100644 index 0acca429b0..0000000000 --- a/Applications/Piano/Track.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * Copyright (c) 2019-2020, William McPherson <willmcpherson2@gmail.com> - * 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 "Music.h" -#include <AK/Noncopyable.h> -#include <AK/SinglyLinkedList.h> -#include <LibAudio/Buffer.h> - -typedef AK::SinglyLinkedListIterator<SinglyLinkedList<RollNote>, RollNote> RollIter; - -class Track { - AK_MAKE_NONCOPYABLE(Track); - AK_MAKE_NONMOVABLE(Track); - -public: - explicit Track(const u32& time); - ~Track(); - - const Vector<Audio::Sample>& recorded_sample() const { return m_recorded_sample; } - const SinglyLinkedList<RollNote>& roll_notes(int note) const { return m_roll_notes[note]; } - int wave() const { return m_wave; } - int attack() const { return m_attack; } - int decay() const { return m_decay; } - int sustain() const { return m_sustain; } - int release() const { return m_release; } - int delay() const { return m_delay; } - - void fill_sample(Sample& sample); - void reset(); - String set_recorded_sample(const StringView& path); - void set_note(int note, Switch); - void set_roll_note(int note, u32 on_sample, u32 off_sample); - void set_wave(int wave); - void set_wave(Direction); - void set_attack(int attack); - void set_decay(int decay); - void set_sustain(int sustain); - void set_release(int release); - void set_delay(int delay); - -private: - Audio::Sample sine(size_t note); - Audio::Sample saw(size_t note); - Audio::Sample square(size_t note); - Audio::Sample triangle(size_t note); - Audio::Sample noise() const; - Audio::Sample recorded_sample(size_t note); - - void sync_roll(int note); - void set_sustain_impl(int sustain); - - Vector<Sample> m_delay_buffer; - - Vector<Audio::Sample> m_recorded_sample; - - u8 m_note_on[note_count] { 0 }; - double m_power[note_count] { 0 }; - double m_pos[note_count]; // Initialized lazily. - Envelope m_envelope[note_count] { Done }; - - int m_wave { first_wave }; - int m_attack; - double m_attack_step; - int m_decay; - double m_decay_step; - int m_sustain; - double m_sustain_level; - int m_release; - double m_release_step[note_count]; - int m_delay { 0 }; - size_t m_delay_samples { 0 }; - size_t m_delay_index { 0 }; - - const u32& m_time; - - SinglyLinkedList<RollNote> m_roll_notes[note_count]; - RollIter m_roll_iters[note_count]; -}; diff --git a/Applications/Piano/TrackManager.cpp b/Applications/Piano/TrackManager.cpp deleted file mode 100644 index 5aa1856a11..0000000000 --- a/Applications/Piano/TrackManager.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * Copyright (c) 2019-2020, William McPherson <willmcpherson2@gmail.com> - * 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 "TrackManager.h" - -TrackManager::TrackManager() -{ - add_track(); -} - -TrackManager::~TrackManager() -{ -} - -void TrackManager::fill_buffer(Span<Sample> buffer) -{ - memset(buffer.data(), 0, buffer_size); - - for (size_t i = 0; i < buffer.size(); ++i) { - for (auto& track : m_tracks) - track->fill_sample(buffer[i]); - - if (++m_time >= roll_length) { - m_time = 0; - if (!m_should_loop) - break; - } - } - - memcpy(m_current_back_buffer.data(), buffer.data(), buffer_size); - swap(m_current_front_buffer, m_current_back_buffer); -} - -void TrackManager::reset() -{ - memset(m_front_buffer.data(), 0, buffer_size); - memset(m_back_buffer.data(), 0, buffer_size); - - m_current_front_buffer = m_front_buffer.span(); - m_current_back_buffer = m_back_buffer.span(); - - m_time = 0; - - for (auto& track : m_tracks) - track->reset(); -} - -void TrackManager::set_note_current_octave(int note, Switch switch_note) -{ - current_track().set_note(note + octave_base(), switch_note); -} - -void TrackManager::set_octave(Direction direction) -{ - if (direction == Up) { - if (m_octave < octave_max) - ++m_octave; - } else { - if (m_octave > octave_min) - --m_octave; - } -} - -void TrackManager::set_octave(int octave) -{ - if (octave <= octave_max && octave >= octave_min) { - m_octave = octave; - } -} - -void TrackManager::add_track() -{ - m_tracks.append(make<Track>(m_time)); -} - -void TrackManager::next_track() -{ - if (++m_current_track >= m_tracks.size()) - m_current_track = 0; -} diff --git a/Applications/Piano/TrackManager.h b/Applications/Piano/TrackManager.h deleted file mode 100644 index 70cb661bc4..0000000000 --- a/Applications/Piano/TrackManager.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * Copyright (c) 2019-2020, William McPherson <willmcpherson2@gmail.com> - * 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 "Music.h" -#include "Track.h" -#include <AK/Array.h> -#include <AK/Noncopyable.h> -#include <AK/NonnullOwnPtr.h> -#include <AK/Vector.h> - -class TrackManager { - AK_MAKE_NONCOPYABLE(TrackManager); - AK_MAKE_NONMOVABLE(TrackManager); - -public: - TrackManager(); - ~TrackManager(); - - Track& current_track() { return *m_tracks[m_current_track]; } - Span<const Sample> buffer() const { return m_current_front_buffer; } - int octave() const { return m_octave; } - int octave_base() const { return (m_octave - octave_min) * 12; } - int time() const { return m_time; } - - void fill_buffer(Span<Sample>); - void reset(); - void set_should_loop(bool b) { m_should_loop = b; } - void set_note_current_octave(int note, Switch); - void set_octave(Direction); - void set_octave(int octave); - void add_track(); - void next_track(); - -private: - Vector<NonnullOwnPtr<Track>> m_tracks; - size_t m_current_track { 0 }; - - Array<Sample, sample_count> m_front_buffer; - Array<Sample, sample_count> m_back_buffer; - Span<Sample> m_current_front_buffer { m_front_buffer.span() }; - Span<Sample> m_current_back_buffer { m_back_buffer.span() }; - - int m_octave { 4 }; - - u32 m_time { 0 }; - - bool m_should_loop { true }; -}; diff --git a/Applications/Piano/WaveWidget.cpp b/Applications/Piano/WaveWidget.cpp deleted file mode 100644 index 9c40b46bde..0000000000 --- a/Applications/Piano/WaveWidget.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * Copyright (c) 2019-2020, William McPherson <willmcpherson2@gmail.com> - * 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 "WaveWidget.h" -#include "TrackManager.h" -#include <AK/NumericLimits.h> -#include <LibGUI/Painter.h> - -WaveWidget::WaveWidget(TrackManager& track_manager) - : m_track_manager(track_manager) -{ -} - -WaveWidget::~WaveWidget() -{ -} - -int WaveWidget::sample_to_y(int sample) const -{ - constexpr int nice_scale_factor = 4; - sample *= nice_scale_factor; - constexpr double sample_max = NumericLimits<i16>::max(); - double percentage = sample / sample_max; - double portion_of_half_height = percentage * ((frame_inner_rect().height() - 1) / 2.0); - double y = (frame_inner_rect().height() / 2.0) + portion_of_half_height; - return y; -} - -void WaveWidget::paint_event(GUI::PaintEvent& event) -{ - GUI::Painter painter(*this); - painter.fill_rect(frame_inner_rect(), Color::Black); - painter.translate(frame_thickness(), frame_thickness()); - - Color left_wave_color = left_wave_colors[m_track_manager.current_track().wave()]; - Color right_wave_color = right_wave_colors[m_track_manager.current_track().wave()]; - auto buffer = m_track_manager.buffer(); - double width_scale = static_cast<double>(frame_inner_rect().width()) / buffer.size(); - - int prev_x = 0; - int prev_y_left = sample_to_y(buffer[0].left); - int prev_y_right = sample_to_y(buffer[0].right); - painter.set_pixel({ prev_x, prev_y_left }, left_wave_color); - painter.set_pixel({ prev_x, prev_y_right }, right_wave_color); - - for (size_t x = 1; x < buffer.size(); ++x) { - int y_left = sample_to_y(buffer[x].left); - int y_right = sample_to_y(buffer[x].right); - - Gfx::IntPoint point1_left(prev_x * width_scale, prev_y_left); - Gfx::IntPoint point2_left(x * width_scale, y_left); - painter.draw_line(point1_left, point2_left, left_wave_color); - - Gfx::IntPoint point1_right(prev_x * width_scale, prev_y_right); - Gfx::IntPoint point2_right(x * width_scale, y_right); - painter.draw_line(point1_right, point2_right, right_wave_color); - - prev_x = x; - prev_y_left = y_left; - prev_y_right = y_right; - } - - GUI::Frame::paint_event(event); -} diff --git a/Applications/Piano/WaveWidget.h b/Applications/Piano/WaveWidget.h deleted file mode 100644 index e8b9cd4d5f..0000000000 --- a/Applications/Piano/WaveWidget.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * Copyright (c) 2019-2020, William McPherson <willmcpherson2@gmail.com> - * 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/Frame.h> - -class TrackManager; - -class WaveWidget final : public GUI::Frame { - C_OBJECT(WaveWidget) -public: - virtual ~WaveWidget() override; - -private: - explicit WaveWidget(TrackManager&); - - virtual void paint_event(GUI::PaintEvent&) override; - - int sample_to_y(int sample) const; - - TrackManager& m_track_manager; -}; diff --git a/Applications/Piano/main.cpp b/Applications/Piano/main.cpp deleted file mode 100644 index dc75e2944d..0000000000 --- a/Applications/Piano/main.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * Copyright (c) 2019-2020, William McPherson <willmcpherson2@gmail.com> - * 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 "MainWidget.h" -#include "TrackManager.h" -#include <AK/Array.h> -#include <LibAudio/ClientConnection.h> -#include <LibAudio/WavWriter.h> -#include <LibCore/EventLoop.h> -#include <LibCore/File.h> -#include <LibGUI/Action.h> -#include <LibGUI/Application.h> -#include <LibGUI/FilePicker.h> -#include <LibGUI/Menu.h> -#include <LibGUI/MenuBar.h> -#include <LibGUI/MessageBox.h> -#include <LibGUI/Window.h> -#include <LibGfx/Bitmap.h> -#include <LibThread/Thread.h> - -int main(int argc, char** argv) -{ - if (pledge("stdio thread rpath accept cpath wpath shared_buffer unix fattr", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto app = GUI::Application::construct(argc, argv); - - if (pledge("stdio thread rpath accept cpath wpath shared_buffer unix", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto audio_client = Audio::ClientConnection::construct(); - audio_client->handshake(); - - TrackManager track_manager; - - auto app_icon = GUI::Icon::default_icon("app-piano"); - auto window = GUI::Window::construct(); - auto& main_widget = window->set_main_widget<MainWidget>(track_manager); - window->set_title("Piano"); - window->resize(840, 600); - window->set_icon(app_icon.bitmap_for_size(16)); - window->show(); - - Audio::WavWriter wav_writer; - Optional<String> save_path; - bool need_to_write_wav = false; - - auto audio_thread = LibThread::Thread::construct([&] { - auto audio = Core::File::construct("/dev/audio"); - if (!audio->open(Core::IODevice::WriteOnly)) { - dbgln("Can't open audio device: {}", audio->error_string()); - return 1; - } - - Array<Sample, sample_count> buffer; - while (!Core::EventLoop::current().was_exit_requested()) { - track_manager.fill_buffer(buffer); - audio->write(reinterpret_cast<u8*>(buffer.data()), buffer_size); - Core::EventLoop::current().post_event(main_widget, make<Core::CustomEvent>(0)); - Core::EventLoop::wake(); - - if (need_to_write_wav) { - need_to_write_wav = false; - track_manager.reset(); - track_manager.set_should_loop(false); - do { - track_manager.fill_buffer(buffer); - wav_writer.write_samples(reinterpret_cast<u8*>(buffer.data()), buffer_size); - } while (track_manager.time()); - track_manager.reset(); - track_manager.set_should_loop(true); - wav_writer.finalize(); - } - } - return 0; - }); - audio_thread->start(); - - auto menubar = GUI::MenuBar::construct(); - - auto& app_menu = menubar->add_menu("Piano"); - app_menu.add_action(GUI::Action::create("Export", { Mod_Ctrl, Key_E }, [&](const GUI::Action&) { - save_path = GUI::FilePicker::get_save_filepath(window, "Untitled", "wav"); - if (!save_path.has_value()) - return; - wav_writer.set_file(save_path.value()); - if (wav_writer.has_error()) { - GUI::MessageBox::show(window, String::formatted("Failed to export WAV file: {}", wav_writer.error_string()), "Error", GUI::MessageBox::Type::Error); - wav_writer.clear_error(); - return; - } - need_to_write_wav = true; - })); - app_menu.add_separator(); - app_menu.add_action(GUI::CommonActions::make_quit_action([](auto&) { - GUI::Application::the()->quit(); - return; - })); - - auto& edit_menu = menubar->add_menu("Edit"); - main_widget.add_actions(edit_menu); - - auto& help_menu = menubar->add_menu("Help"); - help_menu.add_action(GUI::CommonActions::make_about_action("Piano", app_icon, window)); - - app->set_menubar(move(menubar)); - - return app->exec(); -} diff --git a/Applications/PixelPaint/BrushTool.cpp b/Applications/PixelPaint/BrushTool.cpp deleted file mode 100644 index 200e270ea7..0000000000 --- a/Applications/PixelPaint/BrushTool.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (c) 2020, Ben Jilks <benjyjilks@gmail.com> - * 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 "BrushTool.h" -#include "ImageEditor.h" -#include "Layer.h" -#include <LibGUI/Action.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Label.h> -#include <LibGUI/Painter.h> -#include <LibGUI/Slider.h> -#include <LibGfx/Color.h> -#include <LibGfx/Rect.h> -#include <utility> - -namespace PixelPaint { - -BrushTool::BrushTool() -{ -} - -BrushTool::~BrushTool() -{ -} - -void BrushTool::on_mousedown(Layer&, GUI::MouseEvent& event, GUI::MouseEvent&) -{ - if (event.button() != GUI::MouseButton::Left && event.button() != GUI::MouseButton::Right) - return; - - m_last_position = event.position(); -} - -void BrushTool::on_mousemove(Layer& layer, GUI::MouseEvent& event, GUI::MouseEvent&) -{ - if (!(event.buttons() & GUI::MouseButton::Left || event.buttons() & GUI::MouseButton::Right)) - return; - - draw_line(layer.bitmap(), m_editor->color_for(event), m_last_position, event.position()); - layer.did_modify_bitmap(*m_editor->image()); - m_last_position = event.position(); - m_was_drawing = true; -} - -void BrushTool::on_mouseup(Layer&, GUI::MouseEvent&, GUI::MouseEvent&) -{ - if (m_was_drawing) { - m_editor->did_complete_action(); - m_was_drawing = false; - } -} - -void BrushTool::draw_point(Gfx::Bitmap& bitmap, const Gfx::Color& color, const Gfx::IntPoint& point) -{ - for (int y = point.y() - m_size; y < point.y() + m_size; y++) { - for (int x = point.x() - m_size; x < point.x() + m_size; x++) { - auto distance = point.distance_from({ x, y }); - if (x < 0 || x >= bitmap.width() || y < 0 || y >= bitmap.height()) - continue; - if (distance >= m_size) - continue; - - auto falloff = (1.0 - (distance / (float)m_size)) * (1.0f / (100 - m_hardness)); - auto pixel_color = color; - pixel_color.set_alpha(falloff * 255); - bitmap.set_pixel(x, y, bitmap.get_pixel(x, y).blend(pixel_color)); - } - } -} - -void BrushTool::draw_line(Gfx::Bitmap& bitmap, const Gfx::Color& color, const Gfx::IntPoint& start, const Gfx::IntPoint& end) -{ - int length_x = end.x() - start.x(); - int length_y = end.y() - start.y(); - float y_step = length_y == 0 ? 0 : (float)(length_y) / (float)(length_x); - if (y_step > abs(length_y)) - y_step = abs(length_y); - if (y_step < -abs(length_y)) - y_step = -abs(length_y); - if (y_step == 0 && start.x() == end.x()) - return; - - int start_x = start.x(); - int end_x = end.x(); - int start_y = start.y(); - int end_y = end.y(); - if (start_x > end_x) { - swap(start_x, end_x); - swap(start_y, end_y); - } - - float y = start_y; - for (int x = start_x; x <= end_x; x++) { - int start_step_y = y; - int end_step_y = y + y_step; - if (start_step_y > end_step_y) - swap(start_step_y, end_step_y); - for (int i = start_step_y; i <= end_step_y; i++) - draw_point(bitmap, color, { x, i }); - y += y_step; - } -} - -GUI::Widget* BrushTool::get_properties_widget() -{ - if (!m_properties_widget) { - m_properties_widget = GUI::Widget::construct(); - m_properties_widget->set_layout<GUI::VerticalBoxLayout>(); - - auto& size_container = m_properties_widget->add<GUI::Widget>(); - size_container.set_fixed_height(20); - size_container.set_layout<GUI::HorizontalBoxLayout>(); - - auto& size_label = size_container.add<GUI::Label>("Size:"); - size_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - size_label.set_fixed_size(80, 20); - - auto& size_slider = size_container.add<GUI::HorizontalSlider>(); - size_slider.set_fixed_height(20); - size_slider.set_range(1, 100); - size_slider.set_value(m_size); - size_slider.on_change = [this](int value) { - m_size = value; - }; - - auto& hardness_container = m_properties_widget->add<GUI::Widget>(); - hardness_container.set_fixed_height(20); - hardness_container.set_layout<GUI::HorizontalBoxLayout>(); - - auto& hardness_label = hardness_container.add<GUI::Label>("Hardness:"); - hardness_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - hardness_label.set_fixed_size(80, 20); - - auto& hardness_slider = hardness_container.add<GUI::HorizontalSlider>(); - hardness_slider.set_fixed_height(20); - hardness_slider.set_range(1, 99); - hardness_slider.set_value(m_hardness); - hardness_slider.on_change = [this](int value) { - m_hardness = value; - }; - } - - return m_properties_widget.ptr(); -} - -} diff --git a/Applications/PixelPaint/BrushTool.h b/Applications/PixelPaint/BrushTool.h deleted file mode 100644 index 939ccdeef4..0000000000 --- a/Applications/PixelPaint/BrushTool.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2020, Ben Jilks <benjyjilks@gmail.com> - * 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 "Tool.h" - -namespace PixelPaint { - -class BrushTool final : public Tool { -public: - BrushTool(); - virtual ~BrushTool() override; - - virtual void on_mousedown(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; - virtual void on_mousemove(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; - virtual void on_mouseup(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; - virtual GUI::Widget* get_properties_widget() override; - -private: - RefPtr<GUI::Widget> m_properties_widget; - int m_size { 20 }; - int m_hardness { 80 }; - bool m_was_drawing { false }; - Gfx::IntPoint m_last_position; - - void draw_line(Gfx::Bitmap& bitmap, const Gfx::Color& color, const Gfx::IntPoint& start, const Gfx::IntPoint& end); - void draw_point(Gfx::Bitmap& bitmap, const Gfx::Color& color, const Gfx::IntPoint& point); -}; - -} diff --git a/Applications/PixelPaint/BucketTool.cpp b/Applications/PixelPaint/BucketTool.cpp deleted file mode 100644 index 6cffddac20..0000000000 --- a/Applications/PixelPaint/BucketTool.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "BucketTool.h" -#include "ImageEditor.h" -#include "Layer.h" -#include <AK/Queue.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Label.h> -#include <LibGUI/Painter.h> -#include <LibGUI/Slider.h> -#include <LibGfx/Bitmap.h> -#include <LibGfx/Rect.h> - -namespace PixelPaint { - -BucketTool::BucketTool() -{ -} - -BucketTool::~BucketTool() -{ -} - -static float color_distance_squared(const Gfx::Color& lhs, const Gfx::Color& rhs) -{ - int a = rhs.red() - lhs.red(); - int b = rhs.green() - lhs.green(); - int c = rhs.blue() - lhs.blue(); - return (a * a + b * b + c * c) / (255.0f * 255.0f); -} - -static void flood_fill(Gfx::Bitmap& bitmap, const Gfx::IntPoint& start_position, Color target_color, Color fill_color, int threshold) -{ - ASSERT(bitmap.bpp() == 32); - - if (target_color == fill_color) - return; - - if (!bitmap.rect().contains(start_position)) - return; - - float threshold_normalized_squared = (threshold / 100.0f) * (threshold / 100.0f); - - Queue<Gfx::IntPoint> queue; - queue.enqueue(start_position); - while (!queue.is_empty()) { - auto position = queue.dequeue(); - - auto pixel_color = bitmap.get_pixel<Gfx::StorageFormat::RGBA32>(position.x(), position.y()); - if (color_distance_squared(pixel_color, target_color) > threshold_normalized_squared) - continue; - - bitmap.set_pixel<Gfx::StorageFormat::RGBA32>(position.x(), position.y(), fill_color); - - if (position.x() != 0) - queue.enqueue(position.translated(-1, 0)); - - if (position.x() != bitmap.width() - 1) - queue.enqueue(position.translated(1, 0)); - - if (position.y() != 0) - queue.enqueue(position.translated(0, -1)); - - if (position.y() != bitmap.height() - 1) - queue.enqueue(position.translated(0, 1)); - } -} - -void BucketTool::on_mousedown(Layer& layer, GUI::MouseEvent& event, GUI::MouseEvent&) -{ - if (!layer.rect().contains(event.position())) - return; - - GUI::Painter painter(layer.bitmap()); - auto target_color = layer.bitmap().get_pixel(event.x(), event.y()); - - flood_fill(layer.bitmap(), event.position(), target_color, m_editor->color_for(event), m_threshold); - - layer.did_modify_bitmap(*m_editor->image()); - m_editor->did_complete_action(); -} - -GUI::Widget* BucketTool::get_properties_widget() -{ - if (!m_properties_widget) { - m_properties_widget = GUI::Widget::construct(); - m_properties_widget->set_layout<GUI::VerticalBoxLayout>(); - - auto& threshold_container = m_properties_widget->add<GUI::Widget>(); - threshold_container.set_fixed_height(20); - threshold_container.set_layout<GUI::HorizontalBoxLayout>(); - - auto& threshold_label = threshold_container.add<GUI::Label>("Threshold:"); - threshold_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - threshold_label.set_fixed_size(80, 20); - - auto& threshold_slider = threshold_container.add<GUI::HorizontalSlider>(); - threshold_slider.set_fixed_height(20); - threshold_slider.set_range(0, 100); - threshold_slider.set_value(m_threshold); - threshold_slider.on_change = [this](int value) { - m_threshold = value; - }; - } - - return m_properties_widget.ptr(); -} - -} diff --git a/Applications/PixelPaint/BucketTool.h b/Applications/PixelPaint/BucketTool.h deleted file mode 100644 index b4bf700815..0000000000 --- a/Applications/PixelPaint/BucketTool.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "Tool.h" - -namespace PixelPaint { - -class BucketTool final : public Tool { -public: - BucketTool(); - virtual ~BucketTool() override; - - virtual void on_mousedown(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; - virtual GUI::Widget* get_properties_widget() override; - -private: - RefPtr<GUI::Widget> m_properties_widget; - int m_threshold { 0 }; -}; - -} diff --git a/Applications/PixelPaint/CMakeLists.txt b/Applications/PixelPaint/CMakeLists.txt deleted file mode 100644 index 713845c80e..0000000000 --- a/Applications/PixelPaint/CMakeLists.txt +++ /dev/null @@ -1,27 +0,0 @@ -set(SOURCES - BrushTool.cpp - BucketTool.cpp - CreateNewImageDialog.cpp - CreateNewLayerDialog.cpp - EllipseTool.cpp - EraseTool.cpp - Image.cpp - ImageEditor.cpp - Layer.cpp - LayerListWidget.cpp - LayerPropertiesWidget.cpp - LineTool.cpp - main.cpp - MoveTool.cpp - PaletteWidget.cpp - PenTool.cpp - PickerTool.cpp - RectangleTool.cpp - SprayTool.cpp - ToolboxWidget.cpp - ToolPropertiesWidget.cpp - Tool.cpp -) - -serenity_app(PixelPaint ICON app-pixel-paint) -target_link_libraries(PixelPaint LibGUI LibGfx) diff --git a/Applications/PixelPaint/CreateNewImageDialog.cpp b/Applications/PixelPaint/CreateNewImageDialog.cpp deleted file mode 100644 index 0696b69ba8..0000000000 --- a/Applications/PixelPaint/CreateNewImageDialog.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2020, Ben Jilks <benjyjilks@gmail.com> - * 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 "CreateNewImageDialog.h" -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Button.h> -#include <LibGUI/Label.h> -#include <LibGUI/SpinBox.h> -#include <LibGUI/TextBox.h> - -namespace PixelPaint { - -CreateNewImageDialog::CreateNewImageDialog(GUI::Window* parent_window) - : Dialog(parent_window) -{ - set_title("Create new image"); - resize(200, 200); - - auto& main_widget = set_main_widget<GUI::Widget>(); - main_widget.set_fill_with_background_color(true); - - auto& layout = main_widget.set_layout<GUI::VerticalBoxLayout>(); - layout.set_margins({ 4, 4, 4, 4 }); - - auto& name_label = main_widget.add<GUI::Label>("Name:"); - name_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - - m_name_textbox = main_widget.add<GUI::TextBox>(); - m_name_textbox->on_change = [this] { - m_image_name = m_name_textbox->text(); - }; - - auto& width_label = main_widget.add<GUI::Label>("Width:"); - width_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - - auto& width_spinbox = main_widget.add<GUI::SpinBox>(); - - auto& height_label = main_widget.add<GUI::Label>("Height:"); - height_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - - auto& height_spinbox = main_widget.add<GUI::SpinBox>(); - - auto& button_container = main_widget.add<GUI::Widget>(); - button_container.set_layout<GUI::HorizontalBoxLayout>(); - - auto& ok_button = button_container.add<GUI::Button>("OK"); - ok_button.on_click = [this](auto) { - done(ExecOK); - }; - - auto& cancel_button = button_container.add<GUI::Button>("Cancel"); - cancel_button.on_click = [this](auto) { - done(ExecCancel); - }; - - width_spinbox.on_change = [this](int value) { - m_image_size.set_width(value); - }; - - height_spinbox.on_change = [this](int value) { - m_image_size.set_height(value); - }; - - width_spinbox.set_range(0, 16384); - height_spinbox.set_range(0, 16384); -} - -} diff --git a/Applications/PixelPaint/CreateNewImageDialog.h b/Applications/PixelPaint/CreateNewImageDialog.h deleted file mode 100644 index b46b65adab..0000000000 --- a/Applications/PixelPaint/CreateNewImageDialog.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2020, Ben Jilks <benjyjilks@gmail.com> - * 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/Dialog.h> - -namespace PixelPaint { - -class CreateNewImageDialog final : public GUI::Dialog { - C_OBJECT(CreateNewImageDialog) - -public: - const Gfx::IntSize& image_size() const { return m_image_size; } - const String& image_name() const { return m_image_name; } - -private: - CreateNewImageDialog(GUI::Window* parent_window); - - String m_image_name; - Gfx::IntSize m_image_size; - - RefPtr<GUI::TextBox> m_name_textbox; -}; - -} diff --git a/Applications/PixelPaint/CreateNewLayerDialog.cpp b/Applications/PixelPaint/CreateNewLayerDialog.cpp deleted file mode 100644 index 02f65b51ad..0000000000 --- a/Applications/PixelPaint/CreateNewLayerDialog.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2020, 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 "CreateNewLayerDialog.h" -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Button.h> -#include <LibGUI/Label.h> -#include <LibGUI/SpinBox.h> -#include <LibGUI/TextBox.h> - -namespace PixelPaint { - -CreateNewLayerDialog::CreateNewLayerDialog(const Gfx::IntSize& suggested_size, GUI::Window* parent_window) - : Dialog(parent_window) -{ - set_title("Create new layer"); - set_icon(parent_window->icon()); - resize(200, 200); - - auto& main_widget = set_main_widget<GUI::Widget>(); - main_widget.set_fill_with_background_color(true); - - auto& layout = main_widget.set_layout<GUI::VerticalBoxLayout>(); - layout.set_margins({ 4, 4, 4, 4 }); - - auto& name_label = main_widget.add<GUI::Label>("Name:"); - name_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - - m_name_textbox = main_widget.add<GUI::TextBox>(); - m_name_textbox->on_change = [this] { - m_layer_name = m_name_textbox->text(); - }; - - auto& width_label = main_widget.add<GUI::Label>("Width:"); - width_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - - auto& width_spinbox = main_widget.add<GUI::SpinBox>(); - - auto& height_label = main_widget.add<GUI::Label>("Height:"); - height_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - - auto& height_spinbox = main_widget.add<GUI::SpinBox>(); - - auto& button_container = main_widget.add<GUI::Widget>(); - button_container.set_layout<GUI::HorizontalBoxLayout>(); - - auto& ok_button = button_container.add<GUI::Button>("OK"); - ok_button.on_click = [this](auto) { - done(ExecOK); - }; - - auto& cancel_button = button_container.add<GUI::Button>("Cancel"); - cancel_button.on_click = [this](auto) { - done(ExecCancel); - }; - - width_spinbox.on_change = [this](int value) { - m_layer_size.set_width(value); - }; - - height_spinbox.on_change = [this](int value) { - m_layer_size.set_height(value); - }; - - width_spinbox.set_range(0, 16384); - height_spinbox.set_range(0, 16384); - - width_spinbox.set_value(suggested_size.width()); - height_spinbox.set_value(suggested_size.height()); -} - -} diff --git a/Applications/PixelPaint/CreateNewLayerDialog.h b/Applications/PixelPaint/CreateNewLayerDialog.h deleted file mode 100644 index bb07ff9943..0000000000 --- a/Applications/PixelPaint/CreateNewLayerDialog.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2020, 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/Dialog.h> - -namespace PixelPaint { - -class CreateNewLayerDialog final : public GUI::Dialog { - C_OBJECT(CreateNewLayerDialog); - -public: - const Gfx::IntSize& layer_size() const { return m_layer_size; } - const String& layer_name() const { return m_layer_name; } - -private: - CreateNewLayerDialog(const Gfx::IntSize& suggested_size, GUI::Window* parent_window); - - Gfx::IntSize m_layer_size; - String m_layer_name; - - RefPtr<GUI::TextBox> m_name_textbox; -}; - -} diff --git a/Applications/PixelPaint/EllipseTool.cpp b/Applications/PixelPaint/EllipseTool.cpp deleted file mode 100644 index 0501e2fb96..0000000000 --- a/Applications/PixelPaint/EllipseTool.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "EllipseTool.h" -#include "ImageEditor.h" -#include "Layer.h" -#include <LibGUI/Action.h> -#include <LibGUI/Menu.h> -#include <LibGUI/Painter.h> -#include <LibGfx/Rect.h> -#include <math.h> - -namespace PixelPaint { - -EllipseTool::EllipseTool() -{ -} - -EllipseTool::~EllipseTool() -{ -} - -void EllipseTool::draw_using(GUI::Painter& painter, const Gfx::IntRect& ellipse_intersecting_rect) -{ - switch (m_mode) { - case Mode::Outline: - painter.draw_ellipse_intersecting(ellipse_intersecting_rect, m_editor->color_for(m_drawing_button), m_thickness); - break; - default: - ASSERT_NOT_REACHED(); - } -} - -void EllipseTool::on_mousedown(Layer&, GUI::MouseEvent& event, GUI::MouseEvent&) -{ - if (event.button() != GUI::MouseButton::Left && event.button() != GUI::MouseButton::Right) - return; - - if (m_drawing_button != GUI::MouseButton::None) - return; - - m_drawing_button = event.button(); - m_ellipse_start_position = event.position(); - m_ellipse_end_position = event.position(); - m_editor->update(); -} - -void EllipseTool::on_mouseup(Layer& layer, GUI::MouseEvent& event, GUI::MouseEvent&) -{ - if (event.button() == m_drawing_button) { - GUI::Painter painter(layer.bitmap()); - draw_using(painter, Gfx::IntRect::from_two_points(m_ellipse_start_position, m_ellipse_end_position)); - m_drawing_button = GUI::MouseButton::None; - m_editor->update(); - m_editor->did_complete_action(); - } -} - -void EllipseTool::on_mousemove(Layer&, GUI::MouseEvent& event, GUI::MouseEvent&) -{ - if (m_drawing_button == GUI::MouseButton::None) - return; - - m_ellipse_end_position = event.position(); - m_editor->update(); -} - -void EllipseTool::on_second_paint(const Layer& layer, GUI::PaintEvent& event) -{ - if (m_drawing_button == GUI::MouseButton::None) - return; - - GUI::Painter painter(*m_editor); - painter.add_clip_rect(event.rect()); - auto preview_start = m_editor->layer_position_to_editor_position(layer, m_ellipse_start_position).to_type<int>(); - auto preview_end = m_editor->layer_position_to_editor_position(layer, m_ellipse_end_position).to_type<int>(); - draw_using(painter, Gfx::IntRect::from_two_points(preview_start, preview_end)); -} - -void EllipseTool::on_keydown(GUI::KeyEvent& event) -{ - if (event.key() == Key_Escape && m_drawing_button != GUI::MouseButton::None) { - m_drawing_button = GUI::MouseButton::None; - m_editor->update(); - event.accept(); - } -} - -void EllipseTool::on_tool_button_contextmenu(GUI::ContextMenuEvent& event) -{ - if (!m_context_menu) { - m_context_menu = GUI::Menu::construct(); - m_context_menu->add_action(GUI::Action::create("Outline", [this](auto&) { - m_mode = Mode::Outline; - })); - m_context_menu->add_separator(); - m_thickness_actions.set_exclusive(true); - auto insert_action = [&](int size, bool checked = false) { - auto action = GUI::Action::create_checkable(String::number(size), [this, size](auto&) { - m_thickness = size; - }); - action->set_checked(checked); - m_thickness_actions.add_action(*action); - m_context_menu->add_action(move(action)); - }; - insert_action(1, true); - insert_action(2); - insert_action(3); - insert_action(4); - } - m_context_menu->popup(event.screen_position()); -} - -} diff --git a/Applications/PixelPaint/EllipseTool.h b/Applications/PixelPaint/EllipseTool.h deleted file mode 100644 index 0b1c10c89a..0000000000 --- a/Applications/PixelPaint/EllipseTool.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "Tool.h" -#include <LibGUI/ActionGroup.h> -#include <LibGfx/Point.h> - -namespace PixelPaint { - -class EllipseTool final : public Tool { -public: - EllipseTool(); - virtual ~EllipseTool() override; - - virtual void on_mousedown(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; - virtual void on_mousemove(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; - virtual void on_mouseup(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; - virtual void on_tool_button_contextmenu(GUI::ContextMenuEvent&) override; - virtual void on_second_paint(const Layer&, GUI::PaintEvent&) override; - virtual void on_keydown(GUI::KeyEvent&) override; - -private: - enum class Mode { - Outline, - // FIXME: Add Mode::Fill - }; - - void draw_using(GUI::Painter&, const Gfx::IntRect&); - - GUI::MouseButton m_drawing_button { GUI::MouseButton::None }; - Gfx::IntPoint m_ellipse_start_position; - Gfx::IntPoint m_ellipse_end_position; - RefPtr<GUI::Menu> m_context_menu; - int m_thickness { 1 }; - GUI::ActionGroup m_thickness_actions; - Mode m_mode { Mode::Outline }; -}; - -} diff --git a/Applications/PixelPaint/EraseTool.cpp b/Applications/PixelPaint/EraseTool.cpp deleted file mode 100644 index 19d1482ba8..0000000000 --- a/Applications/PixelPaint/EraseTool.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "EraseTool.h" -#include "ImageEditor.h" -#include "Layer.h" -#include <LibGUI/Action.h> -#include <LibGUI/Menu.h> -#include <LibGUI/Painter.h> -#include <LibGfx/Bitmap.h> - -namespace PixelPaint { - -EraseTool::EraseTool() -{ -} - -EraseTool::~EraseTool() -{ -} - -Gfx::IntRect EraseTool::build_rect(const Gfx::IntPoint& pos, const Gfx::IntRect& widget_rect) -{ - const int base_eraser_size = 10; - const int eraser_size = (base_eraser_size * m_thickness); - const int eraser_radius = eraser_size / 2; - const auto ex = pos.x(); - const auto ey = pos.y(); - return Gfx::IntRect(ex - eraser_radius, ey - eraser_radius, eraser_size, eraser_size).intersected(widget_rect); -} - -void EraseTool::on_mousedown(Layer& layer, GUI::MouseEvent& event, GUI::MouseEvent&) -{ - if (event.button() != GUI::MouseButton::Left && event.button() != GUI::MouseButton::Right) - return; - Gfx::IntRect r = build_rect(event.position(), layer.rect()); - GUI::Painter painter(layer.bitmap()); - painter.clear_rect(r, get_color()); - layer.did_modify_bitmap(*m_editor->image()); -} - -void EraseTool::on_mousemove(Layer& layer, GUI::MouseEvent& event, GUI::MouseEvent&) -{ - if (event.buttons() & GUI::MouseButton::Left || event.buttons() & GUI::MouseButton::Right) { - Gfx::IntRect r = build_rect(event.position(), layer.rect()); - GUI::Painter painter(layer.bitmap()); - painter.clear_rect(r, get_color()); - layer.did_modify_bitmap(*m_editor->image()); - } -} - -void EraseTool::on_mouseup(Layer&, GUI::MouseEvent& event, GUI::MouseEvent&) -{ - if (event.button() != GUI::MouseButton::Left && event.button() != GUI::MouseButton::Right) - return; - m_editor->did_complete_action(); -} - -void EraseTool::on_tool_button_contextmenu(GUI::ContextMenuEvent& event) -{ - if (!m_context_menu) { - m_context_menu = GUI::Menu::construct(); - - auto eraser_color_toggler = GUI::Action::create_checkable("Use secondary color", [&](auto& action) { - m_use_secondary_color = action.is_checked(); - }); - eraser_color_toggler->set_checked(m_use_secondary_color); - - m_context_menu->add_action(eraser_color_toggler); - m_context_menu->add_separator(); - - m_thickness_actions.set_exclusive(true); - auto insert_action = [&](int size, bool checked = false) { - auto action = GUI::Action::create_checkable(String::number(size), [this, size](auto&) { - m_thickness = size; - }); - action->set_checked(checked); - m_thickness_actions.add_action(*action); - m_context_menu->add_action(move(action)); - }; - insert_action(1, true); - insert_action(2); - insert_action(3); - insert_action(4); - } - - m_context_menu->popup(event.screen_position()); -} - -Color EraseTool::get_color() const -{ - if (m_use_secondary_color) - return m_editor->secondary_color(); - return Color(255, 255, 255, 0); -} - -} diff --git a/Applications/PixelPaint/EraseTool.h b/Applications/PixelPaint/EraseTool.h deleted file mode 100644 index 16241e87f0..0000000000 --- a/Applications/PixelPaint/EraseTool.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "Tool.h" -#include <LibGUI/ActionGroup.h> -#include <LibGfx/Forward.h> -#include <LibGfx/Point.h> - -namespace PixelPaint { - -class EraseTool final : public Tool { -public: - EraseTool(); - virtual ~EraseTool() override; - - virtual void on_mousedown(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; - virtual void on_mousemove(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; - virtual void on_mouseup(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; - virtual void on_tool_button_contextmenu(GUI::ContextMenuEvent&) override; - -private: - Gfx::Color get_color() const; - Gfx::IntRect build_rect(const Gfx::IntPoint& pos, const Gfx::IntRect& widget_rect); - RefPtr<GUI::Menu> m_context_menu; - - bool m_use_secondary_color { false }; - int m_thickness { 1 }; - GUI::ActionGroup m_thickness_actions; -}; - -} diff --git a/Applications/PixelPaint/FilterParams.h b/Applications/PixelPaint/FilterParams.h deleted file mode 100644 index 398caecf9f..0000000000 --- a/Applications/PixelPaint/FilterParams.h +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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/Action.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Button.h> -#include <LibGUI/CheckBox.h> -#include <LibGUI/Dialog.h> -#include <LibGUI/Menu.h> -#include <LibGUI/Painter.h> -#include <LibGUI/TextBox.h> -#include <LibGfx/Filters/BoxBlurFilter.h> -#include <LibGfx/Filters/GenericConvolutionFilter.h> -#include <LibGfx/Filters/LaplacianFilter.h> -#include <LibGfx/Filters/SharpenFilter.h> -#include <LibGfx/Filters/SpatialGaussianBlurFilter.h> - -namespace PixelPaint { - -template<typename Filter> -struct FilterParameters { -}; - -template<size_t N> -class GenericConvolutionFilterInputDialog : public GUI::Dialog { - C_OBJECT(GenericConvolutionFilterInputDialog); - -public: - const Matrix<N, float>& matrix() const { return m_matrix; } - bool should_wrap() const { return m_should_wrap; } - -private: - explicit GenericConvolutionFilterInputDialog(GUI::Window* parent_window) - : Dialog(parent_window) - { - // FIXME: Help! Make this GUI less ugly. - StringBuilder builder; - builder.appendf("%zux%zu", N, N); - builder.append(" Convolution"); - set_title(builder.string_view()); - - resize(200, 250); - auto& main_widget = set_main_widget<GUI::Frame>(); - main_widget.set_frame_shape(Gfx::FrameShape::Container); - main_widget.set_frame_shadow(Gfx::FrameShadow::Raised); - main_widget.set_fill_with_background_color(true); - auto& layout = main_widget.template set_layout<GUI::VerticalBoxLayout>(); - layout.set_margins({ 4, 4, 4, 4 }); - - size_t index = 0; - size_t columns = N; - size_t rows = N; - - for (size_t row = 0; row < rows; ++row) { - auto& horizontal_container = main_widget.template add<GUI::Widget>(); - horizontal_container.template set_layout<GUI::HorizontalBoxLayout>(); - for (size_t column = 0; column < columns; ++column) { - if (index < columns * rows) { - auto& textbox = horizontal_container.template add<GUI::TextBox>(); - textbox.on_change = [&, row = row, column = column] { - auto& element = m_matrix.elements()[row][column]; - char* endptr = nullptr; - auto value = strtof(textbox.text().characters(), &endptr); - if (endptr != nullptr) - element = value; - else - textbox.set_text(""); - }; - } else { - horizontal_container.template add<GUI::Widget>(); - } - } - } - - auto& norm_checkbox = main_widget.template add<GUI::CheckBox>("Normalize"); - norm_checkbox.set_checked(false); - - auto& wrap_checkbox = main_widget.template add<GUI::CheckBox>("Wrap"); - wrap_checkbox.set_checked(m_should_wrap); - - auto& button = main_widget.template add<GUI::Button>("Done"); - button.on_click = [&](auto) { - m_should_wrap = wrap_checkbox.is_checked(); - if (norm_checkbox.is_checked()) - normalize(m_matrix); - done(ExecOK); - }; - } - - Matrix<N, float> m_matrix {}; - bool m_should_wrap { false }; -}; - -template<size_t N> -struct FilterParameters<Gfx::SpatialGaussianBlurFilter<N>> { - static OwnPtr<typename Gfx::SpatialGaussianBlurFilter<N>::Parameters> get() - { - constexpr static ssize_t offset = N / 2; - Matrix<N, float> kernel; - auto sigma = 1.0f; - auto s = 2.0f * sigma * sigma; - - for (auto x = -offset; x <= offset; x++) { - for (auto y = -offset; y <= offset; y++) { - auto r = sqrt(x * x + y * y); - kernel.elements()[x + offset][y + offset] = (exp(-(r * r) / s)) / (M_PI * s); - } - } - - normalize(kernel); - - return make<typename Gfx::GenericConvolutionFilter<N>::Parameters>(kernel); - } -}; - -template<> -struct FilterParameters<Gfx::SharpenFilter> { - static OwnPtr<Gfx::GenericConvolutionFilter<3>::Parameters> get() - { - return make<Gfx::GenericConvolutionFilter<3>::Parameters>(Matrix<3, float>(0, -1, 0, -1, 5, -1, 0, -1, 0)); - } -}; - -template<> -struct FilterParameters<Gfx::LaplacianFilter> { - static OwnPtr<Gfx::GenericConvolutionFilter<3>::Parameters> get(bool diagonal) - { - if (diagonal) - return make<Gfx::GenericConvolutionFilter<3>::Parameters>(Matrix<3, float>(-1, -1, -1, -1, 8, -1, -1, -1, -1)); - - return make<Gfx::GenericConvolutionFilter<3>::Parameters>(Matrix<3, float>(0, -1, 0, -1, 4, -1, 0, -1, 0)); - } -}; - -template<size_t N> -struct FilterParameters<Gfx::GenericConvolutionFilter<N>> { - static OwnPtr<typename Gfx::GenericConvolutionFilter<N>::Parameters> get(GUI::Window* parent_window) - { - auto input = GenericConvolutionFilterInputDialog<N>::construct(parent_window); - input->exec(); - if (input->result() == GUI::Dialog::ExecOK) - return make<typename Gfx::GenericConvolutionFilter<N>::Parameters>(input->matrix(), input->should_wrap()); - - return {}; - } -}; - -template<size_t N> -struct FilterParameters<Gfx::BoxBlurFilter<N>> { - static OwnPtr<typename Gfx::GenericConvolutionFilter<N>::Parameters> get() - { - Matrix<N, float> kernel; - - for (size_t i = 0; i < N; ++i) { - for (size_t j = 0; j < N; ++j) { - kernel.elements()[i][j] = 1; - } - } - - normalize(kernel); - - return make<typename Gfx::GenericConvolutionFilter<N>::Parameters>(kernel); - } -}; - -} diff --git a/Applications/PixelPaint/Image.cpp b/Applications/PixelPaint/Image.cpp deleted file mode 100644 index 09a075dfe0..0000000000 --- a/Applications/PixelPaint/Image.cpp +++ /dev/null @@ -1,331 +0,0 @@ -/* - * Copyright (c) 2020, 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 "Image.h" -#include "Layer.h" -#include <AK/Base64.h> -#include <AK/JsonObject.h> -#include <AK/JsonObjectSerializer.h> -#include <AK/JsonValue.h> -#include <AK/StringBuilder.h> -#include <LibGUI/Painter.h> -#include <LibGfx/BMPWriter.h> -#include <LibGfx/ImageDecoder.h> -#include <stdio.h> - -//#define PAINT_DEBUG - -namespace PixelPaint { - -RefPtr<Image> Image::create_with_size(const Gfx::IntSize& size) -{ - if (size.is_empty()) - return nullptr; - - if (size.width() > 16384 || size.height() > 16384) - return nullptr; - - return adopt(*new Image(size)); -} - -Image::Image(const Gfx::IntSize& size) - : m_size(size) -{ -} - -void Image::paint_into(GUI::Painter& painter, const Gfx::IntRect& dest_rect) -{ - float scale = (float)dest_rect.width() / (float)rect().width(); - Gfx::PainterStateSaver saver(painter); - painter.add_clip_rect(dest_rect); - for (auto& layer : m_layers) { - if (!layer.is_visible()) - continue; - auto target = dest_rect.translated(layer.location().x() * scale, layer.location().y() * scale); - target.set_size(layer.size().width() * scale, layer.size().height() * scale); - painter.draw_scaled_bitmap(target, layer.bitmap(), layer.rect(), (float)layer.opacity_percent() / 100.0f); - } -} - -RefPtr<Image> Image::create_from_file(const String& file_path) -{ - auto file = fopen(file_path.characters(), "r"); - fseek(file, 0L, SEEK_END); - auto length = ftell(file); - rewind(file); - - auto buffer = ByteBuffer::create_uninitialized(length); - fread(buffer.data(), sizeof(u8), length, file); - fclose(file); - - auto json_or_error = JsonValue::from_string(String::copy(buffer)); - if (!json_or_error.has_value()) - return nullptr; - - auto json = json_or_error.value().as_object(); - auto image = create_with_size({ json.get("width").to_i32(), json.get("height").to_i32() }); - json.get("layers").as_array().for_each([&](JsonValue json_layer) { - auto json_layer_object = json_layer.as_object(); - auto width = json_layer_object.get("width").to_i32(); - auto height = json_layer_object.get("height").to_i32(); - auto name = json_layer_object.get("name").as_string(); - auto layer = Layer::create_with_size(*image, { width, height }, name); - layer->set_location({ json_layer_object.get("locationx").to_i32(), json_layer_object.get("locationy").to_i32() }); - layer->set_opacity_percent(json_layer_object.get("opacity_percent").to_i32()); - layer->set_visible(json_layer_object.get("visible").as_bool()); - layer->set_selected(json_layer_object.get("selected").as_bool()); - - auto bitmap_base64_encoded = json_layer_object.get("bitmap").as_string(); - auto bitmap_data = decode_base64(bitmap_base64_encoded); - auto image_decoder = Gfx::ImageDecoder::create(bitmap_data); - layer->set_bitmap(*image_decoder->bitmap()); - image->add_layer(*layer); - }); - - return image; -} - -void Image::save(const String& file_path) const -{ - // Build json file - StringBuilder builder; - JsonObjectSerializer json(builder); - json.add("width", m_size.width()); - json.add("height", m_size.height()); - { - auto json_layers = json.add_array("layers"); - for (const auto& layer : m_layers) { - Gfx::BMPWriter bmp_dumber; - auto json_layer = json_layers.add_object(); - json_layer.add("width", layer.size().width()); - json_layer.add("height", layer.size().height()); - json_layer.add("name", layer.name()); - json_layer.add("locationx", layer.location().x()); - json_layer.add("locationy", layer.location().y()); - json_layer.add("opacity_percent", layer.opacity_percent()); - json_layer.add("visible", layer.is_visible()); - json_layer.add("selected", layer.is_selected()); - json_layer.add("bitmap", encode_base64(bmp_dumber.dump(layer.bitmap()))); - } - } - json.finish(); - - // Write json to disk - auto file = fopen(file_path.characters(), "w"); - auto byte_buffer = builder.to_byte_buffer(); - fwrite(byte_buffer.data(), sizeof(u8), byte_buffer.size(), file); - fclose(file); -} - -void Image::export_bmp(const String& file_path) -{ - auto bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, m_size); - GUI::Painter painter(*bitmap); - paint_into(painter, { 0, 0, m_size.width(), m_size.height() }); - - Gfx::BMPWriter dumper; - auto bmp = dumper.dump(bitmap); - auto file = fopen(file_path.characters(), "wb"); - fwrite(bmp.data(), sizeof(u8), bmp.size(), file); - fclose(file); -} - -void Image::add_layer(NonnullRefPtr<Layer> layer) -{ - for (auto& existing_layer : m_layers) { - ASSERT(&existing_layer != layer.ptr()); - } - m_layers.append(move(layer)); - - for (auto* client : m_clients) - client->image_did_add_layer(m_layers.size() - 1); - - did_modify_layer_stack(); -} - -RefPtr<Image> Image::take_snapshot() const -{ - auto snapshot = create_with_size(m_size); - for (const auto& layer : m_layers) - snapshot->add_layer(*Layer::create_snapshot(*snapshot, layer)); - return snapshot; -} - -void Image::restore_snapshot(const Image& snapshot) -{ - m_layers.clear(); - select_layer(nullptr); - for (const auto& snapshot_layer : snapshot.m_layers) { - auto layer = Layer::create_snapshot(*this, snapshot_layer); - if (layer->is_selected()) - select_layer(layer.ptr()); - add_layer(*layer); - } - - did_modify_layer_stack(); -} - -size_t Image::index_of(const Layer& layer) const -{ - for (size_t i = 0; i < m_layers.size(); ++i) { - if (&m_layers.at(i) == &layer) - return i; - } - ASSERT_NOT_REACHED(); -} - -void Image::move_layer_to_back(Layer& layer) -{ - NonnullRefPtr<Layer> protector(layer); - auto index = index_of(layer); - m_layers.remove(index); - m_layers.prepend(layer); - - did_modify_layer_stack(); -} - -void Image::move_layer_to_front(Layer& layer) -{ - NonnullRefPtr<Layer> protector(layer); - auto index = index_of(layer); - m_layers.remove(index); - m_layers.append(layer); - - did_modify_layer_stack(); -} - -void Image::move_layer_down(Layer& layer) -{ - NonnullRefPtr<Layer> protector(layer); - auto index = index_of(layer); - if (!index) - return; - m_layers.remove(index); - m_layers.insert(index - 1, layer); - - did_modify_layer_stack(); -} - -void Image::move_layer_up(Layer& layer) -{ - NonnullRefPtr<Layer> protector(layer); - auto index = index_of(layer); - if (index == m_layers.size() - 1) - return; - m_layers.remove(index); - m_layers.insert(index + 1, layer); - - did_modify_layer_stack(); -} - -void Image::change_layer_index(size_t old_index, size_t new_index) -{ - ASSERT(old_index < m_layers.size()); - ASSERT(new_index < m_layers.size()); - auto layer = m_layers.take(old_index); - m_layers.insert(new_index, move(layer)); - did_modify_layer_stack(); -} - -void Image::did_modify_layer_stack() -{ - for (auto* client : m_clients) - client->image_did_modify_layer_stack(); - - did_change(); -} - -void Image::remove_layer(Layer& layer) -{ - NonnullRefPtr<Layer> protector(layer); - auto index = index_of(layer); - m_layers.remove(index); - - for (auto* client : m_clients) - client->image_did_remove_layer(index); - - did_modify_layer_stack(); -} - -void Image::select_layer(Layer* layer) -{ - for (auto* client : m_clients) - client->image_select_layer(layer); -} - -void Image::add_client(ImageClient& client) -{ - ASSERT(!m_clients.contains(&client)); - m_clients.set(&client); -} - -void Image::remove_client(ImageClient& client) -{ - ASSERT(m_clients.contains(&client)); - m_clients.remove(&client); -} - -void Image::layer_did_modify_bitmap(Badge<Layer>, const Layer& layer) -{ - auto layer_index = index_of(layer); - for (auto* client : m_clients) - client->image_did_modify_layer(layer_index); - - did_change(); -} - -void Image::layer_did_modify_properties(Badge<Layer>, const Layer& layer) -{ - auto layer_index = index_of(layer); - for (auto* client : m_clients) - client->image_did_modify_layer(layer_index); - - did_change(); -} - -void Image::did_change() -{ - for (auto* client : m_clients) - client->image_did_change(); -} - -ImageUndoCommand::ImageUndoCommand(Image& image) - : m_snapshot(image.take_snapshot()) - , m_image(image) -{ -} - -void ImageUndoCommand::undo() -{ - m_image.restore_snapshot(*m_snapshot); -} - -void ImageUndoCommand::redo() -{ - undo(); -} - -} diff --git a/Applications/PixelPaint/Image.h b/Applications/PixelPaint/Image.h deleted file mode 100644 index 63184b3ad3..0000000000 --- a/Applications/PixelPaint/Image.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2020, 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 <AK/HashTable.h> -#include <AK/NonnullRefPtrVector.h> -#include <AK/RefCounted.h> -#include <AK/RefPtr.h> -#include <AK/Vector.h> -#include <LibGUI/Command.h> -#include <LibGUI/Forward.h> -#include <LibGfx/Forward.h> -#include <LibGfx/Rect.h> -#include <LibGfx/Size.h> - -namespace PixelPaint { - -class Layer; - -class ImageClient { -public: - virtual void image_did_add_layer(size_t) { } - virtual void image_did_remove_layer(size_t) { } - virtual void image_did_modify_layer(size_t) { } - virtual void image_did_modify_layer_stack() { } - virtual void image_did_change() { } - virtual void image_select_layer(Layer*) { } -}; - -class Image : public RefCounted<Image> { -public: - static RefPtr<Image> create_with_size(const Gfx::IntSize&); - static RefPtr<Image> create_from_file(const String& file_path); - - size_t layer_count() const { return m_layers.size(); } - const Layer& layer(size_t index) const { return m_layers.at(index); } - Layer& layer(size_t index) { return m_layers.at(index); } - - const Gfx::IntSize& size() const { return m_size; } - Gfx::IntRect rect() const { return { {}, m_size }; } - - void add_layer(NonnullRefPtr<Layer>); - RefPtr<Image> take_snapshot() const; - void restore_snapshot(const Image&); - - void paint_into(GUI::Painter&, const Gfx::IntRect& dest_rect); - void save(const String& file_path) const; - void export_bmp(const String& file_path); - - void move_layer_to_front(Layer&); - void move_layer_to_back(Layer&); - void move_layer_up(Layer&); - void move_layer_down(Layer&); - void change_layer_index(size_t old_index, size_t new_index); - void remove_layer(Layer&); - void select_layer(Layer*); - - void add_client(ImageClient&); - void remove_client(ImageClient&); - - void layer_did_modify_bitmap(Badge<Layer>, const Layer&); - void layer_did_modify_properties(Badge<Layer>, const Layer&); - - size_t index_of(const Layer&) const; - -private: - explicit Image(const Gfx::IntSize&); - - void did_change(); - void did_modify_layer_stack(); - - Gfx::IntSize m_size; - NonnullRefPtrVector<Layer> m_layers; - - HashTable<ImageClient*> m_clients; -}; - -class ImageUndoCommand : public GUI::Command { -public: - ImageUndoCommand(Image& image); - - virtual void undo() override; - virtual void redo() override; - -private: - RefPtr<Image> m_snapshot; - Image& m_image; -}; - -} diff --git a/Applications/PixelPaint/ImageEditor.cpp b/Applications/PixelPaint/ImageEditor.cpp deleted file mode 100644 index f29551c050..0000000000 --- a/Applications/PixelPaint/ImageEditor.cpp +++ /dev/null @@ -1,418 +0,0 @@ -/* - * Copyright (c) 2020, 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 "ImageEditor.h" -#include "Image.h" -#include "Layer.h" -#include "MoveTool.h" -#include "Tool.h" -#include <LibGUI/Command.h> -#include <LibGUI/Painter.h> -#include <LibGfx/Palette.h> -#include <LibGfx/Rect.h> - -namespace PixelPaint { - -ImageEditor::ImageEditor() - : m_undo_stack(make<GUI::UndoStack>()) -{ - set_focus_policy(GUI::FocusPolicy::StrongFocus); -} - -ImageEditor::~ImageEditor() -{ - if (m_image) - m_image->remove_client(*this); -} - -void ImageEditor::set_image(RefPtr<Image> image) -{ - if (m_image) - m_image->remove_client(*this); - - m_image = move(image); - m_active_layer = nullptr; - m_undo_stack = make<GUI::UndoStack>(); - m_undo_stack->push(make<ImageUndoCommand>(*m_image)); - update(); - relayout(); - - if (m_image) - m_image->add_client(*this); -} - -void ImageEditor::did_complete_action() -{ - if (!m_image) - return; - m_undo_stack->finalize_current_combo(); - m_undo_stack->push(make<ImageUndoCommand>(*m_image)); -} - -bool ImageEditor::undo() -{ - if (!m_image) - return false; - if (m_undo_stack->can_undo()) { - m_undo_stack->undo(); - layers_did_change(); - return true; - } - return false; -} - -bool ImageEditor::redo() -{ - if (!m_image) - return false; - if (m_undo_stack->can_redo()) { - m_undo_stack->redo(); - layers_did_change(); - return true; - } - return false; -} - -void ImageEditor::paint_event(GUI::PaintEvent& event) -{ - GUI::Frame::paint_event(event); - - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - painter.add_clip_rect(frame_inner_rect()); - - Gfx::StylePainter::paint_transparency_grid(painter, rect(), palette()); - - if (m_image) { - painter.draw_rect(m_editor_image_rect.inflated(2, 2), Color::Black); - m_image->paint_into(painter, m_editor_image_rect); - } - - if (m_active_layer) { - painter.draw_rect(enclosing_int_rect(image_rect_to_editor_rect(m_active_layer->relative_rect())).inflated(2, 2), Color::Black); - } -} - -Gfx::FloatRect ImageEditor::layer_rect_to_editor_rect(const Layer& layer, const Gfx::IntRect& layer_rect) const -{ - return image_rect_to_editor_rect(layer_rect.translated(layer.location())); -} - -Gfx::FloatRect ImageEditor::image_rect_to_editor_rect(const Gfx::IntRect& image_rect) const -{ - Gfx::FloatRect editor_rect; - editor_rect.set_location(image_position_to_editor_position(image_rect.location())); - editor_rect.set_width((float)image_rect.width() * m_scale); - editor_rect.set_height((float)image_rect.height() * m_scale); - return editor_rect; -} - -Gfx::FloatRect ImageEditor::editor_rect_to_image_rect(const Gfx::IntRect& editor_rect) const -{ - Gfx::FloatRect image_rect; - image_rect.set_location(editor_position_to_image_position(editor_rect.location())); - image_rect.set_width((float)editor_rect.width() / m_scale); - image_rect.set_height((float)editor_rect.height() / m_scale); - return image_rect; -} - -Gfx::FloatPoint ImageEditor::layer_position_to_editor_position(const Layer& layer, const Gfx::IntPoint& layer_position) const -{ - return image_position_to_editor_position(layer_position.translated(layer.location())); -} - -Gfx::FloatPoint ImageEditor::image_position_to_editor_position(const Gfx::IntPoint& image_position) const -{ - Gfx::FloatPoint editor_position; - editor_position.set_x(m_editor_image_rect.x() + ((float)image_position.x() * m_scale)); - editor_position.set_y(m_editor_image_rect.y() + ((float)image_position.y() * m_scale)); - return editor_position; -} - -Gfx::FloatPoint ImageEditor::editor_position_to_image_position(const Gfx::IntPoint& editor_position) const -{ - Gfx::FloatPoint image_position; - image_position.set_x(((float)editor_position.x() - m_editor_image_rect.x()) / m_scale); - image_position.set_y(((float)editor_position.y() - m_editor_image_rect.y()) / m_scale); - return image_position; -} - -void ImageEditor::second_paint_event(GUI::PaintEvent& event) -{ - if (m_active_tool && m_active_layer) - m_active_tool->on_second_paint(*m_active_layer, event); -} - -GUI::MouseEvent ImageEditor::event_with_pan_and_scale_applied(const GUI::MouseEvent& event) const -{ - auto image_position = editor_position_to_image_position(event.position()); - return { - static_cast<GUI::Event::Type>(event.type()), - Gfx::IntPoint(image_position.x(), image_position.y()), - event.buttons(), - event.button(), - event.modifiers(), - event.wheel_delta() - }; -} - -GUI::MouseEvent ImageEditor::event_adjusted_for_layer(const GUI::MouseEvent& event, const Layer& layer) const -{ - auto image_position = editor_position_to_image_position(event.position()); - image_position.move_by(-layer.location().x(), -layer.location().y()); - return { - static_cast<GUI::Event::Type>(event.type()), - Gfx::IntPoint(image_position.x(), image_position.y()), - event.buttons(), - event.button(), - event.modifiers(), - event.wheel_delta() - }; -} - -void ImageEditor::mousedown_event(GUI::MouseEvent& event) -{ - if (event.button() == GUI::MouseButton::Middle) { - m_click_position = event.position(); - m_saved_pan_origin = m_pan_origin; - return; - } - - if (!m_active_tool) - return; - - if (is<MoveTool>(*m_active_tool)) { - if (auto* other_layer = layer_at_editor_position(event.position())) { - set_active_layer(other_layer); - } - } - - if (!m_active_layer) - return; - - auto layer_event = event_adjusted_for_layer(event, *m_active_layer); - auto image_event = event_with_pan_and_scale_applied(event); - m_active_tool->on_mousedown(*m_active_layer, layer_event, image_event); -} - -void ImageEditor::mousemove_event(GUI::MouseEvent& event) -{ - if (event.buttons() & GUI::MouseButton::Middle) { - auto delta = event.position() - m_click_position; - m_pan_origin = m_saved_pan_origin.translated( - -delta.x() / m_scale, - -delta.y() / m_scale); - - relayout(); - return; - } - - if (!m_active_layer || !m_active_tool) - return; - auto layer_event = event_adjusted_for_layer(event, *m_active_layer); - auto image_event = event_with_pan_and_scale_applied(event); - - m_active_tool->on_mousemove(*m_active_layer, layer_event, image_event); -} - -void ImageEditor::mouseup_event(GUI::MouseEvent& event) -{ - if (!m_active_layer || !m_active_tool) - return; - auto layer_event = event_adjusted_for_layer(event, *m_active_layer); - auto image_event = event_with_pan_and_scale_applied(event); - m_active_tool->on_mouseup(*m_active_layer, layer_event, image_event); -} - -void ImageEditor::mousewheel_event(GUI::MouseEvent& event) -{ - auto old_scale = m_scale; - - m_scale += -event.wheel_delta() * 0.1f; - if (m_scale < 0.1f) - m_scale = 0.1f; - if (m_scale > 100.0f) - m_scale = 100.0f; - - auto focus_point = Gfx::FloatPoint( - m_pan_origin.x() - ((float)event.x() - (float)width() / 2.0) / old_scale, - m_pan_origin.y() - ((float)event.y() - (float)height() / 2.0) / old_scale); - - m_pan_origin = Gfx::FloatPoint( - focus_point.x() - m_scale / old_scale * (focus_point.x() - m_pan_origin.x()), - focus_point.y() - m_scale / old_scale * (focus_point.y() - m_pan_origin.y())); - - if (old_scale != m_scale) - relayout(); -} - -void ImageEditor::context_menu_event(GUI::ContextMenuEvent& event) -{ - if (!m_active_layer || !m_active_tool) - return; - m_active_tool->on_context_menu(*m_active_layer, event); -} - -void ImageEditor::resize_event(GUI::ResizeEvent& event) -{ - relayout(); - GUI::Frame::resize_event(event); -} - -void ImageEditor::keydown_event(GUI::KeyEvent& event) -{ - if (m_active_tool) - m_active_tool->on_keydown(event); -} - -void ImageEditor::keyup_event(GUI::KeyEvent& event) -{ - if (m_active_tool) - m_active_tool->on_keyup(event); -} - -void ImageEditor::set_active_layer(Layer* layer) -{ - if (m_active_layer == layer) - return; - m_active_layer = layer; - - if (m_active_layer) { - size_t index = 0; - for (; index < m_image->layer_count(); ++index) { - if (&m_image->layer(index) == layer) - break; - } - if (on_active_layer_change) - on_active_layer_change(layer); - } else { - if (on_active_layer_change) - on_active_layer_change({}); - } - - layers_did_change(); -} - -void ImageEditor::set_active_tool(Tool* tool) -{ - if (m_active_tool == tool) - return; - - if (m_active_tool) - m_active_tool->clear(); - - m_active_tool = tool; - - if (m_active_tool) - m_active_tool->setup(*this); -} - -void ImageEditor::layers_did_change() -{ - update(); -} - -Color ImageEditor::color_for(GUI::MouseButton button) const -{ - if (button == GUI::MouseButton::Left) - return m_primary_color; - if (button == GUI::MouseButton::Right) - return m_secondary_color; - ASSERT_NOT_REACHED(); -} - -Color ImageEditor::color_for(const GUI::MouseEvent& event) const -{ - if (event.buttons() & GUI::MouseButton::Left) - return m_primary_color; - if (event.buttons() & GUI::MouseButton::Right) - return m_secondary_color; - ASSERT_NOT_REACHED(); -} - -void ImageEditor::set_primary_color(Color color) -{ - if (m_primary_color == color) - return; - m_primary_color = color; - if (on_primary_color_change) - on_primary_color_change(color); -} - -void ImageEditor::set_secondary_color(Color color) -{ - if (m_secondary_color == color) - return; - m_secondary_color = color; - if (on_secondary_color_change) - on_secondary_color_change(color); -} - -Layer* ImageEditor::layer_at_editor_position(const Gfx::IntPoint& editor_position) -{ - if (!m_image) - return nullptr; - auto image_position = editor_position_to_image_position(editor_position); - for (ssize_t i = m_image->layer_count() - 1; i >= 0; --i) { - auto& layer = m_image->layer(i); - if (!layer.is_visible()) - continue; - if (layer.relative_rect().contains(Gfx::IntPoint(image_position.x(), image_position.y()))) - return const_cast<Layer*>(&layer); - } - return nullptr; -} - -void ImageEditor::relayout() -{ - if (!image()) - return; - auto& image = *this->image(); - - Gfx::IntSize new_size; - new_size.set_width(image.size().width() * m_scale); - new_size.set_height(image.size().height() * m_scale); - m_editor_image_rect.set_size(new_size); - - Gfx::IntPoint new_location; - new_location.set_x((width() / 2) - (new_size.width() / 2) - (m_pan_origin.x() * m_scale)); - new_location.set_y((height() / 2) - (new_size.height() / 2) - (m_pan_origin.y() * m_scale)); - m_editor_image_rect.set_location(new_location); - - update(); -} - -void ImageEditor::image_did_change() -{ - update(); -} - -void ImageEditor::image_select_layer(Layer* layer) -{ - set_active_layer(layer); -} - -} diff --git a/Applications/PixelPaint/ImageEditor.h b/Applications/PixelPaint/ImageEditor.h deleted file mode 100644 index b6cdc8fb62..0000000000 --- a/Applications/PixelPaint/ImageEditor.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (c) 2020, 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 "Image.h" -#include <LibGUI/Frame.h> -#include <LibGUI/UndoStack.h> -#include <LibGfx/Point.h> - -namespace PixelPaint { - -class Layer; -class Tool; - -class ImageEditor final - : public GUI::Frame - , public ImageClient { - C_OBJECT(ImageEditor); - -public: - virtual ~ImageEditor() override; - - const Image* image() const { return m_image; } - Image* image() { return m_image; } - - void set_image(RefPtr<Image>); - - Layer* active_layer() { return m_active_layer; } - void set_active_layer(Layer*); - - Tool* active_tool() { return m_active_tool; } - void set_active_tool(Tool*); - - void did_complete_action(); - bool undo(); - bool redo(); - - void layers_did_change(); - - Layer* layer_at_editor_position(const Gfx::IntPoint&); - - Color primary_color() const { return m_primary_color; } - void set_primary_color(Color); - - Color secondary_color() const { return m_secondary_color; } - void set_secondary_color(Color); - - Color color_for(const GUI::MouseEvent&) const; - Color color_for(GUI::MouseButton) const; - - Function<void(Color)> on_primary_color_change; - Function<void(Color)> on_secondary_color_change; - - Function<void(Layer*)> on_active_layer_change; - - Gfx::FloatRect layer_rect_to_editor_rect(const Layer&, const Gfx::IntRect&) const; - Gfx::FloatRect image_rect_to_editor_rect(const Gfx::IntRect&) const; - Gfx::FloatRect editor_rect_to_image_rect(const Gfx::IntRect&) const; - Gfx::FloatPoint layer_position_to_editor_position(const Layer&, const Gfx::IntPoint&) const; - Gfx::FloatPoint image_position_to_editor_position(const Gfx::IntPoint&) const; - Gfx::FloatPoint editor_position_to_image_position(const Gfx::IntPoint&) const; - -private: - ImageEditor(); - - virtual void paint_event(GUI::PaintEvent&) override; - virtual void second_paint_event(GUI::PaintEvent&) override; - virtual void mousedown_event(GUI::MouseEvent&) override; - virtual void mousemove_event(GUI::MouseEvent&) override; - virtual void mouseup_event(GUI::MouseEvent&) override; - virtual void mousewheel_event(GUI::MouseEvent&) override; - virtual void keydown_event(GUI::KeyEvent&) override; - virtual void keyup_event(GUI::KeyEvent&) override; - virtual void context_menu_event(GUI::ContextMenuEvent&) override; - virtual void resize_event(GUI::ResizeEvent&) override; - - virtual void image_did_change() override; - virtual void image_select_layer(Layer*) override; - - GUI::MouseEvent event_adjusted_for_layer(const GUI::MouseEvent&, const Layer&) const; - GUI::MouseEvent event_with_pan_and_scale_applied(const GUI::MouseEvent&) const; - - void relayout(); - - RefPtr<Image> m_image; - RefPtr<Layer> m_active_layer; - OwnPtr<GUI::UndoStack> m_undo_stack; - - Tool* m_active_tool { nullptr }; - - Color m_primary_color { Color::Black }; - Color m_secondary_color { Color::White }; - - Gfx::IntRect m_editor_image_rect; - float m_scale { 1 }; - Gfx::FloatPoint m_pan_origin; - Gfx::FloatPoint m_saved_pan_origin; - Gfx::IntPoint m_click_position; -}; - -} diff --git a/Applications/PixelPaint/Layer.cpp b/Applications/PixelPaint/Layer.cpp deleted file mode 100644 index e607113c32..0000000000 --- a/Applications/PixelPaint/Layer.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2020, 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 "Layer.h" -#include "Image.h" -#include <LibGfx/Bitmap.h> - -namespace PixelPaint { - -RefPtr<Layer> Layer::create_with_size(Image& image, const Gfx::IntSize& size, const String& name) -{ - if (size.is_empty()) - return nullptr; - - if (size.width() > 16384 || size.height() > 16384) - return nullptr; - - return adopt(*new Layer(image, size, name)); -} - -RefPtr<Layer> Layer::create_with_bitmap(Image& image, const Gfx::Bitmap& bitmap, const String& name) -{ - if (bitmap.size().is_empty()) - return nullptr; - - if (bitmap.size().width() > 16384 || bitmap.size().height() > 16384) - return nullptr; - - return adopt(*new Layer(image, bitmap, name)); -} - -RefPtr<Layer> Layer::create_snapshot(Image& image, const Layer& layer) -{ - auto snapshot = create_with_bitmap(image, *layer.bitmap().clone(), layer.name()); - snapshot->set_opacity_percent(layer.opacity_percent()); - snapshot->set_visible(layer.is_visible()); - snapshot->set_selected(layer.is_selected()); - snapshot->set_location(layer.location()); - return snapshot; -} - -Layer::Layer(Image& image, const Gfx::IntSize& size, const String& name) - : m_image(image) - , m_name(name) -{ - m_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA32, size); -} - -Layer::Layer(Image& image, const Gfx::Bitmap& bitmap, const String& name) - : m_image(image) - , m_name(name) - , m_bitmap(bitmap) -{ -} - -void Layer::did_modify_bitmap(Image& image) -{ - image.layer_did_modify_bitmap({}, *this); -} - -void Layer::set_visible(bool visible) -{ - if (m_visible == visible) - return; - m_visible = visible; - m_image.layer_did_modify_properties({}, *this); -} - -void Layer::set_opacity_percent(int opacity_percent) -{ - if (m_opacity_percent == opacity_percent) - return; - m_opacity_percent = opacity_percent; - m_image.layer_did_modify_properties({}, *this); -} - -void Layer::set_name(const String& name) -{ - if (m_name == name) - return; - m_name = name; - m_image.layer_did_modify_properties({}, *this); -} - -} diff --git a/Applications/PixelPaint/Layer.h b/Applications/PixelPaint/Layer.h deleted file mode 100644 index b3b7d4d603..0000000000 --- a/Applications/PixelPaint/Layer.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2020, 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 <AK/Noncopyable.h> -#include <AK/RefCounted.h> -#include <AK/String.h> -#include <AK/Weakable.h> -#include <LibGfx/Bitmap.h> - -namespace PixelPaint { - -class Image; - -class Layer - : public RefCounted<Layer> - , public Weakable<Layer> { - - AK_MAKE_NONCOPYABLE(Layer); - AK_MAKE_NONMOVABLE(Layer); - -public: - static RefPtr<Layer> create_with_size(Image&, const Gfx::IntSize&, const String& name); - static RefPtr<Layer> create_with_bitmap(Image&, const Gfx::Bitmap&, const String& name); - static RefPtr<Layer> create_snapshot(Image&, const Layer&); - - ~Layer() { } - - const Gfx::IntPoint& location() const { return m_location; } - void set_location(const Gfx::IntPoint& location) { m_location = location; } - - const Gfx::Bitmap& bitmap() const { return *m_bitmap; } - Gfx::Bitmap& bitmap() { return *m_bitmap; } - Gfx::IntSize size() const { return bitmap().size(); } - - Gfx::IntRect relative_rect() const { return { location(), size() }; } - Gfx::IntRect rect() const { return { {}, size() }; } - - const String& name() const { return m_name; } - void set_name(const String&); - - void set_bitmap(Gfx::Bitmap& bitmap) { m_bitmap = bitmap; } - - void did_modify_bitmap(Image&); - - void set_selected(bool selected) { m_selected = selected; } - bool is_selected() const { return m_selected; } - - bool is_visible() const { return m_visible; } - void set_visible(bool visible); - - int opacity_percent() const { return m_opacity_percent; } - void set_opacity_percent(int); - -private: - Layer(Image&, const Gfx::IntSize&, const String& name); - Layer(Image&, const Gfx::Bitmap&, const String& name); - - Image& m_image; - - String m_name; - Gfx::IntPoint m_location; - RefPtr<Gfx::Bitmap> m_bitmap; - - bool m_selected { false }; - bool m_visible { true }; - - int m_opacity_percent { 100 }; -}; - -} diff --git a/Applications/PixelPaint/LayerListWidget.cpp b/Applications/PixelPaint/LayerListWidget.cpp deleted file mode 100644 index 756772bb87..0000000000 --- a/Applications/PixelPaint/LayerListWidget.cpp +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Copyright (c) 2020, 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 "LayerListWidget.h" -#include "Image.h" -#include "ImageEditor.h" -#include "Layer.h" -#include <LibGUI/Painter.h> -#include <LibGfx/Palette.h> - -namespace PixelPaint { - -LayerListWidget::LayerListWidget() -{ -} - -LayerListWidget::~LayerListWidget() -{ - if (m_image) - m_image->remove_client(*this); -} - -void LayerListWidget::set_image(Image* image) -{ - if (m_image == image) - return; - if (m_image) - m_image->remove_client(*this); - m_image = image; - if (m_image) - m_image->add_client(*this); - - rebuild_gadgets(); -} - -void LayerListWidget::rebuild_gadgets() -{ - m_gadgets.clear(); - if (m_image) { - for (size_t layer_index = 0; layer_index < m_image->layer_count(); ++layer_index) { - m_gadgets.append({ layer_index, {}, {}, false, {} }); - } - } - relayout_gadgets(); -} - -void LayerListWidget::resize_event(GUI::ResizeEvent& event) -{ - Widget::resize_event(event); - relayout_gadgets(); -} - -void LayerListWidget::paint_event(GUI::PaintEvent& event) -{ - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - - painter.fill_rect(event.rect(), palette().button()); - - if (!m_image) - return; - - painter.fill_rect(event.rect(), palette().button()); - - auto paint_gadget = [&](auto& gadget) { - auto& layer = m_image->layer(gadget.layer_index); - - auto adjusted_rect = gadget.rect; - - if (gadget.is_moving) { - adjusted_rect.move_by(0, gadget.movement_delta.y()); - } - - if (gadget.is_moving) { - painter.fill_rect(adjusted_rect, palette().selection().lightened(1.5f)); - } else if (layer.is_selected()) { - painter.fill_rect(adjusted_rect, palette().selection()); - } - - painter.draw_rect(adjusted_rect, Color::Black); - - Gfx::IntRect thumbnail_rect { adjusted_rect.x(), adjusted_rect.y(), adjusted_rect.height(), adjusted_rect.height() }; - thumbnail_rect.shrink(8, 8); - painter.draw_scaled_bitmap(thumbnail_rect, layer.bitmap(), layer.bitmap().rect()); - - Gfx::IntRect text_rect { thumbnail_rect.right() + 10, adjusted_rect.y(), adjusted_rect.width(), adjusted_rect.height() }; - text_rect.intersect(adjusted_rect); - - painter.draw_text(text_rect, layer.name(), Gfx::TextAlignment::CenterLeft, layer.is_selected() ? palette().selection_text() : palette().button_text()); - }; - - for (auto& gadget : m_gadgets) { - if (!gadget.is_moving) - paint_gadget(gadget); - } - - if (m_moving_gadget_index.has_value()) - paint_gadget(m_gadgets[m_moving_gadget_index.value()]); -} - -Optional<size_t> LayerListWidget::gadget_at(const Gfx::IntPoint& position) -{ - for (size_t i = 0; i < m_gadgets.size(); ++i) { - if (m_gadgets[i].rect.contains(position)) - return i; - } - return {}; -} - -void LayerListWidget::mousedown_event(GUI::MouseEvent& event) -{ - if (!m_image) - return; - if (event.button() != GUI::MouseButton::Left) - return; - auto gadget_index = gadget_at(event.position()); - if (!gadget_index.has_value()) { - if (on_layer_select) - on_layer_select(nullptr); - return; - } - m_moving_gadget_index = gadget_index; - m_moving_event_origin = event.position(); - auto& gadget = m_gadgets[m_moving_gadget_index.value()]; - auto& layer = m_image->layer(gadget_index.value()); - set_selected_layer(&layer); - gadget.is_moving = true; - gadget.movement_delta = {}; - update(); -} - -void LayerListWidget::mousemove_event(GUI::MouseEvent& event) -{ - if (!m_image) - return; - if (!m_moving_gadget_index.has_value()) - return; - - auto delta = event.position() - m_moving_event_origin; - auto& gadget = m_gadgets[m_moving_gadget_index.value()]; - ASSERT(gadget.is_moving); - gadget.movement_delta = delta; - relayout_gadgets(); -} - -void LayerListWidget::mouseup_event(GUI::MouseEvent& event) -{ - if (!m_image) - return; - if (event.button() != GUI::MouseButton::Left) - return; - if (!m_moving_gadget_index.has_value()) - return; - - size_t old_index = m_moving_gadget_index.value(); - size_t new_index = hole_index_during_move(); - if (new_index >= m_image->layer_count()) - new_index = m_image->layer_count() - 1; - - m_moving_gadget_index = {}; - m_image->change_layer_index(old_index, new_index); -} - -void LayerListWidget::image_did_add_layer(size_t layer_index) -{ - if (m_moving_gadget_index.has_value()) { - m_gadgets[m_moving_gadget_index.value()].is_moving = false; - m_moving_gadget_index = {}; - } - Gadget gadget { layer_index, {}, {}, false, {} }; - m_gadgets.insert(layer_index, move(gadget)); - relayout_gadgets(); -} - -void LayerListWidget::image_did_remove_layer(size_t layer_index) -{ - if (m_moving_gadget_index.has_value()) { - m_gadgets[m_moving_gadget_index.value()].is_moving = false; - m_moving_gadget_index = {}; - } - m_gadgets.remove(layer_index); - relayout_gadgets(); -} - -void LayerListWidget::image_did_modify_layer(size_t layer_index) -{ - update(m_gadgets[layer_index].rect); -} - -void LayerListWidget::image_did_modify_layer_stack() -{ - rebuild_gadgets(); -} - -static constexpr int gadget_height = 30; -static constexpr int gadget_spacing = 1; -static constexpr int vertical_step = gadget_height + gadget_spacing; - -size_t LayerListWidget::hole_index_during_move() const -{ - ASSERT(is_moving_gadget()); - auto& moving_gadget = m_gadgets[m_moving_gadget_index.value()]; - int center_y_of_moving_gadget = moving_gadget.rect.translated(0, moving_gadget.movement_delta.y()).center().y(); - return center_y_of_moving_gadget / vertical_step; -} - -void LayerListWidget::select_bottom_layer() -{ - if (!m_image || !m_image->layer_count()) - return; - set_selected_layer(&m_image->layer(0)); -} - -void LayerListWidget::select_top_layer() -{ - if (!m_image || !m_image->layer_count()) - return; - set_selected_layer(&m_image->layer(m_image->layer_count() - 1)); -} - -void LayerListWidget::move_selection(int delta) -{ - if (!m_image || !m_image->layer_count()) - return; - int new_layer_index = min(max(0, (int)m_image->layer_count() + delta), (int)m_image->layer_count() - 1); - set_selected_layer(&m_image->layer(new_layer_index)); -} - -void LayerListWidget::relayout_gadgets() -{ - int y = 0; - - Optional<size_t> hole_index; - if (is_moving_gadget()) - hole_index = hole_index_during_move(); - - size_t index = 0; - for (auto& gadget : m_gadgets) { - if (gadget.is_moving) - continue; - if (hole_index.has_value() && index == hole_index.value()) - y += vertical_step; - gadget.rect = { 0, y, width(), gadget_height }; - y += vertical_step; - ++index; - } - - update(); -} - -void LayerListWidget::set_selected_layer(Layer* layer) -{ - if (!m_image) - return; - for (size_t i = 0; i < m_image->layer_count(); ++i) - m_image->layer(i).set_selected(layer == &m_image->layer(i)); - if (on_layer_select) - on_layer_select(layer); - update(); -} - -} diff --git a/Applications/PixelPaint/LayerListWidget.h b/Applications/PixelPaint/LayerListWidget.h deleted file mode 100644 index 9550913b8e..0000000000 --- a/Applications/PixelPaint/LayerListWidget.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2020, 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 "Image.h" -#include <LibGUI/Widget.h> - -namespace PixelPaint { - -class LayerListWidget final - : public GUI::Widget - , ImageClient { - C_OBJECT(LayerListWidget); - -public: - virtual ~LayerListWidget() override; - - void set_image(Image*); - - void set_selected_layer(Layer*); - Function<void(Layer*)> on_layer_select; - - void select_bottom_layer(); - void select_top_layer(); - void move_selection(int delta); - -private: - explicit LayerListWidget(); - - virtual void paint_event(GUI::PaintEvent&) override; - virtual void mousedown_event(GUI::MouseEvent&) override; - virtual void mousemove_event(GUI::MouseEvent&) override; - virtual void mouseup_event(GUI::MouseEvent&) override; - virtual void resize_event(GUI::ResizeEvent&) override; - - virtual void image_did_add_layer(size_t) override; - virtual void image_did_remove_layer(size_t) override; - virtual void image_did_modify_layer(size_t) override; - virtual void image_did_modify_layer_stack() override; - - void rebuild_gadgets(); - void relayout_gadgets(); - - size_t hole_index_during_move() const; - - struct Gadget { - size_t layer_index { 0 }; - Gfx::IntRect rect; - Gfx::IntRect temporary_rect_during_move; - bool is_moving { false }; - Gfx::IntPoint movement_delta; - }; - - bool is_moving_gadget() const { return m_moving_gadget_index.has_value(); } - - Optional<size_t> gadget_at(const Gfx::IntPoint&); - - Vector<Gadget> m_gadgets; - RefPtr<Image> m_image; - - Optional<size_t> m_moving_gadget_index; - Gfx::IntPoint m_moving_event_origin; -}; - -} diff --git a/Applications/PixelPaint/LayerPropertiesWidget.cpp b/Applications/PixelPaint/LayerPropertiesWidget.cpp deleted file mode 100644 index cfdee65c0a..0000000000 --- a/Applications/PixelPaint/LayerPropertiesWidget.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2020, 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 "LayerPropertiesWidget.h" -#include "Layer.h" -#include <LibGUI/BoxLayout.h> -#include <LibGUI/CheckBox.h> -#include <LibGUI/GroupBox.h> -#include <LibGUI/Label.h> -#include <LibGUI/OpacitySlider.h> -#include <LibGUI/TextBox.h> -#include <LibGfx/Font.h> - -namespace PixelPaint { - -LayerPropertiesWidget::LayerPropertiesWidget() -{ - set_layout<GUI::VerticalBoxLayout>(); - - auto& group_box = add<GUI::GroupBox>("Layer properties"); - auto& layout = group_box.set_layout<GUI::VerticalBoxLayout>(); - - layout.set_margins({ 10, 20, 10, 10 }); - - auto& name_container = group_box.add<GUI::Widget>(); - name_container.set_fixed_height(20); - name_container.set_layout<GUI::HorizontalBoxLayout>(); - - auto& name_label = name_container.add<GUI::Label>("Name:"); - name_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - name_label.set_fixed_size(80, 20); - - m_name_textbox = name_container.add<GUI::TextBox>(); - m_name_textbox->set_fixed_height(20); - m_name_textbox->on_change = [this] { - if (m_layer) - m_layer->set_name(m_name_textbox->text()); - }; - - auto& opacity_container = group_box.add<GUI::Widget>(); - opacity_container.set_fixed_height(20); - opacity_container.set_layout<GUI::HorizontalBoxLayout>(); - - auto& opacity_label = opacity_container.add<GUI::Label>("Opacity:"); - opacity_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - opacity_label.set_fixed_size(80, 20); - - m_opacity_slider = opacity_container.add<GUI::OpacitySlider>(); - m_opacity_slider->set_range(0, 100); - m_opacity_slider->on_change = [this](int value) { - if (m_layer) - m_layer->set_opacity_percent(value); - }; - - m_visibility_checkbox = group_box.add<GUI::CheckBox>("Visible"); - m_visibility_checkbox->set_fixed_height(20); - m_visibility_checkbox->on_checked = [this](bool checked) { - if (m_layer) - m_layer->set_visible(checked); - }; -} - -LayerPropertiesWidget::~LayerPropertiesWidget() -{ -} - -void LayerPropertiesWidget::set_layer(Layer* layer) -{ - if (m_layer == layer) - return; - - if (layer) { - m_layer = layer->make_weak_ptr(); - m_name_textbox->set_text(layer->name()); - m_opacity_slider->set_value(layer->opacity_percent()); - m_visibility_checkbox->set_checked(layer->is_visible()); - set_enabled(true); - } else { - m_layer = nullptr; - set_enabled(false); - } -} - -} diff --git a/Applications/PixelPaint/LayerPropertiesWidget.h b/Applications/PixelPaint/LayerPropertiesWidget.h deleted file mode 100644 index e07e02ec93..0000000000 --- a/Applications/PixelPaint/LayerPropertiesWidget.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2020, 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 PixelPaint { - -class Layer; - -class LayerPropertiesWidget final : public GUI::Widget { - C_OBJECT(LayerPropertiesWidget); - -public: - virtual ~LayerPropertiesWidget() override; - - void set_layer(Layer*); - -private: - LayerPropertiesWidget(); - - RefPtr<GUI::CheckBox> m_visibility_checkbox; - RefPtr<GUI::OpacitySlider> m_opacity_slider; - RefPtr<GUI::TextBox> m_name_textbox; - - WeakPtr<Layer> m_layer; -}; - -} diff --git a/Applications/PixelPaint/LineTool.cpp b/Applications/PixelPaint/LineTool.cpp deleted file mode 100644 index 7c3e37e836..0000000000 --- a/Applications/PixelPaint/LineTool.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "LineTool.h" -#include "ImageEditor.h" -#include "Layer.h" -#include <LibGUI/Action.h> -#include <LibGUI/Menu.h> -#include <LibGUI/Painter.h> -#include <math.h> - -namespace PixelPaint { - -static Gfx::IntPoint constrain_line_angle(const Gfx::IntPoint& start_pos, const Gfx::IntPoint& end_pos, float angle_increment) -{ - float current_angle = atan2(end_pos.y() - start_pos.y(), end_pos.x() - start_pos.x()) + M_PI * 2.; - - float constrained_angle = ((int)((current_angle + angle_increment / 2.) / angle_increment)) * angle_increment; - - auto diff = end_pos - start_pos; - float line_length = sqrt(diff.x() * diff.x() + diff.y() * diff.y()); - - return { start_pos.x() + (int)(cos(constrained_angle) * line_length), - start_pos.y() + (int)(sin(constrained_angle) * line_length) }; -} - -LineTool::LineTool() -{ -} - -LineTool::~LineTool() -{ -} - -void LineTool::on_mousedown(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent&) -{ - if (layer_event.button() != GUI::MouseButton::Left && layer_event.button() != GUI::MouseButton::Right) - return; - - if (m_drawing_button != GUI::MouseButton::None) - return; - - m_drawing_button = layer_event.button(); - - m_line_start_position = layer_event.position(); - m_line_end_position = layer_event.position(); - - m_editor->update(); -} - -void LineTool::on_mouseup(Layer& layer, GUI::MouseEvent& event, GUI::MouseEvent&) -{ - if (event.button() == m_drawing_button) { - GUI::Painter painter(layer.bitmap()); - painter.draw_line(m_line_start_position, m_line_end_position, m_editor->color_for(m_drawing_button), m_thickness); - m_drawing_button = GUI::MouseButton::None; - layer.did_modify_bitmap(*m_editor->image()); - m_editor->did_complete_action(); - } -} - -void LineTool::on_mousemove(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent&) -{ - if (m_drawing_button == GUI::MouseButton::None) - return; - - if (!m_constrain_angle) { - m_line_end_position = layer_event.position(); - } else { - const float ANGLE_STEP = M_PI / 8.0f; - m_line_end_position = constrain_line_angle(m_line_start_position, layer_event.position(), ANGLE_STEP); - } - m_editor->update(); -} - -void LineTool::on_second_paint(const Layer& layer, GUI::PaintEvent& event) -{ - if (m_drawing_button == GUI::MouseButton::None) - return; - - GUI::Painter painter(*m_editor); - painter.add_clip_rect(event.rect()); - auto preview_start = m_editor->layer_position_to_editor_position(layer, m_line_start_position).to_type<int>(); - auto preview_end = m_editor->layer_position_to_editor_position(layer, m_line_end_position).to_type<int>(); - painter.draw_line(preview_start, preview_end, m_editor->color_for(m_drawing_button), m_thickness); -} - -void LineTool::on_keydown(GUI::KeyEvent& event) -{ - if (event.key() == Key_Escape && m_drawing_button != GUI::MouseButton::None) { - m_drawing_button = GUI::MouseButton::None; - m_editor->update(); - event.accept(); - } - - if (event.key() == Key_Shift) { - m_constrain_angle = true; - m_editor->update(); - event.accept(); - } -} - -void LineTool::on_keyup(GUI::KeyEvent& event) -{ - if (event.key() == Key_Shift) { - m_constrain_angle = false; - m_editor->update(); - event.accept(); - } -} - -void LineTool::on_tool_button_contextmenu(GUI::ContextMenuEvent& event) -{ - if (!m_context_menu) { - m_context_menu = GUI::Menu::construct(); - m_thickness_actions.set_exclusive(true); - auto insert_action = [&](int size, bool checked = false) { - auto action = GUI::Action::create_checkable(String::number(size), [this, size](auto&) { - m_thickness = size; - }); - action->set_checked(checked); - m_thickness_actions.add_action(*action); - m_context_menu->add_action(move(action)); - }; - insert_action(1, true); - insert_action(2); - insert_action(3); - insert_action(4); - } - m_context_menu->popup(event.screen_position()); -} - -} diff --git a/Applications/PixelPaint/LineTool.h b/Applications/PixelPaint/LineTool.h deleted file mode 100644 index aa8757655c..0000000000 --- a/Applications/PixelPaint/LineTool.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "Tool.h" -#include <LibGUI/ActionGroup.h> -#include <LibGfx/Point.h> - -namespace PixelPaint { - -class LineTool final : public Tool { -public: - LineTool(); - virtual ~LineTool() override; - - virtual void on_mousedown(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; - virtual void on_mousemove(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; - virtual void on_mouseup(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; - virtual void on_tool_button_contextmenu(GUI::ContextMenuEvent&) override; - virtual void on_second_paint(const Layer&, GUI::PaintEvent&) override; - virtual void on_keydown(GUI::KeyEvent&) override; - virtual void on_keyup(GUI::KeyEvent&) override; - -private: - GUI::MouseButton m_drawing_button { GUI::MouseButton::None }; - Gfx::IntPoint m_line_start_position; - Gfx::IntPoint m_line_end_position; - - RefPtr<GUI::Menu> m_context_menu; - GUI::ActionGroup m_thickness_actions; - int m_thickness { 1 }; - bool m_constrain_angle { false }; -}; - -} diff --git a/Applications/PixelPaint/MoveTool.cpp b/Applications/PixelPaint/MoveTool.cpp deleted file mode 100644 index ebeba5ba89..0000000000 --- a/Applications/PixelPaint/MoveTool.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (c) 2020, 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 "MoveTool.h" -#include "Image.h" -#include "ImageEditor.h" -#include "Layer.h" -#include <LibGUI/Action.h> -#include <LibGUI/Menu.h> -#include <LibGUI/Window.h> -#include <LibGfx/Bitmap.h> - -namespace PixelPaint { - -MoveTool::MoveTool() -{ -} - -MoveTool::~MoveTool() -{ -} - -void MoveTool::on_mousedown(Layer& layer, GUI::MouseEvent& event, GUI::MouseEvent& image_event) -{ - if (event.button() != GUI::MouseButton::Left) - return; - if (!layer.rect().contains(event.position())) - return; - m_layer_being_moved = layer; - m_event_origin = image_event.position(); - m_layer_origin = layer.location(); - m_editor->window()->set_cursor(Gfx::StandardCursor::Move); -} - -void MoveTool::on_mousemove(Layer&, GUI::MouseEvent&, GUI::MouseEvent& image_event) -{ - if (!m_layer_being_moved) - return; - auto delta = image_event.position() - m_event_origin; - m_layer_being_moved->set_location(m_layer_origin.translated(delta)); - m_editor->layers_did_change(); -} - -void MoveTool::on_mouseup(Layer&, GUI::MouseEvent& event, GUI::MouseEvent&) -{ - if (event.button() != GUI::MouseButton::Left) - return; - m_layer_being_moved = nullptr; - m_editor->window()->set_cursor(Gfx::StandardCursor::None); - m_editor->did_complete_action(); -} - -void MoveTool::on_keydown(GUI::KeyEvent& event) -{ - if (event.modifiers() != 0) - return; - - auto* layer = m_editor->active_layer(); - if (!layer) - return; - - auto new_location = layer->location(); - - switch (event.key()) { - case Key_Up: - new_location.move_by(0, -1); - break; - case Key_Down: - new_location.move_by(0, 1); - break; - case Key_Left: - new_location.move_by(-1, 0); - break; - case Key_Right: - new_location.move_by(1, 0); - break; - default: - return; - } - - layer->set_location(new_location); - m_editor->layers_did_change(); -} - -void MoveTool::on_context_menu(Layer& layer, GUI::ContextMenuEvent& event) -{ - if (!m_context_menu) { - m_context_menu = GUI::Menu::construct(); - m_context_menu->add_action(GUI::CommonActions::make_move_to_front_action( - [this](auto&) { - m_editor->image()->move_layer_to_front(*m_context_menu_layer); - m_editor->layers_did_change(); - }, - m_editor)); - m_context_menu->add_action(GUI::CommonActions::make_move_to_back_action( - [this](auto&) { - m_editor->image()->move_layer_to_back(*m_context_menu_layer); - m_editor->layers_did_change(); - }, - m_editor)); - m_context_menu->add_separator(); - m_context_menu->add_action(GUI::Action::create( - "Delete layer", Gfx::Bitmap::load_from_file("/res/icons/16x16/delete.png"), [this](auto&) { - m_editor->image()->remove_layer(*m_context_menu_layer); - // FIXME: This should not be done imperatively here. Perhaps a Image::Client interface that ImageEditor can implement? - if (m_editor->active_layer() == m_context_menu_layer) - m_editor->set_active_layer(nullptr); - m_editor->layers_did_change(); - }, - m_editor)); - } - m_context_menu_layer = layer; - m_context_menu->popup(event.screen_position()); -} - -} diff --git a/Applications/PixelPaint/MoveTool.h b/Applications/PixelPaint/MoveTool.h deleted file mode 100644 index a0c16e5282..0000000000 --- a/Applications/PixelPaint/MoveTool.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2020, 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 "Tool.h" - -namespace PixelPaint { - -class MoveTool final : public Tool { -public: - MoveTool(); - virtual ~MoveTool() override; - - virtual void on_mousedown(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; - virtual void on_mousemove(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; - virtual void on_mouseup(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; - virtual void on_keydown(GUI::KeyEvent&) override; - virtual void on_context_menu(Layer&, GUI::ContextMenuEvent&) override; - -private: - RefPtr<Layer> m_layer_being_moved; - Gfx::IntPoint m_event_origin; - Gfx::IntPoint m_layer_origin; - RefPtr<GUI::Menu> m_context_menu; - RefPtr<Layer> m_context_menu_layer; -}; - -} diff --git a/Applications/PixelPaint/PaletteWidget.cpp b/Applications/PixelPaint/PaletteWidget.cpp deleted file mode 100644 index 52620b4b31..0000000000 --- a/Applications/PixelPaint/PaletteWidget.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "PaletteWidget.h" -#include "ImageEditor.h" -#include <LibGUI/BoxLayout.h> -#include <LibGUI/ColorPicker.h> -#include <LibGfx/Palette.h> - -namespace PixelPaint { - -class ColorWidget : public GUI::Frame { - C_OBJECT(ColorWidget); - -public: - explicit ColorWidget(Color color, PaletteWidget& palette_widget) - : m_palette_widget(palette_widget) - , m_color(color) - { - } - - virtual ~ColorWidget() override - { - } - - virtual void mousedown_event(GUI::MouseEvent& event) override - { - if (event.modifiers() & KeyModifier::Mod_Ctrl && event.button() == GUI::MouseButton::Left) { - auto dialog = GUI::ColorPicker::construct(m_color, window()); - if (dialog->exec() == GUI::Dialog::ExecOK) { - m_color = dialog->color(); - auto pal = palette(); - pal.set_color(ColorRole::Background, m_color); - set_palette(pal); - update(); - } - return; - } - - if (event.button() == GUI::MouseButton::Left) - m_palette_widget.set_primary_color(m_color); - else if (event.button() == GUI::MouseButton::Right) - m_palette_widget.set_secondary_color(m_color); - } - -private: - PaletteWidget& m_palette_widget; - Color m_color; -}; - -PaletteWidget::PaletteWidget(ImageEditor& editor) - : m_editor(editor) -{ - set_frame_shape(Gfx::FrameShape::Panel); - set_frame_shadow(Gfx::FrameShadow::Raised); - set_frame_thickness(0); - set_fill_with_background_color(true); - - set_fixed_height(34); - - m_secondary_color_widget = add<GUI::Frame>(); - m_secondary_color_widget->set_relative_rect({ 2, 2, 60, 31 }); - m_secondary_color_widget->set_fill_with_background_color(true); - set_secondary_color(m_editor.secondary_color()); - - m_primary_color_widget = add<GUI::Frame>(); - Gfx::IntRect rect { 0, 0, 38, 15 }; - rect.center_within(m_secondary_color_widget->relative_rect()); - m_primary_color_widget->set_relative_rect(rect); - m_primary_color_widget->set_fill_with_background_color(true); - set_primary_color(m_editor.primary_color()); - - m_editor.on_primary_color_change = [this](Color color) { - set_primary_color(color); - }; - - m_editor.on_secondary_color_change = [this](Color color) { - set_secondary_color(color); - }; - - auto& color_container = add<GUI::Widget>(); - color_container.set_relative_rect(m_secondary_color_widget->relative_rect().right() + 2, 2, 500, 32); - color_container.set_layout<GUI::VerticalBoxLayout>(); - color_container.layout()->set_spacing(1); - - auto& top_color_container = color_container.add<GUI::Widget>(); - top_color_container.set_layout<GUI::HorizontalBoxLayout>(); - top_color_container.layout()->set_spacing(1); - - auto& bottom_color_container = color_container.add<GUI::Widget>(); - bottom_color_container.set_layout<GUI::HorizontalBoxLayout>(); - bottom_color_container.layout()->set_spacing(1); - - auto add_color_widget = [&](GUI::Widget& container, Color color) { - auto& color_widget = container.add<ColorWidget>(color, *this); - color_widget.set_fill_with_background_color(true); - auto pal = color_widget.palette(); - pal.set_color(ColorRole::Background, color); - color_widget.set_palette(pal); - }; - - add_color_widget(top_color_container, Color::from_rgb(0x000000)); - add_color_widget(top_color_container, Color::from_rgb(0x808080)); - add_color_widget(top_color_container, Color::from_rgb(0x800000)); - add_color_widget(top_color_container, Color::from_rgb(0x808000)); - add_color_widget(top_color_container, Color::from_rgb(0x008000)); - add_color_widget(top_color_container, Color::from_rgb(0x008080)); - add_color_widget(top_color_container, Color::from_rgb(0x000080)); - add_color_widget(top_color_container, Color::from_rgb(0x800080)); - add_color_widget(top_color_container, Color::from_rgb(0x808040)); - add_color_widget(top_color_container, Color::from_rgb(0x004040)); - add_color_widget(top_color_container, Color::from_rgb(0x0080ff)); - add_color_widget(top_color_container, Color::from_rgb(0x004080)); - add_color_widget(top_color_container, Color::from_rgb(0x8000ff)); - add_color_widget(top_color_container, Color::from_rgb(0x804000)); - - add_color_widget(bottom_color_container, Color::from_rgb(0xffffff)); - add_color_widget(bottom_color_container, Color::from_rgb(0xc0c0c0)); - add_color_widget(bottom_color_container, Color::from_rgb(0xff0000)); - add_color_widget(bottom_color_container, Color::from_rgb(0xffff00)); - add_color_widget(bottom_color_container, Color::from_rgb(0x00ff00)); - add_color_widget(bottom_color_container, Color::from_rgb(0x00ffff)); - add_color_widget(bottom_color_container, Color::from_rgb(0x0000ff)); - add_color_widget(bottom_color_container, Color::from_rgb(0xff00ff)); - add_color_widget(bottom_color_container, Color::from_rgb(0xffff80)); - add_color_widget(bottom_color_container, Color::from_rgb(0x00ff80)); - add_color_widget(bottom_color_container, Color::from_rgb(0x80ffff)); - add_color_widget(bottom_color_container, Color::from_rgb(0x8080ff)); - add_color_widget(bottom_color_container, Color::from_rgb(0xff0080)); - add_color_widget(bottom_color_container, Color::from_rgb(0xff8040)); -} - -PaletteWidget::~PaletteWidget() -{ -} - -void PaletteWidget::set_primary_color(Color color) -{ - m_editor.set_primary_color(color); - auto pal = m_primary_color_widget->palette(); - pal.set_color(ColorRole::Background, color); - m_primary_color_widget->set_palette(pal); - m_primary_color_widget->update(); -} - -void PaletteWidget::set_secondary_color(Color color) -{ - m_editor.set_secondary_color(color); - auto pal = m_secondary_color_widget->palette(); - pal.set_color(ColorRole::Background, color); - m_secondary_color_widget->set_palette(pal); - m_secondary_color_widget->update(); -} - -} diff --git a/Applications/PixelPaint/PaletteWidget.h b/Applications/PixelPaint/PaletteWidget.h deleted file mode 100644 index 02c843f038..0000000000 --- a/Applications/PixelPaint/PaletteWidget.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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/Frame.h> - -namespace PixelPaint { - -class ImageEditor; - -class PaletteWidget final : public GUI::Frame { - C_OBJECT(PaletteWidget); - -public: - virtual ~PaletteWidget() override; - - void set_primary_color(Color); - void set_secondary_color(Color); - -private: - explicit PaletteWidget(ImageEditor&); - - ImageEditor& m_editor; - RefPtr<GUI::Frame> m_primary_color_widget; - RefPtr<GUI::Frame> m_secondary_color_widget; -}; - -} diff --git a/Applications/PixelPaint/PenTool.cpp b/Applications/PixelPaint/PenTool.cpp deleted file mode 100644 index 579231edf7..0000000000 --- a/Applications/PixelPaint/PenTool.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "PenTool.h" -#include "ImageEditor.h" -#include "Layer.h" -#include <LibGUI/Action.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Label.h> -#include <LibGUI/Menu.h> -#include <LibGUI/Painter.h> -#include <LibGUI/Slider.h> - -namespace PixelPaint { - -PenTool::PenTool() -{ -} - -PenTool::~PenTool() -{ -} - -void PenTool::on_mousedown(Layer& layer, GUI::MouseEvent& event, GUI::MouseEvent&) -{ - if (event.button() != GUI::MouseButton::Left && event.button() != GUI::MouseButton::Right) - return; - - GUI::Painter painter(layer.bitmap()); - painter.draw_line(event.position(), event.position(), m_editor->color_for(event), m_thickness); - layer.did_modify_bitmap(*m_editor->image()); - m_last_drawing_event_position = event.position(); -} - -void PenTool::on_mouseup(Layer&, GUI::MouseEvent& event, GUI::MouseEvent&) -{ - if (event.button() == GUI::MouseButton::Left || event.button() == GUI::MouseButton::Right) { - m_last_drawing_event_position = { -1, -1 }; - m_editor->did_complete_action(); - } -} - -void PenTool::on_mousemove(Layer& layer, GUI::MouseEvent& event, GUI::MouseEvent&) -{ - if (!(event.buttons() & GUI::MouseButton::Left || event.buttons() & GUI::MouseButton::Right)) - return; - GUI::Painter painter(layer.bitmap()); - - if (m_last_drawing_event_position != Gfx::IntPoint(-1, -1)) - painter.draw_line(m_last_drawing_event_position, event.position(), m_editor->color_for(event), m_thickness); - else - painter.draw_line(event.position(), event.position(), m_editor->color_for(event), m_thickness); - layer.did_modify_bitmap(*m_editor->image()); - - m_last_drawing_event_position = event.position(); -} - -void PenTool::on_tool_button_contextmenu(GUI::ContextMenuEvent& event) -{ - if (!m_context_menu) { - m_context_menu = GUI::Menu::construct(); - m_thickness_actions.set_exclusive(true); - auto insert_action = [&](int size, bool checked = false) { - auto action = GUI::Action::create_checkable(String::number(size), [this, size](auto&) { - m_thickness = size; - }); - action->set_checked(checked); - m_thickness_actions.add_action(*action); - m_context_menu->add_action(move(action)); - }; - insert_action(1, true); - insert_action(2); - insert_action(3); - insert_action(4); - } - m_context_menu->popup(event.screen_position()); -} - -GUI::Widget* PenTool::get_properties_widget() -{ - if (!m_properties_widget) { - m_properties_widget = GUI::Widget::construct(); - m_properties_widget->set_layout<GUI::VerticalBoxLayout>(); - - auto& thickness_container = m_properties_widget->add<GUI::Widget>(); - thickness_container.set_fixed_height(20); - thickness_container.set_layout<GUI::HorizontalBoxLayout>(); - - auto& thickness_label = thickness_container.add<GUI::Label>("Thickness:"); - thickness_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - thickness_label.set_fixed_size(80, 20); - - auto& thickness_slider = thickness_container.add<GUI::HorizontalSlider>(); - thickness_slider.set_fixed_height(20); - thickness_slider.set_range(1, 20); - thickness_slider.set_value(m_thickness); - thickness_slider.on_change = [this](int value) { - m_thickness = value; - }; - } - - return m_properties_widget.ptr(); -} - -} diff --git a/Applications/PixelPaint/PenTool.h b/Applications/PixelPaint/PenTool.h deleted file mode 100644 index 137e58251e..0000000000 --- a/Applications/PixelPaint/PenTool.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "Tool.h" -#include <LibGUI/ActionGroup.h> -#include <LibGfx/Point.h> - -namespace PixelPaint { - -class PenTool final : public Tool { -public: - PenTool(); - virtual ~PenTool() override; - - virtual void on_mousedown(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; - virtual void on_mousemove(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; - virtual void on_mouseup(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; - virtual void on_tool_button_contextmenu(GUI::ContextMenuEvent&) override; - virtual GUI::Widget* get_properties_widget() override; - -private: - Gfx::IntPoint m_last_drawing_event_position { -1, -1 }; - RefPtr<GUI::Menu> m_context_menu; - RefPtr<GUI::Widget> m_properties_widget; - int m_thickness { 1 }; - GUI::ActionGroup m_thickness_actions; -}; - -} diff --git a/Applications/PixelPaint/PickerTool.cpp b/Applications/PixelPaint/PickerTool.cpp deleted file mode 100644 index 86a7acf961..0000000000 --- a/Applications/PixelPaint/PickerTool.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "PickerTool.h" -#include "ImageEditor.h" -#include "Layer.h" -#include <LibGfx/Bitmap.h> - -namespace PixelPaint { - -PickerTool::PickerTool() -{ -} - -PickerTool::~PickerTool() -{ -} - -void PickerTool::on_mousedown(Layer& layer, GUI::MouseEvent& event, GUI::MouseEvent&) -{ - if (!layer.rect().contains(event.position())) - return; - auto color = layer.bitmap().get_pixel(event.position()); - if (event.button() == GUI::MouseButton::Left) - m_editor->set_primary_color(color); - else if (event.button() == GUI::MouseButton::Right) - m_editor->set_secondary_color(color); -} - -} diff --git a/Applications/PixelPaint/PickerTool.h b/Applications/PixelPaint/PickerTool.h deleted file mode 100644 index 163a0aeeee..0000000000 --- a/Applications/PixelPaint/PickerTool.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "Tool.h" - -namespace PixelPaint { - -class PickerTool final : public Tool { -public: - PickerTool(); - virtual ~PickerTool() override; - - virtual void on_mousedown(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; -}; - -} diff --git a/Applications/PixelPaint/RectangleTool.cpp b/Applications/PixelPaint/RectangleTool.cpp deleted file mode 100644 index a5b023f18b..0000000000 --- a/Applications/PixelPaint/RectangleTool.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "RectangleTool.h" -#include "ImageEditor.h" -#include "Layer.h" -#include <LibGUI/Action.h> -#include <LibGUI/Menu.h> -#include <LibGUI/Painter.h> -#include <LibGfx/Rect.h> -#include <math.h> - -namespace PixelPaint { - -RectangleTool::RectangleTool() -{ -} - -RectangleTool::~RectangleTool() -{ -} - -void RectangleTool::draw_using(GUI::Painter& painter, const Gfx::IntRect& rect) -{ - switch (m_mode) { - case Mode::Fill: - painter.fill_rect(rect, m_editor->color_for(m_drawing_button)); - break; - case Mode::Outline: - painter.draw_rect(rect, m_editor->color_for(m_drawing_button)); - break; - case Mode::Gradient: - painter.fill_rect_with_gradient(rect, m_editor->primary_color(), m_editor->secondary_color()); - break; - default: - ASSERT_NOT_REACHED(); - } -} - -void RectangleTool::on_mousedown(Layer&, GUI::MouseEvent& event, GUI::MouseEvent&) -{ - if (event.button() != GUI::MouseButton::Left && event.button() != GUI::MouseButton::Right) - return; - - if (m_drawing_button != GUI::MouseButton::None) - return; - - m_drawing_button = event.button(); - m_rectangle_start_position = event.position(); - m_rectangle_end_position = event.position(); - m_editor->update(); -} - -void RectangleTool::on_mouseup(Layer& layer, GUI::MouseEvent& event, GUI::MouseEvent&) -{ - if (event.button() == m_drawing_button) { - GUI::Painter painter(layer.bitmap()); - auto rect = Gfx::IntRect::from_two_points(m_rectangle_start_position, m_rectangle_end_position); - draw_using(painter, rect); - m_drawing_button = GUI::MouseButton::None; - layer.did_modify_bitmap(*m_editor->image()); - m_editor->did_complete_action(); - } -} - -void RectangleTool::on_mousemove(Layer&, GUI::MouseEvent& event, GUI::MouseEvent&) -{ - if (m_drawing_button == GUI::MouseButton::None) - return; - - m_rectangle_end_position = event.position(); - m_editor->update(); -} - -void RectangleTool::on_second_paint(const Layer& layer, GUI::PaintEvent& event) -{ - if (m_drawing_button == GUI::MouseButton::None) - return; - - GUI::Painter painter(*m_editor); - painter.add_clip_rect(event.rect()); - auto rect = Gfx::IntRect::from_two_points( - m_editor->layer_position_to_editor_position(layer, m_rectangle_start_position).to_type<int>(), - m_editor->layer_position_to_editor_position(layer, m_rectangle_end_position).to_type<int>()); - draw_using(painter, rect); -} - -void RectangleTool::on_keydown(GUI::KeyEvent& event) -{ - if (event.key() == Key_Escape && m_drawing_button != GUI::MouseButton::None) { - m_drawing_button = GUI::MouseButton::None; - m_editor->update(); - event.accept(); - } -} - -void RectangleTool::on_tool_button_contextmenu(GUI::ContextMenuEvent& event) -{ - if (!m_context_menu) { - m_context_menu = GUI::Menu::construct(); - m_context_menu->add_action(GUI::Action::create("Fill", [this](auto&) { - m_mode = Mode::Fill; - })); - m_context_menu->add_action(GUI::Action::create("Outline", [this](auto&) { - m_mode = Mode::Outline; - })); - m_context_menu->add_action(GUI::Action::create("Gradient", [this](auto&) { - m_mode = Mode::Gradient; - })); - } - m_context_menu->popup(event.screen_position()); -} - -} diff --git a/Applications/PixelPaint/RectangleTool.h b/Applications/PixelPaint/RectangleTool.h deleted file mode 100644 index f7d8c53273..0000000000 --- a/Applications/PixelPaint/RectangleTool.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "Tool.h" -#include <LibGUI/Forward.h> -#include <LibGfx/Point.h> - -namespace PixelPaint { - -class RectangleTool final : public Tool { -public: - RectangleTool(); - virtual ~RectangleTool() override; - - virtual void on_mousedown(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; - virtual void on_mousemove(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; - virtual void on_mouseup(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; - virtual void on_tool_button_contextmenu(GUI::ContextMenuEvent&) override; - virtual void on_second_paint(const Layer&, GUI::PaintEvent&) override; - virtual void on_keydown(GUI::KeyEvent&) override; - -private: - enum class Mode { - Outline, - Fill, - Gradient, - }; - - void draw_using(GUI::Painter&, const Gfx::IntRect&); - - GUI::MouseButton m_drawing_button { GUI::MouseButton::None }; - Gfx::IntPoint m_rectangle_start_position; - Gfx::IntPoint m_rectangle_end_position; - RefPtr<GUI::Menu> m_context_menu; - Mode m_mode { Mode::Outline }; -}; - -} diff --git a/Applications/PixelPaint/SprayTool.cpp b/Applications/PixelPaint/SprayTool.cpp deleted file mode 100644 index 9997c29674..0000000000 --- a/Applications/PixelPaint/SprayTool.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "SprayTool.h" -#include "ImageEditor.h" -#include "Layer.h" -#include <AK/Queue.h> -#include <LibGUI/Action.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Label.h> -#include <LibGUI/Menu.h> -#include <LibGUI/Painter.h> -#include <LibGUI/Slider.h> -#include <LibGfx/Bitmap.h> -#include <math.h> -#include <stdio.h> - -namespace PixelPaint { - -SprayTool::SprayTool() -{ - m_timer = Core::Timer::construct(); - m_timer->on_timeout = [&]() { - paint_it(); - }; - m_timer->set_interval(200); -} - -SprayTool::~SprayTool() -{ -} - -static double nrand() -{ - return double(rand()) / double(RAND_MAX); -} - -void SprayTool::paint_it() -{ - auto* layer = m_editor->active_layer(); - if (!layer) - return; - - auto& bitmap = layer->bitmap(); - GUI::Painter painter(bitmap); - ASSERT(bitmap.bpp() == 32); - m_editor->update(); - const double minimal_radius = 2; - const double base_radius = minimal_radius * m_thickness; - for (int i = 0; i < M_PI * base_radius * base_radius * (m_density / 100.0f); i++) { - double radius = base_radius * nrand(); - double angle = 2 * M_PI * nrand(); - const int xpos = m_last_pos.x() + radius * cos(angle); - const int ypos = m_last_pos.y() - radius * sin(angle); - if (xpos < 0 || xpos >= bitmap.width()) - continue; - if (ypos < 0 || ypos >= bitmap.height()) - continue; - bitmap.set_pixel<Gfx::StorageFormat::RGBA32>(xpos, ypos, m_color); - } - - layer->did_modify_bitmap(*m_editor->image()); -} - -void SprayTool::on_mousedown(Layer&, GUI::MouseEvent& event, GUI::MouseEvent&) -{ - m_color = m_editor->color_for(event); - m_last_pos = event.position(); - m_timer->start(); - paint_it(); -} - -void SprayTool::on_mousemove(Layer&, GUI::MouseEvent& event, GUI::MouseEvent&) -{ - m_last_pos = event.position(); - if (m_timer->is_active()) { - paint_it(); - m_timer->restart(m_timer->interval()); - } -} - -void SprayTool::on_mouseup(Layer&, GUI::MouseEvent&, GUI::MouseEvent&) -{ - if (m_timer->is_active()) { - m_timer->stop(); - m_editor->did_complete_action(); - } -} - -void SprayTool::on_tool_button_contextmenu(GUI::ContextMenuEvent& event) -{ - if (!m_context_menu) { - m_context_menu = GUI::Menu::construct(); - m_thickness_actions.set_exclusive(true); - auto insert_action = [&](int size, bool checked = false) { - auto action = GUI::Action::create_checkable(String::number(size), [this, size](auto&) { - m_thickness = size; - }); - action->set_checked(checked); - m_thickness_actions.add_action(*action); - m_context_menu->add_action(move(action)); - }; - insert_action(1, true); - insert_action(2); - insert_action(3); - insert_action(4); - } - m_context_menu->popup(event.screen_position()); -} - -GUI::Widget* SprayTool::get_properties_widget() -{ - if (!m_properties_widget) { - m_properties_widget = GUI::Widget::construct(); - m_properties_widget->set_layout<GUI::VerticalBoxLayout>(); - - auto& thickness_container = m_properties_widget->add<GUI::Widget>(); - thickness_container.set_fixed_height(20); - thickness_container.set_layout<GUI::HorizontalBoxLayout>(); - - auto& thickness_label = thickness_container.add<GUI::Label>("Thickness:"); - thickness_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - thickness_label.set_fixed_size(80, 20); - - auto& thickness_slider = thickness_container.add<GUI::HorizontalSlider>(); - thickness_slider.set_fixed_height(20); - thickness_slider.set_range(1, 20); - thickness_slider.set_value(m_thickness); - thickness_slider.on_change = [this](int value) { - m_thickness = value; - }; - - auto& density_container = m_properties_widget->add<GUI::Widget>(); - density_container.set_fixed_height(20); - density_container.set_layout<GUI::HorizontalBoxLayout>(); - - auto& density_label = density_container.add<GUI::Label>("Density:"); - density_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - density_label.set_fixed_size(80, 20); - - auto& density_slider = density_container.add<GUI::HorizontalSlider>(); - density_slider.set_fixed_height(30); - density_slider.set_range(1, 100); - density_slider.set_value(m_density); - density_slider.on_change = [this](int value) { - m_density = value; - }; - } - - return m_properties_widget.ptr(); -} - -} diff --git a/Applications/PixelPaint/SprayTool.h b/Applications/PixelPaint/SprayTool.h deleted file mode 100644 index a3e55efd52..0000000000 --- a/Applications/PixelPaint/SprayTool.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "Tool.h" -#include <LibCore/Timer.h> -#include <LibGUI/ActionGroup.h> -#include <LibGUI/Painter.h> - -namespace PixelPaint { - -class SprayTool final : public Tool { -public: - SprayTool(); - virtual ~SprayTool() override; - - virtual void on_mousedown(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; - virtual void on_mouseup(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; - virtual void on_mousemove(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; - virtual void on_tool_button_contextmenu(GUI::ContextMenuEvent&) override; - virtual GUI::Widget* get_properties_widget() override; - -private: - void paint_it(); - - RefPtr<GUI::Widget> m_properties_widget; - RefPtr<Core::Timer> m_timer; - Gfx::IntPoint m_last_pos; - Color m_color; - RefPtr<GUI::Menu> m_context_menu; - GUI::ActionGroup m_thickness_actions; - int m_thickness { 10 }; - int m_density { 40 }; -}; - -} diff --git a/Applications/PixelPaint/Tool.cpp b/Applications/PixelPaint/Tool.cpp deleted file mode 100644 index 846e017410..0000000000 --- a/Applications/PixelPaint/Tool.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "Tool.h" -#include "ImageEditor.h" -#include <LibGUI/Action.h> - -namespace PixelPaint { - -Tool::Tool() -{ -} - -Tool::~Tool() -{ -} - -void Tool::setup(ImageEditor& editor) -{ - m_editor = editor; -} - -void Tool::set_action(GUI::Action* action) -{ - m_action = action; -} - -} diff --git a/Applications/PixelPaint/Tool.h b/Applications/PixelPaint/Tool.h deleted file mode 100644 index e7deb5f1cb..0000000000 --- a/Applications/PixelPaint/Tool.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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/Event.h> -#include <LibGUI/Forward.h> - -namespace PixelPaint { - -class ImageEditor; -class Layer; - -class Tool { -public: - virtual ~Tool(); - - virtual void on_mousedown(Layer&, GUI::MouseEvent&, GUI::MouseEvent&) { } - virtual void on_mousemove(Layer&, GUI::MouseEvent&, GUI::MouseEvent&) { } - virtual void on_mouseup(Layer&, GUI::MouseEvent&, GUI::MouseEvent&) { } - virtual void on_context_menu(Layer&, GUI::ContextMenuEvent&) { } - virtual void on_tool_button_contextmenu(GUI::ContextMenuEvent&) { } - virtual void on_second_paint(const Layer&, GUI::PaintEvent&) { } - virtual void on_keydown(GUI::KeyEvent&) { } - virtual void on_keyup(GUI::KeyEvent&) { } - virtual GUI::Widget* get_properties_widget() { return nullptr; } - - void clear() { m_editor = nullptr; } - void setup(ImageEditor&); - - GUI::Action* action() { return m_action; } - void set_action(GUI::Action*); - -protected: - Tool(); - WeakPtr<ImageEditor> m_editor; - RefPtr<GUI::Action> m_action; -}; - -} diff --git a/Applications/PixelPaint/ToolPropertiesWidget.cpp b/Applications/PixelPaint/ToolPropertiesWidget.cpp deleted file mode 100644 index a27103d3e7..0000000000 --- a/Applications/PixelPaint/ToolPropertiesWidget.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2020, Ben Jilks <benjyjilks@gmail.com> - * 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 "ToolPropertiesWidget.h" -#include "Tool.h" -#include <LibGUI/BoxLayout.h> -#include <LibGUI/GroupBox.h> - -namespace PixelPaint { - -ToolPropertiesWidget::ToolPropertiesWidget() -{ - set_layout<GUI::VerticalBoxLayout>(); - - m_group_box = add<GUI::GroupBox>("Tool properties"); - auto& layout = m_group_box->set_layout<GUI::VerticalBoxLayout>(); - layout.set_margins({ 10, 20, 10, 10 }); -} - -void ToolPropertiesWidget::set_active_tool(Tool* tool) -{ - if (tool == m_active_tool) - return; - - if (m_active_tool_widget != nullptr) - m_group_box->remove_child(*m_active_tool_widget); - - m_active_tool = tool; - m_active_tool_widget = tool->get_properties_widget(); - if (m_active_tool_widget != nullptr) - m_group_box->add_child(*m_active_tool_widget); -} - -ToolPropertiesWidget::~ToolPropertiesWidget() -{ -} - -} diff --git a/Applications/PixelPaint/ToolPropertiesWidget.h b/Applications/PixelPaint/ToolPropertiesWidget.h deleted file mode 100644 index 41c1675cfa..0000000000 --- a/Applications/PixelPaint/ToolPropertiesWidget.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2020, Ben Jilks <benjyjilks@gmail.com> - * 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 <AK/RefPtr.h> -#include <LibGUI/Forward.h> -#include <LibGUI/Widget.h> - -namespace PixelPaint { - -class Tool; - -class ToolPropertiesWidget final : public GUI::Widget { - C_OBJECT(ToolPropertiesWidget); - -public: - virtual ~ToolPropertiesWidget() override; - - void set_active_tool(Tool*); - -private: - ToolPropertiesWidget(); - - RefPtr<GUI::GroupBox> m_group_box; - - Tool* m_active_tool { nullptr }; - GUI::Widget* m_active_tool_widget { nullptr }; -}; - -} diff --git a/Applications/PixelPaint/ToolboxWidget.cpp b/Applications/PixelPaint/ToolboxWidget.cpp deleted file mode 100644 index c746901887..0000000000 --- a/Applications/PixelPaint/ToolboxWidget.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "ToolboxWidget.h" -#include "BrushTool.h" -#include "BucketTool.h" -#include "EllipseTool.h" -#include "EraseTool.h" -#include "LineTool.h" -#include "MoveTool.h" -#include "PenTool.h" -#include "PickerTool.h" -#include "RectangleTool.h" -#include "SprayTool.h" -#include <AK/StringBuilder.h> -#include <LibGUI/Action.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Button.h> -#include <LibGUI/Window.h> - -namespace PixelPaint { - -class ToolButton final : public GUI::Button { - C_OBJECT(ToolButton) -public: - ToolButton(ToolboxWidget& toolbox, const String& name, const GUI::Shortcut& shortcut, OwnPtr<Tool> tool) - : m_toolbox(toolbox) - , m_tool(move(tool)) - { - StringBuilder builder; - builder.append(name); - builder.append(" ("); - builder.append(shortcut.to_string()); - builder.append(")"); - set_tooltip(builder.to_string()); - - m_action = GUI::Action::create_checkable( - name, shortcut, [this](auto& action) { - if (action.is_checked()) - m_toolbox.on_tool_selection(m_tool); - else - m_toolbox.on_tool_selection(nullptr); - }, - toolbox.window()); - - m_tool->set_action(m_action); - set_action(*m_action); - m_toolbox.m_action_group.add_action(*m_action); - } - - const Tool& tool() const { return *m_tool; } - Tool& tool() { return *m_tool; } - - virtual bool is_uncheckable() const override { return false; } - - virtual void context_menu_event(GUI::ContextMenuEvent& event) override - { - m_action->activate(); - m_tool->on_tool_button_contextmenu(event); - } - -private: - ToolboxWidget& m_toolbox; - OwnPtr<Tool> m_tool; - RefPtr<GUI::Action> m_action; -}; - -ToolboxWidget::ToolboxWidget() -{ - set_fill_with_background_color(true); - - set_frame_thickness(1); - set_frame_shape(Gfx::FrameShape::Panel); - set_frame_shadow(Gfx::FrameShadow::Raised); - - set_fixed_width(48); - - set_layout<GUI::VerticalBoxLayout>(); - layout()->set_margins({ 4, 4, 4, 4 }); - - m_action_group.set_exclusive(true); - m_action_group.set_unchecking_allowed(false); - - deferred_invoke([this](auto&) { - setup_tools(); - }); -} - -ToolboxWidget::~ToolboxWidget() -{ -} - -void ToolboxWidget::setup_tools() -{ - auto add_tool = [&](const StringView& name, const StringView& icon_name, const GUI::Shortcut& shortcut, NonnullOwnPtr<Tool> tool) -> ToolButton& { - m_tools.append(tool.ptr()); - auto& button = add<ToolButton>(*this, name, shortcut, move(tool)); - button.set_focus_policy(GUI::FocusPolicy::TabFocus); - button.set_fixed_height(32); - button.set_checkable(true); - button.set_icon(Gfx::Bitmap::load_from_file(String::formatted("/res/icons/pixelpaint/{}.png", icon_name))); - return button; - }; - - add_tool("Move", "move", { 0, Key_M }, make<MoveTool>()); - add_tool("Pen", "pen", { 0, Key_N }, make<PenTool>()); - add_tool("Brush", "brush", { 0, Key_P }, make<BrushTool>()); - add_tool("Bucket Fill", "bucket", { Mod_Shift, Key_B }, make<BucketTool>()); - add_tool("Spray", "spray", { Mod_Shift, Key_S }, make<SprayTool>()); - add_tool("Color Picker", "picker", { 0, Key_O }, make<PickerTool>()); - add_tool("Erase", "eraser", { Mod_Shift, Key_E }, make<EraseTool>()); - add_tool("Line", "line", { Mod_Ctrl | Mod_Shift, Key_L }, make<LineTool>()); - add_tool("Rectangle", "rectangle", { Mod_Ctrl | Mod_Shift, Key_R }, make<RectangleTool>()); - add_tool("Ellipse", "circle", { Mod_Ctrl | Mod_Shift, Key_E }, make<EllipseTool>()); -} - -} diff --git a/Applications/PixelPaint/ToolboxWidget.h b/Applications/PixelPaint/ToolboxWidget.h deleted file mode 100644 index 23794179f6..0000000000 --- a/Applications/PixelPaint/ToolboxWidget.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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/ActionGroup.h> -#include <LibGUI/Frame.h> - -namespace PixelPaint { - -class Tool; - -class ToolboxWidget final : public GUI::Frame { - C_OBJECT(ToolboxWidget) -public: - virtual ~ToolboxWidget() override; - - Function<void(Tool*)> on_tool_selection; - - template<typename Callback> - void for_each_tool(Callback callback) - { - for (auto& tool : m_tools) - callback(*tool); - } - -private: - friend class ToolButton; - - void setup_tools(); - - explicit ToolboxWidget(); - GUI::ActionGroup m_action_group; - Vector<Tool*> m_tools; -}; - -} diff --git a/Applications/PixelPaint/main.cpp b/Applications/PixelPaint/main.cpp deleted file mode 100644 index 02164a52ce..0000000000 --- a/Applications/PixelPaint/main.cpp +++ /dev/null @@ -1,387 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "CreateNewImageDialog.h" -#include "CreateNewLayerDialog.h" -#include "FilterParams.h" -#include "Image.h" -#include "ImageEditor.h" -#include "Layer.h" -#include "LayerListWidget.h" -#include "LayerPropertiesWidget.h" -#include "PaletteWidget.h" -#include "Tool.h" -#include "ToolPropertiesWidget.h" -#include "ToolboxWidget.h" -#include <LibGUI/Action.h> -#include <LibGUI/Application.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Clipboard.h> -#include <LibGUI/FilePicker.h> -#include <LibGUI/Icon.h> -#include <LibGUI/Menu.h> -#include <LibGUI/MenuBar.h> -#include <LibGUI/MessageBox.h> -#include <LibGUI/TableView.h> -#include <LibGUI/Window.h> -#include <LibGfx/Bitmap.h> -#include <LibGfx/Matrix4x4.h> -#include <stdio.h> - -int main(int argc, char** argv) -{ - if (pledge("stdio thread shared_buffer accept rpath unix wpath cpath fattr", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto app = GUI::Application::construct(argc, argv); - - if (pledge("stdio thread shared_buffer accept rpath wpath cpath", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto app_icon = GUI::Icon::default_icon("app-pixel-paint"); - - auto window = GUI::Window::construct(); - window->set_title("PixelPaint"); - window->resize(950, 570); - window->set_icon(app_icon.bitmap_for_size(16)); - - auto& horizontal_container = window->set_main_widget<GUI::Widget>(); - horizontal_container.set_layout<GUI::HorizontalBoxLayout>(); - horizontal_container.layout()->set_spacing(0); - - auto& toolbox = horizontal_container.add<PixelPaint::ToolboxWidget>(); - - auto& vertical_container = horizontal_container.add<GUI::Widget>(); - vertical_container.set_layout<GUI::VerticalBoxLayout>(); - vertical_container.layout()->set_spacing(0); - - auto& image_editor = vertical_container.add<PixelPaint::ImageEditor>(); - image_editor.set_focus(true); - - vertical_container.add<PixelPaint::PaletteWidget>(image_editor); - - auto& right_panel = horizontal_container.add<GUI::Widget>(); - right_panel.set_fill_with_background_color(true); - right_panel.set_fixed_width(230); - right_panel.set_layout<GUI::VerticalBoxLayout>(); - - auto& layer_list_widget = right_panel.add<PixelPaint::LayerListWidget>(); - - auto& layer_properties_widget = right_panel.add<PixelPaint::LayerPropertiesWidget>(); - - auto& tool_properties_widget = right_panel.add<PixelPaint::ToolPropertiesWidget>(); - - toolbox.on_tool_selection = [&](auto* tool) { - image_editor.set_active_tool(tool); - tool_properties_widget.set_active_tool(tool); - }; - - window->show(); - - auto menubar = GUI::MenuBar::construct(); - auto& app_menu = menubar->add_menu("PixelPaint"); - - app_menu.add_action( - GUI::Action::create( - "New", [&](auto&) { - auto dialog = PixelPaint::CreateNewImageDialog::construct(window); - if (dialog->exec() == GUI::Dialog::ExecOK) { - auto image = PixelPaint::Image::create_with_size(dialog->image_size()); - auto bg_layer = PixelPaint::Layer::create_with_size(*image, image->size(), "Background"); - image->add_layer(*bg_layer); - bg_layer->bitmap().fill(Color::White); - - image_editor.set_image(image); - layer_list_widget.set_image(image); - image_editor.set_active_layer(bg_layer); - } - }, - window)); - app_menu.add_action(GUI::CommonActions::make_open_action([&](auto&) { - Optional<String> open_path = GUI::FilePicker::get_open_filepath(window); - - if (!open_path.has_value()) - return; - - auto image = PixelPaint::Image::create_from_file(open_path.value()); - image_editor.set_image(image); - layer_list_widget.set_image(image); - })); - app_menu.add_action(GUI::CommonActions::make_save_as_action([&](auto&) { - if (!image_editor.image()) - return; - - Optional<String> save_path = GUI::FilePicker::get_save_filepath(window, "untitled", "pp"); - - if (!save_path.has_value()) - return; - - image_editor.image()->save(save_path.value()); - })); - auto& export_submenu = app_menu.add_submenu("Export"); - export_submenu.add_action( - GUI::Action::create( - "As BMP", [&](auto&) { - if (!image_editor.image()) - return; - - Optional<String> save_path = GUI::FilePicker::get_save_filepath(window, "untitled", "bmp"); - - if (!save_path.has_value()) - return; - - image_editor.image()->export_bmp(save_path.value()); - }, - window)); - - app_menu.add_separator(); - app_menu.add_action(GUI::CommonActions::make_quit_action([](auto&) { - GUI::Application::the()->quit(); - return; - })); - - auto& edit_menu = menubar->add_menu("Edit"); - auto paste_action = GUI::CommonActions::make_paste_action([&](auto&) { - ASSERT(image_editor.image()); - auto bitmap = GUI::Clipboard::the().bitmap(); - if (!bitmap) - return; - - auto layer = PixelPaint::Layer::create_with_bitmap(*image_editor.image(), *bitmap, "Pasted layer"); - image_editor.image()->add_layer(layer.release_nonnull()); - }); - GUI::Clipboard::the().on_change = [&](auto& mime_type) { - paste_action->set_enabled(mime_type == "image/x-serenityos"); - }; - paste_action->set_enabled(GUI::Clipboard::the().mime_type() == "image/x-serenityos"); - - edit_menu.add_action(paste_action); - - auto undo_action = GUI::CommonActions::make_undo_action([&](auto&) { - ASSERT(image_editor.image()); - image_editor.undo(); - }); - edit_menu.add_action(undo_action); - - auto redo_action = GUI::CommonActions::make_redo_action([&](auto&) { - ASSERT(image_editor.image()); - image_editor.redo(); - }); - edit_menu.add_action(redo_action); - - auto& tool_menu = menubar->add_menu("Tool"); - toolbox.for_each_tool([&](auto& tool) { - if (tool.action()) - tool_menu.add_action(*tool.action()); - return IterationDecision::Continue; - }); - - auto& layer_menu = menubar->add_menu("Layer"); - layer_menu.add_action(GUI::Action::create( - "Create new layer...", { Mod_Ctrl | Mod_Shift, Key_N }, [&](auto&) { - auto dialog = PixelPaint::CreateNewLayerDialog::construct(image_editor.image()->size(), window); - if (dialog->exec() == GUI::Dialog::ExecOK) { - auto layer = PixelPaint::Layer::create_with_size(*image_editor.image(), dialog->layer_size(), dialog->layer_name()); - if (!layer) { - GUI::MessageBox::show_error(window, String::formatted("Unable to create layer with size {}", dialog->size().to_string())); - return; - } - image_editor.image()->add_layer(layer.release_nonnull()); - image_editor.layers_did_change(); - } - }, - window)); - - layer_menu.add_separator(); - layer_menu.add_action(GUI::Action::create( - "Select previous layer", { 0, Key_PageUp }, [&](auto&) { - layer_list_widget.move_selection(1); - }, - window)); - layer_menu.add_action(GUI::Action::create( - "Select next layer", { 0, Key_PageDown }, [&](auto&) { - layer_list_widget.move_selection(-1); - }, - window)); - layer_menu.add_action(GUI::Action::create( - "Select top layer", { 0, Key_Home }, [&](auto&) { - layer_list_widget.select_top_layer(); - }, - window)); - layer_menu.add_action(GUI::Action::create( - "Select bottom layer", { 0, Key_End }, [&](auto&) { - layer_list_widget.select_bottom_layer(); - }, - window)); - layer_menu.add_separator(); - layer_menu.add_action(GUI::Action::create( - "Move active layer up", { Mod_Ctrl, Key_PageUp }, [&](auto&) { - auto active_layer = image_editor.active_layer(); - if (!active_layer) - return; - image_editor.image()->move_layer_up(*active_layer); - }, - window)); - layer_menu.add_action(GUI::Action::create( - "Move active layer down", { Mod_Ctrl, Key_PageDown }, [&](auto&) { - auto active_layer = image_editor.active_layer(); - if (!active_layer) - return; - image_editor.image()->move_layer_down(*active_layer); - }, - window)); - layer_menu.add_separator(); - layer_menu.add_action(GUI::Action::create( - "Remove active layer", { Mod_Ctrl, Key_D }, [&](auto&) { - auto active_layer = image_editor.active_layer(); - if (!active_layer) - return; - image_editor.image()->remove_layer(*active_layer); - image_editor.set_active_layer(nullptr); - }, - window)); - - auto& filter_menu = menubar->add_menu("Filter"); - auto& spatial_filters_menu = filter_menu.add_submenu("Spatial"); - - auto& edge_detect_submenu = spatial_filters_menu.add_submenu("Edge Detect"); - edge_detect_submenu.add_action(GUI::Action::create("Laplacian (cardinal)", [&](auto&) { - if (auto* layer = image_editor.active_layer()) { - Gfx::LaplacianFilter filter; - if (auto parameters = PixelPaint::FilterParameters<Gfx::LaplacianFilter>::get(false)) { - filter.apply(layer->bitmap(), layer->rect(), layer->bitmap(), layer->rect(), *parameters); - image_editor.did_complete_action(); - } - } - })); - edge_detect_submenu.add_action(GUI::Action::create("Laplacian (diagonal)", [&](auto&) { - if (auto* layer = image_editor.active_layer()) { - Gfx::LaplacianFilter filter; - if (auto parameters = PixelPaint::FilterParameters<Gfx::LaplacianFilter>::get(true)) { - filter.apply(layer->bitmap(), layer->rect(), layer->bitmap(), layer->rect(), *parameters); - image_editor.did_complete_action(); - } - } - })); - auto& blur_submenu = spatial_filters_menu.add_submenu("Blur and Sharpen"); - blur_submenu.add_action(GUI::Action::create("Gaussian Blur (3x3)", [&](auto&) { - if (auto* layer = image_editor.active_layer()) { - Gfx::SpatialGaussianBlurFilter<3> filter; - if (auto parameters = PixelPaint::FilterParameters<Gfx::SpatialGaussianBlurFilter<3>>::get()) { - filter.apply(layer->bitmap(), layer->rect(), layer->bitmap(), layer->rect(), *parameters); - image_editor.did_complete_action(); - } - } - })); - blur_submenu.add_action(GUI::Action::create("Gaussian Blur (5x5)", [&](auto&) { - if (auto* layer = image_editor.active_layer()) { - Gfx::SpatialGaussianBlurFilter<5> filter; - if (auto parameters = PixelPaint::FilterParameters<Gfx::SpatialGaussianBlurFilter<5>>::get()) { - filter.apply(layer->bitmap(), layer->rect(), layer->bitmap(), layer->rect(), *parameters); - image_editor.did_complete_action(); - } - } - })); - blur_submenu.add_action(GUI::Action::create("Box Blur (3x3)", [&](auto&) { - if (auto* layer = image_editor.active_layer()) { - Gfx::BoxBlurFilter<3> filter; - if (auto parameters = PixelPaint::FilterParameters<Gfx::BoxBlurFilter<3>>::get()) { - filter.apply(layer->bitmap(), layer->rect(), layer->bitmap(), layer->rect(), *parameters); - image_editor.did_complete_action(); - } - } - })); - blur_submenu.add_action(GUI::Action::create("Box Blur (5x5)", [&](auto&) { - if (auto* layer = image_editor.active_layer()) { - Gfx::BoxBlurFilter<5> filter; - if (auto parameters = PixelPaint::FilterParameters<Gfx::BoxBlurFilter<5>>::get()) { - filter.apply(layer->bitmap(), layer->rect(), layer->bitmap(), layer->rect(), *parameters); - image_editor.did_complete_action(); - } - } - })); - blur_submenu.add_action(GUI::Action::create("Sharpen", [&](auto&) { - if (auto* layer = image_editor.active_layer()) { - Gfx::SharpenFilter filter; - if (auto parameters = PixelPaint::FilterParameters<Gfx::SharpenFilter>::get()) { - filter.apply(layer->bitmap(), layer->rect(), layer->bitmap(), layer->rect(), *parameters); - image_editor.did_complete_action(); - } - } - })); - - spatial_filters_menu.add_separator(); - spatial_filters_menu.add_action(GUI::Action::create("Generic 5x5 Convolution", [&](auto&) { - if (auto* layer = image_editor.active_layer()) { - Gfx::GenericConvolutionFilter<5> filter; - if (auto parameters = PixelPaint::FilterParameters<Gfx::GenericConvolutionFilter<5>>::get(window)) { - filter.apply(layer->bitmap(), layer->rect(), layer->bitmap(), layer->rect(), *parameters); - image_editor.did_complete_action(); - } - } - })); - - auto& help_menu = menubar->add_menu("Help"); - help_menu.add_action(GUI::CommonActions::make_about_action("PixelPaint", app_icon, window)); - - app->set_menubar(move(menubar)); - - image_editor.on_active_layer_change = [&](auto* layer) { - layer_list_widget.set_selected_layer(layer); - layer_properties_widget.set_layer(layer); - }; - - auto image = PixelPaint::Image::create_with_size({ 640, 480 }); - - auto bg_layer = PixelPaint::Layer::create_with_size(*image, { 640, 480 }, "Background"); - image->add_layer(*bg_layer); - bg_layer->bitmap().fill(Color::White); - - auto fg_layer1 = PixelPaint::Layer::create_with_size(*image, { 200, 200 }, "FG Layer 1"); - fg_layer1->set_location({ 50, 50 }); - image->add_layer(*fg_layer1); - fg_layer1->bitmap().fill(Color::Yellow); - - auto fg_layer2 = PixelPaint::Layer::create_with_size(*image, { 100, 100 }, "FG Layer 2"); - fg_layer2->set_location({ 300, 300 }); - image->add_layer(*fg_layer2); - fg_layer2->bitmap().fill(Color::Blue); - - layer_list_widget.on_layer_select = [&](auto* layer) { - image_editor.set_active_layer(layer); - }; - - layer_list_widget.set_image(image); - - image_editor.set_image(image); - image_editor.set_active_layer(bg_layer); - - return app->exec(); -} diff --git a/Applications/QuickShow/CMakeLists.txt b/Applications/QuickShow/CMakeLists.txt deleted file mode 100644 index 8d2b46e217..0000000000 --- a/Applications/QuickShow/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -set(SOURCES - main.cpp - QSWidget.cpp -) - -serenity_app(QuickShow ICON filetype-image) -target_link_libraries(QuickShow LibGUI LibGfx) diff --git a/Applications/QuickShow/QSWidget.cpp b/Applications/QuickShow/QSWidget.cpp deleted file mode 100644 index 94dbe858ea..0000000000 --- a/Applications/QuickShow/QSWidget.cpp +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "QSWidget.h" -#include <AK/StringBuilder.h> -#include <LibCore/DirIterator.h> -#include <LibGUI/MessageBox.h> -#include <LibGUI/Painter.h> -#include <LibGUI/Window.h> -#include <LibGfx/Bitmap.h> -#include <LibGfx/Orientation.h> -#include <LibGfx/Palette.h> - -QSWidget::QSWidget() -{ - set_fill_with_background_color(false); -} - -QSWidget::~QSWidget() -{ -} - -void QSWidget::clear() -{ - m_bitmap = nullptr; - m_path = {}; - - set_scale(100); - update(); -} - -void QSWidget::flip(Gfx::Orientation orientation) -{ - m_bitmap = m_bitmap->flipped(orientation); - set_scale(m_scale); - - resize_window(); -} - -void QSWidget::rotate(Gfx::RotationDirection rotation_direction) -{ - m_bitmap = m_bitmap->rotated(rotation_direction); - set_scale(m_scale); - - resize_window(); -} - -void QSWidget::navigate(Directions direction) -{ - if (m_path == nullptr) - return; - - auto parts = m_path.split('/'); - parts.remove(parts.size() - 1); - StringBuilder sb; - sb.append("/"); - sb.join("/", parts); - AK::String current_dir = sb.to_string(); - - if (m_files_in_same_dir.is_empty()) { - Core::DirIterator iterator(current_dir, Core::DirIterator::Flags::SkipDots); - while (iterator.has_next()) { - String file = iterator.next_full_path(); - if (!Gfx::Bitmap::is_path_a_supported_image_format(file)) - continue; - m_files_in_same_dir.append(file); - } - } - - auto current_index = m_files_in_same_dir.find_first_index(m_path); - if (!current_index.has_value()) { - return; - } - - size_t index = current_index.value(); - if (direction == Directions::Back) { - if (index == 0) { - GUI::MessageBox::show(window(), "This is the first file.", "Cannot open image", GUI::MessageBox::Type::Error); - return; - } - - index--; - } else if (direction == Directions::Forward) { - if (index == m_files_in_same_dir.size() - 1) { - GUI::MessageBox::show(window(), "This is the last file.", "Cannot open image", GUI::MessageBox::Type::Error); - return; - } - - index++; - } else if (direction == Directions::First) { - index = 0; - } else if (direction == Directions::Last) { - index = m_files_in_same_dir.size() - 1; - } - - this->load_from_file(m_files_in_same_dir.at(index)); -} - -void QSWidget::set_scale(int scale) -{ - if (m_bitmap.is_null()) - return; - - if (m_scale == scale) { - update(); - return; - } - - if (scale < 10) - scale = 10; - if (scale > 1000) - scale = 1000; - - if (scale == 100) - m_pan_origin = { 0, 0 }; - - m_scale = scale; - float scale_factor = (float)m_scale / 100.0f; - - Gfx::IntSize new_size; - new_size.set_width(m_bitmap->width() * scale_factor); - new_size.set_height(m_bitmap->height() * scale_factor); - m_bitmap_rect.set_size(new_size); - - if (on_scale_change) - on_scale_change(m_scale, m_bitmap_rect); - - relayout(); -} - -void QSWidget::relayout() -{ - if (m_bitmap.is_null()) - return; - - float scale_factor = (float)m_scale / 100.0f; - Gfx::IntSize new_size = m_bitmap_rect.size(); - - Gfx::IntPoint new_location; - new_location.set_x((width() / 2) - (new_size.width() / 2) - (m_pan_origin.x() * scale_factor)); - new_location.set_y((height() / 2) - (new_size.height() / 2) - (m_pan_origin.y() * scale_factor)); - m_bitmap_rect.set_location(new_location); - - update(); -} - -void QSWidget::resize_event(GUI::ResizeEvent& event) -{ - relayout(); - GUI::Widget::resize_event(event); -} - -void QSWidget::doubleclick_event(GUI::MouseEvent&) -{ - on_doubleclick(); -} - -void QSWidget::paint_event(GUI::PaintEvent& event) -{ - Frame::paint_event(event); - - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - painter.add_clip_rect(frame_inner_rect()); - - Gfx::StylePainter::paint_transparency_grid(painter, frame_inner_rect(), palette()); - - if (!m_bitmap.is_null()) - painter.draw_scaled_bitmap(m_bitmap_rect, *m_bitmap, m_bitmap->rect()); -} - -void QSWidget::mousedown_event(GUI::MouseEvent& event) -{ - if (event.button() != GUI::MouseButton::Left) - return; - m_click_position = event.position(); - m_saved_pan_origin = m_pan_origin; -} - -void QSWidget::mouseup_event([[maybe_unused]] GUI::MouseEvent& event) { } - -void QSWidget::mousemove_event(GUI::MouseEvent& event) -{ - if (!(event.buttons() & GUI::MouseButton::Left)) - return; - - auto delta = event.position() - m_click_position; - float scale_factor = (float)m_scale / 100.0f; - m_pan_origin = m_saved_pan_origin.translated( - -delta.x() / scale_factor, - -delta.y() / scale_factor); - - relayout(); -} - -void QSWidget::mousewheel_event(GUI::MouseEvent& event) -{ - int new_scale = m_scale - event.wheel_delta() * 10; - if (new_scale < 10) - new_scale = 10; - if (new_scale > 1000) - new_scale = 1000; - - if (new_scale == m_scale) { - return; - } - - auto old_scale_factor = (float)m_scale / 100.0f; - auto new_scale_factor = (float)new_scale / 100.0f; - - auto focus_point = Gfx::FloatPoint( - m_pan_origin.x() - ((float)event.x() - (float)width() / 2.0) / old_scale_factor, - m_pan_origin.y() - ((float)event.y() - (float)height() / 2.0) / old_scale_factor); - - m_pan_origin = Gfx::FloatPoint( - focus_point.x() - new_scale_factor / old_scale_factor * (focus_point.x() - m_pan_origin.x()), - focus_point.y() - new_scale_factor / old_scale_factor * (focus_point.y() - m_pan_origin.y())); - - set_scale(new_scale); -} - -void QSWidget::load_from_file(const String& path) -{ - auto bitmap = Gfx::Bitmap::load_from_file(path); - if (!bitmap) { - GUI::MessageBox::show(window(), String::formatted("Failed to open {}", path), "Cannot open image", GUI::MessageBox::Type::Error); - return; - } - - m_path = path; - m_bitmap = bitmap; - m_scale = -1; - set_scale(100); -} - -void QSWidget::drop_event(GUI::DropEvent& event) -{ - event.accept(); - if (on_drop) - on_drop(event); -} - -void QSWidget::resize_window() -{ - if (window()->is_fullscreen()) - return; - - if (!m_bitmap) - return; - - auto new_size = m_bitmap->size(); - - if (new_size.width() < 300) - new_size.set_width(300); - if (new_size.height() < 200) - new_size.set_height(200); - - new_size.set_height(new_size.height() + m_toolbar_height); - window()->resize(new_size); -} diff --git a/Applications/QuickShow/QSWidget.h b/Applications/QuickShow/QSWidget.h deleted file mode 100644 index 047241e25e..0000000000 --- a/Applications/QuickShow/QSWidget.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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/Frame.h> -#include <LibGfx/Bitmap.h> -#include <LibGfx/Point.h> - -class QSLabel; - -class QSWidget final : public GUI::Frame { - C_OBJECT(QSWidget) -public: - enum Directions { - First, - Back, - Forward, - Last - }; - - virtual ~QSWidget() override; - - const Gfx::Bitmap* bitmap() const { return m_bitmap.ptr(); } - const String& path() const { return m_path; } - void set_scale(int); - int scale() { return m_scale; } - void set_toolbar_height(int height) { m_toolbar_height = height; } - int toolbar_height() { return m_toolbar_height; } - - void clear(); - void flip(Gfx::Orientation); - void rotate(Gfx::RotationDirection); - void navigate(Directions); - void load_from_file(const String&); - - Function<void(int, Gfx::IntRect)> on_scale_change; - Function<void()> on_doubleclick; - Function<void(const GUI::DropEvent&)> on_drop; - -private: - QSWidget(); - virtual void doubleclick_event(GUI::MouseEvent&) override; - virtual void paint_event(GUI::PaintEvent&) override; - virtual void resize_event(GUI::ResizeEvent&) override; - virtual void mousedown_event(GUI::MouseEvent&) override; - virtual void mouseup_event(GUI::MouseEvent&) override; - virtual void mousemove_event(GUI::MouseEvent&) override; - virtual void mousewheel_event(GUI::MouseEvent&) override; - virtual void drop_event(GUI::DropEvent&) override; - - void relayout(); - void resize_window(); - - String m_path; - RefPtr<Gfx::Bitmap> m_bitmap; - int m_toolbar_height { 28 }; - - Gfx::IntRect m_bitmap_rect; - int m_scale { -1 }; - Gfx::FloatPoint m_pan_origin; - - Gfx::IntPoint m_click_position; - Gfx::FloatPoint m_saved_pan_origin; - Vector<String> m_files_in_same_dir; -}; diff --git a/Applications/QuickShow/main.cpp b/Applications/QuickShow/main.cpp deleted file mode 100644 index 2319c24a9a..0000000000 --- a/Applications/QuickShow/main.cpp +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "QSWidget.h" -#include <AK/URL.h> -#include <LibCore/ArgsParser.h> -#include <LibCore/MimeData.h> -#include <LibGUI/Action.h> -#include <LibGUI/Application.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Clipboard.h> -#include <LibGUI/Desktop.h> -#include <LibGUI/FilePicker.h> -#include <LibGUI/Label.h> -#include <LibGUI/Menu.h> -#include <LibGUI/MenuBar.h> -#include <LibGUI/MessageBox.h> -#include <LibGUI/ToolBar.h> -#include <LibGUI/ToolBarContainer.h> -#include <LibGUI/Window.h> -#include <LibGfx/Bitmap.h> -#include <LibGfx/Palette.h> -#include <LibGfx/Rect.h> -#include <serenity.h> -#include <spawn.h> -#include <stdio.h> -#include <string.h> - -int main(int argc, char** argv) -{ - if (pledge("stdio shared_buffer accept cpath rpath wpath unix cpath fattr proc exec thread", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto app = GUI::Application::construct(argc, argv); - - if (pledge("stdio shared_buffer accept cpath rpath wpath proc exec thread", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto app_icon = GUI::Icon::default_icon("filetype-image"); - - const char* path = nullptr; - Core::ArgsParser args_parser; - args_parser.add_positional_argument(path, "The image file to be displayed.", "file", Core::ArgsParser::Required::No); - args_parser.parse(argc, argv); - - auto window = GUI::Window::construct(); - window->set_double_buffering_enabled(true); - window->resize(300, 200); - window->set_icon(app_icon.bitmap_for_size(16)); - window->set_title("QuickShow"); - - auto& root_widget = window->set_main_widget<GUI::Widget>(); - root_widget.set_fill_with_background_color(true); - root_widget.set_layout<GUI::VerticalBoxLayout>(); - root_widget.layout()->set_spacing(2); - - auto& toolbar_container = root_widget.add<GUI::ToolBarContainer>(); - auto& main_toolbar = toolbar_container.add<GUI::ToolBar>(); - - auto& widget = root_widget.add<QSWidget>(); - widget.on_scale_change = [&](int scale, Gfx::IntRect rect) { - if (!widget.bitmap()) { - window->set_title("QuickShow"); - return; - } - - window->set_title(String::formatted("{} {} {}% - QuickShow", widget.path(), widget.bitmap()->size().to_string(), scale)); - - if (window->is_fullscreen()) - return; - - if (window->is_maximized()) - return; - - auto w = max(window->width(), rect.width() + 4); - auto h = max(window->height(), rect.height() + widget.toolbar_height() + 6); - window->resize(w, h); - }; - widget.on_drop = [&](auto& event) { - window->move_to_front(); - - if (event.mime_data().has_urls()) { - auto urls = event.mime_data().urls(); - - if (!urls.is_empty()) { - auto url = urls.first(); - widget.load_from_file(url.path()); - } - - pid_t child; - for (size_t i = 1; i < urls.size(); ++i) { - const char* argv[] = { "/bin/QuickShow", urls[i].path().characters(), nullptr }; - if ((errno = posix_spawn(&child, "/bin/QuickShow", nullptr, nullptr, const_cast<char**>(argv), environ))) { - perror("posix_spawn"); - } else { - if (disown(child) < 0) - perror("disown"); - } - } - } - }; - widget.on_doubleclick = [&] { - window->set_fullscreen(!window->is_fullscreen()); - toolbar_container.set_visible(!window->is_fullscreen()); - }; - - // Actions - auto open_action = GUI::CommonActions::make_open_action( - [&](auto&) { - Optional<String> path = GUI::FilePicker::get_open_filepath(window, "Open image..."); - if (path.has_value()) { - widget.load_from_file(path.value()); - } - }); - - auto delete_action = GUI::CommonActions::make_delete_action( - [&](auto&) { - auto path = widget.path(); - if (path.is_empty()) - return; - - auto msgbox_result = GUI::MessageBox::show(window, - String::formatted("Really delete {}?", path), - "Confirm deletion", - GUI::MessageBox::Type::Warning, - GUI::MessageBox::InputType::OKCancel); - - if (msgbox_result == GUI::MessageBox::ExecCancel) - return; - - auto unlink_result = unlink(widget.path().characters()); - dbgln("unlink_result::{}", unlink_result); - - if (unlink_result < 0) { - int saved_errno = errno; - GUI::MessageBox::show(window, - String::formatted("unlink({}) failed: {}", path, strerror(saved_errno)), - "Delete failed", - GUI::MessageBox::Type::Error); - - return; - } - - widget.clear(); - }); - - auto quit_action = GUI::CommonActions::make_quit_action( - [&](auto&) { - app->quit(); - }); - - auto rotate_left_action = GUI::Action::create("Rotate Left", { Mod_None, Key_L }, - [&](auto&) { - widget.rotate(Gfx::RotationDirection::Left); - }); - - auto rotate_right_action = GUI::Action::create("Rotate Right", { Mod_None, Key_R }, - [&](auto&) { - widget.rotate(Gfx::RotationDirection::Right); - }); - - auto vertical_flip_action = GUI::Action::create("Vertical Flip", { Mod_None, Key_V }, - [&](auto&) { - widget.flip(Gfx::Orientation::Vertical); - }); - - auto horizontal_flip_action = GUI::Action::create("Horizontal Flip", { Mod_None, Key_H }, - [&](auto&) { - widget.flip(Gfx::Orientation::Horizontal); - }); - - auto desktop_wallpaper_action = GUI::Action::create("Set as desktop wallpaper", - [&](auto&) { - GUI::Desktop::the().set_wallpaper(widget.path()); - }); - - auto go_first_action = GUI::Action::create("First", { Mod_None, Key_Home }, Gfx::Bitmap::load_from_file("/res/icons/16x16/go-first.png"), - [&](auto&) { - widget.navigate(QSWidget::Directions::First); - }); - - auto go_back_action = GUI::Action::create("Back", { Mod_None, Key_Left }, Gfx::Bitmap::load_from_file("/res/icons/16x16/go-back.png"), - [&](auto&) { - widget.navigate(QSWidget::Directions::Back); - }); - - auto go_forward_action = GUI::Action::create("Forward", { Mod_None, Key_Right }, Gfx::Bitmap::load_from_file("/res/icons/16x16/go-forward.png"), - [&](auto&) { - widget.navigate(QSWidget::Directions::Forward); - }); - - auto go_last_action = GUI::Action::create("Last", { Mod_None, Key_End }, Gfx::Bitmap::load_from_file("/res/icons/16x16/go-last.png"), - [&](auto&) { - widget.navigate(QSWidget::Directions::Last); - }); - - auto full_sceen_action = GUI::CommonActions::make_fullscreen_action( - [&](auto&) { - widget.on_doubleclick(); - }); - - auto zoom_in_action = GUI::Action::create("Zoom In", { Mod_None, Key_Plus }, Gfx::Bitmap::load_from_file("/res/icons/16x16/zoom-in.png"), - [&](auto&) { - widget.set_scale(widget.scale() + 10); - }); - - auto zoom_reset_action = GUI::Action::create("Zoom 100%", { Mod_None, Key_0 }, Gfx::Bitmap::load_from_file("/res/icons/16x16/zoom-reset.png"), - [&](auto&) { - widget.set_scale(100); - }); - - auto zoom_out_action = GUI::Action::create("Zoom Out", { Mod_None, Key_Minus }, Gfx::Bitmap::load_from_file("/res/icons/16x16/zoom-out.png"), - [&](auto&) { - widget.set_scale(widget.scale() - 10); - }); - - auto hide_show_toolbar_action = GUI::Action::create("Hide/Show Toolbar", { Mod_Ctrl, Key_T }, - [&](auto&) { - toolbar_container.set_visible(!toolbar_container.is_visible()); - }); - - auto copy_action = GUI::CommonActions::make_copy_action([&](auto&) { - if (widget.bitmap()) - GUI::Clipboard::the().set_bitmap(*widget.bitmap()); - }); - - main_toolbar.add_action(open_action); - main_toolbar.add_action(delete_action); - main_toolbar.add_separator(); - main_toolbar.add_action(go_first_action); - main_toolbar.add_action(go_back_action); - main_toolbar.add_action(go_forward_action); - main_toolbar.add_action(go_last_action); - main_toolbar.add_separator(); - main_toolbar.add_action(zoom_in_action); - main_toolbar.add_action(zoom_reset_action); - main_toolbar.add_action(zoom_out_action); - - auto menubar = GUI::MenuBar::construct(); - - auto& app_menu = menubar->add_menu("QuickShow"); - app_menu.add_action(open_action); - app_menu.add_action(delete_action); - app_menu.add_separator(); - app_menu.add_action(quit_action); - - auto& image_menu = menubar->add_menu("Image"); - image_menu.add_action(rotate_left_action); - image_menu.add_action(rotate_right_action); - image_menu.add_action(vertical_flip_action); - image_menu.add_action(horizontal_flip_action); - image_menu.add_separator(); - image_menu.add_action(desktop_wallpaper_action); - - auto& navigate_menu = menubar->add_menu("Navigate"); - navigate_menu.add_action(go_first_action); - navigate_menu.add_action(go_back_action); - navigate_menu.add_action(go_forward_action); - navigate_menu.add_action(go_last_action); - - auto& view_menu = menubar->add_menu("View"); - view_menu.add_action(full_sceen_action); - view_menu.add_separator(); - view_menu.add_action(zoom_in_action); - view_menu.add_action(zoom_reset_action); - view_menu.add_action(zoom_out_action); - view_menu.add_separator(); - view_menu.add_action(hide_show_toolbar_action); - - auto& help_menu = menubar->add_menu("Help"); - help_menu.add_action(GUI::CommonActions::make_about_action("QuickShow", app_icon, window)); - - app->set_menubar(move(menubar)); - - if (path != nullptr) { - widget.load_from_file(path); - } - - window->show(); - - return app->exec(); -} diff --git a/Applications/SoundPlayer/CMakeLists.txt b/Applications/SoundPlayer/CMakeLists.txt deleted file mode 100644 index 99e1da4653..0000000000 --- a/Applications/SoundPlayer/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -set(SOURCES - main.cpp - PlaybackManager.cpp - SampleWidget.cpp - SoundPlayerWidget.cpp -) - -serenity_app(SoundPlayer ICON app-sound-player) -target_link_libraries(SoundPlayer LibAudio LibGUI) diff --git a/Applications/SoundPlayer/PlaybackManager.cpp b/Applications/SoundPlayer/PlaybackManager.cpp deleted file mode 100644 index 1421e75215..0000000000 --- a/Applications/SoundPlayer/PlaybackManager.cpp +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "PlaybackManager.h" - -PlaybackManager::PlaybackManager(NonnullRefPtr<Audio::ClientConnection> connection) - : m_connection(connection) -{ - m_timer = Core::Timer::construct(100, [&]() { - if (!m_loader) - return; - next_buffer(); - }); - m_timer->stop(); -} - -PlaybackManager::~PlaybackManager() -{ -} - -void PlaybackManager::set_loader(NonnullRefPtr<Audio::Loader>&& loader) -{ - stop(); - m_loader = loader; - if (m_loader) { - m_total_length = m_loader->total_samples() / static_cast<float>(m_loader->sample_rate()); - m_timer->start(); - load_next_buffer(); - } else { - m_timer->stop(); - } -} - -void PlaybackManager::stop() -{ - set_paused(true); - m_connection->clear_buffer(true); - m_buffers.clear(); - m_last_seek = 0; - m_next_buffer = nullptr; - m_current_buffer = nullptr; - m_next_ptr = 0; - - if (m_loader) - m_loader->reset(); -} - -void PlaybackManager::play() -{ - set_paused(false); -} - -void PlaybackManager::loop(bool loop) -{ - m_loop = loop; -} - -void PlaybackManager::seek(const int position) -{ - if (!m_loader) - return; - - m_last_seek = position; - bool paused_state = m_paused; - set_paused(true); - - m_connection->clear_buffer(true); - m_next_buffer = nullptr; - m_current_buffer = nullptr; - m_next_ptr = 0; - m_buffers.clear(); - m_loader->seek(position); - - if (!paused_state) - set_paused(false); -} - -void PlaybackManager::pause() -{ - set_paused(true); -} - -void PlaybackManager::remove_dead_buffers() -{ - int id = m_connection->get_playing_buffer(); - int current_id = -1; - if (m_current_buffer) - current_id = m_current_buffer->shbuf_id(); - - if (id >= 0 && id != current_id) { - while (!m_buffers.is_empty()) { - --m_next_ptr; - auto buffer = m_buffers.take_first(); - - if (buffer->shbuf_id() == id) { - m_current_buffer = buffer; - break; - } - } - } -} - -void PlaybackManager::load_next_buffer() -{ - if (m_buffers.size() < 10) { - for (int i = 0; i < 20 && m_loader->loaded_samples() < m_loader->total_samples(); i++) { - auto buffer = m_loader->get_more_samples(PLAYBACK_MANAGER_BUFFER_SIZE); - if (buffer) - m_buffers.append(buffer); - } - } - - if (m_next_ptr < m_buffers.size()) { - m_next_buffer = m_buffers.at(m_next_ptr++); - } else { - m_next_buffer = nullptr; - } -} - -void PlaybackManager::set_paused(bool paused) -{ - if (!m_next_buffer && m_loader) - load_next_buffer(); - - m_paused = paused; - m_connection->set_paused(paused); -} - -bool PlaybackManager::toggle_pause() -{ - if (m_paused) { - play(); - } else { - pause(); - } - return m_paused; -} - -void PlaybackManager::next_buffer() -{ - if (on_update) - on_update(); - - if (m_paused) - return; - - remove_dead_buffers(); - if (!m_next_buffer) { - if (!m_connection->get_remaining_samples() && !m_paused) { - dbgln("Exhausted samples :^)"); - if (m_loop) - seek(0); - else - stop(); - } - - return; - } - - bool enqueued = m_connection->try_enqueue(*m_next_buffer); - if (!enqueued) - return; - - load_next_buffer(); -} diff --git a/Applications/SoundPlayer/PlaybackManager.h b/Applications/SoundPlayer/PlaybackManager.h deleted file mode 100644 index 35be78fe9d..0000000000 --- a/Applications/SoundPlayer/PlaybackManager.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 <AK/Vector.h> -#include <LibAudio/Buffer.h> -#include <LibAudio/ClientConnection.h> -#include <LibAudio/Loader.h> -#include <LibCore/Timer.h> - -#define PLAYBACK_MANAGER_BUFFER_SIZE 64 * KiB -#define PLAYBACK_MANAGER_RATE 44100 - -class PlaybackManager final { -public: - PlaybackManager(NonnullRefPtr<Audio::ClientConnection>); - ~PlaybackManager(); - - void play(); - void stop(); - void pause(); - void seek(const int position); - void loop(bool); - bool toggle_pause(); - void set_loader(NonnullRefPtr<Audio::Loader>&&); - - int last_seek() const { return m_last_seek; } - bool is_paused() const { return m_paused; } - float total_length() const { return m_total_length; } - RefPtr<Audio::Buffer> current_buffer() const { return m_current_buffer; } - - NonnullRefPtr<Audio::ClientConnection> connection() const { return m_connection; } - - Function<void()> on_update; - -private: - void next_buffer(); - void set_paused(bool); - void load_next_buffer(); - void remove_dead_buffers(); - - bool m_paused { true }; - bool m_loop = { false }; - size_t m_next_ptr { 0 }; - size_t m_last_seek { 0 }; - float m_total_length { 0 }; - RefPtr<Audio::Loader> m_loader { nullptr }; - NonnullRefPtr<Audio::ClientConnection> m_connection; - RefPtr<Audio::Buffer> m_next_buffer; - RefPtr<Audio::Buffer> m_current_buffer; - Vector<RefPtr<Audio::Buffer>> m_buffers; - RefPtr<Core::Timer> m_timer; -}; diff --git a/Applications/SoundPlayer/SampleWidget.cpp b/Applications/SoundPlayer/SampleWidget.cpp deleted file mode 100644 index b3884a175a..0000000000 --- a/Applications/SoundPlayer/SampleWidget.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "SampleWidget.h" -#include <LibAudio/Buffer.h> -#include <LibGUI/Painter.h> -#include <math.h> - -SampleWidget::SampleWidget() -{ -} - -SampleWidget::~SampleWidget() -{ -} - -void SampleWidget::paint_event(GUI::PaintEvent& event) -{ - GUI::Frame::paint_event(event); - GUI::Painter painter(*this); - - painter.add_clip_rect(event.rect()); - painter.fill_rect(frame_inner_rect(), Color::Black); - - float sample_max = 0; - int count = 0; - int x_offset = frame_inner_rect().x(); - int x = x_offset; - int y_offset = frame_inner_rect().center().y(); - - if (m_buffer) { - int samples_per_pixel = m_buffer->sample_count() / frame_inner_rect().width(); - for (int sample_index = 0; sample_index < m_buffer->sample_count() && (x - x_offset) < frame_inner_rect().width(); ++sample_index) { - float sample = fabsf((float)m_buffer->samples()[sample_index].left); - - sample_max = max(sample, sample_max); - ++count; - - if (count >= samples_per_pixel) { - Gfx::IntPoint min_point = { x, y_offset + static_cast<int>(-sample_max * frame_inner_rect().height() / 2) }; - Gfx::IntPoint max_point = { x++, y_offset + static_cast<int>(sample_max * frame_inner_rect().height() / 2) }; - painter.draw_line(min_point, max_point, Color::Green); - - count = 0; - sample_max = 0; - } - } - } else { - painter.draw_line({ x, y_offset }, { frame_inner_rect().width(), y_offset }, Color::Green); - } -} - -void SampleWidget::set_buffer(Audio::Buffer* buffer) -{ - if (m_buffer == buffer) - return; - m_buffer = buffer; - update(); -} diff --git a/Applications/SoundPlayer/SampleWidget.h b/Applications/SoundPlayer/SampleWidget.h deleted file mode 100644 index 7da9c20b38..0000000000 --- a/Applications/SoundPlayer/SampleWidget.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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/Frame.h> - -namespace Audio { -class Buffer; -} - -class SampleWidget final : public GUI::Frame { - C_OBJECT(SampleWidget) -public: - virtual ~SampleWidget() override; - - void set_buffer(Audio::Buffer*); - -private: - SampleWidget(); - virtual void paint_event(GUI::PaintEvent&) override; - - RefPtr<Audio::Buffer> m_buffer; -}; diff --git a/Applications/SoundPlayer/SoundPlayerWidget.cpp b/Applications/SoundPlayer/SoundPlayerWidget.cpp deleted file mode 100644 index f2066aca89..0000000000 --- a/Applications/SoundPlayer/SoundPlayerWidget.cpp +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "SoundPlayerWidget.h" -#include <AK/StringBuilder.h> -#include <LibCore/MimeData.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Button.h> -#include <LibGUI/Label.h> -#include <LibGUI/MessageBox.h> -#include <math.h> - -SoundPlayerWidget::SoundPlayerWidget(GUI::Window& window, NonnullRefPtr<Audio::ClientConnection> connection) - : m_window(window) - , m_connection(connection) - , m_manager(connection) -{ - set_fill_with_background_color(true); - set_layout<GUI::VerticalBoxLayout>(); - layout()->set_margins({ 2, 2, 2, 2 }); - - auto& status_widget = add<GUI::Widget>(); - status_widget.set_fill_with_background_color(true); - status_widget.set_layout<GUI::HorizontalBoxLayout>(); - - m_elapsed = status_widget.add<GUI::Label>(); - m_elapsed->set_frame_shape(Gfx::FrameShape::Container); - m_elapsed->set_frame_shadow(Gfx::FrameShadow::Sunken); - m_elapsed->set_frame_thickness(2); - m_elapsed->set_fixed_width(80); - - auto& sample_widget_container = status_widget.add<GUI::Widget>(); - sample_widget_container.set_layout<GUI::HorizontalBoxLayout>(); - - m_sample_widget = sample_widget_container.add<SampleWidget>(); - - m_remaining = status_widget.add<GUI::Label>(); - m_remaining->set_frame_shape(Gfx::FrameShape::Container); - m_remaining->set_frame_shadow(Gfx::FrameShadow::Sunken); - m_remaining->set_frame_thickness(2); - m_remaining->set_fixed_width(80); - - m_slider = add<Slider>(Orientation::Horizontal); - m_slider->set_min(0); - m_slider->set_enabled(false); - m_slider->on_knob_released = [&](int value) { m_manager.seek(denormalize_rate(value)); }; - - auto& control_widget = add<GUI::Widget>(); - control_widget.set_fill_with_background_color(true); - control_widget.set_layout<GUI::HorizontalBoxLayout>(); - control_widget.set_fixed_height(30); - control_widget.layout()->set_margins({ 10, 2, 10, 2 }); - control_widget.layout()->set_spacing(10); - - m_play = control_widget.add<GUI::Button>(); - m_play->set_icon(*m_pause_icon); - m_play->set_enabled(false); - m_play->on_click = [this](auto) { - m_play->set_icon(m_manager.toggle_pause() ? *m_play_icon : *m_pause_icon); - }; - - m_stop = control_widget.add<GUI::Button>(); - m_stop->set_enabled(false); - m_stop->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/stop.png")); - m_stop->on_click = [this](auto) { m_manager.stop(); }; - - m_status = add<GUI::Label>(); - m_status->set_frame_shape(Gfx::FrameShape::Box); - m_status->set_frame_shadow(Gfx::FrameShadow::Raised); - m_status->set_frame_thickness(4); - m_status->set_text_alignment(Gfx::TextAlignment::CenterLeft); - m_status->set_fixed_height(18); - m_status->set_text("No file open!"); - - update_position(0); - - m_manager.on_update = [&]() { update_ui(); }; -} - -SoundPlayerWidget::~SoundPlayerWidget() -{ -} - -SoundPlayerWidget::Slider::~Slider() -{ -} - -void SoundPlayerWidget::hide_scope(bool hide) -{ - m_sample_widget->set_visible(!hide); -} - -void SoundPlayerWidget::open_file(String path) -{ - NonnullRefPtr<Audio::Loader> loader = Audio::Loader::create(path); - if (loader->has_error() || !loader->sample_rate()) { - const String error_string = loader->error_string(); - GUI::MessageBox::show(window(), - String::formatted("Failed to load audio file: {} ({})", path, error_string.is_null() ? "Unknown error" : error_string), - "Filetype error", GUI::MessageBox::Type::Error); - return; - } - - m_sample_ratio = PLAYBACK_MANAGER_RATE / static_cast<float>(loader->sample_rate()); - - m_slider->set_max(normalize_rate(static_cast<int>(loader->total_samples()))); - m_slider->set_enabled(true); - m_play->set_enabled(true); - m_stop->set_enabled(true); - - m_window.set_title(String::formatted("{} - SoundPlayer", loader->file()->filename())); - m_status->set_text(String::formatted( - "Sample rate {}Hz, {} channel(s), {} bits per sample", - loader->sample_rate(), - loader->num_channels(), - loader->bits_per_sample())); - - m_manager.set_loader(move(loader)); - update_position(0); -} - -void SoundPlayerWidget::drop_event(GUI::DropEvent& event) -{ - event.accept(); - window()->move_to_front(); - - if (event.mime_data().has_urls()) { - auto urls = event.mime_data().urls(); - if (urls.is_empty()) - return; - open_file(urls.first().path()); - } -} - -int SoundPlayerWidget::normalize_rate(int rate) const -{ - return static_cast<int>(rate * m_sample_ratio); -} - -int SoundPlayerWidget::denormalize_rate(int rate) const -{ - return static_cast<int>(rate / m_sample_ratio); -} - -void SoundPlayerWidget::update_ui() -{ - m_sample_widget->set_buffer(m_manager.current_buffer()); - m_play->set_icon(m_manager.is_paused() ? *m_play_icon : *m_pause_icon); - update_position(m_manager.connection()->get_played_samples()); -} - -void SoundPlayerWidget::update_position(const int position) -{ - int total_norm_samples = position + normalize_rate(m_manager.last_seek()); - float seconds = (total_norm_samples / static_cast<float>(PLAYBACK_MANAGER_RATE)); - float remaining_seconds = m_manager.total_length() - seconds; - - m_elapsed->set_text(String::formatted( - "Elapsed:\n{}:{:02}.{:02}", - static_cast<int>(seconds / 60), - static_cast<int>(seconds) % 60, - static_cast<int>(seconds * 100) % 100)); - - m_remaining->set_text(String::formatted( - "Remaining:\n{}:{:02}.{:02}", - static_cast<int>(remaining_seconds / 60), - static_cast<int>(remaining_seconds) % 60, - static_cast<int>(remaining_seconds * 100) % 100)); - - m_slider->set_value(total_norm_samples); -} diff --git a/Applications/SoundPlayer/SoundPlayerWidget.h b/Applications/SoundPlayer/SoundPlayerWidget.h deleted file mode 100644 index ab7bc60aea..0000000000 --- a/Applications/SoundPlayer/SoundPlayerWidget.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "PlaybackManager.h" -#include "SampleWidget.h" -#include <LibGUI/Button.h> -#include <LibGUI/Label.h> -#include <LibGUI/Slider.h> -#include <LibGUI/Widget.h> -#include <LibGUI/Window.h> - -class SoundPlayerWidget final : public GUI::Widget { - C_OBJECT(SoundPlayerWidget) -public: - virtual ~SoundPlayerWidget() override; - void open_file(String path); - void hide_scope(bool); - PlaybackManager& manager() { return m_manager; } - -private: - explicit SoundPlayerWidget(GUI::Window&, NonnullRefPtr<Audio::ClientConnection>); - - virtual void drop_event(GUI::DropEvent&) override; - - void update_position(const int position); - void update_ui(); - int normalize_rate(int) const; - int denormalize_rate(int) const; - - class Slider final : public GUI::Slider { - C_OBJECT(Slider) - public: - virtual ~Slider() override; - Function<void(int)> on_knob_released; - void set_value(int value) - { - if (!knob_dragging()) - GUI::Slider::set_value(value); - } - - protected: - Slider(Orientation orientation) - : GUI::Slider(orientation) - { - } - - virtual void mouseup_event(GUI::MouseEvent& event) override - { - if (on_knob_released && is_enabled()) - on_knob_released(value()); - - GUI::Slider::mouseup_event(event); - } - }; - - GUI::Window& m_window; - NonnullRefPtr<Audio::ClientConnection> m_connection; - PlaybackManager m_manager; - float m_sample_ratio { 1.0 }; - RefPtr<GUI::Label> m_status; - RefPtr<GUI::Label> m_elapsed; - RefPtr<GUI::Label> m_remaining; - RefPtr<Slider> m_slider; - RefPtr<SampleWidget> m_sample_widget; - RefPtr<Gfx::Bitmap> m_play_icon { Gfx::Bitmap::load_from_file("/res/icons/16x16/play.png") }; - RefPtr<Gfx::Bitmap> m_pause_icon { Gfx::Bitmap::load_from_file("/res/icons/16x16/pause.png") }; - RefPtr<GUI::Button> m_play; - RefPtr<GUI::Button> m_stop; -}; diff --git a/Applications/SoundPlayer/main.cpp b/Applications/SoundPlayer/main.cpp deleted file mode 100644 index 47dbd555c8..0000000000 --- a/Applications/SoundPlayer/main.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "SoundPlayerWidget.h" -#include <LibAudio/ClientConnection.h> -#include <LibGUI/Action.h> -#include <LibGUI/Application.h> -#include <LibGUI/FilePicker.h> -#include <LibGUI/Menu.h> -#include <LibGUI/MenuBar.h> -#include <LibGUI/Window.h> -#include <LibGfx/CharacterBitmap.h> -#include <stdio.h> - -int main(int argc, char** argv) -{ - if (pledge("stdio shared_buffer accept rpath thread unix cpath fattr", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto app = GUI::Application::construct(argc, argv); - - if (pledge("stdio shared_buffer accept rpath thread unix", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto audio_client = Audio::ClientConnection::construct(); - audio_client->handshake(); - - if (pledge("stdio shared_buffer accept rpath thread", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto app_icon = GUI::Icon::default_icon("app-sound-player"); - - auto window = GUI::Window::construct(); - window->set_title("Sound Player"); - window->set_resizable(false); - window->resize(350, 140); - window->set_icon(app_icon.bitmap_for_size(16)); - - auto menubar = GUI::MenuBar::construct(); - auto& app_menu = menubar->add_menu("Sound Player"); - auto& player = window->set_main_widget<SoundPlayerWidget>(window, audio_client); - - if (argc > 1) { - String path = argv[1]; - player.open_file(path); - player.manager().play(); - } - - auto hide_scope = GUI::Action::create_checkable("Hide scope", { Mod_Ctrl, Key_H }, [&](auto& action) { - player.hide_scope(action.is_checked()); - }); - - app_menu.add_action(GUI::CommonActions::make_open_action([&](auto&) { - Optional<String> path = GUI::FilePicker::get_open_filepath(window, "Open sound file..."); - if (path.has_value()) { - player.open_file(path.value()); - } - })); - app_menu.add_action(move(hide_scope)); - app_menu.add_separator(); - app_menu.add_action(GUI::CommonActions::make_quit_action([&](auto&) { - app->quit(); - })); - - auto& playback_menu = menubar->add_menu("Playback"); - - auto loop = GUI::Action::create_checkable("Loop", { Mod_Ctrl, Key_R }, [&](auto& action) { - player.manager().loop(action.is_checked()); - }); - - playback_menu.add_action(move(loop)); - - auto& help_menu = menubar->add_menu("Help"); - help_menu.add_action(GUI::CommonActions::make_about_action("Sound Player", app_icon, window)); - - app->set_menubar(move(menubar)); - - window->show(); - return app->exec(); -} diff --git a/Applications/SpaceAnalyzer/CMakeLists.txt b/Applications/SpaceAnalyzer/CMakeLists.txt deleted file mode 100644 index 23cc18f13e..0000000000 --- a/Applications/SpaceAnalyzer/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -compile_gml(SpaceAnalyzer.gml SpaceAnalyzerGML.h space_analyzer_gml) - -set(SOURCES - main.cpp - TreeMapWidget.cpp - SpaceAnalyzerGML.h -) - -serenity_app(SpaceAnalyzer ICON app-space-analyzer) -target_link_libraries(SpaceAnalyzer LibGfx LibGUI) diff --git a/Applications/SpaceAnalyzer/SpaceAnalyzer.gml b/Applications/SpaceAnalyzer/SpaceAnalyzer.gml deleted file mode 100644 index a4a3363c6a..0000000000 --- a/Applications/SpaceAnalyzer/SpaceAnalyzer.gml +++ /dev/null @@ -1,20 +0,0 @@ -@GUI::Widget { - layout: @GUI::VerticalBoxLayout { - spacing: 0 - } - - @GUI::ToolBarContainer { - @GUI::BreadcrumbBar { - fixed_height: 25 - name: "breadcrumb_bar" - } - } - - @SpaceAnalyzer::TreeMapWidget { - name: "tree_map" - } - - @GUI::StatusBar { - name: "status_bar" - } -} diff --git a/Applications/SpaceAnalyzer/TreeMapWidget.cpp b/Applications/SpaceAnalyzer/TreeMapWidget.cpp deleted file mode 100644 index 97d9b58a9e..0000000000 --- a/Applications/SpaceAnalyzer/TreeMapWidget.cpp +++ /dev/null @@ -1,376 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * 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 "TreeMapWidget.h" -#include <AK/NumberFormat.h> -#include <LibGUI/Painter.h> -#include <LibGUI/WindowServerConnection.h> -#include <LibGfx/Font.h> -#include <WindowServer/WindowManager.h> - -namespace SpaceAnalyzer { - -REGISTER_WIDGET(SpaceAnalyzer, TreeMapWidget) - -TreeMapWidget::TreeMapWidget() - : m_viewpoint(0) -{ -} - -TreeMapWidget::~TreeMapWidget() -{ -} - -static const Color colors[] = { - Color(253, 231, 37), - Color(148, 216, 64), - Color(60, 188, 117), - Color(31, 150, 139), - Color(45, 112, 142), - Color(63, 71, 136), - Color(85, 121, 104), -}; - -static float get_normalized_aspect_ratio(float a, float b) -{ - if (a < b) { - return a / b; - } else { - return b / a; - } -} - -static bool node_is_leaf(const TreeMapNode& node) -{ - return node.num_children() == 0; -} - -bool TreeMapWidget::rect_can_contain_label(const Gfx::IntRect& rect) const -{ - return rect.height() > font().presentation_size() && rect.width() > 20; -} - -bool TreeMapWidget::rect_can_contain_children(const Gfx::IntRect& rect) const -{ - return rect.height() > 10 && rect.width() > 10; -} - -Gfx::IntRect TreeMapWidget::inner_rect_for_frame(const Gfx::IntRect& rect) const -{ - const int margin = 5; - Gfx::IntRect tmp_rect = rect; - tmp_rect.shrink(2, 2); // border - tmp_rect.shrink(2, 2); // shading - if (rect_can_contain_label(rect)) { - tmp_rect.set_y(tmp_rect.y() + font().presentation_size() + margin); - tmp_rect.set_height(tmp_rect.height() - (font().presentation_size() + margin * 2)); - tmp_rect.set_x(tmp_rect.x() + margin); - tmp_rect.set_width(tmp_rect.width() - margin * 2); - } - return tmp_rect; -} - -void TreeMapWidget::paint_cell_frame(GUI::Painter& painter, const TreeMapNode& node, const Gfx::IntRect& cell_rect, int depth, bool fill_frame) const -{ - const Gfx::IntRect border_rect = cell_rect.shrunken(2, 2); - const Gfx::IntRect outer_rect = border_rect.shrunken(2, 2); - const Gfx::IntRect inner_rect = inner_rect_for_frame(cell_rect); - - painter.clear_clip_rect(); - painter.add_clip_rect(cell_rect); - Color color = colors[depth % (sizeof(colors) / sizeof(colors[0]))]; - if (m_selected_node_cache == &node) { - color = color.darkened(0.8f); - } - - // Draw borders. - painter.draw_rect(cell_rect, Color::Black, false); - painter.draw_line(border_rect.bottom_left(), border_rect.top_left(), color.lightened()); - painter.draw_line(border_rect.top_left(), border_rect.top_right(), color.lightened()); - painter.draw_line(border_rect.top_right(), border_rect.bottom_right(), color.darkened()); - painter.draw_line(border_rect.bottom_left(), border_rect.bottom_right(), color.darkened()); - - // Paint the background. - if (fill_frame) { - painter.fill_rect(outer_rect, color); - } else { - for (auto& shard : outer_rect.shatter(inner_rect)) { - painter.fill_rect(shard, color); - } - } - - // Paint text. - if (rect_can_contain_label(outer_rect)) { - Gfx::IntRect text_rect = outer_rect; - text_rect.move_by(2, 2); - painter.draw_text(text_rect, node.name(), font(), Gfx::TextAlignment::TopLeft, Color::Black); - if (node_is_leaf(node)) { - text_rect.move_by(0, font().presentation_size() + 1); - painter.draw_text(text_rect, human_readable_size(node.area()), font(), Gfx::TextAlignment::TopLeft, Color::Black); - } - } -} - -template<typename Function> -void TreeMapWidget::lay_out_children(const TreeMapNode& node, const Gfx::IntRect& rect, int depth, Function callback) -{ - if (node.num_children() == 0) { - return; - } - - // Check if the children are sorted yet, if not do that now. - for (size_t k = 0; k < node.num_children() - 1; k++) { - if (node.child_at(k).area() < node.child_at(k + 1).area()) { - node.sort_children_by_area(); - break; - } - } - - int total_area = node.area(); - Gfx::IntRect canvas = rect; - bool remaining_nodes_are_too_small = false; - for (size_t i = 0; !remaining_nodes_are_too_small && i < node.num_children(); i++) { - const int i_node_area = node.child_at(i).area(); - if (i_node_area == 0) - break; - - const int long_side_size = max(canvas.width(), canvas.height()); - const int short_side_size = min(canvas.width(), canvas.height()); - - int row_or_column_size = (long long int)long_side_size * i_node_area / total_area; - int node_area_sum = i_node_area; - size_t k = i + 1; - - // Try to add nodes to this row or column so long as the worst aspect ratio of - // the new set of nodes is better than the worst aspect ratio of the current set. - { - float best_worst_aspect_ratio_so_far = get_normalized_aspect_ratio(row_or_column_size, short_side_size); - for (; k < node.num_children(); k++) { - // Do a preliminary calculation of the worst aspect ratio of the nodes at index i and k - // if that aspect ratio is better than the 'best_worst_aspect_ratio_so_far' we keep it, - // otherwise it is discarded. - int k_node_area = node.child_at(k).area(); - if (k_node_area == 0) { - break; - } - int new_node_area_sum = node_area_sum + k_node_area; - int new_row_or_column_size = (long long int)long_side_size * new_node_area_sum / total_area; - int i_node_size = (long long int)short_side_size * i_node_area / new_node_area_sum; - int k_node_size = (long long int)short_side_size * k_node_area / new_node_area_sum; - float i_node_aspect_ratio = get_normalized_aspect_ratio(new_row_or_column_size, i_node_size); - float k_node_aspect_ratio = get_normalized_aspect_ratio(new_row_or_column_size, k_node_size); - float new_worst_aspect_ratio = min(i_node_aspect_ratio, k_node_aspect_ratio); - if (new_worst_aspect_ratio < best_worst_aspect_ratio_so_far) { - break; - } - best_worst_aspect_ratio_so_far = new_worst_aspect_ratio; - node_area_sum = new_node_area_sum; - row_or_column_size = new_row_or_column_size; - } - } - - // Paint the elements from 'i' up to and including 'k-1'. - { - const int fixed_side_size = row_or_column_size; - int placement_area = node_area_sum; - int main_dim = short_side_size; - - // Lay out nodes in a row or column. - Orientation orientation = canvas.width() > canvas.height() ? Orientation::Horizontal : Orientation::Vertical; - Gfx::IntRect layout_rect = canvas; - layout_rect.set_primary_size_for_orientation(orientation, fixed_side_size); - for (size_t q = i; q < k; q++) { - auto& child = node.child_at(q); - int node_size = (long long int)main_dim * child.area() / placement_area; - Gfx::IntRect cell_rect = layout_rect; - cell_rect.set_secondary_size_for_orientation(orientation, node_size); - Gfx::IntRect inner_rect = inner_rect_for_frame(cell_rect); - bool is_visual_leaf = child.num_children() == 0 || !rect_can_contain_children(inner_rect); - callback(child, q, cell_rect, depth, is_visual_leaf ? IsVisualLeaf::Yes : IsVisualLeaf::No, IsRemainder::No); - if (cell_rect.width() * cell_rect.height() < 16) { - remaining_nodes_are_too_small = true; - } else { - lay_out_children(child, inner_rect, depth + 1, callback); - } - layout_rect.set_secondary_offset_for_orientation(orientation, layout_rect.secondary_offset_for_orientation(orientation) + node_size); - main_dim -= node_size; - placement_area -= child.area(); - } - canvas.set_primary_offset_for_orientation(orientation, canvas.primary_offset_for_orientation(orientation) + fixed_side_size); - canvas.set_primary_size_for_orientation(orientation, canvas.primary_size_for_orientation(orientation) - fixed_side_size); - } - - // Consume nodes that were added to this row or column. - i = k - 1; - total_area -= node_area_sum; - } - - // If not the entire canvas was filled with nodes, fill the remaining area with a dither pattern. - if (!canvas.is_empty()) { - callback(node, 0, canvas, depth, IsVisualLeaf::No, IsRemainder::Yes); - } -} - -const TreeMapNode* TreeMapWidget::path_node(size_t n) const -{ - if (!m_tree.ptr()) - return nullptr; - const TreeMapNode* iter = &m_tree->root(); - size_t path_index = 0; - while (iter && path_index < m_path.size() && path_index < n) { - size_t child_index = m_path[path_index]; - if (child_index >= iter->num_children()) { - return nullptr; - } - iter = &iter->child_at(child_index); - path_index++; - } - return iter; -} - -void TreeMapWidget::paint_event(GUI::PaintEvent& event) -{ - GUI::Frame::paint_event(event); - GUI::Painter painter(*this); - - m_selected_node_cache = path_node(m_path.size()); - - const TreeMapNode* node = path_node(m_viewpoint); - if (!node) { - painter.fill_rect(frame_inner_rect(), Color::MidGray); - } else if (node_is_leaf(*node)) { - paint_cell_frame(painter, *node, frame_inner_rect(), m_viewpoint - 1, true); - } else { - lay_out_children(*node, frame_inner_rect(), m_viewpoint, [&](const TreeMapNode& node, int, const Gfx::IntRect& rect, int depth, IsVisualLeaf visual_leaf, IsRemainder remainder) { - if (remainder == IsRemainder::No) { - bool fill = visual_leaf == IsVisualLeaf::Yes ? true : false; - paint_cell_frame(painter, node, rect, depth, fill); - } else { - Color color = colors[depth % (sizeof(colors) / sizeof(colors[0]))]; - painter.clear_clip_rect(); - painter.add_clip_rect(rect); - painter.draw_rect(rect, Color::Black); - painter.fill_rect_with_dither_pattern(rect.shrunken(2, 2), color, Color::Black); - } - }); - } -} - -Vector<int> TreeMapWidget::path_to_position(const Gfx::IntPoint& position) -{ - const TreeMapNode* node = path_node(m_viewpoint); - if (!node) { - return {}; - } - Vector<int> path; - lay_out_children(*node, frame_inner_rect(), m_viewpoint, [&](const TreeMapNode&, int index, const Gfx::IntRect& rect, int, IsVisualLeaf, IsRemainder is_remainder) { - if (is_remainder == IsRemainder::No && rect.contains(position)) { - path.append(index); - } - }); - return path; -} - -void TreeMapWidget::mousedown_event(GUI::MouseEvent& event) -{ - const TreeMapNode* node = path_node(m_viewpoint); - if (node && !node_is_leaf(*node)) { - Vector<int> path = path_to_position(event.position()); - if (!path.is_empty()) { - m_path.shrink(m_viewpoint); - m_path.append(path); - if (on_path_change) { - on_path_change(); - } - update(); - } - } -} - -void TreeMapWidget::doubleclick_event(GUI::MouseEvent& event) -{ - const TreeMapNode* node = path_node(m_viewpoint); - if (node && !node_is_leaf(*node)) { - Vector<int> path = path_to_position(event.position()); - m_path.shrink(m_viewpoint); - m_path.append(path); - m_viewpoint = m_path.size(); - if (on_path_change) { - on_path_change(); - } - update(); - } -} - -void TreeMapWidget::mousewheel_event(GUI::MouseEvent& event) -{ - int delta = event.wheel_delta(); - // FIXME: The wheel_delta is premultiplied in the window server, we actually want a raw value here. - int step_size = GUI::WindowServerConnection::the().send_sync<Messages::WindowServer::GetScrollStepSize>()->step_size(); - if (delta > 0) { - size_t step_back = delta / step_size; - if (step_back > m_viewpoint) - step_back = m_viewpoint; - set_viewpoint(m_viewpoint - step_back); - } else { - size_t step_up = (-delta) / step_size; - set_viewpoint(m_viewpoint + step_up); - } -} - -void TreeMapWidget::set_tree(RefPtr<TreeMap> tree) -{ - m_tree = tree; - m_path.clear(); - m_viewpoint = 0; - if (on_path_change) { - on_path_change(); - } - update(); -} - -void TreeMapWidget::set_viewpoint(size_t viewpoint) -{ - if (viewpoint > m_path.size()) - viewpoint = m_path.size(); - m_viewpoint = viewpoint; - if (on_path_change) { - on_path_change(); - } - update(); -} - -size_t TreeMapWidget::path_size() const -{ - return m_path.size() + 1; -} - -size_t TreeMapWidget::viewpoint() const -{ - return m_viewpoint; -} - -} diff --git a/Applications/SpaceAnalyzer/TreeMapWidget.h b/Applications/SpaceAnalyzer/TreeMapWidget.h deleted file mode 100644 index 8b4e1e9269..0000000000 --- a/Applications/SpaceAnalyzer/TreeMapWidget.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * 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/Frame.h> -#include <LibGfx/Rect.h> - -namespace SpaceAnalyzer { - -struct TreeMapNode { - virtual String name() const = 0; - virtual int64_t area() const = 0; - virtual size_t num_children() const = 0; - virtual const TreeMapNode& child_at(size_t i) const = 0; - virtual void sort_children_by_area() const = 0; -}; - -struct TreeMap : public RefCounted<TreeMap> { - virtual ~TreeMap() { } - virtual const TreeMapNode& root() const = 0; -}; - -class TreeMapWidget final : public GUI::Frame { - C_OBJECT(TreeMapWidget) - -public: - virtual ~TreeMapWidget() override; - Function<void()> on_path_change; - size_t path_size() const; - const TreeMapNode* path_node(size_t n) const; - size_t viewpoint() const; - void set_viewpoint(size_t); - void set_tree(RefPtr<TreeMap> tree); - -private: - TreeMapWidget(); - virtual void paint_event(GUI::PaintEvent&) override; - virtual void mousedown_event(GUI::MouseEvent&) override; - virtual void doubleclick_event(GUI::MouseEvent&) override; - virtual void mousewheel_event(GUI::MouseEvent&) override; - - bool rect_can_contain_children(const Gfx::IntRect& rect) const; - bool rect_can_contain_label(const Gfx::IntRect& rect) const; - Gfx::IntRect inner_rect_for_frame(const Gfx::IntRect& rect) const; - - enum class IsVisualLeaf { - Yes, - No - }; - enum class IsRemainder { - Yes, - No - }; - - template<typename Function> - void lay_out_children(const TreeMapNode&, const Gfx::IntRect&, int depth, Function); - void paint_cell_frame(GUI::Painter&, const TreeMapNode&, const Gfx::IntRect&, int depth, bool fill) const; - Vector<int> path_to_position(const Gfx::IntPoint&); - - RefPtr<TreeMap> m_tree; - Vector<int> m_path; - size_t m_viewpoint; - const void* m_selected_node_cache; -}; - -} diff --git a/Applications/SpaceAnalyzer/main.cpp b/Applications/SpaceAnalyzer/main.cpp deleted file mode 100644 index 2ce061f786..0000000000 --- a/Applications/SpaceAnalyzer/main.cpp +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * 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 "TreeMapWidget.h" -#include <AK/Queue.h> -#include <AK/QuickSort.h> -#include <AK/RefCounted.h> -#include <Applications/SpaceAnalyzer/SpaceAnalyzerGML.h> -#include <LibCore/DirIterator.h> -#include <LibCore/File.h> -#include <LibGUI/AboutDialog.h> -#include <LibGUI/Application.h> -#include <LibGUI/BreadcrumbBar.h> -#include <LibGUI/Icon.h> -#include <LibGUI/Menu.h> -#include <LibGUI/MenuBar.h> -#include <LibGUI/StatusBar.h> -#include <sys/stat.h> - -static const char* APP_NAME = "Space Analyzer"; - -struct TreeNode : public SpaceAnalyzer::TreeMapNode { - TreeNode(String name) - : m_name(move(name)) {}; - - virtual String name() const { return m_name; } - virtual int64_t area() const { return m_area; } - virtual size_t num_children() const - { - if (m_children) { - return m_children->size(); - } - return 0; - } - virtual const TreeNode& child_at(size_t i) const { return m_children->at(i); } - virtual void sort_children_by_area() const - { - if (m_children) { - Vector<TreeNode>* children = const_cast<Vector<TreeNode>*>(m_children.ptr()); - quick_sort(*children, [](auto& a, auto& b) { return b.m_area < a.m_area; }); - } - } - - String m_name; - int64_t m_area { 0 }; - OwnPtr<Vector<TreeNode>> m_children; -}; - -struct Tree : public SpaceAnalyzer::TreeMap { - Tree(String root_name) - : m_root(move(root_name)) {}; - virtual ~Tree() {}; - TreeNode m_root; - virtual const SpaceAnalyzer::TreeMapNode& root() const override - { - return m_root; - }; -}; - -struct MountInfo { - String mount_point; - String source; -}; - -static void fill_mounts(Vector<MountInfo>& output) -{ - // Output info about currently mounted filesystems. - auto df = Core::File::construct("/proc/df"); - if (!df->open(Core::IODevice::ReadOnly)) { - fprintf(stderr, "Failed to open /proc/df: %s\n", df->error_string()); - return; - } - - auto content = df->read_all(); - auto json = JsonValue::from_string(content); - ASSERT(json.has_value()); - - json.value().as_array().for_each([&output](auto& value) { - auto filesystem_object = value.as_object(); - MountInfo mount_info; - mount_info.mount_point = filesystem_object.get("mount_point").to_string(); - mount_info.source = filesystem_object.get("source").as_string_or("none"); - output.append(mount_info); - }); -} - -static MountInfo* find_mount_for_path(String path, Vector<MountInfo>& mounts) -{ - MountInfo* result = nullptr; - size_t length = 0; - for (auto& mount_info : mounts) { - String& mount_point = mount_info.mount_point; - if (path.starts_with(mount_point)) { - if (!result || mount_point.length() > length) { - result = &mount_info; - length = mount_point.length(); - } - } - } - return result; -} - -static long long int update_totals(TreeNode& node) -{ - long long int result = 0; - if (node.m_children) { - for (auto& child : *node.m_children) { - result += update_totals(child); - } - node.m_area = result; - } else { - result = node.m_area; - } - return result; -} - -struct QueueEntry { - QueueEntry(String path, TreeNode* node) - : path(move(path)) - , node(node) {}; - String path; - TreeNode* node { nullptr }; -}; - -static void populate_filesize_tree(TreeNode& root, Vector<MountInfo>& mounts, HashMap<int, int>& error_accumulator) -{ - ASSERT(!root.m_name.ends_with("/")); - - Queue<QueueEntry> queue; - queue.enqueue(QueueEntry(root.m_name, &root)); - - StringBuilder builder = StringBuilder(); - builder.append(root.m_name); - builder.append("/"); - MountInfo* root_mount_info = find_mount_for_path(builder.to_string(), mounts); - if (!root_mount_info) { - return; - } - while (!queue.is_empty()) { - QueueEntry queue_entry = queue.dequeue(); - - builder.clear(); - builder.append(queue_entry.path); - builder.append("/"); - - MountInfo* mount_info = find_mount_for_path(builder.to_string(), mounts); - if (!mount_info || (mount_info != root_mount_info && mount_info->source != root_mount_info->source)) { - continue; - } - - Core::DirIterator dir_iterator(builder.to_string(), Core::DirIterator::SkipParentAndBaseDir); - if (dir_iterator.has_error()) { - int error_sum = error_accumulator.get(dir_iterator.error()).value_or(0); - error_accumulator.set(dir_iterator.error(), error_sum + 1); - } else { - queue_entry.node->m_children = make<Vector<TreeNode>>(); - while (dir_iterator.has_next()) { - queue_entry.node->m_children->append(TreeNode(dir_iterator.next_path())); - } - for (auto& child : *queue_entry.node->m_children) { - String& name = child.m_name; - int name_len = name.length(); - builder.append(name); - struct stat st; - int stat_result = lstat(builder.to_string().characters(), &st); - if (stat_result < 0) { - int error_sum = error_accumulator.get(errno).value_or(0); - error_accumulator.set(errno, error_sum + 1); - } else { - if (S_ISDIR(st.st_mode)) { - queue.enqueue(QueueEntry(builder.to_string(), &child)); - } else { - child.m_area = st.st_size; - } - } - builder.trim(name_len); - } - } - } - - update_totals(root); -} - -static void analyze(RefPtr<Tree> tree, SpaceAnalyzer::TreeMapWidget& treemapwidget, GUI::StatusBar& statusbar) -{ - // Build an in-memory tree mirroring the filesystem and for each node - // calculate the sum of the file size for all its descendants. - TreeNode* root = &tree->m_root; - Vector<MountInfo> mounts; - fill_mounts(mounts); - HashMap<int, int> error_accumulator; - populate_filesize_tree(*root, mounts, error_accumulator); - - // Display an error summary in the statusbar. - if (!error_accumulator.is_empty()) { - StringBuilder builder; - bool first = true; - builder.append("Some directories were not analyzed: "); - for (auto& key : error_accumulator.keys()) { - if (!first) { - builder.append(", "); - } - builder.append(strerror(key)); - builder.append(" ("); - int value = error_accumulator.get(key).value(); - builder.append(String::number(value)); - if (value == 1) { - builder.append(" time"); - } else { - builder.append(" times"); - } - builder.append(")"); - first = false; - } - statusbar.set_text(builder.to_string()); - } else { - statusbar.set_text("No errors"); - } - treemapwidget.set_tree(tree); -} - -int main(int argc, char* argv[]) -{ - auto app = GUI::Application::construct(argc, argv); - - RefPtr<Tree> tree = adopt(*new Tree("")); - - // Configure application window. - auto app_icon = GUI::Icon::default_icon("app-space-analyzer"); - auto window = GUI::Window::construct(); - window->set_title(APP_NAME); - window->resize(640, 480); - window->set_icon(app_icon.bitmap_for_size(16)); - - // Load widgets. - auto& mainwidget = window->set_main_widget<GUI::Widget>(); - mainwidget.load_from_gml(space_analyzer_gml); - auto& breadcrumbbar = *mainwidget.find_descendant_of_type_named<GUI::BreadcrumbBar>("breadcrumb_bar"); - auto& treemapwidget = *mainwidget.find_descendant_of_type_named<SpaceAnalyzer::TreeMapWidget>("tree_map"); - auto& statusbar = *mainwidget.find_descendant_of_type_named<GUI::StatusBar>("status_bar"); - - // Configure the menubar. - auto menubar = GUI::MenuBar::construct(); - auto& app_menu = menubar->add_menu(APP_NAME); - app_menu.add_action(GUI::Action::create("Analyze", [&](auto&) { - analyze(tree, treemapwidget, statusbar); - })); - app_menu.add_action(GUI::CommonActions::make_quit_action([&](auto&) { - app->quit(); - })); - auto& help_menu = menubar->add_menu("Help"); - help_menu.add_action(GUI::CommonActions::make_about_action(APP_NAME, app_icon, window)); - app->set_menubar(move(menubar)); - - // Configure event handlers. - breadcrumbbar.on_segment_click = [&](size_t index) { - ASSERT(index < treemapwidget.path_size()); - treemapwidget.set_viewpoint(index); - }; - treemapwidget.on_path_change = [&]() { - breadcrumbbar.clear_segments(); - for (size_t k = 0; k < treemapwidget.path_size(); k++) { - if (k == 0) { - breadcrumbbar.append_segment("/"); - } else { - const SpaceAnalyzer::TreeMapNode* node = treemapwidget.path_node(k); - breadcrumbbar.append_segment(node->name()); - } - } - breadcrumbbar.set_selected_segment(treemapwidget.viewpoint()); - }; - - // At startup automatically do an analysis of root. - analyze(tree, treemapwidget, statusbar); - - window->show(); - return app->exec(); -} diff --git a/Applications/Spreadsheet/CMakeLists.txt b/Applications/Spreadsheet/CMakeLists.txt deleted file mode 100644 index f41922655a..0000000000 --- a/Applications/Spreadsheet/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -compile_gml(CondFormatting.gml CondFormattingGML.h cond_fmt_gml) -compile_gml(CondView.gml CondFormattingViewGML.h cond_fmt_view_gml) - -set(SOURCES - Cell.cpp - CellSyntaxHighlighter.cpp - CellType/Date.cpp - CellType/Format.cpp - CellType/Identity.cpp - CellType/Numeric.cpp - CellType/String.cpp - CellType/Type.cpp - CellTypeDialog.cpp - CondFormattingGML.h - CondFormattingViewGML.h - HelpWindow.cpp - JSIntegration.cpp - Readers/XSV.cpp - Spreadsheet.cpp - SpreadsheetModel.cpp - SpreadsheetView.cpp - SpreadsheetWidget.cpp - Workbook.cpp - main.cpp -) - -serenity_app(Spreadsheet ICON app-spreadsheet) -target_link_libraries(Spreadsheet LibGUI LibJS LibWeb) diff --git a/Applications/Spreadsheet/Cell.cpp b/Applications/Spreadsheet/Cell.cpp deleted file mode 100644 index d2f511753f..0000000000 --- a/Applications/Spreadsheet/Cell.cpp +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "Cell.h" -#include "Spreadsheet.h" -#include <AK/StringBuilder.h> -#include <AK/TemporaryChange.h> - -namespace Spreadsheet { - -void Cell::set_data(String new_data) -{ - if (m_data == new_data) - return; - - if (new_data.starts_with("=")) { - new_data = new_data.substring(1, new_data.length() - 1); - m_kind = Formula; - } else { - m_kind = LiteralString; - } - - m_data = move(new_data); - m_dirty = true; - m_evaluated_externally = false; -} - -void Cell::set_data(JS::Value new_data) -{ - m_dirty = true; - m_evaluated_externally = true; - - StringBuilder builder; - - builder.append(new_data.to_string_without_side_effects()); - m_data = builder.build(); - - m_evaluated_data = move(new_data); -} - -void Cell::set_type(const CellType* type) -{ - m_type = type; -} - -void Cell::set_type(const StringView& name) -{ - auto* cell_type = CellType::get_by_name(name); - if (cell_type) { - return set_type(cell_type); - } - - ASSERT_NOT_REACHED(); -} - -void Cell::set_type_metadata(CellTypeMetadata&& metadata) -{ - m_type_metadata = move(metadata); -} - -const CellType& Cell::type() const -{ - if (m_type) - return *m_type; - - if (m_kind == LiteralString) { - if (m_data.to_int().has_value()) - return *CellType::get_by_name("Numeric"); - } - - return *CellType::get_by_name("Identity"); -} - -String Cell::typed_display() const -{ - return type().display(const_cast<Cell&>(*this), m_type_metadata); -} - -JS::Value Cell::typed_js_data() const -{ - return type().js_value(const_cast<Cell&>(*this), m_type_metadata); -} - -void Cell::update_data(Badge<Sheet>) -{ - TemporaryChange cell_change { m_sheet->current_evaluated_cell(), this }; - if (!m_dirty) - return; - - m_js_exception = {}; - - if (m_dirty) { - m_dirty = false; - if (m_kind == Formula) { - if (!m_evaluated_externally) { - auto [value, exception] = m_sheet->evaluate(m_data, this); - m_evaluated_data = value; - m_js_exception = move(exception); - } - } - - for (auto& ref : m_referencing_cells) { - if (ref) { - ref->m_dirty = true; - ref->update(); - } - } - } - - m_evaluated_formats.background_color.clear(); - m_evaluated_formats.foreground_color.clear(); - if (!m_js_exception) { - StringBuilder builder; - for (auto& fmt : m_conditional_formats) { - if (!fmt.condition.is_empty()) { - builder.clear(); - builder.append("return ("); - builder.append(fmt.condition); - builder.append(')'); - auto [value, exception] = m_sheet->evaluate(builder.string_view(), this); - if (exception) { - m_js_exception = move(exception); - } else { - if (value.to_boolean()) { - if (fmt.background_color.has_value()) - m_evaluated_formats.background_color = fmt.background_color; - if (fmt.foreground_color.has_value()) - m_evaluated_formats.foreground_color = fmt.foreground_color; - } - } - } - } - } -} - -void Cell::update() -{ - m_sheet->update(*this); -} - -JS::Value Cell::js_data() -{ - if (m_dirty) - update(); - - if (m_kind == Formula) - return m_evaluated_data; - - return JS::js_string(m_sheet->interpreter().heap(), m_data); -} - -String Cell::source() const -{ - StringBuilder builder; - if (m_kind == Formula) - builder.append('='); - builder.append(m_data); - return builder.to_string(); -} - -// FIXME: Find a better way to figure out dependencies -void Cell::reference_from(Cell* other) -{ - if (!other || other == this) - return; - - if (!m_referencing_cells.find_if([other](const auto& ptr) { return ptr.ptr() == other; }).is_end()) - return; - - m_referencing_cells.append(other->make_weak_ptr()); -} - -void Cell::copy_from(const Cell& other) -{ - m_dirty = true; - m_evaluated_externally = other.m_evaluated_externally; - m_data = other.m_data; - m_evaluated_data = other.m_evaluated_data; - m_kind = other.m_kind; - m_type = other.m_type; - m_type_metadata = other.m_type_metadata; - m_conditional_formats = other.m_conditional_formats; - m_evaluated_formats = other.m_evaluated_formats; - if (!other.m_js_exception) - m_js_exception = other.m_js_exception; -} - -} diff --git a/Applications/Spreadsheet/Cell.h b/Applications/Spreadsheet/Cell.h deleted file mode 100644 index fef9031d5e..0000000000 --- a/Applications/Spreadsheet/Cell.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "CellType/Type.h" -#include "ConditionalFormatting.h" -#include "Forward.h" -#include "JSIntegration.h" -#include "Position.h" -#include <AK/String.h> -#include <AK/Types.h> -#include <AK/WeakPtr.h> - -namespace Spreadsheet { - -struct Cell : public Weakable<Cell> { - enum Kind { - LiteralString, - Formula, - }; - - Cell(String data, Position position, WeakPtr<Sheet> sheet) - : m_dirty(false) - , m_data(move(data)) - , m_kind(LiteralString) - , m_sheet(sheet) - , m_position(move(position)) - { - } - - Cell(String source, JS::Value&& cell_value, Position position, WeakPtr<Sheet> sheet) - : m_dirty(false) - , m_data(move(source)) - , m_evaluated_data(move(cell_value)) - , m_kind(Formula) - , m_sheet(sheet) - , m_position(move(position)) - { - } - - void reference_from(Cell*); - - void set_data(String new_data); - void set_data(JS::Value new_data); - bool dirty() const { return m_dirty; } - void clear_dirty() { m_dirty = false; } - - void set_exception(JS::Exception* exc) { m_js_exception = exc; } - JS::Exception* exception() const { return m_js_exception; } - - const String& data() const { return m_data; } - const JS::Value& evaluated_data() const { return m_evaluated_data; } - Kind kind() const { return m_kind; } - const Vector<WeakPtr<Cell>>& referencing_cells() const { return m_referencing_cells; } - - void set_type(const StringView& name); - void set_type(const CellType*); - void set_type_metadata(CellTypeMetadata&&); - - const Position& position() const { return m_position; } - void set_position(Position position, Badge<Sheet>) - { - if (position != m_position) { - m_dirty = true; - m_position = move(position); - } - } - - const Format& evaluated_formats() const { return m_evaluated_formats; } - Format& evaluated_formats() { return m_evaluated_formats; } - const Vector<ConditionalFormat>& conditional_formats() const { return m_conditional_formats; } - void set_conditional_formats(Vector<ConditionalFormat>&& fmts) - { - m_dirty = true; - m_conditional_formats = move(fmts); - } - - String typed_display() const; - JS::Value typed_js_data() const; - - const CellType& type() const; - const CellTypeMetadata& type_metadata() const { return m_type_metadata; } - CellTypeMetadata& type_metadata() { return m_type_metadata; } - - String source() const; - - JS::Value js_data(); - - void update(); - void update_data(Badge<Sheet>); - - const Sheet& sheet() const { return *m_sheet; } - Sheet& sheet() { return *m_sheet; } - - void copy_from(const Cell&); - -private: - bool m_dirty { false }; - bool m_evaluated_externally { false }; - String m_data; - JS::Value m_evaluated_data; - JS::Exception* m_js_exception { nullptr }; - Kind m_kind { LiteralString }; - WeakPtr<Sheet> m_sheet; - Vector<WeakPtr<Cell>> m_referencing_cells; - const CellType* m_type { nullptr }; - CellTypeMetadata m_type_metadata; - Position m_position; - - Vector<ConditionalFormat> m_conditional_formats; - Format m_evaluated_formats; -}; - -} diff --git a/Applications/Spreadsheet/CellSyntaxHighlighter.cpp b/Applications/Spreadsheet/CellSyntaxHighlighter.cpp deleted file mode 100644 index c26d2aa238..0000000000 --- a/Applications/Spreadsheet/CellSyntaxHighlighter.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "CellSyntaxHighlighter.h" -#include <LibGUI/JSSyntaxHighlighter.h> -#include <LibGUI/TextEditor.h> -#include <LibGfx/Palette.h> -#include <LibJS/Lexer.h> - -namespace Spreadsheet { - -void CellSyntaxHighlighter::rehighlight(Gfx::Palette palette) -{ - ASSERT(m_editor); - auto text = m_editor->text(); - m_editor->document().spans().clear(); - if (!text.starts_with('=')) { - m_editor->update(); - return; - } - - JSSyntaxHighlighter::rehighlight(palette); - - // Highlight the '=' - m_editor->document().spans().empend( - GUI::TextRange { { 0, 0 }, { 0, 1 } }, - Gfx::TextAttributes { - palette.syntax_keyword(), - Optional<Color> {}, - false, - false, - }, - nullptr, - false); - - if (m_cell && m_cell->exception()) { - auto range = m_cell->exception()->source_ranges().first(); - GUI::TextRange text_range { { range.start.line - 1, range.start.column }, { range.end.line - 1, range.end.column - 1 } }; - m_editor->document().spans().prepend( - GUI::TextDocumentSpan { - text_range, - Gfx::TextAttributes { - Color::Black, - Color::Red, - false, - false, - }, - nullptr, - false }); - } - m_editor->update(); -} - -CellSyntaxHighlighter::~CellSyntaxHighlighter() -{ -} - -} diff --git a/Applications/Spreadsheet/CellSyntaxHighlighter.h b/Applications/Spreadsheet/CellSyntaxHighlighter.h deleted file mode 100644 index f02f7e2bbb..0000000000 --- a/Applications/Spreadsheet/CellSyntaxHighlighter.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "Cell.h" -#include <LibGUI/JSSyntaxHighlighter.h> -#include <LibGUI/SyntaxHighlighter.h> - -namespace Spreadsheet { - -class CellSyntaxHighlighter final : public GUI::JSSyntaxHighlighter { -public: - CellSyntaxHighlighter() { } - virtual ~CellSyntaxHighlighter() override; - - virtual void rehighlight(Gfx::Palette) override; - void set_cell(const Cell* cell) { m_cell = cell; } - -private: - const Cell* m_cell { nullptr }; -}; - -} diff --git a/Applications/Spreadsheet/CellType/Date.cpp b/Applications/Spreadsheet/CellType/Date.cpp deleted file mode 100644 index 005620006f..0000000000 --- a/Applications/Spreadsheet/CellType/Date.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "Date.h" -#include "../Cell.h" -#include "../Spreadsheet.h" -#include <AK/ScopeGuard.h> -#include <LibCore/DateTime.h> - -namespace Spreadsheet { - -DateCell::DateCell() - : CellType("Date") -{ -} - -DateCell::~DateCell() -{ -} - -String DateCell::display(Cell& cell, const CellTypeMetadata& metadata) const -{ - ScopeGuard propagate_exception { [&cell] { - if (auto exc = cell.sheet().interpreter().exception()) { - cell.sheet().interpreter().vm().clear_exception(); - cell.set_exception(exc); - } - } }; - auto timestamp = js_value(cell, metadata); - auto string = Core::DateTime::from_timestamp(timestamp.to_i32(cell.sheet().global_object())).to_string(metadata.format.is_empty() ? "%Y-%m-%d %H:%M:%S" : metadata.format.characters()); - - if (metadata.length >= 0) - return string.substring(0, metadata.length); - - return string; -} - -JS::Value DateCell::js_value(Cell& cell, const CellTypeMetadata&) const -{ - auto js_data = cell.js_data(); - auto value = js_data.to_double(cell.sheet().global_object()); - return JS::Value(value / 1000); // Turn it to seconds -} - -} diff --git a/Applications/Spreadsheet/CellType/Date.h b/Applications/Spreadsheet/CellType/Date.h deleted file mode 100644 index 3221fad0f1..0000000000 --- a/Applications/Spreadsheet/CellType/Date.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "Type.h" - -namespace Spreadsheet { - -class DateCell : public CellType { - -public: - DateCell(); - virtual ~DateCell() override; - virtual String display(Cell&, const CellTypeMetadata&) const override; - virtual JS::Value js_value(Cell&, const CellTypeMetadata&) const override; -}; - -} diff --git a/Applications/Spreadsheet/CellType/Format.cpp b/Applications/Spreadsheet/CellType/Format.cpp deleted file mode 100644 index d7c590a550..0000000000 --- a/Applications/Spreadsheet/CellType/Format.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "Format.h" -#include <AK/PrintfImplementation.h> -#include <AK/String.h> -#include <AK/StringBuilder.h> - -namespace Spreadsheet { - -template<typename T, typename V> -struct SingleEntryListNext { - ALWAYS_INLINE T operator()(V value) const - { - return (T)value; - } -}; - -template<typename PutChFunc, typename ArgumentListRefT, template<typename T, typename U = ArgumentListRefT> typename NextArgument> -struct PrintfImpl : public PrintfImplementation::PrintfImpl<PutChFunc, ArgumentListRefT, NextArgument> { - ALWAYS_INLINE PrintfImpl(PutChFunc& putch, char*& bufptr, const int& nwritten) - : PrintfImplementation::PrintfImpl<PutChFunc, ArgumentListRefT, NextArgument>(putch, bufptr, nwritten) - { - } - - // Disallow pointer formats. - ALWAYS_INLINE int format_n(const PrintfImplementation::ModifierState&, ArgumentListRefT&) const - { - return 0; - } - ALWAYS_INLINE int format_s(const PrintfImplementation::ModifierState&, ArgumentListRefT&) const - { - return 0; - } -}; - -String format_double(const char* format, double value) -{ - StringBuilder builder; - auto putch = [&](auto, auto ch) { builder.append(ch); }; - printf_internal<decltype(putch), PrintfImpl, double, SingleEntryListNext>(putch, nullptr, format, value); - - return builder.build(); -} - -} diff --git a/Applications/Spreadsheet/CellType/Format.h b/Applications/Spreadsheet/CellType/Format.h deleted file mode 100644 index 98f3cdc741..0000000000 --- a/Applications/Spreadsheet/CellType/Format.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 <AK/Forward.h> - -namespace Spreadsheet { - -String format_double(const char* format, double value); - -} diff --git a/Applications/Spreadsheet/CellType/Identity.cpp b/Applications/Spreadsheet/CellType/Identity.cpp deleted file mode 100644 index eed7cb190c..0000000000 --- a/Applications/Spreadsheet/CellType/Identity.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "Identity.h" -#include "../Cell.h" -#include "../Spreadsheet.h" - -namespace Spreadsheet { - -IdentityCell::IdentityCell() - : CellType("Identity") -{ -} - -IdentityCell::~IdentityCell() -{ -} - -String IdentityCell::display(Cell& cell, const CellTypeMetadata&) const -{ - return cell.js_data().to_string_without_side_effects(); -} - -JS::Value IdentityCell::js_value(Cell& cell, const CellTypeMetadata&) const -{ - return cell.js_data(); -} - -} diff --git a/Applications/Spreadsheet/CellType/Identity.h b/Applications/Spreadsheet/CellType/Identity.h deleted file mode 100644 index 09089153ec..0000000000 --- a/Applications/Spreadsheet/CellType/Identity.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "Type.h" - -namespace Spreadsheet { - -class IdentityCell : public CellType { - -public: - IdentityCell(); - virtual ~IdentityCell() override; - virtual String display(Cell&, const CellTypeMetadata&) const override; - virtual JS::Value js_value(Cell&, const CellTypeMetadata&) const override; -}; - -} diff --git a/Applications/Spreadsheet/CellType/Numeric.cpp b/Applications/Spreadsheet/CellType/Numeric.cpp deleted file mode 100644 index d58d48cd85..0000000000 --- a/Applications/Spreadsheet/CellType/Numeric.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "Numeric.h" -#include "../Cell.h" -#include "../Spreadsheet.h" -#include "Format.h" -#include <AK/ScopeGuard.h> - -namespace Spreadsheet { - -NumericCell::NumericCell() - : CellType("Numeric") -{ -} - -NumericCell::~NumericCell() -{ -} - -String NumericCell::display(Cell& cell, const CellTypeMetadata& metadata) const -{ - ScopeGuard propagate_exception { [&cell] { - if (auto exc = cell.sheet().interpreter().exception()) { - cell.sheet().interpreter().vm().clear_exception(); - cell.set_exception(exc); - } - } }; - auto value = js_value(cell, metadata); - String string; - if (metadata.format.is_empty()) - string = value.to_string_without_side_effects(); - else - string = format_double(metadata.format.characters(), value.to_double(cell.sheet().global_object())); - - if (metadata.length >= 0) - return string.substring(0, metadata.length); - - return string; -} - -JS::Value NumericCell::js_value(Cell& cell, const CellTypeMetadata&) const -{ - ScopeGuard propagate_exception { [&cell] { - if (auto exc = cell.sheet().interpreter().exception()) { - cell.sheet().interpreter().vm().clear_exception(); - cell.set_exception(exc); - } - } }; - return cell.js_data().to_number(cell.sheet().global_object()); -} - -} diff --git a/Applications/Spreadsheet/CellType/Numeric.h b/Applications/Spreadsheet/CellType/Numeric.h deleted file mode 100644 index e0cd4a295a..0000000000 --- a/Applications/Spreadsheet/CellType/Numeric.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "Type.h" - -namespace Spreadsheet { - -class NumericCell : public CellType { - -public: - NumericCell(); - virtual ~NumericCell() override; - virtual String display(Cell&, const CellTypeMetadata&) const override; - virtual JS::Value js_value(Cell&, const CellTypeMetadata&) const override; -}; - -} diff --git a/Applications/Spreadsheet/CellType/String.cpp b/Applications/Spreadsheet/CellType/String.cpp deleted file mode 100644 index ccda9947fe..0000000000 --- a/Applications/Spreadsheet/CellType/String.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "String.h" -#include "../Cell.h" -#include "../Spreadsheet.h" - -namespace Spreadsheet { - -StringCell::StringCell() - : CellType("String") -{ -} - -StringCell::~StringCell() -{ -} - -String StringCell::display(Cell& cell, const CellTypeMetadata& metadata) const -{ - auto string = cell.js_data().to_string_without_side_effects(); - if (metadata.length >= 0) - return string.substring(0, metadata.length); - - return string; -} - -JS::Value StringCell::js_value(Cell& cell, const CellTypeMetadata& metadata) const -{ - auto string = display(cell, metadata); - return JS::js_string(cell.sheet().interpreter().heap(), string); -} - -} diff --git a/Applications/Spreadsheet/CellType/String.h b/Applications/Spreadsheet/CellType/String.h deleted file mode 100644 index e4ba28eb24..0000000000 --- a/Applications/Spreadsheet/CellType/String.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "Type.h" - -namespace Spreadsheet { - -class StringCell : public CellType { - -public: - StringCell(); - virtual ~StringCell() override; - virtual String display(Cell&, const CellTypeMetadata&) const override; - virtual JS::Value js_value(Cell&, const CellTypeMetadata&) const override; -}; - -} diff --git a/Applications/Spreadsheet/CellType/Type.cpp b/Applications/Spreadsheet/CellType/Type.cpp deleted file mode 100644 index cd64e8f239..0000000000 --- a/Applications/Spreadsheet/CellType/Type.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "Type.h" -#include "Date.h" -#include "Identity.h" -#include "Numeric.h" -#include "String.h" -#include <AK/HashMap.h> -#include <AK/OwnPtr.h> - -static HashMap<String, Spreadsheet::CellType*> s_cell_types; -static Spreadsheet::StringCell s_string_cell; -static Spreadsheet::NumericCell s_numeric_cell; -static Spreadsheet::IdentityCell s_identity_cell; -static Spreadsheet::DateCell s_date_cell; - -namespace Spreadsheet { - -const CellType* CellType::get_by_name(const StringView& name) -{ - return s_cell_types.get(name).value_or(nullptr); -} - -Vector<StringView> CellType::names() -{ - Vector<StringView> names; - for (auto& it : s_cell_types) - names.append(it.key); - return names; -} - -CellType::CellType(const StringView& name) - : m_name(name) -{ - ASSERT(!s_cell_types.contains(name)); - s_cell_types.set(name, this); -} - -} diff --git a/Applications/Spreadsheet/CellType/Type.h b/Applications/Spreadsheet/CellType/Type.h deleted file mode 100644 index 0145a8fa8d..0000000000 --- a/Applications/Spreadsheet/CellType/Type.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "../ConditionalFormatting.h" -#include "../Forward.h" -#include <AK/Forward.h> -#include <AK/String.h> -#include <LibGfx/Color.h> -#include <LibGfx/TextAlignment.h> -#include <LibJS/Forward.h> - -namespace Spreadsheet { - -struct CellTypeMetadata { - int length { -1 }; - String format; - Gfx::TextAlignment alignment { Gfx::TextAlignment::CenterRight }; - Format static_format; -}; - -class CellType { -public: - static const CellType* get_by_name(const StringView&); - static Vector<StringView> names(); - - virtual String display(Cell&, const CellTypeMetadata&) const = 0; - virtual JS::Value js_value(Cell&, const CellTypeMetadata&) const = 0; - virtual ~CellType() { } - - const String& name() const { return m_name; } - -protected: - CellType(const StringView& name); - -private: - String m_name; -}; - -} diff --git a/Applications/Spreadsheet/CellTypeDialog.cpp b/Applications/Spreadsheet/CellTypeDialog.cpp deleted file mode 100644 index b94feab5e5..0000000000 --- a/Applications/Spreadsheet/CellTypeDialog.cpp +++ /dev/null @@ -1,483 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "CellTypeDialog.h" -#include "Cell.h" -#include "Spreadsheet.h" -#include <AK/StringBuilder.h> -#include <Applications/Spreadsheet/CondFormattingGML.h> -#include <Applications/Spreadsheet/CondFormattingViewGML.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Button.h> -#include <LibGUI/CheckBox.h> -#include <LibGUI/ColorInput.h> -#include <LibGUI/ComboBox.h> -#include <LibGUI/ItemListModel.h> -#include <LibGUI/JSSyntaxHighlighter.h> -#include <LibGUI/Label.h> -#include <LibGUI/ListView.h> -#include <LibGUI/SpinBox.h> -#include <LibGUI/TabWidget.h> -#include <LibGUI/TextEditor.h> -#include <LibGUI/Widget.h> -#include <LibGfx/FontDatabase.h> - -REGISTER_WIDGET(Spreadsheet, ConditionsView); - -namespace Spreadsheet { - -CellTypeDialog::CellTypeDialog(const Vector<Position>& positions, Sheet& sheet, GUI::Window* parent) - : GUI::Dialog(parent) -{ - ASSERT(!positions.is_empty()); - - StringBuilder builder; - - if (positions.size() == 1) - builder.appendff("Format cell {}{}", positions.first().column, positions.first().row); - else - builder.appendff("Format {} cells", positions.size()); - - set_title(builder.string_view()); - set_icon(parent->icon()); - resize(285, 360); - - auto& main_widget = set_main_widget<GUI::Widget>(); - main_widget.set_layout<GUI::VerticalBoxLayout>().set_margins({ 4, 4, 4, 4 }); - main_widget.set_fill_with_background_color(true); - - auto& tab_widget = main_widget.add<GUI::TabWidget>(); - setup_tabs(tab_widget, positions, sheet); - - auto& buttonbox = main_widget.add<GUI::Widget>(); - buttonbox.set_shrink_to_fit(true); - auto& button_layout = buttonbox.set_layout<GUI::HorizontalBoxLayout>(); - button_layout.set_spacing(10); - button_layout.add_spacer(); - auto& ok_button = buttonbox.add<GUI::Button>("OK"); - ok_button.set_fixed_width(80); - ok_button.on_click = [&](auto) { done(ExecOK); }; -} - -const Vector<String> g_horizontal_alignments { "Left", "Center", "Right" }; -const Vector<String> g_vertical_alignments { "Top", "Center", "Bottom" }; -Vector<String> g_types; - -constexpr static CellTypeDialog::VerticalAlignment vertical_alignment_from(Gfx::TextAlignment alignment) -{ - switch (alignment) { - case Gfx::TextAlignment::CenterRight: - case Gfx::TextAlignment::CenterLeft: - case Gfx::TextAlignment::Center: - return CellTypeDialog::VerticalAlignment::Center; - - case Gfx::TextAlignment::TopRight: - case Gfx::TextAlignment::TopLeft: - return CellTypeDialog::VerticalAlignment::Top; - - case Gfx::TextAlignment::BottomRight: - return CellTypeDialog::VerticalAlignment::Bottom; - } - - return CellTypeDialog::VerticalAlignment::Center; -} - -constexpr static CellTypeDialog::HorizontalAlignment horizontal_alignment_from(Gfx::TextAlignment alignment) -{ - switch (alignment) { - case Gfx::TextAlignment::Center: - return CellTypeDialog::HorizontalAlignment::Center; - - case Gfx::TextAlignment::CenterRight: - case Gfx::TextAlignment::TopRight: - case Gfx::TextAlignment::BottomRight: - return CellTypeDialog::HorizontalAlignment::Right; - - case Gfx::TextAlignment::TopLeft: - case Gfx::TextAlignment::CenterLeft: - return CellTypeDialog::HorizontalAlignment::Left; - } - - return CellTypeDialog::HorizontalAlignment::Right; -} - -void CellTypeDialog::setup_tabs(GUI::TabWidget& tabs, const Vector<Position>& positions, Sheet& sheet) -{ - g_types.clear(); - for (auto& type_name : CellType::names()) - g_types.append(type_name); - - Vector<Cell*> cells; - for (auto& position : positions) { - if (auto cell = sheet.at(position)) - cells.append(cell); - } - - if (cells.size() == 1) { - auto& cell = *cells.first(); - m_format = cell.type_metadata().format; - m_length = cell.type_metadata().length; - m_type = &cell.type(); - m_vertical_alignment = vertical_alignment_from(cell.type_metadata().alignment); - m_horizontal_alignment = horizontal_alignment_from(cell.type_metadata().alignment); - m_static_format = cell.type_metadata().static_format; - m_conditional_formats = cell.conditional_formats(); - } - - auto& type_tab = tabs.add_tab<GUI::Widget>("Type"); - type_tab.set_layout<GUI::HorizontalBoxLayout>().set_margins({ 4, 4, 4, 4 }); - { - auto& left_side = type_tab.add<GUI::Widget>(); - left_side.set_layout<GUI::VerticalBoxLayout>(); - auto& right_side = type_tab.add<GUI::Widget>(); - right_side.set_layout<GUI::VerticalBoxLayout>(); - right_side.set_fixed_width(170); - - auto& type_list = left_side.add<GUI::ListView>(); - type_list.set_model(*GUI::ItemListModel<String>::create(g_types)); - type_list.set_should_hide_unnecessary_scrollbars(true); - type_list.on_selection = [&](auto& index) { - if (!index.is_valid()) { - m_type = nullptr; - return; - } - - m_type = CellType::get_by_name(g_types.at(index.row())); - }; - - { - auto& checkbox = right_side.add<GUI::CheckBox>("Override max length"); - auto& spinbox = right_side.add<GUI::SpinBox>(); - checkbox.set_checked(m_length != -1); - spinbox.set_min(0); - spinbox.set_enabled(m_length != -1); - if (m_length > -1) - spinbox.set_value(m_length); - - checkbox.on_checked = [&](auto checked) { - spinbox.set_enabled(checked); - if (!checked) { - m_length = -1; - spinbox.set_value(0); - } - }; - spinbox.on_change = [&](auto value) { - m_length = value; - }; - } - { - auto& checkbox = right_side.add<GUI::CheckBox>("Override display format"); - auto& editor = right_side.add<GUI::TextEditor>(); - checkbox.set_checked(!m_format.is_empty()); - editor.set_should_hide_unnecessary_scrollbars(true); - editor.set_enabled(!m_format.is_empty()); - editor.set_text(m_format); - - checkbox.on_checked = [&](auto checked) { - editor.set_enabled(checked); - if (!checked) - m_format = String::empty(); - editor.set_text(m_format); - }; - editor.on_change = [&] { - m_format = editor.text(); - }; - } - } - - auto& alignment_tab = tabs.add_tab<GUI::Widget>("Alignment"); - alignment_tab.set_layout<GUI::VerticalBoxLayout>().set_margins({ 4, 4, 4, 4 }); - { - // FIXME: Frame? - // Horizontal alignment - { - auto& horizontal_alignment_selection_container = alignment_tab.add<GUI::Widget>(); - horizontal_alignment_selection_container.set_layout<GUI::HorizontalBoxLayout>(); - horizontal_alignment_selection_container.layout()->set_margins({ 0, 4, 0, 0 }); - horizontal_alignment_selection_container.set_fixed_height(22); - - auto& horizontal_alignment_label = horizontal_alignment_selection_container.add<GUI::Label>(); - horizontal_alignment_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - horizontal_alignment_label.set_text("Horizontal text alignment"); - - auto& horizontal_combobox = alignment_tab.add<GUI::ComboBox>(); - horizontal_combobox.set_only_allow_values_from_model(true); - horizontal_combobox.set_model(*GUI::ItemListModel<String>::create(g_horizontal_alignments)); - horizontal_combobox.set_selected_index((int)m_horizontal_alignment); - horizontal_combobox.on_change = [&](auto&, const GUI::ModelIndex& index) { - switch (index.row()) { - case 0: - m_horizontal_alignment = HorizontalAlignment::Left; - break; - case 1: - m_horizontal_alignment = HorizontalAlignment::Center; - break; - case 2: - m_horizontal_alignment = HorizontalAlignment::Right; - break; - default: - ASSERT_NOT_REACHED(); - } - }; - } - - // Vertical alignment - { - auto& vertical_alignment_container = alignment_tab.add<GUI::Widget>(); - vertical_alignment_container.set_layout<GUI::HorizontalBoxLayout>(); - vertical_alignment_container.layout()->set_margins({ 0, 4, 0, 0 }); - vertical_alignment_container.set_fixed_height(22); - - auto& vertical_alignment_label = vertical_alignment_container.add<GUI::Label>(); - vertical_alignment_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - vertical_alignment_label.set_text("Vertical text alignment"); - - auto& vertical_combobox = alignment_tab.add<GUI::ComboBox>(); - vertical_combobox.set_only_allow_values_from_model(true); - vertical_combobox.set_model(*GUI::ItemListModel<String>::create(g_vertical_alignments)); - vertical_combobox.set_selected_index((int)m_vertical_alignment); - vertical_combobox.on_change = [&](auto&, const GUI::ModelIndex& index) { - switch (index.row()) { - case 0: - m_vertical_alignment = VerticalAlignment::Top; - break; - case 1: - m_vertical_alignment = VerticalAlignment::Center; - break; - case 2: - m_vertical_alignment = VerticalAlignment::Bottom; - break; - default: - ASSERT_NOT_REACHED(); - } - }; - } - } - - auto& colors_tab = tabs.add_tab<GUI::Widget>("Color"); - colors_tab.set_layout<GUI::VerticalBoxLayout>().set_margins({ 4, 4, 4, 4 }); - { - // Static formatting - { - auto& static_formatting_container = colors_tab.add<GUI::Widget>(); - static_formatting_container.set_layout<GUI::VerticalBoxLayout>(); - static_formatting_container.set_shrink_to_fit(true); - - // Foreground - { - // FIXME: Somehow allow unsetting these. - auto& foreground_container = static_formatting_container.add<GUI::Widget>(); - foreground_container.set_layout<GUI::HorizontalBoxLayout>(); - foreground_container.layout()->set_margins({ 0, 4, 0, 0 }); - foreground_container.set_fixed_height(22); - - auto& foreground_label = foreground_container.add<GUI::Label>(); - foreground_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - foreground_label.set_text("Static foreground color"); - - auto& foreground_selector = foreground_container.add<GUI::ColorInput>(); - if (m_static_format.foreground_color.has_value()) - foreground_selector.set_color(m_static_format.foreground_color.value()); - foreground_selector.on_change = [&]() { - m_static_format.foreground_color = foreground_selector.color(); - }; - } - - // Background - { - // FIXME: Somehow allow unsetting these. - auto& background_container = static_formatting_container.add<GUI::Widget>(); - background_container.set_layout<GUI::HorizontalBoxLayout>(); - background_container.layout()->set_margins({ 0, 4, 0, 0 }); - background_container.set_fixed_height(22); - - auto& background_label = background_container.add<GUI::Label>(); - background_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - background_label.set_text("Static background color"); - - auto& background_selector = background_container.add<GUI::ColorInput>(); - if (m_static_format.background_color.has_value()) - background_selector.set_color(m_static_format.background_color.value()); - background_selector.on_change = [&]() { - m_static_format.background_color = background_selector.color(); - }; - } - } - } - - auto& conditional_fmt_tab = tabs.add_tab<GUI::Widget>("Conditional format"); - conditional_fmt_tab.load_from_gml(cond_fmt_gml); - { - auto& view = *conditional_fmt_tab.find_descendant_of_type_named<Spreadsheet::ConditionsView>("conditions_view"); - view.set_formats(&m_conditional_formats); - - auto& add_button = *conditional_fmt_tab.find_descendant_of_type_named<GUI::Button>("add_button"); - add_button.on_click = [&](auto) { - view.add_format(); - }; - - // FIXME: Disable this when empty. - auto& remove_button = *conditional_fmt_tab.find_descendant_of_type_named<GUI::Button>("remove_button"); - remove_button.on_click = [&](auto) { - view.remove_top(); - }; - } -} - -CellTypeMetadata CellTypeDialog::metadata() const -{ - CellTypeMetadata metadata; - metadata.format = m_format; - metadata.length = m_length; - metadata.static_format = m_static_format; - - switch (m_vertical_alignment) { - case VerticalAlignment::Top: - switch (m_horizontal_alignment) { - case HorizontalAlignment::Left: - metadata.alignment = Gfx::TextAlignment::TopLeft; - break; - case HorizontalAlignment::Center: - metadata.alignment = Gfx::TextAlignment::Center; // TopCenter? - break; - case HorizontalAlignment::Right: - metadata.alignment = Gfx::TextAlignment::TopRight; - break; - } - break; - case VerticalAlignment::Center: - switch (m_horizontal_alignment) { - case HorizontalAlignment::Left: - metadata.alignment = Gfx::TextAlignment::CenterLeft; - break; - case HorizontalAlignment::Center: - metadata.alignment = Gfx::TextAlignment::Center; - break; - case HorizontalAlignment::Right: - metadata.alignment = Gfx::TextAlignment::CenterRight; - break; - } - break; - case VerticalAlignment::Bottom: - switch (m_horizontal_alignment) { - case HorizontalAlignment::Left: - metadata.alignment = Gfx::TextAlignment::CenterLeft; // BottomLeft? - break; - case HorizontalAlignment::Center: - metadata.alignment = Gfx::TextAlignment::Center; - break; - case HorizontalAlignment::Right: - metadata.alignment = Gfx::TextAlignment::BottomRight; - break; - } - break; - } - - return metadata; -} - -ConditionView::ConditionView(ConditionalFormat& fmt) - : m_format(fmt) -{ - load_from_gml(cond_fmt_view_gml); - - auto& fg_input = *find_descendant_of_type_named<GUI::ColorInput>("foreground_input"); - auto& bg_input = *find_descendant_of_type_named<GUI::ColorInput>("background_input"); - auto& formula_editor = *find_descendant_of_type_named<GUI::TextEditor>("formula_editor"); - - if (m_format.foreground_color.has_value()) - fg_input.set_color(m_format.foreground_color.value()); - - if (m_format.background_color.has_value()) - bg_input.set_color(m_format.background_color.value()); - - formula_editor.set_text(m_format.condition); - - // FIXME: Allow unsetting these. - fg_input.on_change = [&] { - m_format.foreground_color = fg_input.color(); - }; - - bg_input.on_change = [&] { - m_format.background_color = bg_input.color(); - }; - - formula_editor.set_syntax_highlighter(make<GUI::JSSyntaxHighlighter>()); - formula_editor.set_should_hide_unnecessary_scrollbars(true); - formula_editor.set_font(&Gfx::FontDatabase::default_fixed_width_font()); - formula_editor.on_change = [&] { - m_format.condition = formula_editor.text(); - }; -} - -ConditionView::~ConditionView() -{ -} - -ConditionsView::ConditionsView() -{ - set_layout<GUI::VerticalBoxLayout>().set_spacing(2); -} - -void ConditionsView::set_formats(Vector<ConditionalFormat>* formats) -{ - ASSERT(!m_formats); - - m_formats = formats; - - for (auto& entry : *m_formats) - m_widgets.append(add<ConditionView>(entry)); -} - -void ConditionsView::add_format() -{ - ASSERT(m_formats); - - m_formats->empend(); - auto& last = m_formats->last(); - - m_widgets.append(add<ConditionView>(last)); - - update(); -} - -void ConditionsView::remove_top() -{ - ASSERT(m_formats); - - if (m_formats->is_empty()) - return; - - m_formats->take_last(); - m_widgets.take_last()->remove_from_parent(); - update(); -} - -ConditionsView::~ConditionsView() -{ -} - -} diff --git a/Applications/Spreadsheet/CellTypeDialog.h b/Applications/Spreadsheet/CellTypeDialog.h deleted file mode 100644 index ede19ebd51..0000000000 --- a/Applications/Spreadsheet/CellTypeDialog.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "CellType/Type.h" -#include "ConditionalFormatting.h" -#include "Forward.h" -#include <LibGUI/Dialog.h> - -namespace Spreadsheet { - -class CellTypeDialog : public GUI::Dialog { - C_OBJECT(CellTypeDialog); - -public: - CellTypeMetadata metadata() const; - const CellType* type() const { return m_type; } - Vector<ConditionalFormat> conditional_formats() { return m_conditional_formats; } - - enum class HorizontalAlignment : int { - Left = 0, - Center, - Right, - }; - enum class VerticalAlignment : int { - Top = 0, - Center, - Bottom, - }; - -private: - CellTypeDialog(const Vector<Position>&, Sheet&, GUI::Window* parent = nullptr); - void setup_tabs(GUI::TabWidget&, const Vector<Position>&, Sheet&); - - const CellType* m_type { nullptr }; - - int m_length { -1 }; - String m_format; - HorizontalAlignment m_horizontal_alignment { HorizontalAlignment::Right }; - VerticalAlignment m_vertical_alignment { VerticalAlignment::Center }; - Format m_static_format; - Vector<ConditionalFormat> m_conditional_formats; -}; - -} diff --git a/Applications/Spreadsheet/CondFormatting.gml b/Applications/Spreadsheet/CondFormatting.gml deleted file mode 100644 index 437b5a1c3d..0000000000 --- a/Applications/Spreadsheet/CondFormatting.gml +++ /dev/null @@ -1,40 +0,0 @@ -@GUI::Widget { - name: "main" - fill_with_background_color: true - - layout: @GUI::VerticalBoxLayout { - margins: [4, 4, 4, 4] - spacing: 4 - } - - @Spreadsheet::ConditionsView { - name: "conditions_view" - } - - @GUI::Widget { - shrink_to_fit: true - - layout: @GUI::HorizontalBoxLayout { - spacing: 10 - } - - @GUI::Widget { - } - - @GUI::Button { - name: "add_button" - text: "Add" - fixed_width: 70 - } - - @GUI::Button { - name: "remove_button" - text: "Remove" - fixed_width: 70 - } - - @GUI::Widget { - } - - } -} diff --git a/Applications/Spreadsheet/CondView.gml b/Applications/Spreadsheet/CondView.gml deleted file mode 100644 index d2161da89f..0000000000 --- a/Applications/Spreadsheet/CondView.gml +++ /dev/null @@ -1,54 +0,0 @@ -@GUI::Widget { - layout: @GUI::VerticalBoxLayout { - } - - @GUI::Widget { - shrink_to_fit: true - - layout: @GUI::HorizontalBoxLayout { - } - - @GUI::Label { - text: "if..." - fixed_width: 40 - } - - @GUI::TextEditor { - name: "formula_editor" - fixed_height: 25 - tooltip: "Use 'value' to refer to the current cell's value" - } - } - - @GUI::Widget { - shrink_to_fit: true - - layout: @GUI::HorizontalBoxLayout { - } - - @GUI::Label { - text: "Foreground..." - fixed_width: 150 - } - - @GUI::ColorInput { - name: "foreground_input" - } - } - - @GUI::Widget { - shrink_to_fit: true - - layout: @GUI::HorizontalBoxLayout { - } - - @GUI::Label { - text: "Background..." - fixed_width: 150 - } - - @GUI::ColorInput { - name: "background_input" - } - } -} diff --git a/Applications/Spreadsheet/ConditionalFormatting.h b/Applications/Spreadsheet/ConditionalFormatting.h deleted file mode 100644 index 8e32bc8046..0000000000 --- a/Applications/Spreadsheet/ConditionalFormatting.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "Forward.h" -#include <AK/String.h> -#include <LibGUI/ScrollableWidget.h> -#include <LibGfx/Color.h> - -namespace Spreadsheet { - -struct Format { - Optional<Color> foreground_color; - Optional<Color> background_color; -}; - -struct ConditionalFormat : public Format { - String condition; -}; - -class ConditionView : public GUI::Widget { - C_OBJECT(ConditionView) -public: - virtual ~ConditionView() override; - -private: - ConditionView(ConditionalFormat&); - - ConditionalFormat& m_format; -}; - -class ConditionsView : public GUI::Widget { - C_OBJECT(ConditionsView) -public: - virtual ~ConditionsView() override; - - void set_formats(Vector<ConditionalFormat>*); - - void add_format(); - void remove_top(); - -private: - ConditionsView(); - - Vector<ConditionalFormat>* m_formats { nullptr }; - NonnullRefPtrVector<GUI::Widget> m_widgets; -}; - -} diff --git a/Applications/Spreadsheet/Forward.h b/Applications/Spreadsheet/Forward.h deleted file mode 100644 index 183c684972..0000000000 --- a/Applications/Spreadsheet/Forward.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 - -namespace Spreadsheet { - -class ConditionView; -class Sheet; -class SheetGlobalObject; -class Workbook; -class WorkbookObject; -struct Cell; -struct ConditionalFormat; -struct Format; -struct Position; - -} diff --git a/Applications/Spreadsheet/HelpWindow.cpp b/Applications/Spreadsheet/HelpWindow.cpp deleted file mode 100644 index eecd0bbb45..0000000000 --- a/Applications/Spreadsheet/HelpWindow.cpp +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "HelpWindow.h" -#include "SpreadsheetWidget.h" -#include <AK/LexicalPath.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Frame.h> -#include <LibGUI/ListView.h> -#include <LibGUI/MessageBox.h> -#include <LibGUI/Model.h> -#include <LibGUI/Splitter.h> -#include <LibMarkdown/Document.h> -#include <LibWeb/Layout/Node.h> -#include <LibWeb/OutOfProcessWebView.h> - -namespace Spreadsheet { - -class HelpListModel final : public GUI::Model { -public: - static NonnullRefPtr<HelpListModel> create() { return adopt(*new HelpListModel); } - - virtual ~HelpListModel() override { } - - virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return m_keys.size(); } - virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return 1; } - virtual void update() override { } - - virtual GUI::Variant data(const GUI::ModelIndex& index, GUI::ModelRole role = GUI::ModelRole::Display) const override - { - if (role == GUI::ModelRole::Display) { - return key(index); - } - - return {}; - } - - String key(const GUI::ModelIndex& index) const { return m_keys[index.row()]; } - - void set_from(const JsonObject& object) - { - m_keys.clear(); - object.for_each_member([this](auto& name, auto&) { - m_keys.append(name); - }); - did_update(); - } - -private: - HelpListModel() - { - } - - Vector<String> m_keys; -}; - -RefPtr<HelpWindow> HelpWindow::s_the { nullptr }; - -HelpWindow::HelpWindow(GUI::Window* parent) - : GUI::Window(parent) -{ - resize(530, 365); - set_title("Spreadsheet Functions Help"); - set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-help.png")); - - auto& widget = set_main_widget<GUI::Widget>(); - widget.set_layout<GUI::VerticalBoxLayout>(); - widget.set_fill_with_background_color(true); - - auto& splitter = widget.add<GUI::HorizontalSplitter>(); - auto& left_frame = splitter.add<GUI::Frame>(); - left_frame.set_layout<GUI::VerticalBoxLayout>(); - left_frame.set_fixed_width(100); - m_listview = left_frame.add<GUI::ListView>(); - m_listview->set_activates_on_selection(true); - m_listview->set_model(HelpListModel::create()); - - m_webview = splitter.add<Web::OutOfProcessWebView>(); - m_webview->on_link_click = [this](auto& url, auto&, auto&&) { - ASSERT(url.protocol() == "spreadsheet"); - if (url.host() == "example") { - auto entry = LexicalPath(url.path()).basename(); - auto doc_option = m_docs.get(entry); - if (!doc_option.is_object()) { - GUI::MessageBox::show_error(this, String::formatted("No documentation entry found for '{}'", url.path())); - return; - } - auto& doc = doc_option.as_object(); - const auto& name = url.fragment(); - - auto example_data_value = doc.get_or("example_data", JsonObject {}); - if (!example_data_value.is_object()) { - GUI::MessageBox::show_error(this, String::formatted("No example data found for '{}'", url.path())); - return; - } - - auto& example_data = example_data_value.as_object(); - auto value = example_data.get(name); - if (!value.is_object()) { - GUI::MessageBox::show_error(this, String::formatted("Example '{}' not found for '{}'", name, url.path())); - return; - } - - auto window = GUI::Window::construct(this); - window->resize(size()); - window->set_icon(icon()); - window->set_title(String::formatted("Spreadsheet Help - Example {} for {}", name, entry)); - window->on_close = [window = window.ptr()] { window->remove_from_parent(); }; - - auto& widget = window->set_main_widget<SpreadsheetWidget>(NonnullRefPtrVector<Sheet> {}, false); - auto sheet = Sheet::from_json(value.as_object(), widget.workbook()); - if (!sheet) { - GUI::MessageBox::show_error(this, String::formatted("Corrupted example '{}' in '{}'", name, url.path())); - return; - } - - widget.add_sheet(sheet.release_nonnull()); - window->show(); - } else if (url.host() == "doc") { - auto entry = LexicalPath(url.path()).basename(); - m_webview->load(URL::create_with_data("text/html", render(entry))); - } else { - dbgln("Invalid spreadsheet action domain '{}'", url.host()); - } - }; - - m_listview->on_activation = [this](auto& index) { - if (!m_webview) - return; - - auto key = static_cast<HelpListModel*>(m_listview->model())->key(index); - m_webview->load(URL::create_with_data("text/html", render(key))); - }; -} - -String HelpWindow::render(const StringView& key) -{ - auto doc_option = m_docs.get(key); - ASSERT(doc_option.is_object()); - - auto& doc = doc_option.as_object(); - - auto name = doc.get("name").to_string(); - auto argc = doc.get("argc").to_u32(0); - auto argnames_value = doc.get("argnames"); - ASSERT(argnames_value.is_array()); - auto& argnames = argnames_value.as_array(); - - auto docstring = doc.get("doc").to_string(); - auto examples_value = doc.get_or("examples", JsonObject {}); - ASSERT(examples_value.is_object()); - auto& examples = examples_value.as_object(); - - StringBuilder markdown_builder; - - markdown_builder.append("# NAME\n`"); - markdown_builder.append(name); - markdown_builder.append("`\n\n"); - - markdown_builder.append("# ARGUMENTS\n"); - if (argc > 0) - markdown_builder.appendff("{} required argument(s):\n", argc); - else - markdown_builder.appendf("No required arguments.\n"); - - for (size_t i = 0; i < argc; ++i) - markdown_builder.appendff("- `{}`\n", argnames.at(i).to_string()); - - if (argc > 0) - markdown_builder.append("\n"); - - if ((size_t)argnames.size() > argc) { - auto opt_count = argnames.size() - argc; - markdown_builder.appendff("{} optional argument(s):\n", opt_count); - for (size_t i = argc; i < (size_t)argnames.size(); ++i) - markdown_builder.appendff("- `{}`\n", argnames.at(i).to_string()); - markdown_builder.append("\n"); - } - - markdown_builder.append("# DESCRIPTION\n"); - markdown_builder.append(docstring); - markdown_builder.append("\n\n"); - - if (!examples.is_empty()) { - markdown_builder.append("# EXAMPLES\n"); - examples.for_each_member([&](auto& text, auto& description_value) { - dbgln("- {}\n\n```js\n{}\n```\n", description_value.to_string(), text); - markdown_builder.appendff("- {}\n\n```js\n{}\n```\n", description_value.to_string(), text); - }); - } - - auto document = Markdown::Document::parse(markdown_builder.string_view()); - return document->render_to_html(); -} - -void HelpWindow::set_docs(JsonObject&& docs) -{ - m_docs = move(docs); - static_cast<HelpListModel*>(m_listview->model())->set_from(m_docs); - m_listview->update(); -} - -HelpWindow::~HelpWindow() -{ -} -} diff --git a/Applications/Spreadsheet/HelpWindow.h b/Applications/Spreadsheet/HelpWindow.h deleted file mode 100644 index 1c02d90eed..0000000000 --- a/Applications/Spreadsheet/HelpWindow.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 <AK/JsonObject.h> -#include <LibGUI/Dialog.h> -#include <LibGUI/Widget.h> -#include <LibGUI/Window.h> -#include <LibWeb/OutOfProcessWebView.h> - -namespace Spreadsheet { - -class HelpWindow : public GUI::Window { - C_OBJECT(HelpWindow); - -public: - static NonnullRefPtr<HelpWindow> the(GUI::Window* window) - { - if (s_the) - return *s_the; - - return *(s_the = adopt(*new HelpWindow(window))); - } - - virtual ~HelpWindow() override; - - void set_docs(JsonObject&& docs); - -private: - static RefPtr<HelpWindow> s_the; - String render(const StringView& key); - HelpWindow(GUI::Window* parent = nullptr); - - JsonObject m_docs; - RefPtr<Web::OutOfProcessWebView> m_webview; - RefPtr<GUI::ListView> m_listview; -}; - -} diff --git a/Applications/Spreadsheet/JSIntegration.cpp b/Applications/Spreadsheet/JSIntegration.cpp deleted file mode 100644 index 12a249593e..0000000000 --- a/Applications/Spreadsheet/JSIntegration.cpp +++ /dev/null @@ -1,444 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "JSIntegration.h" -#include "Spreadsheet.h" -#include "Workbook.h" -#include <LibJS/Lexer.h> -#include <LibJS/Runtime/Error.h> -#include <LibJS/Runtime/GlobalObject.h> -#include <LibJS/Runtime/Object.h> -#include <LibJS/Runtime/Value.h> - -namespace Spreadsheet { - -Optional<FunctionAndArgumentIndex> get_function_and_argument_index(StringView source) -{ - JS::Lexer lexer { source }; - // Track <identifier> <OpenParen>'s, and how many complete expressions are inside the parenthesised expression. - Vector<size_t> state; - StringView last_name; - Vector<StringView> names; - size_t open_parens_since_last_commit = 0; - size_t open_curlies_and_brackets_since_last_commit = 0; - bool previous_was_identifier = false; - auto token = lexer.next(); - while (token.type() != JS::TokenType::Eof) { - switch (token.type()) { - case JS::TokenType::Identifier: - previous_was_identifier = true; - last_name = token.value(); - break; - case JS::TokenType::ParenOpen: - if (!previous_was_identifier) { - open_parens_since_last_commit++; - break; - } - previous_was_identifier = false; - state.append(0); - names.append(last_name); - break; - case JS::TokenType::ParenClose: - previous_was_identifier = false; - if (open_parens_since_last_commit == 0) { - state.take_last(); - names.take_last(); - break; - } - --open_parens_since_last_commit; - break; - case JS::TokenType::Comma: - previous_was_identifier = false; - if (open_parens_since_last_commit == 0 && open_curlies_and_brackets_since_last_commit == 0) { - state.last()++; - break; - } - break; - case JS::TokenType::BracketOpen: - previous_was_identifier = false; - open_curlies_and_brackets_since_last_commit++; - break; - case JS::TokenType::BracketClose: - previous_was_identifier = false; - if (open_curlies_and_brackets_since_last_commit > 0) - open_curlies_and_brackets_since_last_commit--; - break; - case JS::TokenType::CurlyOpen: - previous_was_identifier = false; - open_curlies_and_brackets_since_last_commit++; - break; - case JS::TokenType::CurlyClose: - previous_was_identifier = false; - if (open_curlies_and_brackets_since_last_commit > 0) - open_curlies_and_brackets_since_last_commit--; - break; - default: - previous_was_identifier = false; - break; - } - - token = lexer.next(); - } - if (!names.is_empty() && !state.is_empty()) - return FunctionAndArgumentIndex { names.last(), state.last() }; - return {}; -} - -SheetGlobalObject::SheetGlobalObject(Sheet& sheet) - : m_sheet(sheet) -{ -} - -SheetGlobalObject::~SheetGlobalObject() -{ -} - -JS::Value SheetGlobalObject::get(const JS::PropertyName& name, JS::Value receiver) const -{ - if (name.is_string()) { - if (name.as_string() == "value") { - if (auto cell = m_sheet.current_evaluated_cell()) - return cell->js_data(); - - return JS::js_undefined(); - } - if (auto pos = Sheet::parse_cell_name(name.as_string()); pos.has_value()) { - auto& cell = m_sheet.ensure(pos.value()); - cell.reference_from(m_sheet.current_evaluated_cell()); - return cell.typed_js_data(); - } - } - - return GlobalObject::get(name, receiver); -} - -bool SheetGlobalObject::put(const JS::PropertyName& name, JS::Value value, JS::Value receiver) -{ - if (name.is_string()) { - if (auto pos = Sheet::parse_cell_name(name.as_string()); pos.has_value()) { - auto& cell = m_sheet.ensure(pos.value()); - if (auto current = m_sheet.current_evaluated_cell()) - current->reference_from(&cell); - - cell.set_data(value); // FIXME: This produces un-savable state! - return true; - } - } - - return GlobalObject::put(name, value, receiver); -} - -void SheetGlobalObject::initialize() -{ - GlobalObject::initialize(); - define_native_function("get_real_cell_contents", get_real_cell_contents, 1); - define_native_function("set_real_cell_contents", set_real_cell_contents, 2); - define_native_function("parse_cell_name", parse_cell_name, 1); - define_native_function("current_cell_position", current_cell_position, 0); - define_native_function("column_arithmetic", column_arithmetic, 2); - define_native_function("column_index", column_index, 1); -} - -void SheetGlobalObject::visit_edges(Visitor& visitor) -{ - GlobalObject::visit_edges(visitor); - for (auto& it : m_sheet.cells()) { - if (it.value->exception()) - visitor.visit(it.value->exception()); - visitor.visit(it.value->evaluated_data()); - } -} - -JS_DEFINE_NATIVE_FUNCTION(SheetGlobalObject::get_real_cell_contents) -{ - auto* this_object = vm.this_value(global_object).to_object(global_object); - if (!this_object) - return JS::js_null(); - - if (StringView("SheetGlobalObject") != this_object->class_name()) { - vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "SheetGlobalObject"); - return {}; - } - - auto sheet_object = static_cast<SheetGlobalObject*>(this_object); - - if (vm.argument_count() != 1) { - vm.throw_exception<JS::TypeError>(global_object, "Expected exactly one argument to get_real_cell_contents()"); - return {}; - } - - auto name_value = vm.argument(0); - if (!name_value.is_string()) { - vm.throw_exception<JS::TypeError>(global_object, "Expected a String argument to get_real_cell_contents()"); - return {}; - } - auto position = Sheet::parse_cell_name(name_value.as_string().string()); - if (!position.has_value()) { - vm.throw_exception<JS::TypeError>(global_object, "Invalid cell name"); - return {}; - } - - const auto* cell = sheet_object->m_sheet.at(position.value()); - if (!cell) - return JS::js_undefined(); - - if (cell->kind() == Spreadsheet::Cell::Kind::Formula) - return JS::js_string(vm.heap(), String::formatted("={}", cell->data())); - - return JS::js_string(vm.heap(), cell->data()); -} - -JS_DEFINE_NATIVE_FUNCTION(SheetGlobalObject::set_real_cell_contents) -{ - auto* this_object = vm.this_value(global_object).to_object(global_object); - if (!this_object) - return JS::js_null(); - - if (StringView("SheetGlobalObject") != this_object->class_name()) { - vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "SheetGlobalObject"); - return {}; - } - - auto sheet_object = static_cast<SheetGlobalObject*>(this_object); - - if (vm.argument_count() != 2) { - vm.throw_exception<JS::TypeError>(global_object, "Expected exactly two arguments to set_real_cell_contents()"); - return {}; - } - - auto name_value = vm.argument(0); - if (!name_value.is_string()) { - vm.throw_exception<JS::TypeError>(global_object, "Expected the first argument of set_real_cell_contents() to be a String"); - return {}; - } - auto position = Sheet::parse_cell_name(name_value.as_string().string()); - if (!position.has_value()) { - vm.throw_exception<JS::TypeError>(global_object, "Invalid cell name"); - return {}; - } - - auto new_contents_value = vm.argument(1); - if (!new_contents_value.is_string()) { - vm.throw_exception<JS::TypeError>(global_object, "Expected the second argument of set_real_cell_contents() to be a String"); - return {}; - } - - auto& cell = sheet_object->m_sheet.ensure(position.value()); - auto& new_contents = new_contents_value.as_string().string(); - cell.set_data(new_contents); - return JS::js_null(); -} - -JS_DEFINE_NATIVE_FUNCTION(SheetGlobalObject::parse_cell_name) -{ - if (vm.argument_count() != 1) { - vm.throw_exception<JS::TypeError>(global_object, "Expected exactly one argument to parse_cell_name()"); - return {}; - } - auto name_value = vm.argument(0); - if (!name_value.is_string()) { - vm.throw_exception<JS::TypeError>(global_object, "Expected a String argument to parse_cell_name()"); - return {}; - } - auto position = Sheet::parse_cell_name(name_value.as_string().string()); - if (!position.has_value()) - return JS::js_undefined(); - - auto object = JS::Object::create_empty(global_object); - object->put("column", JS::js_string(vm, position.value().column)); - object->put("row", JS::Value((unsigned)position.value().row)); - - return object; -} - -JS_DEFINE_NATIVE_FUNCTION(SheetGlobalObject::current_cell_position) -{ - if (vm.argument_count() != 0) { - vm.throw_exception<JS::TypeError>(global_object, "Expected no arguments to current_cell_position()"); - return {}; - } - - auto* this_object = vm.this_value(global_object).to_object(global_object); - if (!this_object) - return JS::js_null(); - - if (StringView("SheetGlobalObject") != this_object->class_name()) { - vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "SheetGlobalObject"); - return {}; - } - - auto sheet_object = static_cast<SheetGlobalObject*>(this_object); - auto* current_cell = sheet_object->m_sheet.current_evaluated_cell(); - if (!current_cell) - return JS::js_null(); - - auto position = current_cell->position(); - - auto object = JS::Object::create_empty(global_object); - object->put("column", JS::js_string(vm, position.column)); - object->put("row", JS::Value((unsigned)position.row)); - - return object; -} - -JS_DEFINE_NATIVE_FUNCTION(SheetGlobalObject::column_index) -{ - if (vm.argument_count() != 1) { - vm.throw_exception<JS::TypeError>(global_object, "Expected exactly one argument to column_index()"); - return {}; - } - - auto column_name = vm.argument(0); - if (!column_name.is_string()) { - vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "String"); - return {}; - } - - auto& column_name_str = column_name.as_string().string(); - - auto* this_object = vm.this_value(global_object).to_object(global_object); - if (!this_object) - return JS::js_null(); - - if (StringView("SheetGlobalObject") != this_object->class_name()) { - vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "SheetGlobalObject"); - return {}; - } - - auto sheet_object = static_cast<SheetGlobalObject*>(this_object); - auto& sheet = sheet_object->m_sheet; - auto column_index = sheet.column_index(column_name_str); - if (!column_index.has_value()) { - vm.throw_exception(global_object, JS::TypeError::create(global_object, String::formatted("'{}' is not a valid column", column_name_str))); - return {}; - } - - return JS::Value((i32)column_index.value()); -} - -JS_DEFINE_NATIVE_FUNCTION(SheetGlobalObject::column_arithmetic) -{ - if (vm.argument_count() != 2) { - vm.throw_exception<JS::TypeError>(global_object, "Expected exactly two arguments to column_arithmetic()"); - return {}; - } - - auto column_name = vm.argument(0); - if (!column_name.is_string()) { - vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "String"); - return {}; - } - - auto& column_name_str = column_name.as_string().string(); - - auto offset = vm.argument(1).to_number(global_object); - if (!offset.is_number()) - return {}; - - auto offset_number = offset.as_i32(); - - auto* this_object = vm.this_value(global_object).to_object(global_object); - if (!this_object) - return JS::js_null(); - - if (StringView("SheetGlobalObject") != this_object->class_name()) { - vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "SheetGlobalObject"); - return {}; - } - - auto sheet_object = static_cast<SheetGlobalObject*>(this_object); - auto& sheet = sheet_object->m_sheet; - auto new_column = sheet.column_arithmetic(column_name_str, offset_number); - if (!new_column.has_value()) { - vm.throw_exception(global_object, JS::TypeError::create(global_object, String::formatted("'{}' is not a valid column", column_name_str))); - return {}; - } - - return JS::js_string(vm, new_column.release_value()); -} - -WorkbookObject::WorkbookObject(Workbook& workbook) - : JS::Object(*JS::Object::create_empty(workbook.global_object())) - , m_workbook(workbook) -{ -} - -WorkbookObject::~WorkbookObject() -{ -} - -void WorkbookObject::initialize(JS::GlobalObject& global_object) -{ - Object::initialize(global_object); - define_native_function("sheet", sheet, 1); -} - -void WorkbookObject::visit_edges(Visitor& visitor) -{ - Base::visit_edges(visitor); - for (auto& sheet : m_workbook.sheets()) - visitor.visit(&sheet.global_object()); -} - -JS_DEFINE_NATIVE_FUNCTION(WorkbookObject::sheet) -{ - if (vm.argument_count() != 1) { - vm.throw_exception<JS::TypeError>(global_object, "Expected exactly one argument to sheet()"); - return {}; - } - auto name_value = vm.argument(0); - if (!name_value.is_string() && !name_value.is_number()) { - vm.throw_exception<JS::TypeError>(global_object, "Expected a String or Number argument to sheet()"); - return {}; - } - - auto* this_object = vm.this_value(global_object).to_object(global_object); - if (!this_object) - return {}; - - if (!is<WorkbookObject>(this_object)) { - vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "WorkbookObject"); - return {}; - } - - auto& workbook = static_cast<WorkbookObject*>(this_object)->m_workbook; - - if (name_value.is_string()) { - auto& name = name_value.as_string().string(); - for (auto& sheet : workbook.sheets()) { - if (sheet.name() == name) - return JS::Value(&sheet.global_object()); - } - } else { - auto index = name_value.as_size_t(); - if (index < workbook.sheets().size()) - return JS::Value(&workbook.sheets()[index].global_object()); - } - - return JS::js_undefined(); -} - -} diff --git a/Applications/Spreadsheet/JSIntegration.h b/Applications/Spreadsheet/JSIntegration.h deleted file mode 100644 index 88f46d929d..0000000000 --- a/Applications/Spreadsheet/JSIntegration.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "Forward.h" -#include <LibJS/Forward.h> -#include <LibJS/Runtime/GlobalObject.h> - -namespace Spreadsheet { - -struct FunctionAndArgumentIndex { - String function_name; - size_t argument_index { 0 }; -}; -Optional<FunctionAndArgumentIndex> get_function_and_argument_index(StringView source); - -class SheetGlobalObject final : public JS::GlobalObject { - JS_OBJECT(SheetGlobalObject, JS::GlobalObject); - -public: - SheetGlobalObject(Sheet&); - - virtual ~SheetGlobalObject() override; - - virtual JS::Value get(const JS::PropertyName&, JS::Value receiver = {}) const override; - virtual bool put(const JS::PropertyName&, JS::Value value, JS::Value receiver = {}) override; - virtual void initialize() override; - - JS_DECLARE_NATIVE_FUNCTION(get_real_cell_contents); - JS_DECLARE_NATIVE_FUNCTION(set_real_cell_contents); - JS_DECLARE_NATIVE_FUNCTION(parse_cell_name); - JS_DECLARE_NATIVE_FUNCTION(current_cell_position); - JS_DECLARE_NATIVE_FUNCTION(column_index); - JS_DECLARE_NATIVE_FUNCTION(column_arithmetic); - -private: - virtual void visit_edges(Visitor&) override; - Sheet& m_sheet; -}; - -class WorkbookObject final : public JS::Object { - JS_OBJECT(WorkbookObject, JS::Object); - -public: - WorkbookObject(Workbook&); - - virtual ~WorkbookObject() override; - - virtual void initialize(JS::GlobalObject&) override; - - JS_DECLARE_NATIVE_FUNCTION(sheet); - -private: - virtual void visit_edges(Visitor&) override; - Workbook& m_workbook; -}; - -} diff --git a/Applications/Spreadsheet/Position.h b/Applications/Spreadsheet/Position.h deleted file mode 100644 index af7d7f70a9..0000000000 --- a/Applications/Spreadsheet/Position.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 <AK/String.h> -#include <AK/Types.h> -#include <AK/URL.h> - -namespace Spreadsheet { - -struct Position { - String column; - size_t row { 0 }; - - bool operator==(const Position& other) const - { - return row == other.row && column == other.column; - } - - bool operator!=(const Position& other) const - { - return !(other == *this); - } - - URL to_url() const - { - URL url; - url.set_protocol("spreadsheet"); - url.set_host("cell"); - url.set_path(String::formatted("/{}", getpid())); - url.set_fragment(String::formatted("{}{}", column, row)); - return url; - } -}; - -} diff --git a/Applications/Spreadsheet/Readers/CSV.h b/Applications/Spreadsheet/Readers/CSV.h deleted file mode 100644 index 866ae67141..0000000000 --- a/Applications/Spreadsheet/Readers/CSV.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "XSV.h" -#include <AK/Forward.h> -#include <AK/StringView.h> - -namespace Reader { - -class CSV : public XSV { -public: - CSV(StringView source, ParserBehaviour behaviours = default_behaviours()) - : XSV(source, { ",", "\"", ParserTraits::Repeat }, behaviours) - { - } -}; - -} diff --git a/Applications/Spreadsheet/Readers/Test/TestXSV.cpp b/Applications/Spreadsheet/Readers/Test/TestXSV.cpp deleted file mode 100644 index b80093d556..0000000000 --- a/Applications/Spreadsheet/Readers/Test/TestXSV.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 <AK/TestSuite.h> - -#include "../CSV.h" -#include "../XSV.h" -#include <LibCore/File.h> - -TEST_CASE(should_parse_valid_data) -{ - { - auto data = R"~~~(Foo, Bar, Baz - 1, 2, 3 - 4, 5, 6 - """x", y"z, 9)~~~"; - auto csv = Reader::CSV { data, Reader::default_behaviours() | Reader::ParserBehaviour::ReadHeaders | Reader::ParserBehaviour::TrimLeadingFieldSpaces }; - EXPECT(!csv.has_error()); - - EXPECT_EQ(csv[0]["Foo"], "1"); - EXPECT_EQ(csv[2]["Foo"], "\"x"); - EXPECT_EQ(csv[2]["Bar"], "y\"z"); - } - - { - auto data = R"~~~(Foo, Bar, Baz - 1 , 2, 3 - 4, "5 " , 6 - """x", y"z, 9 )~~~"; - auto csv = Reader::CSV { data, Reader::default_behaviours() | Reader::ParserBehaviour::ReadHeaders | Reader::ParserBehaviour::TrimLeadingFieldSpaces | Reader::ParserBehaviour::TrimTrailingFieldSpaces }; - EXPECT(!csv.has_error()); - - EXPECT_EQ(csv[0]["Foo"], "1"); - EXPECT_EQ(csv[1]["Bar"], "5 "); - EXPECT_EQ(csv[2]["Foo"], "\"x"); - EXPECT_EQ(csv[2]["Baz"], "9"); - } -} - -TEST_CASE(should_fail_nicely) -{ - { - auto data = R"~~~(Foo, Bar, Baz - x, y)~~~"; - auto csv = Reader::CSV { data, Reader::default_behaviours() | Reader::ParserBehaviour::ReadHeaders | Reader::ParserBehaviour::TrimLeadingFieldSpaces }; - EXPECT(csv.has_error()); - EXPECT_EQ(csv.error(), Reader::ReadError::NonConformingColumnCount); - } - - { - auto data = R"~~~(Foo, Bar, Baz - x, y, "z)~~~"; - auto csv = Reader::CSV { data, Reader::default_behaviours() | Reader::ParserBehaviour::ReadHeaders | Reader::ParserBehaviour::TrimLeadingFieldSpaces }; - EXPECT(csv.has_error()); - EXPECT_EQ(csv.error(), Reader::ReadError::QuoteFailure); - } -} - -TEST_CASE(should_iterate_rows) -{ - auto data = R"~~~(Foo, Bar, Baz - 1, 2, 3 - 4, 5, 6 - """x", y"z, 9)~~~"; - auto csv = Reader::CSV { data, Reader::default_behaviours() | Reader::ParserBehaviour::ReadHeaders | Reader::ParserBehaviour::TrimLeadingFieldSpaces }; - EXPECT(!csv.has_error()); - - bool ran = false; - for (auto row : csv) - ran = !row[0].is_empty(); - - EXPECT(ran); -} - -BENCHMARK_CASE(fairly_big_data) -{ - auto file_or_error = Core::File::open(__FILE__ ".data", Core::IODevice::OpenMode::ReadOnly); - EXPECT_EQ_FORCE(file_or_error.is_error(), false); - - auto data = file_or_error.value()->read_all(); - auto csv = Reader::CSV { data, Reader::default_behaviours() | Reader::ParserBehaviour::ReadHeaders }; - - EXPECT(!csv.has_error()); - EXPECT_EQ(csv.size(), 100000u); -} - -TEST_MAIN(XSV) diff --git a/Applications/Spreadsheet/Readers/XSV.cpp b/Applications/Spreadsheet/Readers/XSV.cpp deleted file mode 100644 index 99a61b0abc..0000000000 --- a/Applications/Spreadsheet/Readers/XSV.cpp +++ /dev/null @@ -1,272 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "XSV.h" -#include <AK/StringBuilder.h> - -namespace Reader { - -ParserBehaviour operator&(ParserBehaviour left, ParserBehaviour right) -{ - return static_cast<ParserBehaviour>(static_cast<u32>(left) & static_cast<u32>(right)); -} - -ParserBehaviour operator|(ParserBehaviour left, ParserBehaviour right) -{ - return static_cast<ParserBehaviour>(static_cast<u32>(left) | static_cast<u32>(right)); -} - -void XSV::set_error(ReadError error) -{ - if (m_error == ReadError::None) - m_error = error; -} - -Vector<String> XSV::headers() const -{ - Vector<String> headers; - for (auto& field : m_names) - headers.append(field.is_string_view ? field.as_string_view : field.as_string.view()); - - return headers; -} - -void XSV::parse() -{ - if ((m_behaviours & ParserBehaviour::ReadHeaders) != ParserBehaviour::None) - read_headers(); - - while (!has_error() && !m_lexer.is_eof()) - m_rows.append(read_row()); - - if (!m_lexer.is_eof()) - set_error(ReadError::DataPastLogicalEnd); -} - -void XSV::read_headers() -{ - if (!m_names.is_empty()) { - set_error(ReadError::InternalError); - m_names.clear(); - } - - m_names = read_row(true); -} - -Vector<XSV::Field> XSV::read_row(bool header_row) -{ - Vector<Field> row; - bool first = true; - while (!(m_lexer.is_eof() || m_lexer.next_is('\n') || m_lexer.next_is("\r\n")) && (first || m_lexer.consume_specific(m_traits.separator))) { - first = false; - row.append(read_one_field()); - } - - if (!m_lexer.is_eof()) { - auto crlf_ok = m_lexer.consume_specific("\r\n"); - if (!crlf_ok) { - auto lf_ok = m_lexer.consume_specific('\n'); - if (!lf_ok) - set_error(ReadError::DataPastLogicalEnd); - } - } - - if (!header_row && (m_behaviours & ParserBehaviour::ReadHeaders) != ParserBehaviour::None && row.size() != m_names.size()) - set_error(ReadError::NonConformingColumnCount); - - return row; -} - -XSV::Field XSV::read_one_field() -{ - if ((m_behaviours & ParserBehaviour::TrimLeadingFieldSpaces) != ParserBehaviour::None) - m_lexer.consume_while(is_any_of(" \t\v")); - - bool is_quoted = false; - Field field; - if (m_lexer.next_is(m_traits.quote.view())) { - is_quoted = true; - field = read_one_quoted_field(); - } else { - field = read_one_unquoted_field(); - } - - if ((m_behaviours & ParserBehaviour::TrimTrailingFieldSpaces) != ParserBehaviour::None) { - m_lexer.consume_while(is_any_of(" \t\v")); - - if (!is_quoted) { - // Also have to trim trailing spaces from unquoted fields. - StringView view; - if (field.is_string_view) - view = field.as_string_view; - else - view = field.as_string; - - if (!view.is_empty()) { - ssize_t i = view.length() - 1; - for (; i >= 0; --i) { - if (!view.substring_view(i, 1).is_one_of(" ", "\t", "\v")) - break; - } - view = view.substring_view(0, i + 1); - } - - if (field.is_string_view) - field.as_string_view = view; - else - field.as_string = field.as_string.substring(0, view.length()); - } - } - - return field; -} - -XSV::Field XSV::read_one_quoted_field() -{ - if (!m_lexer.consume_specific(m_traits.quote)) - set_error(ReadError::InternalError); - - size_t start = m_lexer.tell(), end = start; - bool is_copy = false; - StringBuilder builder; - auto allow_newlines = (m_behaviours & ParserBehaviour::AllowNewlinesInFields) != ParserBehaviour::None; - - for (; !m_lexer.is_eof();) { - char ch; - switch (m_traits.quote_escape) { - case ParserTraits::Backslash: - if (m_lexer.consume_specific('\\') && m_lexer.consume_specific(m_traits.quote)) { - // If there is an escaped quote, we have no choice but to make a copy. - if (!is_copy) { - is_copy = true; - builder.append(m_source.substring_view(start, end - start)); - } - builder.append(m_traits.quote); - end = m_lexer.tell(); - continue; - } - break; - case ParserTraits::Repeat: - if (m_lexer.consume_specific(m_traits.quote)) { - if (m_lexer.consume_specific(m_traits.quote)) { - // If there is an escaped quote, we have no choice but to make a copy. - if (!is_copy) { - is_copy = true; - builder.append(m_source.substring_view(start, end - start)); - } - builder.append(m_traits.quote); - end = m_lexer.tell(); - continue; - } - for (size_t i = 0; i < m_traits.quote.length(); ++i) - m_lexer.retreat(); - goto end; - } - break; - } - - if (m_lexer.next_is(m_traits.quote.view())) - goto end; - - if (!allow_newlines) { - if (m_lexer.next_is('\n') || m_lexer.next_is("\r\n")) - goto end; - } - - ch = m_lexer.consume(); - if (is_copy) - builder.append(ch); - end = m_lexer.tell(); - continue; - - end: - break; - } - - if (!m_lexer.consume_specific(m_traits.quote)) - set_error(ReadError::QuoteFailure); - - if (is_copy) - return { {}, builder.to_string(), false }; - - return { m_source.substring_view(start, end - start), {}, true }; -} - -XSV::Field XSV::read_one_unquoted_field() -{ - size_t start = m_lexer.tell(), end = start; - bool allow_quote_in_field = (m_behaviours & ParserBehaviour::QuoteOnlyInFieldStart) != ParserBehaviour::None; - - for (; !m_lexer.is_eof();) { - if (m_lexer.next_is(m_traits.separator.view())) - break; - - if (m_lexer.next_is("\r\n") || m_lexer.next_is("\n")) - break; - - if (m_lexer.consume_specific(m_traits.quote)) { - if (!allow_quote_in_field) - set_error(ReadError::QuoteFailure); - end = m_lexer.tell(); - continue; - } - - m_lexer.consume(); - end = m_lexer.tell(); - } - - return { m_source.substring_view(start, end - start), {}, true }; -} - -StringView XSV::Row::operator[](StringView name) const -{ - ASSERT(!m_xsv.m_names.is_empty()); - auto it = m_xsv.m_names.find_if([&](const auto& entry) { return name == entry; }); - ASSERT(!it.is_end()); - - return (*this)[it.index()]; -} - -StringView XSV::Row::operator[](size_t column) const -{ - auto& field = m_xsv.m_rows[m_index][column]; - if (field.is_string_view) - return field.as_string_view; - return field.as_string; -} - -const XSV::Row XSV::operator[](size_t index) const -{ - return const_cast<XSV&>(*this)[index]; -} - -XSV::Row XSV::operator[](size_t index) -{ - ASSERT(m_rows.size() > index); - return Row { *this, index }; -} - -} diff --git a/Applications/Spreadsheet/Readers/XSV.h b/Applications/Spreadsheet/Readers/XSV.h deleted file mode 100644 index 0b32ca767d..0000000000 --- a/Applications/Spreadsheet/Readers/XSV.h +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 <AK/GenericLexer.h> -#include <AK/String.h> -#include <AK/StringView.h> -#include <AK/Types.h> -#include <AK/Vector.h> - -namespace Reader { - -enum class ParserBehaviour : u32 { - None = 0, - ReadHeaders = 1, - AllowNewlinesInFields = ReadHeaders << 1, - TrimLeadingFieldSpaces = ReadHeaders << 2, - TrimTrailingFieldSpaces = ReadHeaders << 3, - QuoteOnlyInFieldStart = ReadHeaders << 4, -}; - -ParserBehaviour operator&(ParserBehaviour left, ParserBehaviour right); -ParserBehaviour operator|(ParserBehaviour left, ParserBehaviour right); - -struct ParserTraits { - String separator; - String quote { "\"" }; - enum { - Repeat, - Backslash, - } quote_escape { Repeat }; -}; - -#define ENUMERATE_READ_ERRORS() \ - E(None, "No errors") \ - E(NonConformingColumnCount, "Header count does not match given column count") \ - E(QuoteFailure, "Quoting failure") \ - E(InternalError, "Internal error") \ - E(DataPastLogicalEnd, "Exrta data past the logical end of the rows") - -enum class ReadError { -#define E(name, _) name, - ENUMERATE_READ_ERRORS() -#undef E -}; - -inline constexpr ParserBehaviour default_behaviours() -{ - return ParserBehaviour::QuoteOnlyInFieldStart; -} - -class XSV { -public: - XSV(StringView source, const ParserTraits& traits, ParserBehaviour behaviours = default_behaviours()) - : m_source(source) - , m_lexer(m_source) - , m_traits(traits) - , m_behaviours(behaviours) - { - parse(); - } - - virtual ~XSV() { } - - bool has_error() const { return m_error != ReadError::None; } - ReadError error() const { return m_error; } - String error_string() const - { - switch (m_error) { -#define E(x, y) \ - case ReadError::x: \ - return y; - - ENUMERATE_READ_ERRORS(); -#undef E - } - ASSERT_NOT_REACHED(); - } - - size_t size() const { return m_rows.size(); } - Vector<String> headers() const; - - class Row { - public: - explicit Row(XSV& xsv, size_t index) - : m_xsv(xsv) - , m_index(index) - { - } - - StringView operator[](StringView name) const; - StringView operator[](size_t column) const; - - size_t index() const { return m_index; } - - // FIXME: Implement begin() and end(), keeping `Field' out of the API. - - private: - XSV& m_xsv; - size_t m_index { 0 }; - }; - - template<bool const_> - class RowIterator { - public: - explicit RowIterator(const XSV& xsv, size_t init_index = 0) requires(const_) - : m_xsv(const_cast<XSV&>(xsv)) - , m_index(init_index) - { - } - - explicit RowIterator(XSV& xsv, size_t init_index = 0) requires(!const_) - : m_xsv(xsv) - , m_index(init_index) - { - } - - Row operator*() const { return Row { m_xsv, m_index }; } - Row operator*() requires(!const_) { return Row { m_xsv, m_index }; } - - RowIterator& operator++() - { - ++m_index; - return *this; - } - - bool is_end() const { return m_index == m_xsv.m_rows.size(); } - bool operator==(const RowIterator& other) const - { - return m_index == other.m_index && &m_xsv == &other.m_xsv; - } - bool operator==(const RowIterator<!const_>& other) const - { - return m_index == other.m_index && &m_xsv == &other.m_xsv; - } - - private: - XSV& m_xsv; - size_t m_index { 0 }; - }; - - const Row operator[](size_t index) const; - Row operator[](size_t index); - - auto begin() { return RowIterator<false>(*this); } - auto end() { return RowIterator<false>(*this, m_rows.size()); } - - auto begin() const { return RowIterator<true>(*this); } - auto end() const { return RowIterator<true>(*this, m_rows.size()); } - - using ConstIterator = RowIterator<true>; - using Iterator = RowIterator<false>; - -private: - struct Field { - StringView as_string_view; - String as_string; // This member only used if the parser couldn't use the original source verbatim. - bool is_string_view { true }; - - bool operator==(StringView other) const - { - if (is_string_view) - return other == as_string_view; - return as_string == other; - } - }; - void set_error(ReadError error); - void parse(); - void read_headers(); - Vector<Field> read_row(bool header_row = false); - Field read_one_field(); - Field read_one_quoted_field(); - Field read_one_unquoted_field(); - - StringView m_source; - GenericLexer m_lexer; - const ParserTraits& m_traits; - ParserBehaviour m_behaviours; - Vector<Field> m_names; - Vector<Vector<Field>> m_rows; - ReadError m_error { ReadError::None }; -}; - -} diff --git a/Applications/Spreadsheet/Spreadsheet.cpp b/Applications/Spreadsheet/Spreadsheet.cpp deleted file mode 100644 index 0d20b22fdd..0000000000 --- a/Applications/Spreadsheet/Spreadsheet.cpp +++ /dev/null @@ -1,734 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "Spreadsheet.h" -#include "JSIntegration.h" -#include "Workbook.h" -#include <AK/ByteBuffer.h> -#include <AK/GenericLexer.h> -#include <AK/JsonArray.h> -#include <AK/JsonObject.h> -#include <AK/JsonParser.h> -#include <AK/ScopeGuard.h> -#include <AK/TemporaryChange.h> -#include <AK/URL.h> -#include <LibCore/File.h> -#include <LibJS/Parser.h> -#include <LibJS/Runtime/Function.h> -#include <ctype.h> - -//#define COPY_DEBUG - -namespace Spreadsheet { - -Sheet::Sheet(const StringView& name, Workbook& workbook) - : Sheet(workbook) -{ - m_name = name; - - for (size_t i = 0; i < default_row_count; ++i) - add_row(); - - for (size_t i = 0; i < default_column_count; ++i) - add_column(); -} - -Sheet::Sheet(Workbook& workbook) - : m_workbook(workbook) -{ - JS::DeferGC defer_gc(m_workbook.interpreter().heap()); - m_global_object = m_workbook.interpreter().heap().allocate_without_global_object<SheetGlobalObject>(*this); - global_object().initialize(); - global_object().put("workbook", m_workbook.workbook_object()); - global_object().put("thisSheet", &global_object()); // Self-reference is unfortunate, but required. - - // Sadly, these have to be evaluated once per sheet. - auto file_or_error = Core::File::open("/res/js/Spreadsheet/runtime.js", Core::IODevice::OpenMode::ReadOnly); - if (!file_or_error.is_error()) { - auto buffer = file_or_error.value()->read_all(); - JS::Parser parser { JS::Lexer(buffer) }; - if (parser.has_errors()) { - warnln("Spreadsheet: Failed to parse runtime code"); - parser.print_errors(); - } else { - interpreter().run(global_object(), parser.parse_program()); - if (auto exc = interpreter().exception()) { - warnln("Spreadsheet: Failed to run runtime code: "); - for (auto& t : exc->trace()) - warnln("{}", t); - interpreter().vm().clear_exception(); - } - } - } -} - -Sheet::~Sheet() -{ -} - -JS::Interpreter& Sheet::interpreter() const -{ - return m_workbook.interpreter(); -} - -size_t Sheet::add_row() -{ - return m_rows++; -} - -static String convert_to_string(size_t value, unsigned base = 26, StringView map = {}) -{ - if (map.is_null()) - map = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - - ASSERT(base >= 2 && base <= map.length()); - - // The '8 bits per byte' assumption may need to go? - Array<char, round_up_to_power_of_two(sizeof(size_t) * 8 + 1, 2)> buffer; - size_t i = 0; - do { - buffer[i++] = map[value % base]; - value /= base; - } while (value > 0); - - // NOTE: Weird as this may seem, the thing that comes after 'A' is 'AA', which as a number would be '00' - // to make this work, only the most significant digit has to be in a range of (1..25) as opposed to (0..25), - // but only if it's not the only digit in the string. - if (i > 1) - --buffer[i - 1]; - - for (size_t j = 0; j < i / 2; ++j) - swap(buffer[j], buffer[i - j - 1]); - - return String { ReadonlyBytes(buffer.data(), i) }; -} - -static size_t convert_from_string(StringView str, unsigned base = 26, StringView map = {}) -{ - if (map.is_null()) - map = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - - ASSERT(base >= 2 && base <= map.length()); - - size_t value = 0; - for (size_t i = str.length(); i > 0; --i) { - auto digit_value = map.find_first_of(str[i - 1]).value_or(0); - // NOTE: Refer to the note in `convert_to_string()'. - if (i == str.length() && str.length() > 1) - ++digit_value; - value = value * base + digit_value; - } - - return value; -} - -String Sheet::add_column() -{ - auto next_column = convert_to_string(m_columns.size()); - m_columns.append(next_column); - return next_column; -} - -void Sheet::update() -{ - if (m_should_ignore_updates) { - m_update_requested = true; - return; - } - m_visited_cells_in_update.clear(); - Vector<Cell*> cells_copy; - - // Grab a copy as updates might insert cells into the table. - for (auto& it : m_cells) { - if (it.value->dirty()) { - cells_copy.append(it.value); - m_workbook.set_dirty(true); - } - } - - for (auto& cell : cells_copy) - update(*cell); - - m_visited_cells_in_update.clear(); -} - -void Sheet::update(Cell& cell) -{ - if (m_should_ignore_updates) { - m_update_requested = true; - return; - } - if (cell.dirty()) { - if (has_been_visited(&cell)) { - // This may be part of an cyclic reference chain, - // so just ignore it. - cell.clear_dirty(); - return; - } - m_visited_cells_in_update.set(&cell); - cell.update_data({}); - } -} - -Sheet::ValueAndException Sheet::evaluate(const StringView& source, Cell* on_behalf_of) -{ - TemporaryChange cell_change { m_current_cell_being_evaluated, on_behalf_of }; - ScopeGuard clear_exception { [&] { interpreter().vm().clear_exception(); } }; - - auto parser = JS::Parser(JS::Lexer(source)); - if (parser.has_errors() || interpreter().exception()) - return { JS::js_undefined(), interpreter().exception() }; - - auto program = parser.parse_program(); - interpreter().run(global_object(), program); - if (interpreter().exception()) { - auto exc = interpreter().exception(); - return { JS::js_undefined(), exc }; - } - - auto value = interpreter().vm().last_value(); - if (value.is_empty()) - return { JS::js_undefined(), {} }; - return { value, {} }; -} - -Cell* Sheet::at(const StringView& name) -{ - auto pos = parse_cell_name(name); - if (pos.has_value()) - return at(pos.value()); - - return nullptr; -} - -Cell* Sheet::at(const Position& position) -{ - auto it = m_cells.find(position); - - if (it == m_cells.end()) - return nullptr; - - return it->value; -} - -Optional<Position> Sheet::parse_cell_name(const StringView& name) -{ - GenericLexer lexer(name); - auto col = lexer.consume_while(isalpha); - auto row = lexer.consume_while(isdigit); - - if (!lexer.is_eof() || row.is_empty() || col.is_empty()) - return {}; - - return Position { col, row.to_uint().value() }; -} - -Optional<size_t> Sheet::column_index(const StringView& column_name) const -{ - auto index = convert_from_string(column_name); - if (m_columns.size() <= index || m_columns[index] != column_name) - return {}; - - return index; -} - -Optional<String> Sheet::column_arithmetic(const StringView& column_name, int offset) -{ - auto maybe_index = column_index(column_name); - if (!maybe_index.has_value()) - return {}; - - if (offset < 0 && maybe_index.value() < (size_t)(0 - offset)) - return m_columns.first(); - - auto index = maybe_index.value() + offset; - if (m_columns.size() > index) - return m_columns[index]; - - for (size_t i = m_columns.size(); i <= index; ++i) - add_column(); - - return m_columns.last(); -} - -Cell* Sheet::from_url(const URL& url) -{ - auto maybe_position = position_from_url(url); - if (!maybe_position.has_value()) - return nullptr; - - return at(maybe_position.value()); -} - -Optional<Position> Sheet::position_from_url(const URL& url) const -{ - if (!url.is_valid()) { - dbgln("Invalid url: {}", url.to_string()); - return {}; - } - - if (url.protocol() != "spreadsheet" || url.host() != "cell") { - dbgln("Bad url: {}", url.to_string()); - return {}; - } - - // FIXME: Figure out a way to do this cross-process. - ASSERT(url.path() == String::formatted("/{}", getpid())); - - return parse_cell_name(url.fragment()); -} - -Position Sheet::offset_relative_to(const Position& base, const Position& offset, const Position& offset_base) const -{ - auto offset_column_it = m_columns.find(offset.column); - auto offset_base_column_it = m_columns.find(offset_base.column); - auto base_column_it = m_columns.find(base.column); - - if (offset_column_it.is_end()) { - dbgln("Column '{}' does not exist!", offset.column); - return base; - } - if (offset_base_column_it.is_end()) { - dbgln("Column '{}' does not exist!", offset.column); - return base; - } - if (base_column_it.is_end()) { - dbgln("Column '{}' does not exist!", offset.column); - return offset; - } - - auto new_column = column(offset_column_it.index() + base_column_it.index() - offset_base_column_it.index()); - auto new_row = offset.row + base.row - offset_base.row; - - return { move(new_column), new_row }; -} - -void Sheet::copy_cells(Vector<Position> from, Vector<Position> to, Optional<Position> resolve_relative_to) -{ - auto copy_to = [&](auto& source_position, Position target_position) { - auto& target_cell = ensure(target_position); - auto* source_cell = at(source_position); - - if (!source_cell) { - target_cell.set_data(""); - return; - } - - target_cell.copy_from(*source_cell); - }; - - if (from.size() == to.size()) { - auto from_it = from.begin(); - // FIXME: Ordering. - for (auto& position : to) - copy_to(*from_it++, position); - - return; - } - - if (to.size() == 1) { - // Resolve each index as relative to the first index offset from the selection. - auto& target = to.first(); - - for (auto& position : from) { -#ifdef COPY_DEBUG - dbg() << "Paste from '" << position.to_url() << "' to '" << target.to_url() << "'"; -#endif - copy_to(position, resolve_relative_to.has_value() ? offset_relative_to(target, position, resolve_relative_to.value()) : target); - } - - return; - } - - if (from.size() == 1) { - // Fill the target selection with the single cell. - auto& source = from.first(); - for (auto& position : to) { -#ifdef COPY_DEBUG - dbg() << "Paste from '" << source.to_url() << "' to '" << position.to_url() << "'"; -#endif - copy_to(source, resolve_relative_to.has_value() ? offset_relative_to(position, source, resolve_relative_to.value()) : position); - } - return; - } - - // Just disallow misaligned copies. - dbgln("Cannot copy {} cells to {} cells", from.size(), to.size()); -} - -RefPtr<Sheet> Sheet::from_json(const JsonObject& object, Workbook& workbook) -{ - auto sheet = adopt(*new Sheet(workbook)); - auto rows = object.get("rows").to_u32(default_row_count); - auto columns = object.get("columns"); - auto name = object.get("name").as_string_or("Sheet"); - - sheet->set_name(name); - - for (size_t i = 0; i < max(rows, (unsigned)Sheet::default_row_count); ++i) - sheet->add_row(); - - // FIXME: Better error checking. - if (columns.is_array()) { - columns.as_array().for_each([&](auto& value) { - sheet->m_columns.append(value.as_string()); - return IterationDecision::Continue; - }); - } - - if (sheet->m_columns.size() < default_column_count && sheet->columns_are_standard()) { - for (size_t i = sheet->m_columns.size(); i < default_column_count; ++i) - sheet->add_column(); - } - - auto cells = object.get("cells").as_object(); - auto json = sheet->interpreter().global_object().get("JSON"); - auto& parse_function = json.as_object().get("parse").as_function(); - - auto read_format = [](auto& format, const auto& obj) { - if (auto value = obj.get("foreground_color"); value.is_string()) - format.foreground_color = Color::from_string(value.as_string()); - if (auto value = obj.get("background_color"); value.is_string()) - format.background_color = Color::from_string(value.as_string()); - }; - - cells.for_each_member([&](auto& name, JsonValue& value) { - auto position_option = parse_cell_name(name); - if (!position_option.has_value()) - return IterationDecision::Continue; - - auto position = position_option.value(); - auto& obj = value.as_object(); - auto kind = obj.get("kind").as_string_or("LiteralString") == "LiteralString" ? Cell::LiteralString : Cell::Formula; - - OwnPtr<Cell> cell; - switch (kind) { - case Cell::LiteralString: - cell = make<Cell>(obj.get("value").to_string(), position, *sheet); - break; - case Cell::Formula: { - auto& interpreter = sheet->interpreter(); - auto value = interpreter.vm().call(parse_function, json, JS::js_string(interpreter.heap(), obj.get("value").as_string())); - cell = make<Cell>(obj.get("source").to_string(), move(value), position, *sheet); - break; - } - } - - auto type_name = obj.get_or("type", "Numeric").to_string(); - cell->set_type(type_name); - - auto type_meta = obj.get("type_metadata"); - if (type_meta.is_object()) { - auto& meta_obj = type_meta.as_object(); - auto meta = cell->type_metadata(); - if (auto value = meta_obj.get("length"); value.is_number()) - meta.length = value.to_i32(); - if (auto value = meta_obj.get("format"); value.is_string()) - meta.format = value.as_string(); - read_format(meta.static_format, meta_obj); - - cell->set_type_metadata(move(meta)); - } - - auto conditional_formats = obj.get("conditional_formats"); - auto cformats = cell->conditional_formats(); - if (conditional_formats.is_array()) { - conditional_formats.as_array().for_each([&](const auto& fmt_val) { - if (!fmt_val.is_object()) - return IterationDecision::Continue; - - auto& fmt_obj = fmt_val.as_object(); - auto fmt_cond = fmt_obj.get("condition").to_string(); - if (fmt_cond.is_empty()) - return IterationDecision::Continue; - - ConditionalFormat fmt; - fmt.condition = move(fmt_cond); - read_format(fmt, fmt_obj); - cformats.append(move(fmt)); - - return IterationDecision::Continue; - }); - cell->set_conditional_formats(move(cformats)); - } - - auto evaluated_format = obj.get("evaluated_formats"); - if (evaluated_format.is_object()) { - auto& evaluated_format_obj = evaluated_format.as_object(); - auto& evaluated_fmts = cell->evaluated_formats(); - - read_format(evaluated_fmts, evaluated_format_obj); - } - - sheet->m_cells.set(position, cell.release_nonnull()); - return IterationDecision::Continue; - }); - - return sheet; -} - -Position Sheet::written_data_bounds() const -{ - Position bound; - for (auto& entry : m_cells) { - if (entry.key.row >= bound.row) - bound.row = entry.key.row; - if (entry.key.column >= bound.column) - bound.column = entry.key.column; - } - - return bound; -} - -/// The sheet is allowed to have nonstandard column names -/// this checks whether all existing columns are 'standard' -/// (i.e. as generated by 'convert_to_string()' -bool Sheet::columns_are_standard() const -{ - for (size_t i = 0; i < m_columns.size(); ++i) { - if (m_columns[i] != convert_to_string(i)) - return false; - } - - return true; -} - -JsonObject Sheet::to_json() const -{ - JsonObject object; - object.set("name", m_name); - - auto save_format = [](const auto& format, auto& obj) { - if (format.foreground_color.has_value()) - obj.set("foreground_color", format.foreground_color.value().to_string()); - if (format.background_color.has_value()) - obj.set("background_color", format.background_color.value().to_string()); - }; - - auto bottom_right = written_data_bounds(); - - if (!columns_are_standard()) { - auto columns = JsonArray(); - for (auto& column : m_columns) - columns.append(column); - object.set("columns", move(columns)); - } - object.set("rows", bottom_right.row + 1); - - JsonObject cells; - for (auto& it : m_cells) { - StringBuilder builder; - builder.append(it.key.column); - builder.appendff("{}", it.key.row); - auto key = builder.to_string(); - - JsonObject data; - data.set("kind", it.value->kind() == Cell::Kind::Formula ? "Formula" : "LiteralString"); - if (it.value->kind() == Cell::Formula) { - data.set("source", it.value->data()); - auto json = interpreter().global_object().get("JSON"); - auto stringified = interpreter().vm().call(json.as_object().get("stringify").as_function(), json, it.value->evaluated_data()); - data.set("value", stringified.to_string_without_side_effects()); - } else { - data.set("value", it.value->data()); - } - - // Set type & meta - auto& type = it.value->type(); - auto& meta = it.value->type_metadata(); - data.set("type", type.name()); - - JsonObject metadata_object; - metadata_object.set("length", meta.length); - metadata_object.set("format", meta.format); -#if 0 - metadata_object.set("alignment", alignment_to_string(meta.alignment)); -#endif - save_format(meta.static_format, metadata_object); - - data.set("type_metadata", move(metadata_object)); - - // Set conditional formats - JsonArray conditional_formats; - for (auto& fmt : it.value->conditional_formats()) { - JsonObject fmt_object; - fmt_object.set("condition", fmt.condition); - save_format(fmt, fmt_object); - - conditional_formats.append(move(fmt_object)); - } - - data.set("conditional_formats", move(conditional_formats)); - - auto& evaluated_formats = it.value->evaluated_formats(); - JsonObject evaluated_formats_obj; - - save_format(evaluated_formats, evaluated_formats_obj); - data.set("evaluated_formats", move(evaluated_formats_obj)); - - cells.set(key, move(data)); - } - object.set("cells", move(cells)); - - return object; -} - -Vector<Vector<String>> Sheet::to_xsv() const -{ - Vector<Vector<String>> data; - - auto bottom_right = written_data_bounds(); - - // First row = headers. - size_t column_count = m_columns.size(); - if (columns_are_standard()) { - column_count = convert_from_string(bottom_right.column) + 1; - Vector<String> cols; - for (size_t i = 0; i < column_count; ++i) - cols.append(m_columns[i]); - data.append(move(cols)); - } else { - data.append(m_columns); - } - - for (size_t i = 0; i <= bottom_right.row; ++i) { - Vector<String> row; - row.resize(column_count); - for (size_t j = 0; j < column_count; ++j) { - auto cell = at({ m_columns[j], i }); - if (cell) - row[j] = cell->typed_display(); - } - - data.append(move(row)); - } - - return data; -} - -RefPtr<Sheet> Sheet::from_xsv(const Reader::XSV& xsv, Workbook& workbook) -{ - auto cols = xsv.headers(); - auto rows = xsv.size(); - - auto sheet = adopt(*new Sheet(workbook)); - sheet->m_columns = cols; - for (size_t i = 0; i < max(rows, Sheet::default_row_count); ++i) - sheet->add_row(); - if (sheet->columns_are_standard()) { - for (size_t i = sheet->m_columns.size(); i < Sheet::default_column_count; ++i) - sheet->add_column(); - } - - for (auto row : xsv) { - for (size_t i = 0; i < cols.size(); ++i) { - auto str = row[i]; - if (str.is_empty()) - continue; - Position position { cols[i], row.index() }; - auto cell = make<Cell>(str, position, *sheet); - sheet->m_cells.set(position, move(cell)); - } - } - - return sheet; -} - -JsonObject Sheet::gather_documentation() const -{ - JsonObject object; - const JS::PropertyName doc_name { "__documentation" }; - - auto add_docs_from = [&](auto& it, auto& global_object) { - auto value = global_object.get(it.key); - if (!value.is_function() && !value.is_object()) - return; - - auto& value_object = value.is_object() ? value.as_object() : value.as_function(); - if (!value_object.has_own_property(doc_name)) - return; - - dbgln("Found '{}'", it.key.to_display_string()); - auto doc = value_object.get(doc_name); - if (!doc.is_string()) - return; - - JsonParser parser(doc.to_string_without_side_effects()); - auto doc_object = parser.parse(); - - if (doc_object.has_value()) - object.set(it.key.to_display_string(), doc_object.value()); - else - dbgln("Sheet::gather_documentation(): Failed to parse the documentation for '{}'!", it.key.to_display_string()); - }; - - for (auto& it : interpreter().global_object().shape().property_table()) - add_docs_from(it, interpreter().global_object()); - - for (auto& it : global_object().shape().property_table()) - add_docs_from(it, global_object()); - - m_cached_documentation = move(object); - return m_cached_documentation.value(); -} - -String Sheet::generate_inline_documentation_for(StringView function, size_t argument_index) -{ - if (!m_cached_documentation.has_value()) - gather_documentation(); - - auto& docs = m_cached_documentation.value(); - auto entry = docs.get(function); - if (entry.is_null() || !entry.is_object()) - return String::formatted("{}(...???{})", function, argument_index); - - auto& entry_object = entry.as_object(); - size_t argc = entry_object.get("argc").to_int(0); - auto argnames_value = entry_object.get("argnames"); - if (!argnames_value.is_array()) - return String::formatted("{}(...{}???{})", function, argc, argument_index); - auto& argnames = argnames_value.as_array(); - StringBuilder builder; - builder.appendff("{}(", function); - for (size_t i = 0; i < (size_t)argnames.size(); ++i) { - if (i != 0 && i < (size_t)argnames.size()) - builder.append(", "); - if (i == argument_index) - builder.append('<'); - else if (i >= argc) - builder.append('['); - builder.append(argnames[i].to_string()); - if (i == argument_index) - builder.append('>'); - else if (i >= argc) - builder.append(']'); - } - - builder.append(')'); - return builder.build(); -} - -} diff --git a/Applications/Spreadsheet/Spreadsheet.h b/Applications/Spreadsheet/Spreadsheet.h deleted file mode 100644 index bbef6c8116..0000000000 --- a/Applications/Spreadsheet/Spreadsheet.h +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "Cell.h" -#include "Forward.h" -#include "Readers/XSV.h" -#include <AK/HashMap.h> -#include <AK/HashTable.h> -#include <AK/String.h> -#include <AK/StringBuilder.h> -#include <AK/Traits.h> -#include <AK/Types.h> -#include <AK/WeakPtr.h> -#include <AK/Weakable.h> -#include <LibCore/Object.h> -#include <LibJS/Interpreter.h> - -namespace Spreadsheet { - -class Sheet : public Core::Object { - C_OBJECT(Sheet); - -public: - constexpr static size_t default_row_count = 100; - constexpr static size_t default_column_count = 26; - - ~Sheet(); - - static Optional<Position> parse_cell_name(const StringView&); - Optional<size_t> column_index(const StringView& column_name) const; - Optional<String> column_arithmetic(const StringView& column_name, int offset); - - Cell* from_url(const URL&); - const Cell* from_url(const URL& url) const { return const_cast<Sheet*>(this)->from_url(url); } - Optional<Position> position_from_url(const URL& url) const; - - /// Resolve 'offset' to an absolute position assuming 'base' is at 'offset_base'. - /// Effectively, "Walk the distance between 'offset' and 'offset_base' away from 'base'". - Position offset_relative_to(const Position& base, const Position& offset, const Position& offset_base) const; - - JsonObject to_json() const; - static RefPtr<Sheet> from_json(const JsonObject&, Workbook&); - - Vector<Vector<String>> to_xsv() const; - static RefPtr<Sheet> from_xsv(const Reader::XSV&, Workbook&); - - const String& name() const { return m_name; } - void set_name(const StringView& name) { m_name = name; } - - JsonObject gather_documentation() const; - - const HashTable<Position>& selected_cells() const { return m_selected_cells; } - HashTable<Position>& selected_cells() { return m_selected_cells; } - const HashMap<Position, NonnullOwnPtr<Cell>>& cells() const { return m_cells; } - HashMap<Position, NonnullOwnPtr<Cell>>& cells() { return m_cells; } - - Cell* at(const Position& position); - const Cell* at(const Position& position) const { return const_cast<Sheet*>(this)->at(position); } - - const Cell* at(const StringView& name) const { return const_cast<Sheet*>(this)->at(name); } - Cell* at(const StringView&); - - const Cell& ensure(const Position& position) const { return const_cast<Sheet*>(this)->ensure(position); } - Cell& ensure(const Position& position) - { - if (auto cell = at(position)) - return *cell; - - m_cells.set(position, make<Cell>(String::empty(), position, *this)); - return *at(position); - } - - size_t add_row(); - String add_column(); - - size_t row_count() const { return m_rows; } - size_t column_count() const { return m_columns.size(); } - const Vector<String>& columns() const { return m_columns; } - const String& column(size_t index) - { - for (size_t i = column_count(); i < index; ++i) - add_column(); - - ASSERT(column_count() > index); - return m_columns[index]; - } - const String& column(size_t index) const - { - ASSERT(column_count() > index); - return m_columns[index]; - } - - void update(); - void update(Cell&); - void disable_updates() { m_should_ignore_updates = true; } - void enable_updates() - { - m_should_ignore_updates = false; - if (m_update_requested) { - m_update_requested = false; - update(); - } - } - - struct ValueAndException { - JS::Value value; - JS::Exception* exception { nullptr }; - }; - ValueAndException evaluate(const StringView&, Cell* = nullptr); - JS::Interpreter& interpreter() const; - SheetGlobalObject& global_object() const { return *m_global_object; } - - Cell*& current_evaluated_cell() { return m_current_cell_being_evaluated; } - bool has_been_visited(Cell* cell) const { return m_visited_cells_in_update.contains(cell); } - - const Workbook& workbook() const { return m_workbook; } - - void copy_cells(Vector<Position> from, Vector<Position> to, Optional<Position> resolve_relative_to = {}); - - /// Gives the bottom-right corner of the smallest bounding box containing all the written data. - Position written_data_bounds() const; - - bool columns_are_standard() const; - - String generate_inline_documentation_for(StringView function, size_t argument_index); - -private: - explicit Sheet(Workbook&); - explicit Sheet(const StringView& name, Workbook&); - - String m_name; - Vector<String> m_columns; - size_t m_rows { 0 }; - HashMap<Position, NonnullOwnPtr<Cell>> m_cells; - HashTable<Position> m_selected_cells; - - Workbook& m_workbook; - mutable SheetGlobalObject* m_global_object; - - Cell* m_current_cell_being_evaluated { nullptr }; - - HashTable<Cell*> m_visited_cells_in_update; - bool m_should_ignore_updates { false }; - bool m_update_requested { false }; - mutable Optional<JsonObject> m_cached_documentation; -}; - -} - -namespace AK { - -template<> -struct Traits<Spreadsheet::Position> : public GenericTraits<Spreadsheet::Position> { - static constexpr bool is_trivial() { return false; } - static unsigned hash(const Spreadsheet::Position& p) - { - return pair_int_hash( - string_hash(p.column.characters(), p.column.length()), - u64_hash(p.row)); - } -}; - -} diff --git a/Applications/Spreadsheet/SpreadsheetModel.cpp b/Applications/Spreadsheet/SpreadsheetModel.cpp deleted file mode 100644 index 70193e4b63..0000000000 --- a/Applications/Spreadsheet/SpreadsheetModel.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "SpreadsheetModel.h" -#include "ConditionalFormatting.h" -#include <AK/URL.h> -#include <LibGUI/AbstractView.h> -#include <LibJS/Runtime/Error.h> -#include <LibJS/Runtime/Object.h> - -namespace Spreadsheet { - -SheetModel::~SheetModel() -{ -} - -GUI::Variant SheetModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const -{ - if (!index.is_valid()) - return {}; - - if (role == GUI::ModelRole::Display) { - const auto* cell = m_sheet->at({ m_sheet->column(index.column()), (size_t)index.row() }); - if (!cell) - return String::empty(); - - if (cell->kind() == Spreadsheet::Cell::Formula) { - if (auto exception = cell->exception()) { - StringBuilder builder; - builder.append("Error: "); - auto value = exception->value(); - if (value.is_object()) { - auto& object = value.as_object(); - if (is<JS::Error>(object)) { - auto error = object.get("message").to_string_without_side_effects(); - builder.append(error); - return builder.to_string(); - } - } - auto error = value.to_string(cell->sheet().global_object()); - // This is annoying, but whatever. - cell->sheet().interpreter().vm().clear_exception(); - - builder.append(error); - return builder.to_string(); - } - } - - return cell->typed_display(); - } - - if (role == GUI::ModelRole::MimeData) - return Position { m_sheet->column(index.column()), (size_t)index.row() }.to_url().to_string(); - - if (role == GUI::ModelRole::TextAlignment) { - const auto* cell = m_sheet->at({ m_sheet->column(index.column()), (size_t)index.row() }); - if (!cell) - return {}; - - return cell->type_metadata().alignment; - } - - if (role == GUI::ModelRole::ForegroundColor) { - const auto* cell = m_sheet->at({ m_sheet->column(index.column()), (size_t)index.row() }); - if (!cell) - return {}; - - if (cell->kind() == Spreadsheet::Cell::Formula) { - if (cell->exception()) - return Color(Color::Red); - } - - if (cell->evaluated_formats().foreground_color.has_value()) - return cell->evaluated_formats().foreground_color.value(); - - if (auto color = cell->type_metadata().static_format.foreground_color; color.has_value()) - return color.value(); - - return {}; - } - - if (role == GUI::ModelRole::BackgroundColor) { - const auto* cell = m_sheet->at({ m_sheet->column(index.column()), (size_t)index.row() }); - if (!cell) - return {}; - - if (cell->evaluated_formats().background_color.has_value()) - return cell->evaluated_formats().background_color.value(); - - if (auto color = cell->type_metadata().static_format.background_color; color.has_value()) - return color.value(); - - return {}; - } - - return {}; -} - -RefPtr<Core::MimeData> SheetModel::mime_data(const GUI::ModelSelection& selection) const -{ - auto mime_data = GUI::Model::mime_data(selection); - - bool first = true; - const GUI::ModelIndex* cursor = nullptr; - const_cast<SheetModel*>(this)->for_each_view([&](const GUI::AbstractView& view) { - if (!first) - return; - cursor = &view.cursor_index(); - first = false; - }); - - ASSERT(cursor); - - Position cursor_position { m_sheet->column(cursor->column()), (size_t)cursor->row() }; - auto new_data = String::formatted("{}\n{}", - cursor_position.to_url().to_string(), - StringView(mime_data->data("text/x-spreadsheet-data"))); - mime_data->set_data("text/x-spreadsheet-data", new_data.to_byte_buffer()); - - return mime_data; -} - -String SheetModel::column_name(int index) const -{ - if (index < 0) - return {}; - - return m_sheet->column(index); -} - -bool SheetModel::is_editable(const GUI::ModelIndex& index) const -{ - if (!index.is_valid()) - return false; - - return true; -} - -void SheetModel::set_data(const GUI::ModelIndex& index, const GUI::Variant& value) -{ - if (!index.is_valid()) - return; - - auto& cell = m_sheet->ensure({ m_sheet->column(index.column()), (size_t)index.row() }); - cell.set_data(value.to_string()); - update(); -} - -void SheetModel::update() -{ - m_sheet->update(); - did_update(UpdateFlag::DontInvalidateIndexes); -} - -} diff --git a/Applications/Spreadsheet/SpreadsheetModel.h b/Applications/Spreadsheet/SpreadsheetModel.h deleted file mode 100644 index 14527d4c37..0000000000 --- a/Applications/Spreadsheet/SpreadsheetModel.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "Spreadsheet.h" -#include <LibGUI/Model.h> - -namespace Spreadsheet { - -class SheetModel final : public GUI::Model { -public: - static NonnullRefPtr<SheetModel> create(Sheet& sheet) { return adopt(*new SheetModel(sheet)); } - virtual ~SheetModel() override; - - virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return m_sheet->row_count(); } - virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return m_sheet->column_count(); } - virtual String column_name(int) const override; - virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override; - virtual RefPtr<Core::MimeData> mime_data(const GUI::ModelSelection&) const override; - virtual bool is_editable(const GUI::ModelIndex&) const override; - virtual void set_data(const GUI::ModelIndex&, const GUI::Variant&) override; - virtual void update() override; - virtual bool is_column_sortable(int) const override { return false; } - virtual StringView drag_data_type() const override { return "text/x-spreadsheet-data"; } - Sheet& sheet() { return *m_sheet; } - -private: - explicit SheetModel(Sheet& sheet) - : m_sheet(sheet) - { - } - - NonnullRefPtr<Sheet> m_sheet; -}; - -} diff --git a/Applications/Spreadsheet/SpreadsheetView.cpp b/Applications/Spreadsheet/SpreadsheetView.cpp deleted file mode 100644 index 9762f77578..0000000000 --- a/Applications/Spreadsheet/SpreadsheetView.cpp +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "SpreadsheetView.h" -#include "CellTypeDialog.h" -#include "SpreadsheetModel.h" -#include <AK/ScopeGuard.h> -#include <AK/URL.h> -#include <LibCore/MimeData.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/HeaderView.h> -#include <LibGUI/Menu.h> -#include <LibGUI/ModelEditingDelegate.h> -#include <LibGUI/Painter.h> -#include <LibGUI/ScrollBar.h> -#include <LibGUI/TableView.h> -#include <LibGfx/Palette.h> - -namespace Spreadsheet { - -SpreadsheetView::~SpreadsheetView() -{ -} - -void SpreadsheetView::EditingDelegate::set_value(const GUI::Variant& value) -{ - if (value.as_string().is_null()) { - StringModelEditingDelegate::set_value(""); - commit(); - return; - } - - if (m_has_set_initial_value) - return StringModelEditingDelegate::set_value(value); - - m_has_set_initial_value = true; - const auto option = m_sheet.at({ m_sheet.column(index().column()), (size_t)index().row() }); - if (option) - return StringModelEditingDelegate::set_value(option->source()); - - StringModelEditingDelegate::set_value(""); -} - -void InfinitelyScrollableTableView::did_scroll() -{ - TableView::did_scroll(); - auto& vscrollbar = vertical_scrollbar(); - auto& hscrollbar = horizontal_scrollbar(); - if (vscrollbar.is_visible() && vscrollbar.value() == vscrollbar.max()) { - if (on_reaching_vertical_end) - on_reaching_vertical_end(); - } - if (hscrollbar.is_visible() && hscrollbar.value() == hscrollbar.max()) { - if (on_reaching_horizontal_end) - on_reaching_horizontal_end(); - } -} - -void InfinitelyScrollableTableView::mousemove_event(GUI::MouseEvent& event) -{ - if (auto model = this->model()) { - auto index = index_at_event_position(event.position()); - if (!index.is_valid()) - return TableView::mousemove_event(event); - - auto& sheet = static_cast<SheetModel&>(*model).sheet(); - sheet.disable_updates(); - ScopeGuard sheet_update_enabler { [&] { sheet.enable_updates(); } }; - - auto holding_left_button = !!(event.buttons() & GUI::MouseButton::Left); - auto rect = content_rect(index); - auto distance = rect.center().absolute_relative_distance_to(event.position()); - if (distance.x() >= rect.width() / 2 - 5 && distance.y() >= rect.height() / 2 - 5) { - set_override_cursor(Gfx::StandardCursor::Crosshair); - m_should_intercept_drag = false; - if (holding_left_button) { - m_has_committed_to_dragging = true; - // Force a drag to happen by moving the mousedown position to the center of the cell. - m_left_mousedown_position = rect.center(); - } - } else if (!m_should_intercept_drag) { - set_override_cursor(Gfx::StandardCursor::Arrow); - if (!holding_left_button) { - m_starting_selection_index = index; - } else { - m_should_intercept_drag = true; - m_might_drag = false; - } - } - - if (holding_left_button && m_should_intercept_drag && !m_has_committed_to_dragging) { - if (!m_starting_selection_index.is_valid()) - m_starting_selection_index = index; - - Vector<GUI::ModelIndex> new_selection; - for (auto i = min(m_starting_selection_index.row(), index.row()), imax = max(m_starting_selection_index.row(), index.row()); i <= imax; ++i) { - for (auto j = min(m_starting_selection_index.column(), index.column()), jmax = max(m_starting_selection_index.column(), index.column()); j <= jmax; ++j) { - auto index = model->index(i, j); - if (index.is_valid()) - new_selection.append(move(index)); - } - } - - if (!event.ctrl()) - selection().clear(); - selection().add_all(new_selection); - } - } - - TableView::mousemove_event(event); -} - -void InfinitelyScrollableTableView::mouseup_event(GUI::MouseEvent& event) -{ - m_should_intercept_drag = false; - m_has_committed_to_dragging = false; - TableView::mouseup_event(event); -} - -void SpreadsheetView::update_with_model() -{ - m_table_view->model()->update(); - m_table_view->update(); -} - -SpreadsheetView::SpreadsheetView(Sheet& sheet) - : m_sheet(sheet) -{ - set_layout<GUI::VerticalBoxLayout>().set_margins({ 2, 2, 2, 2 }); - m_table_view = add<InfinitelyScrollableTableView>(); - m_table_view->set_grid_style(GUI::TableView::GridStyle::Both); - m_table_view->set_selection_behavior(GUI::AbstractView::SelectionBehavior::SelectItems); - m_table_view->set_edit_triggers(GUI::AbstractView::EditTrigger::EditKeyPressed | GUI::AbstractView::AnyKeyPressed | GUI::AbstractView::DoubleClicked); - m_table_view->set_tab_key_navigation_enabled(true); - m_table_view->row_header().set_visible(true); - m_table_view->set_model(SheetModel::create(*m_sheet)); - m_table_view->on_reaching_vertical_end = [&]() { - for (size_t i = 0; i < 100; ++i) { - auto index = m_sheet->add_row(); - m_table_view->set_column_painting_delegate(index, make<TableCellPainter>(*m_table_view)); - }; - update_with_model(); - }; - m_table_view->on_reaching_horizontal_end = [&]() { - for (size_t i = 0; i < 10; ++i) { - m_sheet->add_column(); - auto last_column_index = m_sheet->column_count() - 1; - m_table_view->set_column_width(last_column_index, 50); - m_table_view->set_column_header_alignment(last_column_index, Gfx::TextAlignment::Center); - } - update_with_model(); - }; - - set_focus_proxy(m_table_view); - - // FIXME: This is dumb. - for (size_t i = 0; i < m_sheet->column_count(); ++i) { - m_table_view->set_column_painting_delegate(i, make<TableCellPainter>(*m_table_view)); - m_table_view->set_column_width(i, 50); - m_table_view->set_column_header_alignment(i, Gfx::TextAlignment::Center); - } - - m_table_view->set_alternating_row_colors(false); - m_table_view->set_highlight_selected_rows(false); - m_table_view->set_editable(true); - m_table_view->aid_create_editing_delegate = [this](auto&) { - auto delegate = make<EditingDelegate>(*m_sheet); - delegate->on_cursor_key_pressed = [this](auto& event) { - m_table_view->stop_editing(); - m_table_view->event(event); - }; - return delegate; - }; - - m_table_view->on_selection_change = [&] { - m_sheet->selected_cells().clear(); - for (auto& index : m_table_view->selection().indexes()) { - Position position { m_sheet->column(index.column()), (size_t)index.row() }; - m_sheet->selected_cells().set(position); - } - - if (m_table_view->selection().is_empty() && on_selection_dropped) - return on_selection_dropped(); - - Vector<Position> selected_positions; - selected_positions.ensure_capacity(m_table_view->selection().size()); - for (auto& selection : m_table_view->selection().indexes()) - selected_positions.empend(m_sheet->column(selection.column()), (size_t)selection.row()); - - if (on_selection_changed) { - on_selection_changed(move(selected_positions)); - update_with_model(); - }; - }; - - m_table_view->on_activation = [this](auto&) { - m_table_view->move_cursor(GUI::AbstractView::CursorMovement::Down, GUI::AbstractView::SelectionUpdate::Set); - }; - - m_table_view->on_context_menu_request = [&](const GUI::ModelIndex&, const GUI::ContextMenuEvent& event) { - // NOTE: We ignore the specific cell for now. - m_cell_range_context_menu->popup(event.screen_position()); - }; - - m_cell_range_context_menu = GUI::Menu::construct(); - m_cell_range_context_menu->add_action(GUI::Action::create("Type and Formatting...", [this](auto&) { - Vector<Position> positions; - for (auto& index : m_table_view->selection().indexes()) { - Position position { m_sheet->column(index.column()), (size_t)index.row() }; - positions.append(move(position)); - } - - if (positions.is_empty()) { - auto& index = m_table_view->cursor_index(); - Position position { m_sheet->column(index.column()), (size_t)index.row() }; - positions.append(move(position)); - } - - auto dialog = CellTypeDialog::construct(positions, *m_sheet, window()); - if (dialog->exec() == GUI::Dialog::ExecOK) { - for (auto& position : positions) { - auto& cell = m_sheet->ensure(position); - cell.set_type(dialog->type()); - cell.set_type_metadata(dialog->metadata()); - cell.set_conditional_formats(dialog->conditional_formats()); - } - - m_table_view->update(); - } - })); - - m_table_view->on_drop = [&](const GUI::ModelIndex& index, const GUI::DropEvent& event) { - if (!index.is_valid()) - return; - - ScopeGuard update_after_drop { [this] { update(); } }; - - if (event.mime_data().has_format("text/x-spreadsheet-data")) { - auto data = event.mime_data().data("text/x-spreadsheet-data"); - StringView urls { data.data(), data.size() }; - Vector<Position> source_positions, target_positions; - - for (auto& line : urls.lines(false)) { - auto position = m_sheet->position_from_url(line); - if (position.has_value()) - source_positions.append(position.release_value()); - } - - // Drop always has a single target. - Position target { m_sheet->column(index.column()), (size_t)index.row() }; - target_positions.append(move(target)); - - if (source_positions.is_empty()) - return; - - auto first_position = source_positions.take_first(); - m_sheet->copy_cells(move(source_positions), move(target_positions), first_position); - - return; - } - - if (event.mime_data().has_text()) { - auto* target_cell = m_sheet->at({ m_sheet->column(index.column()), (size_t)index.row() }); - ASSERT(target_cell); - - target_cell->set_data(event.text()); - return; - } - }; -} - -void SpreadsheetView::hide_event(GUI::HideEvent&) -{ - if (on_selection_dropped) - on_selection_dropped(); -} - -void SpreadsheetView::show_event(GUI::ShowEvent&) -{ - if (on_selection_changed && !m_table_view->selection().is_empty()) { - Vector<Position> selected_positions; - selected_positions.ensure_capacity(m_table_view->selection().size()); - for (auto& selection : m_table_view->selection().indexes()) - selected_positions.empend(m_sheet->column(selection.column()), (size_t)selection.row()); - - on_selection_changed(move(selected_positions)); - } -} - -void SpreadsheetView::TableCellPainter::paint(GUI::Painter& painter, const Gfx::IntRect& rect, const Gfx::Palette& palette, const GUI::ModelIndex& index) -{ - // Draw a border. - // Undo the horizontal padding done by the table view... - auto cell_rect = rect.inflated(m_table_view.horizontal_padding() * 2, 0); - - if (auto bg = index.data(GUI::ModelRole::BackgroundColor); bg.is_color()) - painter.fill_rect(cell_rect, bg.as_color()); - - if (m_table_view.selection().contains(index)) { - Color fill_color = palette.selection(); - fill_color.set_alpha(80); - painter.fill_rect(cell_rect, fill_color); - } - - auto text_color = index.data(GUI::ModelRole::ForegroundColor).to_color(palette.color(m_table_view.foreground_role())); - auto data = index.data(); - auto text_alignment = index.data(GUI::ModelRole::TextAlignment).to_text_alignment(Gfx::TextAlignment::CenterRight); - painter.draw_text(rect, data.to_string(), m_table_view.font_for_index(index), text_alignment, text_color, Gfx::TextElision::Right); -} - -} diff --git a/Applications/Spreadsheet/SpreadsheetView.h b/Applications/Spreadsheet/SpreadsheetView.h deleted file mode 100644 index bc07d31bc3..0000000000 --- a/Applications/Spreadsheet/SpreadsheetView.h +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "Spreadsheet.h" -#include <LibGUI/AbstractTableView.h> -#include <LibGUI/ModelEditingDelegate.h> -#include <LibGUI/TableView.h> -#include <LibGUI/Widget.h> -#include <string.h> - -namespace Spreadsheet { - -class CellEditor final : public GUI::TextEditor { - C_OBJECT(CellEditor); - -public: - virtual ~CellEditor() { } - - Function<void(GUI::KeyEvent&)> on_cursor_key_pressed; - -private: - CellEditor() - : TextEditor(TextEditor::Type::SingleLine) - { - } - - static bool is_navigation(const GUI::KeyEvent& event) - { - if (event.modifiers() == KeyModifier::Mod_Shift && event.key() == KeyCode::Key_Tab) - return true; - - if (event.modifiers()) - return false; - - switch (event.key()) { - case KeyCode::Key_Tab: - case KeyCode::Key_Left: - case KeyCode::Key_Right: - case KeyCode::Key_Up: - case KeyCode::Key_Down: - case KeyCode::Key_Return: - return true; - default: - return false; - } - } - - virtual void keydown_event(GUI::KeyEvent& event) override - { - if (is_navigation(event)) - on_cursor_key_pressed(event); - else - TextEditor::keydown_event(event); - } -}; - -class InfinitelyScrollableTableView : public GUI::TableView { - C_OBJECT(InfinitelyScrollableTableView) -public: - Function<void()> on_reaching_vertical_end; - Function<void()> on_reaching_horizontal_end; - -private: - virtual void did_scroll() override; - virtual void mousemove_event(GUI::MouseEvent&) override; - virtual void mouseup_event(GUI::MouseEvent&) override; - - bool m_should_intercept_drag { false }; - bool m_has_committed_to_dragging { false }; - GUI::ModelIndex m_starting_selection_index; -}; - -class SpreadsheetView final : public GUI::Widget { - C_OBJECT(SpreadsheetView); - -public: - ~SpreadsheetView(); - - const Sheet& sheet() const { return *m_sheet; } - Sheet& sheet() { return *m_sheet; } - - const GUI::ModelIndex* cursor() const - { - return &m_table_view->cursor_index(); - } - - Function<void(Vector<Position>&&)> on_selection_changed; - Function<void()> on_selection_dropped; - -private: - virtual void hide_event(GUI::HideEvent&) override; - virtual void show_event(GUI::ShowEvent&) override; - - void update_with_model(); - - SpreadsheetView(Sheet&); - - class EditingDelegate final : public GUI::StringModelEditingDelegate { - public: - EditingDelegate(const Sheet& sheet) - : m_sheet(sheet) - { - } - virtual void set_value(const GUI::Variant& value) override; - - virtual RefPtr<Widget> create_widget() override - { - auto textbox = CellEditor::construct(); - textbox->on_escape_pressed = [this] { - rollback(); - }; - textbox->on_cursor_key_pressed = [this](auto& event) { - commit(); - on_cursor_key_pressed(event); - }; - return textbox; - } - - Function<void(GUI::KeyEvent&)> on_cursor_key_pressed; - - private: - bool m_has_set_initial_value { false }; - const Sheet& m_sheet; - }; - - class TableCellPainter final : public GUI::TableCellPaintingDelegate { - public: - TableCellPainter(const GUI::TableView& view) - : m_table_view(view) - { - } - void paint(GUI::Painter&, const Gfx::IntRect&, const Gfx::Palette&, const GUI::ModelIndex&) override; - - private: - const GUI::TableView& m_table_view; - }; - - NonnullRefPtr<Sheet> m_sheet; - RefPtr<InfinitelyScrollableTableView> m_table_view; - RefPtr<GUI::Menu> m_cell_range_context_menu; -}; - -} diff --git a/Applications/Spreadsheet/SpreadsheetWidget.cpp b/Applications/Spreadsheet/SpreadsheetWidget.cpp deleted file mode 100644 index 65e48e3f12..0000000000 --- a/Applications/Spreadsheet/SpreadsheetWidget.cpp +++ /dev/null @@ -1,347 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "SpreadsheetWidget.h" -#include "CellSyntaxHighlighter.h" -#include "HelpWindow.h" -#include "LibGUI/InputBox.h" -#include <LibCore/File.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Button.h> -#include <LibGUI/FilePicker.h> -#include <LibGUI/Label.h> -#include <LibGUI/Menu.h> -#include <LibGUI/MessageBox.h> -#include <LibGUI/Splitter.h> -#include <LibGUI/TabWidget.h> -#include <LibGUI/TextEditor.h> -#include <LibGfx/FontDatabase.h> -#include <string.h> - -namespace Spreadsheet { - -SpreadsheetWidget::SpreadsheetWidget(NonnullRefPtrVector<Sheet>&& sheets, bool should_add_sheet_if_empty) - : m_workbook(make<Workbook>(move(sheets))) -{ - set_fill_with_background_color(true); - set_layout<GUI::VerticalBoxLayout>().set_margins({ 2, 2, 2, 2 }); - auto& container = add<GUI::VerticalSplitter>(); - - auto& top_bar = container.add<GUI::Frame>(); - top_bar.set_layout<GUI::HorizontalBoxLayout>().set_spacing(1); - top_bar.set_fixed_height(26); - auto& current_cell_label = top_bar.add<GUI::Label>(""); - current_cell_label.set_fixed_width(50); - - auto& help_button = top_bar.add<GUI::Button>("🛈"); - help_button.set_fixed_size(20, 20); - help_button.on_click = [&](auto) { - auto docs = m_selected_view->sheet().gather_documentation(); - auto help_window = HelpWindow::the(window()); - help_window->set_docs(move(docs)); - help_window->show(); - }; - - auto& cell_value_editor = top_bar.add<GUI::TextEditor>(GUI::TextEditor::Type::SingleLine); - cell_value_editor.set_font(Gfx::FontDatabase::default_fixed_width_font()); - cell_value_editor.set_scrollbars_enabled(false); - - cell_value_editor.set_syntax_highlighter(make<CellSyntaxHighlighter>()); - cell_value_editor.set_enabled(false); - current_cell_label.set_enabled(false); - - m_tab_widget = container.add<GUI::TabWidget>(); - m_tab_widget->set_tab_position(GUI::TabWidget::TabPosition::Bottom); - - m_cell_value_editor = cell_value_editor; - m_current_cell_label = current_cell_label; - m_inline_documentation_window = GUI::Window::construct(window()); - m_inline_documentation_window->set_rect(m_cell_value_editor->rect().translated(0, m_cell_value_editor->height() + 7).inflated(6, 6)); - m_inline_documentation_window->set_window_type(GUI::WindowType::Tooltip); - m_inline_documentation_window->set_resizable(false); - auto& inline_widget = m_inline_documentation_window->set_main_widget<GUI::Frame>(); - inline_widget.set_fill_with_background_color(true); - inline_widget.set_layout<GUI::VerticalBoxLayout>().set_margins({ 4, 4, 4, 4 }); - inline_widget.set_frame_shape(Gfx::FrameShape::Box); - m_inline_documentation_label = inline_widget.add<GUI::Label>(); - m_inline_documentation_label->set_fill_with_background_color(true); - m_inline_documentation_label->set_autosize(false); - m_inline_documentation_label->set_text_alignment(Gfx::TextAlignment::CenterLeft); - - if (!m_workbook->has_sheets() && should_add_sheet_if_empty) - m_workbook->add_sheet("Sheet 1"); - - m_tab_context_menu = GUI::Menu::construct(); - auto rename_action = GUI::Action::create("Rename...", [this](auto&) { - ASSERT(m_tab_context_menu_sheet_view); - - auto& sheet = m_tab_context_menu_sheet_view->sheet(); - String new_name; - if (GUI::InputBox::show(new_name, window(), String::formatted("New name for '{}'", sheet.name()), "Rename sheet") == GUI::Dialog::ExecOK) { - sheet.set_name(new_name); - sheet.update(); - m_tab_widget->set_tab_title(static_cast<GUI::Widget&>(*m_tab_context_menu_sheet_view), new_name); - } - }); - m_tab_context_menu->add_action(rename_action); - m_tab_context_menu->add_action(GUI::Action::create("Add new sheet...", [this](auto&) { - String name; - if (GUI::InputBox::show(name, window(), "Name for new sheet", "Create sheet") == GUI::Dialog::ExecOK) { - NonnullRefPtrVector<Sheet> new_sheets; - new_sheets.append(m_workbook->add_sheet(name)); - setup_tabs(move(new_sheets)); - } - })); - - setup_tabs(m_workbook->sheets()); -} - -void SpreadsheetWidget::resize_event(GUI::ResizeEvent& event) -{ - GUI::Widget::resize_event(event); - if (m_inline_documentation_window && m_cell_value_editor && window()) - m_inline_documentation_window->set_rect(m_cell_value_editor->screen_relative_rect().translated(0, m_cell_value_editor->height() + 7).inflated(6, 6)); -} - -void SpreadsheetWidget::setup_tabs(NonnullRefPtrVector<Sheet> new_sheets) -{ - RefPtr<GUI::Widget> first_tab_widget; - for (auto& sheet : new_sheets) { - auto& tab = m_tab_widget->add_tab<SpreadsheetView>(sheet.name(), sheet); - if (!first_tab_widget) - first_tab_widget = &tab; - } - - auto change = [&](auto& selected_widget) { - if (m_selected_view) { - m_selected_view->on_selection_changed = nullptr; - m_selected_view->on_selection_dropped = nullptr; - }; - m_selected_view = &static_cast<SpreadsheetView&>(selected_widget); - m_selected_view->on_selection_changed = [&](Vector<Position>&& selection) { - if (selection.is_empty()) { - m_current_cell_label->set_enabled(false); - m_current_cell_label->set_text({}); - m_cell_value_editor->on_change = nullptr; - m_cell_value_editor->on_focusin = nullptr; - m_cell_value_editor->on_focusout = nullptr; - m_cell_value_editor->set_text(""); - m_cell_value_editor->set_enabled(false); - return; - } - - if (selection.size() == 1) { - auto& position = selection.first(); - StringBuilder builder; - builder.append(position.column); - builder.appendff("{}", position.row); - m_current_cell_label->set_enabled(true); - m_current_cell_label->set_text(builder.string_view()); - - auto& cell = m_selected_view->sheet().ensure(position); - m_cell_value_editor->on_change = nullptr; - m_cell_value_editor->set_text(cell.source()); - m_cell_value_editor->on_change = [&] { - auto text = m_cell_value_editor->text(); - // FIXME: Lines? - auto offset = m_cell_value_editor->cursor().column(); - try_generate_tip_for_input_expression(text, offset); - cell.set_data(move(text)); - m_selected_view->sheet().update(); - update(); - }; - m_cell_value_editor->set_enabled(true); - static_cast<CellSyntaxHighlighter*>(const_cast<GUI::SyntaxHighlighter*>(m_cell_value_editor->syntax_highlighter()))->set_cell(&cell); - return; - } - - // There are many cells selected, change all of them. - StringBuilder builder; - builder.appendff("<{}>", selection.size()); - m_current_cell_label->set_enabled(true); - m_current_cell_label->set_text(builder.string_view()); - - Vector<Cell*> cells; - for (auto& position : selection) - cells.append(&m_selected_view->sheet().ensure(position)); - - auto first_cell = cells.first(); - m_cell_value_editor->on_change = nullptr; - m_cell_value_editor->set_text(""); - m_should_change_selected_cells = false; - m_cell_value_editor->on_focusin = [this] { m_should_change_selected_cells = true; }; - m_cell_value_editor->on_focusout = [this] { m_should_change_selected_cells = false; }; - m_cell_value_editor->on_change = [cells = move(cells), this] { - if (m_should_change_selected_cells) { - auto text = m_cell_value_editor->text(); - // FIXME: Lines? - auto offset = m_cell_value_editor->cursor().column(); - try_generate_tip_for_input_expression(text, offset); - for (auto* cell : cells) - cell->set_data(text); - m_selected_view->sheet().update(); - update(); - } - }; - m_cell_value_editor->set_enabled(true); - static_cast<CellSyntaxHighlighter*>(const_cast<GUI::SyntaxHighlighter*>(m_cell_value_editor->syntax_highlighter()))->set_cell(first_cell); - }; - m_selected_view->on_selection_dropped = [&]() { - m_cell_value_editor->set_enabled(false); - static_cast<CellSyntaxHighlighter*>(const_cast<GUI::SyntaxHighlighter*>(m_cell_value_editor->syntax_highlighter()))->set_cell(nullptr); - m_cell_value_editor->set_text(""); - m_current_cell_label->set_enabled(false); - m_current_cell_label->set_text(""); - }; - }; - - if (first_tab_widget) - change(*first_tab_widget); - - m_tab_widget->on_change = [change = move(change)](auto& selected_widget) { - change(selected_widget); - }; - - m_tab_widget->on_context_menu_request = [&](auto& widget, auto& event) { - m_tab_context_menu_sheet_view = widget; - m_tab_context_menu->popup(event.screen_position()); - }; -} - -void SpreadsheetWidget::try_generate_tip_for_input_expression(StringView source, size_t cursor_offset) -{ - m_inline_documentation_window->set_rect(m_cell_value_editor->screen_relative_rect().translated(0, m_cell_value_editor->height() + 7).inflated(6, 6)); - if (!m_selected_view || !source.starts_with('=')) { - m_inline_documentation_window->hide(); - return; - } - auto maybe_function_and_argument = get_function_and_argument_index(source.substring_view(0, cursor_offset)); - if (!maybe_function_and_argument.has_value()) { - m_inline_documentation_window->hide(); - return; - } - - auto& [name, index] = maybe_function_and_argument.value(); - auto& sheet = m_selected_view->sheet(); - auto text = sheet.generate_inline_documentation_for(name, index); - if (text.is_empty()) { - m_inline_documentation_window->hide(); - } else { - m_inline_documentation_label->set_text(move(text)); - m_inline_documentation_window->show(); - } -} - -void SpreadsheetWidget::save(const StringView& filename) -{ - auto result = m_workbook->save(filename); - if (result.is_error()) - GUI::MessageBox::show_error(window(), result.error()); -} - -void SpreadsheetWidget::load(const StringView& filename) -{ - auto result = m_workbook->load(filename); - if (result.is_error()) { - GUI::MessageBox::show_error(window(), result.error()); - return; - } - - m_tab_widget->on_change = nullptr; - m_cell_value_editor->on_change = nullptr; - m_current_cell_label->set_text(""); - m_should_change_selected_cells = false; - while (auto* widget = m_tab_widget->active_widget()) { - m_tab_widget->remove_tab(*widget); - } - - setup_tabs(m_workbook->sheets()); -} - -bool SpreadsheetWidget::request_close() -{ - if (!m_workbook->dirty()) - return true; - - auto result = GUI::MessageBox::show(window(), "The spreadsheet has been modified. Would you like to save?", "Unsaved changes", GUI::MessageBox::Type::Warning, GUI::MessageBox::InputType::YesNoCancel); - - if (result == GUI::MessageBox::ExecYes) { - if (current_filename().is_empty()) { - String name = "workbook"; - Optional<String> save_path = GUI::FilePicker::get_save_filepath(window(), name, "sheets"); - if (!save_path.has_value()) - return false; - - save(save_path.value()); - } else { - save(current_filename()); - } - return true; - } - - if (result == GUI::MessageBox::ExecNo) - return true; - - return false; -} - -void SpreadsheetWidget::add_sheet() -{ - StringBuilder name; - name.append("Sheet"); - name.appendff(" {}", m_workbook->sheets().size() + 1); - - NonnullRefPtrVector<Sheet> new_sheets; - new_sheets.append(m_workbook->add_sheet(name.string_view())); - setup_tabs(move(new_sheets)); -} - -void SpreadsheetWidget::add_sheet(NonnullRefPtr<Sheet>&& sheet) -{ - ASSERT(m_workbook == &sheet->workbook()); - - NonnullRefPtrVector<Sheet> new_sheets; - new_sheets.append(move(sheet)); - m_workbook->sheets().append(new_sheets); - setup_tabs(new_sheets); -} - -void SpreadsheetWidget::set_filename(const String& filename) -{ - if (m_workbook->set_filename(filename)) { - StringBuilder builder; - builder.append("Spreadsheet - "); - builder.append(current_filename()); - - window()->set_title(builder.string_view()); - window()->update(); - } -} - -SpreadsheetWidget::~SpreadsheetWidget() -{ -} -} diff --git a/Applications/Spreadsheet/SpreadsheetWidget.h b/Applications/Spreadsheet/SpreadsheetWidget.h deleted file mode 100644 index b29fc557d6..0000000000 --- a/Applications/Spreadsheet/SpreadsheetWidget.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "SpreadsheetView.h" -#include "Workbook.h" -#include <AK/NonnullRefPtrVector.h> -#include <LibGUI/Widget.h> - -namespace Spreadsheet { - -class SpreadsheetWidget final : public GUI::Widget { - C_OBJECT(SpreadsheetWidget); - -public: - ~SpreadsheetWidget(); - - void save(const StringView& filename); - void load(const StringView& filename); - bool request_close(); - void add_sheet(); - void add_sheet(NonnullRefPtr<Sheet>&&); - - const String& current_filename() const { return m_workbook->current_filename(); } - const Sheet& current_worksheet() const { return m_selected_view->sheet(); } - Sheet& current_worksheet() { return m_selected_view->sheet(); } - void set_filename(const String& filename); - - Workbook& workbook() { return *m_workbook; } - const Workbook& workbook() const { return *m_workbook; } - - const GUI::ModelIndex* current_selection_cursor() const - { - if (!m_selected_view) - return nullptr; - - return m_selected_view->cursor(); - } - -private: - virtual void resize_event(GUI::ResizeEvent&) override; - - explicit SpreadsheetWidget(NonnullRefPtrVector<Sheet>&& sheets = {}, bool should_add_sheet_if_empty = true); - - void setup_tabs(NonnullRefPtrVector<Sheet> new_sheets); - - void try_generate_tip_for_input_expression(StringView source, size_t offset); - - SpreadsheetView* m_selected_view { nullptr }; - RefPtr<GUI::Label> m_current_cell_label; - RefPtr<GUI::TextEditor> m_cell_value_editor; - RefPtr<GUI::Window> m_inline_documentation_window; - RefPtr<GUI::Label> m_inline_documentation_label; - RefPtr<GUI::TabWidget> m_tab_widget; - RefPtr<GUI::Menu> m_tab_context_menu; - RefPtr<SpreadsheetView> m_tab_context_menu_sheet_view; - bool m_should_change_selected_cells { false }; - - OwnPtr<Workbook> m_workbook; -}; - -} diff --git a/Applications/Spreadsheet/Workbook.cpp b/Applications/Spreadsheet/Workbook.cpp deleted file mode 100644 index db5bf16cb5..0000000000 --- a/Applications/Spreadsheet/Workbook.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "Workbook.h" -#include "JSIntegration.h" -#include "Readers/CSV.h" -#include "Writers/CSV.h" -#include <AK/ByteBuffer.h> -#include <AK/JsonArray.h> -#include <AK/JsonObject.h> -#include <AK/JsonObjectSerializer.h> -#include <AK/JsonParser.h> -#include <AK/Stream.h> -#include <LibCore/File.h> -#include <LibCore/FileStream.h> -#include <LibCore/MimeData.h> -#include <LibJS/Parser.h> -#include <LibJS/Runtime/GlobalObject.h> -#include <string.h> - -namespace Spreadsheet { - -static JS::VM& global_vm() -{ - static RefPtr<JS::VM> vm; - if (!vm) - vm = JS::VM::create(); - return *vm; -} - -Workbook::Workbook(NonnullRefPtrVector<Sheet>&& sheets) - : m_sheets(move(sheets)) - , m_interpreter(JS::Interpreter::create<JS::GlobalObject>(global_vm())) - , m_interpreter_scope(JS::VM::InterpreterExecutionScope(interpreter())) -{ - m_workbook_object = interpreter().heap().allocate<WorkbookObject>(global_object(), *this); - global_object().put("workbook", workbook_object()); -} - -bool Workbook::set_filename(const String& filename) -{ - if (m_current_filename == filename) - return false; - - m_current_filename = filename; - return true; -} - -Result<bool, String> Workbook::load(const StringView& filename) -{ - auto file_or_error = Core::File::open(filename, Core::IODevice::OpenMode::ReadOnly); - if (file_or_error.is_error()) { - StringBuilder sb; - sb.append("Failed to open "); - sb.append(filename); - sb.append(" for reading. Error: "); - sb.append(file_or_error.error()); - - return sb.to_string(); - } - - auto mime = Core::guess_mime_type_based_on_filename(filename); - - if (mime == "text/csv") { - // FIXME: Prompt the user for settings. - NonnullRefPtrVector<Sheet> sheets; - - auto sheet = Sheet::from_xsv(Reader::CSV(file_or_error.value()->read_all(), Reader::default_behaviours() | Reader::ParserBehaviour::ReadHeaders), *this); - if (sheet) - sheets.append(sheet.release_nonnull()); - - m_sheets.clear(); - m_sheets = move(sheets); - } else { - // Assume JSON. - auto json_value_option = JsonParser(file_or_error.value()->read_all()).parse(); - if (!json_value_option.has_value()) { - StringBuilder sb; - sb.append("Failed to parse "); - sb.append(filename); - - return sb.to_string(); - } - - auto& json_value = json_value_option.value(); - if (!json_value.is_array()) { - StringBuilder sb; - sb.append("Did not find a spreadsheet in "); - sb.append(filename); - - return sb.to_string(); - } - - NonnullRefPtrVector<Sheet> sheets; - - auto& json_array = json_value.as_array(); - json_array.for_each([&](auto& sheet_json) { - if (!sheet_json.is_object()) - return IterationDecision::Continue; - - auto sheet = Sheet::from_json(sheet_json.as_object(), *this); - if (!sheet) - return IterationDecision::Continue; - - sheets.append(sheet.release_nonnull()); - - return IterationDecision::Continue; - }); - - m_sheets.clear(); - m_sheets = move(sheets); - } - - set_filename(filename); - - return true; -} - -Result<bool, String> Workbook::save(const StringView& filename) -{ - auto mime = Core::guess_mime_type_based_on_filename(filename); - auto file = Core::File::construct(filename); - file->open(Core::IODevice::WriteOnly); - if (!file->is_open()) { - StringBuilder sb; - sb.append("Failed to open "); - sb.append(filename); - sb.append(" for write. Error: "); - sb.append(file->error_string()); - - return sb.to_string(); - } - - if (mime == "text/csv") { - // FIXME: Prompt the user for settings and which sheet to export. - Core::OutputFileStream stream { file }; - auto data = m_sheets[0].to_xsv(); - auto header_string = data.take_first(); - Vector<StringView> headers; - for (auto& str : header_string) - headers.append(str); - Writer::CSV csv { stream, data, headers }; - if (csv.has_error()) - return String::formatted("Unable to save file, CSV writer error: {}", csv.error_string()); - } else { - JsonArray array; - for (auto& sheet : m_sheets) - array.append(sheet.to_json()); - - auto file_content = array.to_string(); - bool result = file->write(file_content); - if (!result) { - int error_number = errno; - StringBuilder sb; - sb.append("Unable to save file. Error: "); - sb.append(strerror(error_number)); - - return sb.to_string(); - } - } - - set_filename(filename); - set_dirty(false); - return true; -} - -} diff --git a/Applications/Spreadsheet/Workbook.h b/Applications/Spreadsheet/Workbook.h deleted file mode 100644 index 63c016d501..0000000000 --- a/Applications/Spreadsheet/Workbook.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "Forward.h" -#include "Spreadsheet.h" -#include <AK/NonnullOwnPtrVector.h> -#include <AK/Result.h> - -namespace Spreadsheet { - -class Workbook { -public: - Workbook(NonnullRefPtrVector<Sheet>&& sheets); - - Result<bool, String> save(const StringView& filename); - Result<bool, String> load(const StringView& filename); - - const String& current_filename() const { return m_current_filename; } - bool set_filename(const String& filename); - bool dirty() { return m_dirty; } - void set_dirty(bool dirty) { m_dirty = dirty; } - - bool has_sheets() const { return !m_sheets.is_empty(); } - - const NonnullRefPtrVector<Sheet>& sheets() const { return m_sheets; } - NonnullRefPtrVector<Sheet> sheets() { return m_sheets; } - - Sheet& add_sheet(const StringView& name) - { - auto sheet = Sheet::construct(name, *this); - m_sheets.append(sheet); - return *sheet; - } - - JS::Interpreter& interpreter() { return *m_interpreter; } - const JS::Interpreter& interpreter() const { return *m_interpreter; } - - JS::GlobalObject& global_object() { return m_interpreter->global_object(); } - const JS::GlobalObject& global_object() const { return m_interpreter->global_object(); } - - WorkbookObject* workbook_object() { return m_workbook_object; } - -private: - NonnullRefPtrVector<Sheet> m_sheets; - NonnullOwnPtr<JS::Interpreter> m_interpreter; - JS::VM::InterpreterExecutionScope m_interpreter_scope; - WorkbookObject* m_workbook_object { nullptr }; - - String m_current_filename; - bool m_dirty { false }; -}; - -} diff --git a/Applications/Spreadsheet/Writers/CSV.h b/Applications/Spreadsheet/Writers/CSV.h deleted file mode 100644 index 49940fbdf1..0000000000 --- a/Applications/Spreadsheet/Writers/CSV.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "XSV.h" -#include <AK/Forward.h> -#include <AK/StringView.h> - -namespace Writer { - -template<typename ContainerType> -class CSV : public XSV<ContainerType> { -public: - CSV(OutputStream& output, const ContainerType& data, const Vector<StringView>& headers = {}, WriterBehaviour behaviours = default_behaviours()) - : XSV<ContainerType>(output, data, { ",", "\"", WriterTraits::Repeat }, headers, behaviours) - { - } -}; - -} diff --git a/Applications/Spreadsheet/Writers/Test/TestXSVWriter.cpp b/Applications/Spreadsheet/Writers/Test/TestXSVWriter.cpp deleted file mode 100644 index 4971658431..0000000000 --- a/Applications/Spreadsheet/Writers/Test/TestXSVWriter.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 <AK/TestSuite.h> - -#include "../CSV.h" -#include "../XSV.h" -#include <AK/MemoryStream.h> - -TEST_CASE(can_write) -{ - Vector<Vector<int>> data = { - { 1, 2, 3 }, - { 4, 5, 6 }, - { 7, 8, 9 }, - }; - - auto buffer = ByteBuffer::create_uninitialized(1024); - OutputMemoryStream stream { buffer }; - - Writer::CSV csv(stream, data); - - auto expected_output = R"~(1,2,3 -4,5,6 -7,8,9 -)~"; - - EXPECT_EQ(StringView { stream.bytes() }, expected_output); -} - -TEST_CASE(can_write_with_header) -{ - Vector<Vector<int>> data = { - { 1, 2, 3 }, - { 4, 5, 6 }, - { 7, 8, 9 }, - }; - - auto buffer = ByteBuffer::create_uninitialized(1024); - OutputMemoryStream stream { buffer }; - - Writer::CSV csv(stream, data, { "A", "B\"", "C" }); - - auto expected_output = R"~(A,"B""",C -1,2,3 -4,5,6 -7,8,9 -)~"; - - EXPECT_EQ(StringView { stream.bytes() }, expected_output); -} - -TEST_CASE(can_write_with_different_behaviours) -{ - Vector<Vector<String>> data = { - { "Well", "Hello\"", "Friends" }, - { "We\"ll", "Hello,", " Friends" }, - }; - - auto buffer = ByteBuffer::create_uninitialized(1024); - OutputMemoryStream stream { buffer }; - - Writer::CSV csv(stream, data, { "A", "B\"", "C" }, Writer::WriterBehaviour::QuoteOnlyInFieldStart | Writer::WriterBehaviour::WriteHeaders); - - auto expected_output = R"~(A,B",C -Well,Hello",Friends -We"ll,"Hello,", Friends -)~"; - - EXPECT_EQ(StringView { stream.bytes() }, expected_output); -} - -TEST_MAIN(XSV) diff --git a/Applications/Spreadsheet/Writers/XSV.h b/Applications/Spreadsheet/Writers/XSV.h deleted file mode 100644 index 7a065f87d1..0000000000 --- a/Applications/Spreadsheet/Writers/XSV.h +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 <AK/GenericLexer.h> -#include <AK/OwnPtr.h> -#include <AK/Stream.h> -#include <AK/String.h> -#include <AK/StringView.h> -#include <AK/Types.h> -#include <AK/Vector.h> - -namespace Writer { - -enum class WriterBehaviour : u32 { - None = 0, - WriteHeaders = 1, - AllowNewlinesInFields = WriteHeaders << 1, - QuoteOnlyInFieldStart = WriteHeaders << 2, - QuoteAll = WriteHeaders << 3, -}; - -inline WriterBehaviour operator&(WriterBehaviour left, WriterBehaviour right) -{ - return static_cast<WriterBehaviour>(static_cast<u32>(left) & static_cast<u32>(right)); -} - -inline WriterBehaviour operator|(WriterBehaviour left, WriterBehaviour right) -{ - return static_cast<WriterBehaviour>(static_cast<u32>(left) | static_cast<u32>(right)); -} - -struct WriterTraits { - String separator; - String quote { "\"" }; - enum { - Repeat, - Backslash, - } quote_escape { Repeat }; -}; - -#define ENUMERATE_WRITE_ERRORS() \ - E(None, "No errors") \ - E(NonConformingColumnCount, "Header count does not match given column count") \ - E(InternalError, "Internal error") - -enum class WriteError { -#define E(name, _) name, - ENUMERATE_WRITE_ERRORS() -#undef E -}; - -inline constexpr WriterBehaviour default_behaviours() -{ - return WriterBehaviour::None; -} - -template<typename ContainerType> -class XSV { -public: - XSV(OutputStream& output, const ContainerType& data, const WriterTraits& traits, const Vector<StringView>& headers = {}, WriterBehaviour behaviours = default_behaviours()) - : m_data(data) - , m_traits(traits) - , m_behaviours(behaviours) - , m_names(headers) - , m_output(output) - { - if (!headers.is_empty()) - m_behaviours = m_behaviours | WriterBehaviour::WriteHeaders; - - generate(); - } - - virtual ~XSV() { } - - bool has_error() const { return m_error != WriteError::None; } - WriteError error() const { return m_error; } - String error_string() const - { - switch (m_error) { -#define E(x, y) \ - case WriteError::x: \ - return y; - - ENUMERATE_WRITE_ERRORS(); -#undef E - } - ASSERT_NOT_REACHED(); - } - -private: - void set_error(WriteError error) - { - if (m_error == WriteError::None) - m_error = error; - } - - void generate() - { - auto with_headers = (m_behaviours & WriterBehaviour::WriteHeaders) != WriterBehaviour::None; - if (with_headers) { - write_row(m_names); - if (m_output.write({ "\n", 1 }) != 1) - set_error(WriteError::InternalError); - } - - for (auto&& row : m_data) { - if (with_headers) { - if (row.size() != m_names.size()) - set_error(WriteError::NonConformingColumnCount); - } - - write_row(row); - if (m_output.write({ "\n", 1 }) != 1) - set_error(WriteError::InternalError); - } - } - - template<typename T> - void write_row(T&& row) - { - bool first = true; - for (auto&& entry : row) { - if (!first) { - if (m_output.write(m_traits.separator.bytes()) != m_traits.separator.length()) - set_error(WriteError::InternalError); - } - first = false; - write_entry(entry); - } - } - - template<typename T> - void write_entry(T&& entry) - { - auto string = String::formatted("{}", FormatIfSupported(entry)); - - auto safe_to_write_normally = !string.contains("\n") && !string.contains(m_traits.separator); - if (safe_to_write_normally) { - if ((m_behaviours & WriterBehaviour::QuoteOnlyInFieldStart) == WriterBehaviour::None) - safe_to_write_normally = !string.contains(m_traits.quote); - else - safe_to_write_normally = !string.starts_with(m_traits.quote); - } - if (safe_to_write_normally) { - if (m_output.write(string.bytes()) != string.length()) - set_error(WriteError::InternalError); - return; - } - - if (m_output.write(m_traits.quote.bytes()) != m_traits.quote.length()) - set_error(WriteError::InternalError); - - GenericLexer lexer(string); - while (!lexer.is_eof()) { - if (lexer.consume_specific(m_traits.quote)) { - switch (m_traits.quote_escape) { - case WriterTraits::Repeat: - if (m_output.write(m_traits.quote.bytes()) != m_traits.quote.length()) - set_error(WriteError::InternalError); - if (m_output.write(m_traits.quote.bytes()) != m_traits.quote.length()) - set_error(WriteError::InternalError); - break; - case WriterTraits::Backslash: - if (m_output.write({ "\\", 1 }) != 1) - set_error(WriteError::InternalError); - if (m_output.write(m_traits.quote.bytes()) != m_traits.quote.length()) - set_error(WriteError::InternalError); - break; - } - continue; - } - - auto ch = lexer.consume(); - if (m_output.write({ &ch, 1 }) != 1) - set_error(WriteError::InternalError); - } - - if (m_output.write(m_traits.quote.bytes()) != m_traits.quote.length()) - set_error(WriteError::InternalError); - } - - const ContainerType& m_data; - const WriterTraits& m_traits; - WriterBehaviour m_behaviours; - const Vector<StringView>& m_names; - WriteError m_error { WriteError::None }; - OutputStream& m_output; -}; - -} diff --git a/Applications/Spreadsheet/main.cpp b/Applications/Spreadsheet/main.cpp deleted file mode 100644 index e13a89cdac..0000000000 --- a/Applications/Spreadsheet/main.cpp +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "HelpWindow.h" -#include "Spreadsheet.h" -#include "SpreadsheetWidget.h" -#include <AK/ScopeGuard.h> -#include <LibCore/ArgsParser.h> -#include <LibCore/File.h> -#include <LibGUI/Application.h> -#include <LibGUI/Clipboard.h> -#include <LibGUI/FilePicker.h> -#include <LibGUI/Forward.h> -#include <LibGUI/Icon.h> -#include <LibGUI/Menu.h> -#include <LibGUI/MenuBar.h> -#include <LibGUI/Window.h> - -int main(int argc, char* argv[]) -{ - if (pledge("stdio shared_buffer accept rpath unix cpath wpath fattr thread", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto app = GUI::Application::construct(argc, argv); - - if (pledge("stdio thread rpath accept cpath wpath shared_buffer unix", nullptr) < 0) { - perror("pledge"); - return 1; - } - - const char* filename = nullptr; - - Core::ArgsParser args_parser; - args_parser.add_positional_argument(filename, "File to read from", "file", Core::ArgsParser::Required::No); - - args_parser.parse(argc, argv); - - if (filename) { - if (!Core::File::exists(filename) || Core::File::is_directory(filename)) { - warnln("File does not exist or is a directory: {}", filename); - return 1; - } - } - - if (unveil("/tmp/portal/webcontent", "rw") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/etc", "r") < 0) { - perror("unveil"); - return 1; - } - - if (unveil(Core::StandardPaths::home_directory().characters(), "rwc") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/res", "r") < 0) { - perror("unveil"); - return 1; - } - - if (unveil(nullptr, nullptr) < 0) { - perror("unveil"); - return 1; - } - - auto app_icon = GUI::Icon::default_icon("app-spreadsheet"); - auto window = GUI::Window::construct(); - window->set_title("Spreadsheet"); - window->resize(640, 480); - window->set_icon(app_icon.bitmap_for_size(16)); - - auto& spreadsheet_widget = window->set_main_widget<Spreadsheet::SpreadsheetWidget>(NonnullRefPtrVector<Spreadsheet::Sheet> {}, filename == nullptr); - - if (filename) - spreadsheet_widget.load(filename); - - auto menubar = GUI::MenuBar::construct(); - auto& app_menu = menubar->add_menu("Spreadsheet"); - - app_menu.add_action(GUI::Action::create("Add New Sheet", Gfx::Bitmap::load_from_file("/res/icons/16x16/new-tab.png"), [&](auto&) { - spreadsheet_widget.add_sheet(); - })); - - app_menu.add_separator(); - - app_menu.add_action(GUI::CommonActions::make_quit_action([&](auto&) { - if (!spreadsheet_widget.request_close()) - return; - app->quit(0); - })); - - window->on_close_request = [&]() -> GUI::Window::CloseRequestDecision { - if (spreadsheet_widget.request_close()) - return GUI::Window::CloseRequestDecision::Close; - return GUI::Window::CloseRequestDecision::StayOpen; - }; - - auto& file_menu = menubar->add_menu("File"); - file_menu.add_action(GUI::CommonActions::make_open_action([&](auto&) { - Optional<String> load_path = GUI::FilePicker::get_open_filepath(window); - if (!load_path.has_value()) - return; - - spreadsheet_widget.load(load_path.value()); - })); - - file_menu.add_action(GUI::CommonActions::make_save_action([&](auto&) { - if (spreadsheet_widget.current_filename().is_empty()) { - String name = "workbook"; - Optional<String> save_path = GUI::FilePicker::get_save_filepath(window, name, "sheets"); - if (!save_path.has_value()) - return; - - spreadsheet_widget.save(save_path.value()); - } else { - spreadsheet_widget.save(spreadsheet_widget.current_filename()); - } - })); - - file_menu.add_action(GUI::CommonActions::make_save_as_action([&](auto&) { - auto current_filename = spreadsheet_widget.current_filename(); - String name = "workbook"; - Optional<String> save_path = GUI::FilePicker::get_save_filepath(window, name, "sheets"); - if (!save_path.has_value()) - return; - - spreadsheet_widget.save(save_path.value()); - - if (!current_filename.is_empty()) - spreadsheet_widget.set_filename(current_filename); - })); - - auto& edit_menu = menubar->add_menu("Edit"); - edit_menu.add_action(GUI::CommonActions::make_copy_action([&](auto&) { - /// text/x-spreadsheet-data: - /// - currently selected cell - /// - selected cell+ - auto& cells = spreadsheet_widget.current_worksheet().selected_cells(); - ASSERT(!cells.is_empty()); - StringBuilder text_builder, url_builder; - bool first = true; - auto cursor = spreadsheet_widget.current_selection_cursor(); - if (cursor) { - Spreadsheet::Position position { spreadsheet_widget.current_worksheet().column(cursor->column()), (size_t)cursor->row() }; - url_builder.append(position.to_url().to_string()); - url_builder.append('\n'); - } - - for (auto& cell : cells) { - if (first && !cursor) { - url_builder.append(cell.to_url().to_string()); - url_builder.append('\n'); - } - - url_builder.append(cell.to_url().to_string()); - url_builder.append('\n'); - - auto cell_data = spreadsheet_widget.current_worksheet().at(cell); - if (!first) - text_builder.append('\t'); - if (cell_data) - text_builder.append(cell_data->data()); - first = false; - } - HashMap<String, String> metadata; - metadata.set("text/x-spreadsheet-data", url_builder.to_string()); - - GUI::Clipboard::the().set_data(text_builder.string_view().bytes(), "text/plain", move(metadata)); - }, - window)); - edit_menu.add_action(GUI::CommonActions::make_paste_action([&](auto&) { - ScopeGuard update_after_paste { [&] { spreadsheet_widget.update(); } }; - - auto& cells = spreadsheet_widget.current_worksheet().selected_cells(); - ASSERT(!cells.is_empty()); - const auto& data = GUI::Clipboard::the().data_and_type(); - if (auto spreadsheet_data = data.metadata.get("text/x-spreadsheet-data"); spreadsheet_data.has_value()) { - Vector<Spreadsheet::Position> source_positions, target_positions; - auto& sheet = spreadsheet_widget.current_worksheet(); - - for (auto& line : spreadsheet_data.value().split_view('\n')) { - dbgln("Paste line '{}'", line); - auto position = sheet.position_from_url(line); - if (position.has_value()) - source_positions.append(position.release_value()); - } - - for (auto& position : spreadsheet_widget.current_worksheet().selected_cells()) - target_positions.append(position); - - if (source_positions.is_empty()) - return; - - auto first_position = source_positions.take_first(); - sheet.copy_cells(move(source_positions), move(target_positions), first_position); - } else { - for (auto& cell : spreadsheet_widget.current_worksheet().selected_cells()) - spreadsheet_widget.current_worksheet().ensure(cell).set_data(StringView { data.data.data(), data.data.size() }); - spreadsheet_widget.update(); - } - }, - window)); - - auto& help_menu = menubar->add_menu("Help"); - - help_menu.add_action(GUI::Action::create( - "Functions Help", [&](auto&) { - auto docs = spreadsheet_widget.current_worksheet().gather_documentation(); - auto help_window = Spreadsheet::HelpWindow::the(window); - help_window->set_docs(move(docs)); - help_window->show(); - }, - window)); - - app_menu.add_separator(); - - help_menu.add_action(GUI::CommonActions::make_about_action("Spreadsheet", app_icon, window)); - - app->set_menubar(move(menubar)); - - window->show(); - - return app->exec(); -} diff --git a/Applications/SystemMonitor/CMakeLists.txt b/Applications/SystemMonitor/CMakeLists.txt deleted file mode 100644 index ee2e4d2323..0000000000 --- a/Applications/SystemMonitor/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -set(SOURCES - DevicesModel.cpp - GraphWidget.cpp - InterruptsWidget.cpp - main.cpp - MemoryStatsWidget.cpp - NetworkStatisticsWidget.cpp - ProcessFileDescriptorMapWidget.cpp - ProcessMemoryMapWidget.cpp - ProcessModel.cpp - ProcessUnveiledPathsWidget.cpp - ThreadStackWidget.cpp -) - -serenity_app(SystemMonitor ICON app-system-monitor) -target_link_libraries(SystemMonitor LibGUI LibPCIDB) diff --git a/Applications/SystemMonitor/DevicesModel.cpp b/Applications/SystemMonitor/DevicesModel.cpp deleted file mode 100644 index f0692cdd24..0000000000 --- a/Applications/SystemMonitor/DevicesModel.cpp +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@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 "DevicesModel.h" -#include <AK/JsonArray.h> -#include <AK/JsonObject.h> -#include <AK/JsonValue.h> -#include <LibCore/DirIterator.h> -#include <LibCore/File.h> -#include <sys/stat.h> - -NonnullRefPtr<DevicesModel> DevicesModel::create() -{ - return adopt(*new DevicesModel); -} - -DevicesModel::DevicesModel() -{ -} - -DevicesModel::~DevicesModel() -{ -} - -int DevicesModel::row_count(const GUI::ModelIndex&) const -{ - return m_devices.size(); -} - -int DevicesModel::column_count(const GUI::ModelIndex&) const -{ - return Column::__Count; -} - -String DevicesModel::column_name(int column) const -{ - switch (column) { - case Column::Device: - return "Device"; - case Column::Major: - return "Major"; - case Column::Minor: - return "Minor"; - case Column::ClassName: - return "Class"; - case Column::Type: - return "Type"; - default: - ASSERT_NOT_REACHED(); - } -} - -GUI::Variant DevicesModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const -{ - ASSERT(is_valid(index)); - - if (role == GUI::ModelRole::TextAlignment) { - switch (index.column()) { - case Column::Device: - return Gfx::TextAlignment::CenterLeft; - case Column::Major: - return Gfx::TextAlignment::CenterRight; - case Column::Minor: - return Gfx::TextAlignment::CenterRight; - case Column::ClassName: - return Gfx::TextAlignment::CenterLeft; - case Column::Type: - return Gfx::TextAlignment::CenterLeft; - } - return {}; - } - - if (role == GUI::ModelRole::Sort) { - const DeviceInfo& device = m_devices[index.row()]; - switch (index.column()) { - case Column::Device: - return device.path; - case Column::Major: - return device.major; - case Column::Minor: - return device.minor; - case Column::ClassName: - return device.class_name; - case Column::Type: - return device.type; - default: - ASSERT_NOT_REACHED(); - } - } - - if (role == GUI::ModelRole::Display) { - const DeviceInfo& device = m_devices[index.row()]; - switch (index.column()) { - case Column::Device: - return device.path; - case Column::Major: - return device.major; - case Column::Minor: - return device.minor; - case Column::ClassName: - return device.class_name; - case Column::Type: - switch (device.type) { - case DeviceInfo::Type::Block: - return "Block"; - case DeviceInfo::Type::Character: - return "Character"; - default: - ASSERT_NOT_REACHED(); - } - default: - ASSERT_NOT_REACHED(); - } - } - - return {}; -} - -void DevicesModel::update() -{ - auto proc_devices = Core::File::construct("/proc/devices"); - if (!proc_devices->open(Core::IODevice::OpenMode::ReadOnly)) - ASSERT_NOT_REACHED(); - - auto json = JsonValue::from_string(proc_devices->read_all()); - ASSERT(json.has_value()); - - m_devices.clear(); - json.value().as_array().for_each([this](auto& value) { - JsonObject device = value.as_object(); - DeviceInfo device_info; - - device_info.major = device.get("major").to_uint(); - device_info.minor = device.get("minor").to_uint(); - device_info.class_name = device.get("class_name").to_string(); - - String type_str = device.get("type").to_string(); - if (type_str == "block") - device_info.type = DeviceInfo::Type::Block; - else if (type_str == "character") - device_info.type = DeviceInfo::Type::Character; - else - ASSERT_NOT_REACHED(); - - m_devices.append(move(device_info)); - }); - - auto fill_in_paths_from_dir = [this](const String& dir) { - Core::DirIterator dir_iter { dir, Core::DirIterator::Flags::SkipDots }; - while (dir_iter.has_next()) { - auto name = dir_iter.next_path(); - auto path = String::format("%s/%s", dir.characters(), name.characters()); - struct stat statbuf; - if (lstat(path.characters(), &statbuf) != 0) { - ASSERT_NOT_REACHED(); - } - if (!S_ISBLK(statbuf.st_mode) && !S_ISCHR(statbuf.st_mode)) - continue; - unsigned _major = major(statbuf.st_rdev); - unsigned _minor = minor(statbuf.st_rdev); - - auto it = m_devices.find_if([_major, _minor](const auto& device_info) { - return device_info.major == _major && device_info.minor == _minor; - }); - if (it != m_devices.end()) { - (*it).path = move(path); - } - } - }; - - fill_in_paths_from_dir("/dev"); - fill_in_paths_from_dir("/dev/pts"); - - did_update(); -} diff --git a/Applications/SystemMonitor/DevicesModel.h b/Applications/SystemMonitor/DevicesModel.h deleted file mode 100644 index dd8cacc7e0..0000000000 --- a/Applications/SystemMonitor/DevicesModel.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@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 <AK/String.h> -#include <AK/Vector.h> -#include <LibGUI/Model.h> - -class DevicesModel final : public GUI::Model { -public: - enum Column { - Device = 0, - Major, - Minor, - ClassName, - Type, - __Count - }; - - virtual ~DevicesModel() override; - static NonnullRefPtr<DevicesModel> create(); - - virtual int row_count(const GUI::ModelIndex&) const override; - virtual int column_count(const GUI::ModelIndex&) const override; - virtual String column_name(int column) const override; - virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override; - virtual void update() override; - -private: - DevicesModel(); - - struct DeviceInfo { - String path; - unsigned major; - unsigned minor; - String class_name; - enum Type { - Block, - Character - }; - Type type; - }; - - Vector<DeviceInfo> m_devices; -}; diff --git a/Applications/SystemMonitor/GraphWidget.cpp b/Applications/SystemMonitor/GraphWidget.cpp deleted file mode 100644 index 5c1d9982d4..0000000000 --- a/Applications/SystemMonitor/GraphWidget.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "GraphWidget.h" -#include <LibGUI/Painter.h> -#include <LibGfx/Font.h> -#include <LibGfx/Path.h> - -GraphWidget::GraphWidget() -{ -} - -GraphWidget::~GraphWidget() -{ -} - -void GraphWidget::add_value(Vector<int, 1>&& value) -{ - m_values.enqueue(move(value)); - update(); -} - -void GraphWidget::paint_event(GUI::PaintEvent& event) -{ - GUI::Frame::paint_event(event); - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - painter.add_clip_rect(frame_inner_rect()); - painter.fill_rect(event.rect(), m_background_color); - - auto inner_rect = frame_inner_rect(); - float scale = (float)inner_rect.height() / (float)m_max; - - if (!m_values.is_empty()) { - // Draw one set of values at a time - for (size_t k = 0; k < m_value_format.size(); k++) { - const auto& format = m_value_format[k]; - if (format.line_color == Color::Transparent && format.background_color == Color::Transparent) - continue; - m_calculated_points.clear_with_capacity(); - for (size_t i = 0; i < m_values.size(); i++) { - int x = inner_rect.right() - (i * 2) + 1; - if (x < 0) - break; - const auto& current_values = m_values.at(m_values.size() - i - 1); - if (current_values.size() <= k) { - // Don't have a data point - m_calculated_points.append({ -1, -1 }); - continue; - } - float value = current_values[k]; - if (m_stack_values) { - for (size_t l = k + 1; l < current_values.size(); l++) - value += current_values[l]; - } - float scaled_value = value * scale; - Gfx::IntPoint current_point { x, inner_rect.bottom() - (int)scaled_value }; - m_calculated_points.append(current_point); - } - ASSERT(m_calculated_points.size() <= m_values.size()); - if (format.background_color != Color::Transparent) { - // Fill the background for the area we have values for - Gfx::Path path; - size_t points_in_path = 0; - bool started_path = false; - const Gfx::IntPoint* current_point = nullptr; - const Gfx::IntPoint* first_point = nullptr; - auto check_fill_area = [&]() { - if (!started_path) - return; - if (points_in_path > 1) { - ASSERT(current_point); - ASSERT(first_point); - path.line_to({ current_point->x() - 1, inner_rect.bottom() + 1 }); - path.line_to({ first_point->x() + 1, inner_rect.bottom() + 1 }); - path.close(); - painter.fill_path(path, format.background_color, Gfx::Painter::WindingRule::EvenOdd); - } else if (points_in_path == 1 && current_point) { - // Can't fill any area, we only have one data point. - // Just draw a vertical line as a "fill"... - painter.draw_line(*current_point, { current_point->x(), inner_rect.bottom() }, format.background_color); - } - path = {}; - points_in_path = 0; - first_point = nullptr; - started_path = false; - }; - for (size_t i = 0; i < m_calculated_points.size(); i++) { - current_point = &m_calculated_points[i]; - if (current_point->x() < 0) { - check_fill_area(); - continue; - } - if (!started_path) { - path.move_to({ current_point->x() + 1, current_point->y() }); - points_in_path = 1; - first_point = current_point; - started_path = true; - } else { - path.line_to({ current_point->x(), current_point->y() }); - points_in_path++; - } - } - check_fill_area(); - } - if (format.line_color != Color::Transparent) { - // Draw the line for the data points we have - const Gfx::IntPoint* previous_point = nullptr; - for (size_t i = 0; i < m_calculated_points.size(); i++) { - const auto& current_point = m_calculated_points[i]; - if (current_point.x() < 0) { - previous_point = nullptr; - continue; - } - if (previous_point) - painter.draw_line(*previous_point, current_point, format.line_color); - previous_point = ¤t_point; - } - } - } - } - - if (!m_values.is_empty() && !m_value_format.is_empty()) { - const auto& current_values = m_values.last(); - int y = 0; - for (size_t i = 0; i < min(m_value_format.size(), current_values.size()); i++) { - const auto& format = m_value_format[i]; - if (!format.text_formatter) - continue; - auto constrain_rect = inner_rect.shrunken(8, 8); - auto text_rect = constrain_rect.translated(0, y).intersected(constrain_rect); - text_rect.set_height(font().glyph_height()); - auto text = format.text_formatter(current_values[i]); - if (format.text_shadow_color != Color::Transparent) - painter.draw_text(text_rect.translated(1, 1), text.characters(), Gfx::TextAlignment::CenterRight, format.text_shadow_color); - painter.draw_text(text_rect, text.characters(), Gfx::TextAlignment::CenterRight, format.line_color); - y += text_rect.height() + 4; - } - } -} diff --git a/Applications/SystemMonitor/GraphWidget.h b/Applications/SystemMonitor/GraphWidget.h deleted file mode 100644 index fc40f59bbd..0000000000 --- a/Applications/SystemMonitor/GraphWidget.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 <AK/CircularQueue.h> -#include <LibGUI/Frame.h> - -class GraphWidget final : public GUI::Frame { - C_OBJECT(GraphWidget) -public: - virtual ~GraphWidget() override; - - void set_max(int max) { m_max = max; } - int max() const { return m_max; } - - void add_value(Vector<int, 1>&&); - - void set_background_color(Color color) { m_background_color = color; } - - struct ValueFormat { - Color line_color { Color::Transparent }; - Color background_color { Color::Transparent }; - Color text_shadow_color { Color::Transparent }; - Function<String(int)> text_formatter; - }; - void set_value_format(size_t index, ValueFormat&& format) - { - if (m_value_format.size() <= index) - m_value_format.resize(index + 1); - m_value_format[index] = move(format); - } - void set_stack_values(bool stack_values) { m_stack_values = stack_values; } - -private: - explicit GraphWidget(); - - virtual void paint_event(GUI::PaintEvent&) override; - - int m_max { 100 }; - Vector<ValueFormat, 1> m_value_format; - CircularQueue<Vector<int, 1>, 4000> m_values; - Color m_background_color { Color::Black }; - bool m_stack_values { false }; - - Vector<Gfx::IntPoint, 1> m_calculated_points; -}; diff --git a/Applications/SystemMonitor/InterruptsWidget.cpp b/Applications/SystemMonitor/InterruptsWidget.cpp deleted file mode 100644 index 3e56f29ec4..0000000000 --- a/Applications/SystemMonitor/InterruptsWidget.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "InterruptsWidget.h" -#include <LibGUI/BoxLayout.h> -#include <LibGUI/GroupBox.h> -#include <LibGUI/JsonArrayModel.h> -#include <LibGUI/SortingProxyModel.h> -#include <LibGUI/TableView.h> - -InterruptsWidget::InterruptsWidget() -{ - on_first_show = [this](auto&) { - set_layout<GUI::VerticalBoxLayout>(); - layout()->set_margins({ 4, 4, 4, 4 }); - - Vector<GUI::JsonArrayModel::FieldSpec> interrupts_field; - interrupts_field.empend("interrupt_line", "Line", Gfx::TextAlignment::CenterRight); - interrupts_field.empend("purpose", "Purpose", Gfx::TextAlignment::CenterLeft); - interrupts_field.empend("controller", "Controller", Gfx::TextAlignment::CenterLeft); - interrupts_field.empend("cpu_handler", "CPU Handler", Gfx::TextAlignment::CenterRight); - interrupts_field.empend("device_sharing", "# Devices Sharing", Gfx::TextAlignment::CenterRight); - interrupts_field.empend("call_count", "Call Count", Gfx::TextAlignment::CenterRight); - - m_interrupt_table_view = add<GUI::TableView>(); - m_interrupt_model = GUI::JsonArrayModel::create("/proc/interrupts", move(interrupts_field)); - m_interrupt_table_view->set_model(GUI::SortingProxyModel::create(*m_interrupt_model)); - - m_update_timer = add<Core::Timer>( - 1000, [this] { - update_model(); - }); - - update_model(); - }; -} - -InterruptsWidget::~InterruptsWidget() -{ -} - -void InterruptsWidget::update_model() -{ - m_interrupt_table_view->model()->update(); -} diff --git a/Applications/SystemMonitor/InterruptsWidget.h b/Applications/SystemMonitor/InterruptsWidget.h deleted file mode 100644 index bc11215217..0000000000 --- a/Applications/SystemMonitor/InterruptsWidget.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 <LibCore/Timer.h> -#include <LibGUI/LazyWidget.h> - -class InterruptsWidget final : public GUI::LazyWidget { - C_OBJECT(InterruptsWidget) -public: - virtual ~InterruptsWidget() override; - -private: - InterruptsWidget(); - void update_model(); - - RefPtr<GUI::TableView> m_interrupt_table_view; - RefPtr<GUI::JsonArrayModel> m_interrupt_model; - RefPtr<Core::Timer> m_update_timer; -}; diff --git a/Applications/SystemMonitor/MemoryStatsWidget.cpp b/Applications/SystemMonitor/MemoryStatsWidget.cpp deleted file mode 100644 index 6675ea8c4a..0000000000 --- a/Applications/SystemMonitor/MemoryStatsWidget.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "MemoryStatsWidget.h" -#include "GraphWidget.h" -#include <AK/ByteBuffer.h> -#include <AK/JsonObject.h> -#include <LibCore/File.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Label.h> -#include <LibGUI/Painter.h> -#include <LibGfx/Font.h> -#include <LibGfx/FontDatabase.h> -#include <LibGfx/StylePainter.h> -#include <stdio.h> -#include <stdlib.h> - -static MemoryStatsWidget* s_the; - -MemoryStatsWidget* MemoryStatsWidget::the() -{ - return s_the; -} - -MemoryStatsWidget::MemoryStatsWidget(GraphWidget& graph) - : m_graph(graph) -{ - ASSERT(!s_the); - s_the = this; - - set_fixed_height(110); - - set_layout<GUI::VerticalBoxLayout>(); - layout()->set_margins({ 0, 8, 0, 0 }); - layout()->set_spacing(3); - - auto build_widgets_for_label = [this](const String& description) -> RefPtr<GUI::Label> { - auto& container = add<GUI::Widget>(); - container.set_layout<GUI::HorizontalBoxLayout>(); - container.set_fixed_size(275, 12); - auto& description_label = container.add<GUI::Label>(description); - description_label.set_font(Gfx::FontDatabase::default_bold_font()); - description_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - auto& label = container.add<GUI::Label>(); - label.set_text_alignment(Gfx::TextAlignment::CenterRight); - return label; - }; - - m_user_physical_pages_label = build_widgets_for_label("Physical memory:"); - m_user_physical_pages_committed_label = build_widgets_for_label("Committed memory:"); - m_supervisor_physical_pages_label = build_widgets_for_label("Supervisor physical:"); - m_kmalloc_space_label = build_widgets_for_label("Kernel heap:"); - m_kmalloc_count_label = build_widgets_for_label("Calls kmalloc:"); - m_kfree_count_label = build_widgets_for_label("Calls kfree:"); - m_kmalloc_difference_label = build_widgets_for_label("Difference:"); - - refresh(); -} - -MemoryStatsWidget::~MemoryStatsWidget() -{ -} - -static inline size_t page_count_to_kb(size_t kb) -{ - return (kb * 4096) / 1024; -} - -static inline size_t bytes_to_kb(size_t bytes) -{ - return bytes / 1024; -} - -void MemoryStatsWidget::refresh() -{ - auto proc_memstat = Core::File::construct("/proc/memstat"); - if (!proc_memstat->open(Core::IODevice::OpenMode::ReadOnly)) - ASSERT_NOT_REACHED(); - - auto file_contents = proc_memstat->read_all(); - auto json_result = JsonValue::from_string(file_contents); - ASSERT(json_result.has_value()); - auto json = json_result.value().as_object(); - - [[maybe_unused]] unsigned kmalloc_eternal_allocated = json.get("kmalloc_eternal_allocated").to_u32(); - unsigned kmalloc_allocated = json.get("kmalloc_allocated").to_u32(); - unsigned kmalloc_available = json.get("kmalloc_available").to_u32(); - unsigned user_physical_allocated = json.get("user_physical_allocated").to_u32(); - unsigned user_physical_available = json.get("user_physical_available").to_u32(); - unsigned user_physical_committed = json.get("user_physical_committed").to_u32(); - unsigned user_physical_uncommitted = json.get("user_physical_uncommitted").to_u32(); - unsigned super_physical_alloc = json.get("super_physical_allocated").to_u32(); - unsigned super_physical_free = json.get("super_physical_available").to_u32(); - unsigned kmalloc_call_count = json.get("kmalloc_call_count").to_u32(); - unsigned kfree_call_count = json.get("kfree_call_count").to_u32(); - - size_t kmalloc_bytes_total = kmalloc_allocated + kmalloc_available; - size_t user_physical_pages_total = user_physical_allocated + user_physical_available; - size_t supervisor_pages_total = super_physical_alloc + super_physical_free; - - size_t physical_pages_total = user_physical_pages_total + supervisor_pages_total; - size_t physical_pages_in_use = user_physical_allocated + super_physical_alloc; - size_t total_userphysical_and_swappable_pages = user_physical_allocated + user_physical_committed + user_physical_uncommitted; - - m_kmalloc_space_label->set_text(String::formatted("{}K/{}K", bytes_to_kb(kmalloc_allocated), bytes_to_kb(kmalloc_bytes_total))); - m_user_physical_pages_label->set_text(String::formatted("{}K/{}K", page_count_to_kb(physical_pages_in_use), page_count_to_kb(physical_pages_total))); - m_user_physical_pages_committed_label->set_text(String::formatted("{}K", page_count_to_kb(user_physical_committed))); - m_supervisor_physical_pages_label->set_text(String::formatted("{}K/{}K", page_count_to_kb(super_physical_alloc), page_count_to_kb(supervisor_pages_total))); - m_kmalloc_count_label->set_text(String::formatted("{}", kmalloc_call_count)); - m_kfree_count_label->set_text(String::formatted("{}", kfree_call_count)); - m_kmalloc_difference_label->set_text(String::formatted("{:+}", kmalloc_call_count - kfree_call_count)); - - m_graph.set_max(page_count_to_kb(total_userphysical_and_swappable_pages) + bytes_to_kb(kmalloc_bytes_total)); - m_graph.add_value({ (int)page_count_to_kb(user_physical_committed), (int)page_count_to_kb(user_physical_allocated), (int)bytes_to_kb(kmalloc_bytes_total) }); -} diff --git a/Applications/SystemMonitor/MemoryStatsWidget.h b/Applications/SystemMonitor/MemoryStatsWidget.h deleted file mode 100644 index e915730257..0000000000 --- a/Applications/SystemMonitor/MemoryStatsWidget.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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> - -class GraphWidget; - -class MemoryStatsWidget final : public GUI::Widget { - C_OBJECT(MemoryStatsWidget) -public: - static MemoryStatsWidget* the(); - - virtual ~MemoryStatsWidget() override; - - void refresh(); - -private: - MemoryStatsWidget(GraphWidget& graph); - - GraphWidget& m_graph; - RefPtr<GUI::Label> m_user_physical_pages_label; - RefPtr<GUI::Label> m_user_physical_pages_committed_label; - RefPtr<GUI::Label> m_supervisor_physical_pages_label; - RefPtr<GUI::Label> m_kmalloc_space_label; - RefPtr<GUI::Label> m_kmalloc_count_label; - RefPtr<GUI::Label> m_kfree_count_label; - RefPtr<GUI::Label> m_kmalloc_difference_label; -}; diff --git a/Applications/SystemMonitor/NetworkStatisticsWidget.cpp b/Applications/SystemMonitor/NetworkStatisticsWidget.cpp deleted file mode 100644 index 7ee2450ee1..0000000000 --- a/Applications/SystemMonitor/NetworkStatisticsWidget.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "NetworkStatisticsWidget.h" -#include <LibGUI/BoxLayout.h> -#include <LibGUI/GroupBox.h> -#include <LibGUI/JsonArrayModel.h> -#include <LibGUI/SortingProxyModel.h> -#include <LibGUI/TableView.h> - -NetworkStatisticsWidget::NetworkStatisticsWidget() -{ - on_first_show = [this](auto&) { - set_layout<GUI::VerticalBoxLayout>(); - layout()->set_margins({ 4, 4, 4, 4 }); - set_fill_with_background_color(true); - - auto& adapters_group_box = add<GUI::GroupBox>("Adapters"); - adapters_group_box.set_layout<GUI::VerticalBoxLayout>(); - adapters_group_box.layout()->set_margins({ 6, 16, 6, 6 }); - adapters_group_box.set_fixed_height(120); - - m_adapter_table_view = adapters_group_box.add<GUI::TableView>(); - - Vector<GUI::JsonArrayModel::FieldSpec> net_adapters_fields; - net_adapters_fields.empend("name", "Name", Gfx::TextAlignment::CenterLeft); - net_adapters_fields.empend("class_name", "Class", Gfx::TextAlignment::CenterLeft); - net_adapters_fields.empend("mac_address", "MAC", Gfx::TextAlignment::CenterLeft); - net_adapters_fields.empend("ipv4_address", "IPv4", Gfx::TextAlignment::CenterLeft); - net_adapters_fields.empend("packets_in", "Pkt In", Gfx::TextAlignment::CenterRight); - net_adapters_fields.empend("packets_out", "Pkt Out", Gfx::TextAlignment::CenterRight); - net_adapters_fields.empend("bytes_in", "Bytes In", Gfx::TextAlignment::CenterRight); - net_adapters_fields.empend("bytes_out", "Bytes Out", Gfx::TextAlignment::CenterRight); - m_adapter_model = GUI::JsonArrayModel::create("/proc/net/adapters", move(net_adapters_fields)); - m_adapter_table_view->set_model(GUI::SortingProxyModel::create(*m_adapter_model)); - - auto& sockets_group_box = add<GUI::GroupBox>("Sockets"); - sockets_group_box.set_layout<GUI::VerticalBoxLayout>(); - sockets_group_box.layout()->set_margins({ 6, 16, 6, 6 }); - - m_socket_table_view = sockets_group_box.add<GUI::TableView>(); - - Vector<GUI::JsonArrayModel::FieldSpec> net_tcp_fields; - net_tcp_fields.empend("peer_address", "Peer", Gfx::TextAlignment::CenterLeft); - net_tcp_fields.empend("peer_port", "Port", Gfx::TextAlignment::CenterRight); - net_tcp_fields.empend("local_address", "Local", Gfx::TextAlignment::CenterLeft); - net_tcp_fields.empend("local_port", "Port", Gfx::TextAlignment::CenterRight); - net_tcp_fields.empend("state", "State", Gfx::TextAlignment::CenterLeft); - net_tcp_fields.empend("ack_number", "Ack#", Gfx::TextAlignment::CenterRight); - net_tcp_fields.empend("sequence_number", "Seq#", Gfx::TextAlignment::CenterRight); - net_tcp_fields.empend("packets_in", "Pkt In", Gfx::TextAlignment::CenterRight); - net_tcp_fields.empend("packets_out", "Pkt Out", Gfx::TextAlignment::CenterRight); - net_tcp_fields.empend("bytes_in", "Bytes In", Gfx::TextAlignment::CenterRight); - net_tcp_fields.empend("bytes_out", "Bytes Out", Gfx::TextAlignment::CenterRight); - m_socket_model = GUI::JsonArrayModel::create("/proc/net/tcp", move(net_tcp_fields)); - m_socket_table_view->set_model(GUI::SortingProxyModel::create(*m_socket_model)); - - m_update_timer = add<Core::Timer>( - 1000, [this] { - update_models(); - }); - - update_models(); - }; -} - -NetworkStatisticsWidget::~NetworkStatisticsWidget() -{ -} - -void NetworkStatisticsWidget::update_models() -{ - m_adapter_table_view->model()->update(); - m_socket_table_view->model()->update(); -} diff --git a/Applications/SystemMonitor/NetworkStatisticsWidget.h b/Applications/SystemMonitor/NetworkStatisticsWidget.h deleted file mode 100644 index d49a9641c8..0000000000 --- a/Applications/SystemMonitor/NetworkStatisticsWidget.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 <LibCore/Timer.h> -#include <LibGUI/LazyWidget.h> - -class NetworkStatisticsWidget final : public GUI::LazyWidget { - C_OBJECT(NetworkStatisticsWidget) -public: - virtual ~NetworkStatisticsWidget() override; - -private: - NetworkStatisticsWidget(); - void update_models(); - - RefPtr<GUI::TableView> m_adapter_table_view; - RefPtr<GUI::TableView> m_socket_table_view; - RefPtr<GUI::JsonArrayModel> m_adapter_model; - RefPtr<GUI::JsonArrayModel> m_socket_model; - RefPtr<Core::Timer> m_update_timer; -}; diff --git a/Applications/SystemMonitor/ProcessFileDescriptorMapWidget.cpp b/Applications/SystemMonitor/ProcessFileDescriptorMapWidget.cpp deleted file mode 100644 index 48dfae098d..0000000000 --- a/Applications/SystemMonitor/ProcessFileDescriptorMapWidget.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "ProcessFileDescriptorMapWidget.h" -#include <LibGUI/BoxLayout.h> -#include <LibGUI/JsonArrayModel.h> -#include <LibGUI/SortingProxyModel.h> -#include <LibGUI/TableView.h> - -ProcessFileDescriptorMapWidget::ProcessFileDescriptorMapWidget() -{ - set_layout<GUI::VerticalBoxLayout>(); - layout()->set_margins({ 4, 4, 4, 4 }); - m_table_view = add<GUI::TableView>(); - - Vector<GUI::JsonArrayModel::FieldSpec> pid_fds_fields; - pid_fds_fields.empend("fd", "FD", Gfx::TextAlignment::CenterRight); - pid_fds_fields.empend("class", "Class", Gfx::TextAlignment::CenterLeft); - pid_fds_fields.empend("offset", "Offset", Gfx::TextAlignment::CenterRight); - pid_fds_fields.empend("absolute_path", "Path", Gfx::TextAlignment::CenterLeft); - pid_fds_fields.empend("Access", Gfx::TextAlignment::CenterLeft, [](auto& object) { - return object.get("seekable").to_bool() ? "Seekable" : "Sequential"; - }); - pid_fds_fields.empend("Blocking", Gfx::TextAlignment::CenterLeft, [](auto& object) { - return object.get("blocking").to_bool() ? "Blocking" : "Nonblocking"; - }); - pid_fds_fields.empend("On exec", Gfx::TextAlignment::CenterLeft, [](auto& object) { - return object.get("cloexec").to_bool() ? "Close" : "Keep"; - }); - pid_fds_fields.empend("Can read", Gfx::TextAlignment::CenterLeft, [](auto& object) { - return object.get("can_read").to_bool() ? "Yes" : "No"; - }); - pid_fds_fields.empend("Can write", Gfx::TextAlignment::CenterLeft, [](auto& object) { - return object.get("can_write").to_bool() ? "Yes" : "No"; - }); - - m_model = GUI::JsonArrayModel::create({}, move(pid_fds_fields)); - m_table_view->set_model(GUI::SortingProxyModel::create(*m_model)); -} - -ProcessFileDescriptorMapWidget::~ProcessFileDescriptorMapWidget() -{ -} - -void ProcessFileDescriptorMapWidget::set_pid(pid_t pid) -{ - if (m_pid == pid) - return; - m_pid = pid; - m_model->set_json_path(String::formatted("/proc/{}/fds", m_pid)); -} diff --git a/Applications/SystemMonitor/ProcessFileDescriptorMapWidget.h b/Applications/SystemMonitor/ProcessFileDescriptorMapWidget.h deleted file mode 100644 index 2c45577525..0000000000 --- a/Applications/SystemMonitor/ProcessFileDescriptorMapWidget.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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> - -class ProcessFileDescriptorMapWidget final : public GUI::Widget { - C_OBJECT(ProcessFileDescriptorMapWidget); - -public: - virtual ~ProcessFileDescriptorMapWidget() override; - - void set_pid(pid_t); - -private: - ProcessFileDescriptorMapWidget(); - - RefPtr<GUI::TableView> m_table_view; - RefPtr<GUI::JsonArrayModel> m_model; - pid_t m_pid { -1 }; -}; diff --git a/Applications/SystemMonitor/ProcessMemoryMapWidget.cpp b/Applications/SystemMonitor/ProcessMemoryMapWidget.cpp deleted file mode 100644 index d36a7f57ab..0000000000 --- a/Applications/SystemMonitor/ProcessMemoryMapWidget.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "ProcessMemoryMapWidget.h" -#include <LibCore/Timer.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/JsonArrayModel.h> -#include <LibGUI/Painter.h> -#include <LibGUI/SortingProxyModel.h> -#include <LibGUI/TableView.h> -#include <LibGfx/Palette.h> - -class PagemapPaintingDelegate final : public GUI::TableCellPaintingDelegate { -public: - virtual ~PagemapPaintingDelegate() override { } - - virtual void paint(GUI::Painter& painter, const Gfx::IntRect& a_rect, const Gfx::Palette&, const GUI::ModelIndex& index) override - { - auto rect = a_rect.shrunken(2, 2); - auto pagemap = index.data(GUI::ModelRole::Custom).to_string(); - - float scale_factor = (float)pagemap.length() / (float)rect.width(); - - for (int i = 0; i < rect.width(); ++i) { - int x = rect.x() + i; - char c = pagemap[(float)i * scale_factor]; - Color color; - if (c == 'N') // Null (no page at all, typically an inode-backed page that hasn't been paged in.) - color = Color::White; - else if (c == 'Z') // Zero (globally shared zero page, typically an untouched anonymous page.) - color = Color::from_rgb(0xc0c0ff); - else if (c == 'P') // Physical (a resident page) - color = Color::Black; - else - ASSERT_NOT_REACHED(); - - painter.draw_line({ x, rect.top() }, { x, rect.bottom() }, color); - } - - painter.draw_rect(rect, Color::Black); - } -}; - -ProcessMemoryMapWidget::ProcessMemoryMapWidget() -{ - set_layout<GUI::VerticalBoxLayout>(); - layout()->set_margins({ 4, 4, 4, 4 }); - m_table_view = add<GUI::TableView>(); - Vector<GUI::JsonArrayModel::FieldSpec> pid_vm_fields; - pid_vm_fields.empend( - "Address", Gfx::TextAlignment::CenterLeft, - [](auto& object) { return String::formatted("{:#x}", object.get("address").to_u32()); }, - [](auto& object) { return object.get("address").to_u32(); }); - pid_vm_fields.empend("size", "Size", Gfx::TextAlignment::CenterRight); - pid_vm_fields.empend("amount_resident", "Resident", Gfx::TextAlignment::CenterRight); - pid_vm_fields.empend("amount_dirty", "Dirty", Gfx::TextAlignment::CenterRight); - pid_vm_fields.empend("Access", Gfx::TextAlignment::CenterLeft, [](auto& object) { - StringBuilder builder; - if (!object.get("user_accessible").to_bool()) - builder.append('K'); - if (object.get("readable").to_bool()) - builder.append('R'); - if (object.get("writable").to_bool()) - builder.append('W'); - if (object.get("executable").to_bool()) - builder.append('X'); - if (object.get("shared").to_bool()) - builder.append('S'); - if (object.get("stack").to_bool()) - builder.append('T'); - return builder.to_string(); - }); - pid_vm_fields.empend("vmobject", "VMObject type", Gfx::TextAlignment::CenterLeft); - pid_vm_fields.empend("Purgeable", Gfx::TextAlignment::CenterLeft, [](auto& object) { - if (object.get("volatile").to_bool()) - return "Volatile"; - return "Non-volatile"; - }); - pid_vm_fields.empend( - "Page map", Gfx::TextAlignment::CenterLeft, - [](auto&) { - return GUI::Variant(); - }, - [](auto&) { - return GUI::Variant(0); - }, - [](const JsonObject& object) { - auto pagemap = object.get("pagemap").as_string_or({}); - return pagemap; - }); - pid_vm_fields.empend("cow_pages", "# CoW", Gfx::TextAlignment::CenterRight); - pid_vm_fields.empend("name", "Name", Gfx::TextAlignment::CenterLeft); - m_json_model = GUI::JsonArrayModel::create({}, move(pid_vm_fields)); - m_table_view->set_model(GUI::SortingProxyModel::create(*m_json_model)); - - m_table_view->set_column_painting_delegate(7, make<PagemapPaintingDelegate>()); - - m_table_view->set_key_column_and_sort_order(0, GUI::SortOrder::Ascending); - m_timer = add<Core::Timer>(1000, [this] { refresh(); }); -} - -ProcessMemoryMapWidget::~ProcessMemoryMapWidget() -{ -} - -void ProcessMemoryMapWidget::set_pid(pid_t pid) -{ - if (m_pid == pid) - return; - m_pid = pid; - m_json_model->set_json_path(String::formatted("/proc/{}/vm", pid)); -} - -void ProcessMemoryMapWidget::refresh() -{ - if (m_pid != -1) - m_json_model->update(); -} diff --git a/Applications/SystemMonitor/ProcessMemoryMapWidget.h b/Applications/SystemMonitor/ProcessMemoryMapWidget.h deleted file mode 100644 index 71a554fe51..0000000000 --- a/Applications/SystemMonitor/ProcessMemoryMapWidget.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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> - -class ProcessMemoryMapWidget final : public GUI::Widget { - C_OBJECT(ProcessMemoryMapWidget); - -public: - virtual ~ProcessMemoryMapWidget() override; - - void set_pid(pid_t); - void refresh(); - -private: - ProcessMemoryMapWidget(); - RefPtr<GUI::TableView> m_table_view; - RefPtr<GUI::JsonArrayModel> m_json_model; - pid_t m_pid { -1 }; - RefPtr<Core::Timer> m_timer; -}; diff --git a/Applications/SystemMonitor/ProcessModel.cpp b/Applications/SystemMonitor/ProcessModel.cpp deleted file mode 100644 index c12a5503ab..0000000000 --- a/Applications/SystemMonitor/ProcessModel.cpp +++ /dev/null @@ -1,454 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "ProcessModel.h" -#include "GraphWidget.h" -#include <AK/JsonArray.h> -#include <AK/JsonObject.h> -#include <AK/JsonValue.h> -#include <AK/SharedBuffer.h> -#include <LibCore/File.h> -#include <LibCore/ProcessStatisticsReader.h> -#include <LibGUI/FileIconProvider.h> -#include <fcntl.h> -#include <stdio.h> - -static ProcessModel* s_the; - -ProcessModel& ProcessModel::the() -{ - ASSERT(s_the); - return *s_the; -} - -ProcessModel::ProcessModel() -{ - ASSERT(!s_the); - s_the = this; - m_generic_process_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/gear.png"); - m_high_priority_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/highpriority.png"); - m_low_priority_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/lowpriority.png"); - m_normal_priority_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/normalpriority.png"); - - auto file = Core::File::construct("/proc/cpuinfo"); - if (file->open(Core::IODevice::ReadOnly)) { - auto json = JsonValue::from_string({ file->read_all() }); - auto cpuinfo_array = json.value().as_array(); - cpuinfo_array.for_each([&](auto& value) { - auto& cpu_object = value.as_object(); - auto cpu_id = cpu_object.get("processor").as_u32(); - m_cpus.append(make<CpuInfo>(cpu_id)); - }); - } - - if (m_cpus.is_empty()) - m_cpus.append(make<CpuInfo>(0)); -} - -ProcessModel::~ProcessModel() -{ -} - -int ProcessModel::row_count(const GUI::ModelIndex&) const -{ - return m_pids.size(); -} - -int ProcessModel::column_count(const GUI::ModelIndex&) const -{ - return Column::__Count; -} - -String ProcessModel::column_name(int column) const -{ - switch (column) { - case Column::Icon: - return ""; - case Column::PID: - return "PID"; - case Column::TID: - return "TID"; - case Column::PPID: - return "PPID"; - case Column::PGID: - return "PGID"; - case Column::SID: - return "SID"; - case Column::State: - return "State"; - case Column::User: - return "User"; - case Column::Priority: - return "Pr"; - case Column::EffectivePriority: - return "EPr"; - case Column::Virtual: - return "Virtual"; - case Column::Physical: - return "Physical"; - case Column::DirtyPrivate: - return "DirtyP"; - case Column::CleanInode: - return "CleanI"; - case Column::PurgeableVolatile: - return "Purg:V"; - case Column::PurgeableNonvolatile: - return "Purg:N"; - case Column::CPU: - return "CPU"; - case Column::Processor: - return "Processor"; - case Column::Name: - return "Name"; - case Column::Syscalls: - return "Syscalls"; - case Column::InodeFaults: - return "F:Inode"; - case Column::ZeroFaults: - return "F:Zero"; - case Column::CowFaults: - return "F:CoW"; - case Column::IPv4SocketReadBytes: - return "IPv4 In"; - case Column::IPv4SocketWriteBytes: - return "IPv4 Out"; - case Column::UnixSocketReadBytes: - return "Unix In"; - case Column::UnixSocketWriteBytes: - return "Unix Out"; - case Column::FileReadBytes: - return "File In"; - case Column::FileWriteBytes: - return "File Out"; - case Column::Pledge: - return "Pledge"; - case Column::Veil: - return "Veil"; - default: - ASSERT_NOT_REACHED(); - } -} - -static String pretty_byte_size(size_t size) -{ - return String::formatted("{}K", size / 1024); -} - -GUI::Variant ProcessModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const -{ - ASSERT(is_valid(index)); - - if (role == GUI::ModelRole::TextAlignment) { - switch (index.column()) { - case Column::Icon: - case Column::Name: - case Column::State: - case Column::User: - case Column::Pledge: - case Column::Veil: - return Gfx::TextAlignment::CenterLeft; - case Column::PID: - case Column::TID: - case Column::PPID: - case Column::PGID: - case Column::SID: - case Column::Priority: - case Column::EffectivePriority: - case Column::Virtual: - case Column::Physical: - case Column::DirtyPrivate: - case Column::CleanInode: - case Column::PurgeableVolatile: - case Column::PurgeableNonvolatile: - case Column::CPU: - case Column::Processor: - case Column::Syscalls: - case Column::InodeFaults: - case Column::ZeroFaults: - case Column::CowFaults: - case Column::FileReadBytes: - case Column::FileWriteBytes: - case Column::UnixSocketReadBytes: - case Column::UnixSocketWriteBytes: - case Column::IPv4SocketReadBytes: - case Column::IPv4SocketWriteBytes: - return Gfx::TextAlignment::CenterRight; - default: - ASSERT_NOT_REACHED(); - } - } - - auto it = m_threads.find(m_pids[index.row()]); - auto& thread = *(*it).value; - - if (role == GUI::ModelRole::Sort) { - switch (index.column()) { - case Column::Icon: - return 0; - case Column::PID: - return thread.current_state.pid; - case Column::TID: - return thread.current_state.tid; - case Column::PPID: - return thread.current_state.ppid; - case Column::PGID: - return thread.current_state.pgid; - case Column::SID: - return thread.current_state.sid; - case Column::State: - return thread.current_state.state; - case Column::User: - return thread.current_state.user; - case Column::Priority: - return thread.current_state.priority; - case Column::EffectivePriority: - return thread.current_state.effective_priority; - case Column::Virtual: - return (int)thread.current_state.amount_virtual; - case Column::Physical: - return (int)thread.current_state.amount_resident; - case Column::DirtyPrivate: - return (int)thread.current_state.amount_dirty_private; - case Column::CleanInode: - return (int)thread.current_state.amount_clean_inode; - case Column::PurgeableVolatile: - return (int)thread.current_state.amount_purgeable_volatile; - case Column::PurgeableNonvolatile: - return (int)thread.current_state.amount_purgeable_nonvolatile; - case Column::CPU: - return thread.current_state.cpu_percent; - case Column::Processor: - return thread.current_state.cpu; - case Column::Name: - return thread.current_state.name; - case Column::Syscalls: - return thread.current_state.syscall_count; - case Column::InodeFaults: - return thread.current_state.inode_faults; - case Column::ZeroFaults: - return thread.current_state.zero_faults; - case Column::CowFaults: - return thread.current_state.cow_faults; - case Column::IPv4SocketReadBytes: - return thread.current_state.ipv4_socket_read_bytes; - case Column::IPv4SocketWriteBytes: - return thread.current_state.ipv4_socket_write_bytes; - case Column::UnixSocketReadBytes: - return thread.current_state.unix_socket_read_bytes; - case Column::UnixSocketWriteBytes: - return thread.current_state.unix_socket_write_bytes; - case Column::FileReadBytes: - return thread.current_state.file_read_bytes; - case Column::FileWriteBytes: - return thread.current_state.file_write_bytes; - case Column::Pledge: - return thread.current_state.pledge; - case Column::Veil: - return thread.current_state.veil; - } - ASSERT_NOT_REACHED(); - return {}; - } - - if (role == GUI::ModelRole::Display) { - switch (index.column()) { - case Column::Icon: { - auto icon = GUI::FileIconProvider::icon_for_executable(thread.current_state.executable); - if (auto* bitmap = icon.bitmap_for_size(16)) - return *bitmap; - return *m_generic_process_icon; - } - case Column::PID: - return thread.current_state.pid; - case Column::TID: - return thread.current_state.tid; - case Column::PPID: - return thread.current_state.ppid; - case Column::PGID: - return thread.current_state.pgid; - case Column::SID: - return thread.current_state.sid; - case Column::State: - return thread.current_state.state; - case Column::User: - return thread.current_state.user; - case Column::Priority: - return thread.current_state.priority; - case Column::EffectivePriority: - return thread.current_state.effective_priority; - case Column::Virtual: - return pretty_byte_size(thread.current_state.amount_virtual); - case Column::Physical: - return pretty_byte_size(thread.current_state.amount_resident); - case Column::DirtyPrivate: - return pretty_byte_size(thread.current_state.amount_dirty_private); - case Column::CleanInode: - return pretty_byte_size(thread.current_state.amount_clean_inode); - case Column::PurgeableVolatile: - return pretty_byte_size(thread.current_state.amount_purgeable_volatile); - case Column::PurgeableNonvolatile: - return pretty_byte_size(thread.current_state.amount_purgeable_nonvolatile); - case Column::CPU: - return thread.current_state.cpu_percent; - case Column::Processor: - return thread.current_state.cpu; - case Column::Name: - return thread.current_state.name; - case Column::Syscalls: - return thread.current_state.syscall_count; - case Column::InodeFaults: - return thread.current_state.inode_faults; - case Column::ZeroFaults: - return thread.current_state.zero_faults; - case Column::CowFaults: - return thread.current_state.cow_faults; - case Column::IPv4SocketReadBytes: - return thread.current_state.ipv4_socket_read_bytes; - case Column::IPv4SocketWriteBytes: - return thread.current_state.ipv4_socket_write_bytes; - case Column::UnixSocketReadBytes: - return thread.current_state.unix_socket_read_bytes; - case Column::UnixSocketWriteBytes: - return thread.current_state.unix_socket_write_bytes; - case Column::FileReadBytes: - return thread.current_state.file_read_bytes; - case Column::FileWriteBytes: - return thread.current_state.file_write_bytes; - case Column::Pledge: - return thread.current_state.pledge; - case Column::Veil: - return thread.current_state.veil; - } - } - - return {}; -} - -void ProcessModel::update() -{ - auto previous_pid_count = m_pids.size(); - auto all_processes = Core::ProcessStatisticsReader::get_all(m_proc_all); - - u64 last_sum_ticks_scheduled = 0, last_sum_ticks_scheduled_kernel = 0; - for (auto& it : m_threads) { - auto& current_state = it.value->current_state; - last_sum_ticks_scheduled += current_state.ticks_user + current_state.ticks_kernel; - last_sum_ticks_scheduled_kernel += current_state.ticks_kernel; - } - - HashTable<PidAndTid> live_pids; - u64 sum_ticks_scheduled = 0, sum_ticks_scheduled_kernel = 0; - if (all_processes.has_value()) { - for (auto& it : all_processes.value()) { - for (auto& thread : it.value.threads) { - ThreadState state; - state.pid = it.value.pid; - state.user = it.value.username; - state.pledge = it.value.pledge; - state.veil = it.value.veil; - state.syscall_count = thread.syscall_count; - state.inode_faults = thread.inode_faults; - state.zero_faults = thread.zero_faults; - state.cow_faults = thread.cow_faults; - state.unix_socket_read_bytes = thread.unix_socket_read_bytes; - state.unix_socket_write_bytes = thread.unix_socket_write_bytes; - state.ipv4_socket_read_bytes = thread.ipv4_socket_read_bytes; - state.ipv4_socket_write_bytes = thread.ipv4_socket_write_bytes; - state.file_read_bytes = thread.file_read_bytes; - state.file_write_bytes = thread.file_write_bytes; - state.amount_virtual = it.value.amount_virtual; - state.amount_resident = it.value.amount_resident; - state.amount_dirty_private = it.value.amount_dirty_private; - state.amount_clean_inode = it.value.amount_clean_inode; - state.amount_purgeable_volatile = it.value.amount_purgeable_volatile; - state.amount_purgeable_nonvolatile = it.value.amount_purgeable_nonvolatile; - - state.name = thread.name; - state.executable = it.value.executable; - - state.ppid = it.value.ppid; - state.tid = thread.tid; - state.pgid = it.value.pgid; - state.sid = it.value.sid; - state.times_scheduled = thread.times_scheduled; - state.ticks_user = thread.ticks_user; - state.ticks_kernel = thread.ticks_kernel; - state.cpu = thread.cpu; - state.cpu_percent = 0; - state.priority = thread.priority; - state.effective_priority = thread.effective_priority; - state.state = thread.state; - sum_ticks_scheduled += thread.ticks_user + thread.ticks_kernel; - sum_ticks_scheduled_kernel += thread.ticks_kernel; - { - auto pit = m_threads.find({ it.value.pid, thread.tid }); - if (pit == m_threads.end()) - m_threads.set({ it.value.pid, thread.tid }, make<Thread>()); - } - auto pit = m_threads.find({ it.value.pid, thread.tid }); - ASSERT(pit != m_threads.end()); - (*pit).value->previous_state = (*pit).value->current_state; - (*pit).value->current_state = state; - - live_pids.set({ it.value.pid, thread.tid }); - } - } - } - - m_pids.clear(); - for (auto& c : m_cpus) { - c.total_cpu_percent = 0.0; - c.total_cpu_percent_kernel = 0.0; - } - Vector<PidAndTid, 16> pids_to_remove; - for (auto& it : m_threads) { - if (!live_pids.contains(it.key)) { - pids_to_remove.append(it.key); - continue; - } - auto& process = *it.value; - u32 ticks_scheduled_diff = (process.current_state.ticks_user + process.current_state.ticks_kernel) - - (process.previous_state.ticks_user + process.previous_state.ticks_kernel); - u32 ticks_scheduled_diff_kernel = process.current_state.ticks_kernel - process.previous_state.ticks_kernel; - process.current_state.cpu_percent = ((float)ticks_scheduled_diff * 100) / (float)(sum_ticks_scheduled - last_sum_ticks_scheduled); - process.current_state.cpu_percent_kernel = ((float)ticks_scheduled_diff_kernel * 100) / (float)(sum_ticks_scheduled - last_sum_ticks_scheduled); - if (it.key.pid != 0) { - auto& cpu_info = m_cpus[process.current_state.cpu]; - cpu_info.total_cpu_percent += process.current_state.cpu_percent; - cpu_info.total_cpu_percent_kernel += process.current_state.cpu_percent_kernel; - m_pids.append(it.key); - } - } - for (auto pid : pids_to_remove) - m_threads.remove(pid); - - if (on_cpu_info_change) - on_cpu_info_change(m_cpus); - - // FIXME: This is a rather hackish way of invalidating indexes. - // It would be good if GUI::Model had a way to orchestrate removal/insertion while preserving indexes. - did_update(previous_pid_count == m_pids.size() ? GUI::Model::UpdateFlag::DontInvalidateIndexes : GUI::Model::UpdateFlag::InvalidateAllIndexes); -} diff --git a/Applications/SystemMonitor/ProcessModel.h b/Applications/SystemMonitor/ProcessModel.h deleted file mode 100644 index 11f39cac76..0000000000 --- a/Applications/SystemMonitor/ProcessModel.h +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 <AK/HashMap.h> -#include <AK/NonnullOwnPtrVector.h> -#include <AK/String.h> -#include <AK/Vector.h> -#include <LibGUI/Model.h> -#include <unistd.h> - -class GraphWidget; - -struct PidAndTid { - bool operator==(const PidAndTid& other) const - { - return pid == other.pid && tid == other.tid; - } - pid_t pid; - int tid; -}; - -class ProcessModel final : public GUI::Model { -public: - enum Column { - Icon = 0, - Name, - CPU, - Processor, - State, - Priority, - EffectivePriority, - User, - PID, - TID, - PPID, - PGID, - SID, - Virtual, - Physical, - DirtyPrivate, - CleanInode, - PurgeableVolatile, - PurgeableNonvolatile, - Veil, - Pledge, - Syscalls, - InodeFaults, - ZeroFaults, - CowFaults, - FileReadBytes, - FileWriteBytes, - UnixSocketReadBytes, - UnixSocketWriteBytes, - IPv4SocketReadBytes, - IPv4SocketWriteBytes, - __Count - }; - - static ProcessModel& the(); - - static NonnullRefPtr<ProcessModel> create() { return adopt(*new ProcessModel); } - virtual ~ProcessModel() override; - - virtual int row_count(const GUI::ModelIndex&) const override; - virtual int column_count(const GUI::ModelIndex&) const override; - virtual String column_name(int column) const override; - virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override; - virtual void update() override; - - struct CpuInfo { - u32 id; - float total_cpu_percent { 0.0 }; - float total_cpu_percent_kernel { 0.0 }; - - CpuInfo(u32 id) - : id(id) - { - } - }; - - Function<void(const NonnullOwnPtrVector<CpuInfo>&)> on_cpu_info_change; - - const NonnullOwnPtrVector<CpuInfo>& cpus() const { return m_cpus; } - -private: - ProcessModel(); - - struct ThreadState { - pid_t tid; - pid_t pid; - pid_t ppid; - pid_t pgid; - pid_t sid; - unsigned times_scheduled; - unsigned ticks_user; - unsigned ticks_kernel; - String executable; - String name; - String state; - String user; - String pledge; - String veil; - u32 cpu; - u32 priority; - u32 effective_priority; - size_t amount_virtual; - size_t amount_resident; - size_t amount_dirty_private; - size_t amount_clean_inode; - size_t amount_purgeable_volatile; - size_t amount_purgeable_nonvolatile; - unsigned syscall_count; - unsigned inode_faults; - unsigned zero_faults; - unsigned cow_faults; - unsigned unix_socket_read_bytes; - unsigned unix_socket_write_bytes; - unsigned ipv4_socket_read_bytes; - unsigned ipv4_socket_write_bytes; - unsigned file_read_bytes; - unsigned file_write_bytes; - float cpu_percent; - float cpu_percent_kernel; - }; - - struct Thread { - ThreadState current_state; - ThreadState previous_state; - }; - - HashMap<uid_t, String> m_usernames; - HashMap<PidAndTid, NonnullOwnPtr<Thread>> m_threads; - NonnullOwnPtrVector<CpuInfo> m_cpus; - Vector<PidAndTid> m_pids; - RefPtr<Gfx::Bitmap> m_generic_process_icon; - RefPtr<Gfx::Bitmap> m_high_priority_icon; - RefPtr<Gfx::Bitmap> m_low_priority_icon; - RefPtr<Gfx::Bitmap> m_normal_priority_icon; - RefPtr<Core::File> m_proc_all; -}; - -namespace AK { -template<> -struct Traits<PidAndTid> : public GenericTraits<PidAndTid> { - static unsigned hash(const PidAndTid& value) { return pair_int_hash(value.pid, value.tid); } -}; -} diff --git a/Applications/SystemMonitor/ProcessUnveiledPathsWidget.cpp b/Applications/SystemMonitor/ProcessUnveiledPathsWidget.cpp deleted file mode 100644 index 9e2d22443c..0000000000 --- a/Applications/SystemMonitor/ProcessUnveiledPathsWidget.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "ProcessUnveiledPathsWidget.h" -#include <LibGUI/BoxLayout.h> -#include <LibGUI/JsonArrayModel.h> -#include <LibGUI/SortingProxyModel.h> -#include <LibGUI/TableView.h> - -ProcessUnveiledPathsWidget::ProcessUnveiledPathsWidget() -{ - set_layout<GUI::VerticalBoxLayout>(); - layout()->set_margins({ 4, 4, 4, 4 }); - m_table_view = add<GUI::TableView>(); - - Vector<GUI::JsonArrayModel::FieldSpec> pid_unveil_fields; - pid_unveil_fields.empend("path", "Path", Gfx::TextAlignment::CenterLeft); - pid_unveil_fields.empend("permissions", "Permissions", Gfx::TextAlignment::CenterLeft); - - m_model = GUI::JsonArrayModel::create({}, move(pid_unveil_fields)); - m_table_view->set_model(GUI::SortingProxyModel::create(*m_model)); -} - -ProcessUnveiledPathsWidget::~ProcessUnveiledPathsWidget() -{ -} - -void ProcessUnveiledPathsWidget::set_pid(pid_t pid) -{ - if (m_pid == pid) - return; - m_pid = pid; - m_model->set_json_path(String::formatted("/proc/{}/unveil", m_pid)); -} diff --git a/Applications/SystemMonitor/ProcessUnveiledPathsWidget.h b/Applications/SystemMonitor/ProcessUnveiledPathsWidget.h deleted file mode 100644 index d1eac898b2..0000000000 --- a/Applications/SystemMonitor/ProcessUnveiledPathsWidget.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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> - -class ProcessUnveiledPathsWidget final : public GUI::Widget { - C_OBJECT(ProcessUnveiledPathsWidget); - -public: - virtual ~ProcessUnveiledPathsWidget() override; - - void set_pid(pid_t); - -private: - ProcessUnveiledPathsWidget(); - - RefPtr<GUI::TableView> m_table_view; - RefPtr<GUI::JsonArrayModel> m_model; - pid_t m_pid { -1 }; -}; diff --git a/Applications/SystemMonitor/ThreadStackWidget.cpp b/Applications/SystemMonitor/ThreadStackWidget.cpp deleted file mode 100644 index 01e37791a0..0000000000 --- a/Applications/SystemMonitor/ThreadStackWidget.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "ThreadStackWidget.h" -#include <AK/ByteBuffer.h> -#include <LibCore/File.h> -#include <LibCore/Timer.h> -#include <LibGUI/BoxLayout.h> - -ThreadStackWidget::ThreadStackWidget() -{ - set_layout<GUI::VerticalBoxLayout>(); - layout()->set_margins({ 4, 4, 4, 4 }); - m_stack_editor = add<GUI::TextEditor>(); - m_stack_editor->set_mode(GUI::TextEditor::ReadOnly); - - m_timer = add<Core::Timer>(1000, [this] { refresh(); }); -} - -ThreadStackWidget::~ThreadStackWidget() -{ -} - -void ThreadStackWidget::set_ids(pid_t pid, pid_t tid) -{ - if (m_pid == pid && m_tid == tid) - return; - m_pid = pid; - m_tid = tid; - refresh(); -} - -void ThreadStackWidget::refresh() -{ - auto file = Core::File::construct(String::formatted("/proc/{}/stacks/{}", m_pid, m_tid)); - if (!file->open(Core::IODevice::ReadOnly)) { - m_stack_editor->set_text(String::formatted("Unable to open {}", file->filename())); - return; - } - - auto new_text = file->read_all(); - if (m_stack_editor->text() != new_text) { - m_stack_editor->set_text(new_text); - } -} diff --git a/Applications/SystemMonitor/ThreadStackWidget.h b/Applications/SystemMonitor/ThreadStackWidget.h deleted file mode 100644 index 5a10e93b0a..0000000000 --- a/Applications/SystemMonitor/ThreadStackWidget.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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/TextEditor.h> -#include <LibGUI/Widget.h> - -class ThreadStackWidget final : public GUI::Widget { - C_OBJECT(ThreadStackWidget) -public: - virtual ~ThreadStackWidget() override; - - void set_ids(pid_t pid, pid_t tid); - void refresh(); - -private: - ThreadStackWidget(); - - pid_t m_pid { -1 }; - pid_t m_tid { -1 }; - RefPtr<GUI::TextEditor> m_stack_editor; - RefPtr<Core::Timer> m_timer; -}; diff --git a/Applications/SystemMonitor/main.cpp b/Applications/SystemMonitor/main.cpp deleted file mode 100644 index bdf88e44d1..0000000000 --- a/Applications/SystemMonitor/main.cpp +++ /dev/null @@ -1,670 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "DevicesModel.h" -#include "GraphWidget.h" -#include "InterruptsWidget.h" -#include "MemoryStatsWidget.h" -#include "NetworkStatisticsWidget.h" -#include "ProcessFileDescriptorMapWidget.h" -#include "ProcessMemoryMapWidget.h" -#include "ProcessModel.h" -#include "ProcessUnveiledPathsWidget.h" -#include "ThreadStackWidget.h" -#include <AK/NumberFormat.h> -#include <LibCore/ArgsParser.h> -#include <LibCore/Timer.h> -#include <LibGUI/Action.h> -#include <LibGUI/ActionGroup.h> -#include <LibGUI/Application.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/GroupBox.h> -#include <LibGUI/Icon.h> -#include <LibGUI/JsonArrayModel.h> -#include <LibGUI/Label.h> -#include <LibGUI/LazyWidget.h> -#include <LibGUI/Menu.h> -#include <LibGUI/MenuBar.h> -#include <LibGUI/Painter.h> -#include <LibGUI/SortingProxyModel.h> -#include <LibGUI/Splitter.h> -#include <LibGUI/TabWidget.h> -#include <LibGUI/TableView.h> -#include <LibGUI/ToolBar.h> -#include <LibGUI/Widget.h> -#include <LibGUI/Window.h> -#include <LibGfx/Palette.h> -#include <LibPCIDB/Database.h> -#include <serenity.h> -#include <signal.h> -#include <spawn.h> -#include <stdio.h> -#include <unistd.h> - -static NonnullRefPtr<GUI::Widget> build_file_systems_tab(); -static NonnullRefPtr<GUI::Widget> build_pci_devices_tab(); -static NonnullRefPtr<GUI::Widget> build_devices_tab(); -static NonnullRefPtr<GUI::Widget> build_graphs_tab(); -static NonnullRefPtr<GUI::Widget> build_processors_tab(); - -class UnavailableProcessWidget final : public GUI::Frame { - C_OBJECT(UnavailableProcessWidget) -public: - virtual ~UnavailableProcessWidget() override { } - - const String& text() const { return m_text; } - void set_text(String text) { m_text = move(text); } - -private: - UnavailableProcessWidget(String text) - : m_text(move(text)) - { - } - - virtual void paint_event(GUI::PaintEvent& event) override - { - Frame::paint_event(event); - if (text().is_empty()) - return; - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - painter.draw_text(frame_inner_rect(), text(), Gfx::TextAlignment::Center, palette().window_text(), Gfx::TextElision::Right); - } - - String m_text; -}; - -static bool can_access_pid(pid_t pid) -{ - auto path = String::formatted("/proc/{}", pid); - return access(path.characters(), X_OK) == 0; -} - -int main(int argc, char** argv) -{ - if (pledge("stdio proc shared_buffer accept rpath exec unix cpath fattr", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto app = GUI::Application::construct(argc, argv); - - if (pledge("stdio proc shared_buffer accept rpath exec", nullptr) < 0) { - perror("pledge"); - return 1; - } - - if (unveil("/etc/passwd", "r") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/res", "r") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/proc", "r") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/dev", "r") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/bin", "r") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/bin/Profiler", "rx") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/bin/Inspector", "rx") < 0) { - perror("unveil"); - return 1; - } - - unveil(nullptr, nullptr); - - const char* args_tab = "processes"; - Core::ArgsParser parser; - parser.add_option(args_tab, "Tab, one of 'processes', 'graphs', 'fs', 'pci', 'devices', 'network', 'processors' or 'interrupts'", "open-tab", 't', "tab"); - parser.parse(argc, argv); - StringView args_tab_view = args_tab; - - auto app_icon = GUI::Icon::default_icon("app-system-monitor"); - - auto window = GUI::Window::construct(); - window->set_title("System Monitor"); - window->resize(680, 400); - - auto& keeper = window->set_main_widget<GUI::Widget>(); - keeper.set_layout<GUI::VerticalBoxLayout>(); - keeper.set_fill_with_background_color(true); - keeper.layout()->set_margins({ 2, 2, 2, 2 }); - - auto& tabwidget = keeper.add<GUI::TabWidget>(); - - auto process_container_splitter = GUI::VerticalSplitter::construct(); - tabwidget.add_widget("Processes", process_container_splitter); - process_container_splitter->layout()->set_margins({ 4, 4, 4, 4 }); - - auto& process_table_container = process_container_splitter->add<GUI::Widget>(); - - auto graphs_widget = build_graphs_tab(); - tabwidget.add_widget("Graphs", graphs_widget); - - auto file_systems_widget = build_file_systems_tab(); - tabwidget.add_widget("File systems", file_systems_widget); - - auto pci_devices_widget = build_pci_devices_tab(); - tabwidget.add_widget("PCI devices", pci_devices_widget); - - auto devices_widget = build_devices_tab(); - tabwidget.add_widget("Devices", devices_widget); - - auto network_stats_widget = NetworkStatisticsWidget::construct(); - tabwidget.add_widget("Network", network_stats_widget); - - auto processors_widget = build_processors_tab(); - tabwidget.add_widget("Processors", processors_widget); - - auto interrupts_widget = InterruptsWidget::construct(); - tabwidget.add_widget("Interrupts", interrupts_widget); - - process_table_container.set_layout<GUI::VerticalBoxLayout>(); - process_table_container.layout()->set_spacing(0); - - auto& process_table_view = process_table_container.add<GUI::TableView>(); - process_table_view.set_column_headers_visible(true); - process_table_view.set_model(GUI::SortingProxyModel::create(ProcessModel::create())); - process_table_view.set_key_column_and_sort_order(ProcessModel::Column::CPU, GUI::SortOrder::Descending); - process_table_view.model()->update(); - - auto& refresh_timer = window->add<Core::Timer>( - 3000, [&] { - process_table_view.model()->update(); - if (auto* memory_stats_widget = MemoryStatsWidget::the()) - memory_stats_widget->refresh(); - }); - - auto selected_id = [&](ProcessModel::Column column) -> pid_t { - if (process_table_view.selection().is_empty()) - return -1; - auto pid_index = process_table_view.model()->index(process_table_view.selection().first().row(), column); - return pid_index.data().to_i32(); - }; - - auto kill_action = GUI::Action::create("Kill process", { Mod_Ctrl, Key_K }, Gfx::Bitmap::load_from_file("/res/icons/16x16/kill.png"), [&](const GUI::Action&) { - pid_t pid = selected_id(ProcessModel::Column::PID); - if (pid != -1) - kill(pid, SIGKILL); - }); - - auto stop_action = GUI::Action::create("Stop process", { Mod_Ctrl, Key_S }, Gfx::Bitmap::load_from_file("/res/icons/16x16/stop-hand.png"), [&](const GUI::Action&) { - pid_t pid = selected_id(ProcessModel::Column::PID); - if (pid != -1) - kill(pid, SIGSTOP); - }); - - auto continue_action = GUI::Action::create("Continue process", { Mod_Ctrl, Key_C }, Gfx::Bitmap::load_from_file("/res/icons/16x16/continue.png"), [&](const GUI::Action&) { - pid_t pid = selected_id(ProcessModel::Column::PID); - if (pid != -1) - kill(pid, SIGCONT); - }); - - auto profile_action = GUI::Action::create("Profile process", { Mod_Ctrl, Key_P }, - Gfx::Bitmap::load_from_file("/res/icons/16x16/app-profiler.png"), [&](auto&) { - pid_t pid = selected_id(ProcessModel::Column::PID); - if (pid != -1) { - auto pid_string = String::format("%d", pid); - pid_t child; - const char* argv[] = { "/bin/Profiler", "--pid", pid_string.characters(), nullptr }; - if ((errno = posix_spawn(&child, "/bin/Profiler", nullptr, nullptr, const_cast<char**>(argv), environ))) { - perror("posix_spawn"); - } else { - if (disown(child) < 0) - perror("disown"); - } - } - }); - - auto inspect_action = GUI::Action::create("Inspect process", { Mod_Ctrl, Key_I }, - Gfx::Bitmap::load_from_file("/res/icons/16x16/app-inspector.png"), [&](auto&) { - pid_t pid = selected_id(ProcessModel::Column::PID); - if (pid != -1) { - auto pid_string = String::format("%d", pid); - pid_t child; - const char* argv[] = { "/bin/Inspector", pid_string.characters(), nullptr }; - if ((errno = posix_spawn(&child, "/bin/Inspector", nullptr, nullptr, const_cast<char**>(argv), environ))) { - perror("posix_spawn"); - } else { - if (disown(child) < 0) - perror("disown"); - } - } - }); - - auto menubar = GUI::MenuBar::construct(); - auto& app_menu = menubar->add_menu("System Monitor"); - app_menu.add_action(GUI::CommonActions::make_quit_action([](auto&) { - GUI::Application::the()->quit(); - return; - })); - - auto& process_menu = menubar->add_menu("Process"); - process_menu.add_action(kill_action); - process_menu.add_action(stop_action); - process_menu.add_action(continue_action); - process_menu.add_separator(); - process_menu.add_action(profile_action); - process_menu.add_action(inspect_action); - - auto process_context_menu = GUI::Menu::construct(); - process_context_menu->add_action(kill_action); - process_context_menu->add_action(stop_action); - process_context_menu->add_action(continue_action); - process_context_menu->add_separator(); - process_context_menu->add_action(profile_action); - process_context_menu->add_action(inspect_action); - process_table_view.on_context_menu_request = [&]([[maybe_unused]] const GUI::ModelIndex& index, const GUI::ContextMenuEvent& event) { - process_context_menu->popup(event.screen_position()); - }; - - auto& frequency_menu = menubar->add_menu("Frequency"); - GUI::ActionGroup frequency_action_group; - frequency_action_group.set_exclusive(true); - - auto make_frequency_action = [&](auto& title, int interval, bool checked = false) { - auto action = GUI::Action::create_checkable(title, [&refresh_timer, interval](auto&) { - refresh_timer.restart(interval); - }); - action->set_checked(checked); - frequency_action_group.add_action(*action); - frequency_menu.add_action(*action); - }; - - make_frequency_action("1 sec", 1000); - make_frequency_action("3 sec", 3000, true); - make_frequency_action("5 sec", 5000); - - auto& help_menu = menubar->add_menu("Help"); - help_menu.add_action(GUI::CommonActions::make_about_action("System Monitor", app_icon, window)); - - app->set_menubar(move(menubar)); - - auto& process_tab_unused_widget = process_container_splitter->add<UnavailableProcessWidget>("No process selected"); - process_tab_unused_widget.set_visible(true); - - auto& process_tab_widget = process_container_splitter->add<GUI::TabWidget>(); - process_tab_widget.set_tab_position(GUI::TabWidget::TabPosition::Bottom); - process_tab_widget.set_visible(false); - - auto& memory_map_widget = process_tab_widget.add_tab<ProcessMemoryMapWidget>("Memory map"); - auto& open_files_widget = process_tab_widget.add_tab<ProcessFileDescriptorMapWidget>("Open files"); - auto& unveiled_paths_widget = process_tab_widget.add_tab<ProcessUnveiledPathsWidget>("Unveiled paths"); - auto& stack_widget = process_tab_widget.add_tab<ThreadStackWidget>("Stack"); - - process_table_view.on_selection = [&](auto&) { - auto pid = selected_id(ProcessModel::Column::PID); - auto tid = selected_id(ProcessModel::Column::TID); - if (!can_access_pid(pid)) { - process_tab_widget.set_visible(false); - process_tab_unused_widget.set_text("Process cannot be accessed"); - process_tab_unused_widget.set_visible(true); - return; - } - - process_tab_widget.set_visible(true); - process_tab_unused_widget.set_visible(false); - open_files_widget.set_pid(pid); - stack_widget.set_ids(pid, tid); - memory_map_widget.set_pid(pid); - unveiled_paths_widget.set_pid(pid); - }; - - window->show(); - - window->set_icon(app_icon.bitmap_for_size(16)); - - if (args_tab_view == "processes") - tabwidget.set_active_widget(process_container_splitter); - else if (args_tab_view == "graphs") - tabwidget.set_active_widget(graphs_widget); - else if (args_tab_view == "fs") - tabwidget.set_active_widget(file_systems_widget); - else if (args_tab_view == "pci") - tabwidget.set_active_widget(pci_devices_widget); - else if (args_tab_view == "devices") - tabwidget.set_active_widget(devices_widget); - else if (args_tab_view == "network") - tabwidget.set_active_widget(network_stats_widget); - else if (args_tab_view == "processors") - tabwidget.set_active_widget(processors_widget); - else if (args_tab_view == "interrupts") - tabwidget.set_active_widget(interrupts_widget); - - return app->exec(); -} - -class ProgressBarPaintingDelegate final : public GUI::TableCellPaintingDelegate { -public: - virtual ~ProgressBarPaintingDelegate() override { } - - virtual void paint(GUI::Painter& painter, const Gfx::IntRect& a_rect, const Palette& palette, const GUI::ModelIndex& index) override - { - auto rect = a_rect.shrunken(2, 2); - auto percentage = index.data(GUI::ModelRole::Custom).to_i32(); - - auto data = index.data(); - String text; - if (data.is_string()) - text = data.as_string(); - Gfx::StylePainter::paint_progress_bar(painter, rect, palette, 0, 100, percentage, text); - painter.draw_rect(rect, Color::Black); - } -}; - -NonnullRefPtr<GUI::Widget> build_file_systems_tab() -{ - auto fs_widget = GUI::LazyWidget::construct(); - - fs_widget->on_first_show = [](GUI::LazyWidget& self) { - self.set_layout<GUI::VerticalBoxLayout>(); - self.layout()->set_margins({ 4, 4, 4, 4 }); - auto& fs_table_view = self.add<GUI::TableView>(); - - Vector<GUI::JsonArrayModel::FieldSpec> df_fields; - df_fields.empend("mount_point", "Mount point", Gfx::TextAlignment::CenterLeft); - df_fields.empend("class_name", "Class", Gfx::TextAlignment::CenterLeft); - df_fields.empend("source", "Source", Gfx::TextAlignment::CenterLeft); - df_fields.empend( - "Size", Gfx::TextAlignment::CenterRight, - [](const JsonObject& object) { - StringBuilder size_builder; - size_builder.append(" "); - size_builder.append(human_readable_size(object.get("total_block_count").to_u32() * object.get("block_size").to_u32())); - size_builder.append(" "); - return size_builder.to_string(); - }, - [](const JsonObject& object) { - return object.get("total_block_count").to_u32() * object.get("block_size").to_u32(); - }, - [](const JsonObject& object) { - auto total_blocks = object.get("total_block_count").to_u32(); - if (total_blocks == 0) - return 0; - auto free_blocks = object.get("free_block_count").to_u32(); - auto used_blocks = total_blocks - free_blocks; - int percentage = (int)((float)used_blocks / (float)total_blocks * 100.0f); - return percentage; - }); - df_fields.empend( - "Used", Gfx::TextAlignment::CenterRight, - [](const JsonObject& object) { - auto total_blocks = object.get("total_block_count").to_u32(); - auto free_blocks = object.get("free_block_count").to_u32(); - auto used_blocks = total_blocks - free_blocks; - return human_readable_size(used_blocks * object.get("block_size").to_u32()); }, - [](const JsonObject& object) { - auto total_blocks = object.get("total_block_count").to_u32(); - auto free_blocks = object.get("free_block_count").to_u32(); - auto used_blocks = total_blocks - free_blocks; - return used_blocks * object.get("block_size").to_u32(); - }); - df_fields.empend( - "Available", Gfx::TextAlignment::CenterRight, - [](const JsonObject& object) { - return human_readable_size(object.get("free_block_count").to_u32() * object.get("block_size").to_u32()); - }, - [](const JsonObject& object) { - return object.get("free_block_count").to_u32() * object.get("block_size").to_u32(); - }); - df_fields.empend("Access", Gfx::TextAlignment::CenterLeft, [](const JsonObject& object) { - bool readonly = object.get("readonly").to_bool(); - int mount_flags = object.get("mount_flags").to_int(); - return readonly || (mount_flags & MS_RDONLY) ? "Read-only" : "Read/Write"; - }); - df_fields.empend("Mount flags", Gfx::TextAlignment::CenterLeft, [](const JsonObject& object) { - int mount_flags = object.get("mount_flags").to_int(); - StringBuilder builder; - bool first = true; - auto check = [&](int flag, const char* name) { - if (!(mount_flags & flag)) - return; - if (!first) - builder.append(','); - builder.append(name); - first = false; - }; - check(MS_NODEV, "nodev"); - check(MS_NOEXEC, "noexec"); - check(MS_NOSUID, "nosuid"); - check(MS_BIND, "bind"); - check(MS_RDONLY, "ro"); - if (builder.string_view().is_empty()) - return String("defaults"); - return builder.to_string(); - }); - df_fields.empend("free_block_count", "Free blocks", Gfx::TextAlignment::CenterRight); - df_fields.empend("total_block_count", "Total blocks", Gfx::TextAlignment::CenterRight); - df_fields.empend("free_inode_count", "Free inodes", Gfx::TextAlignment::CenterRight); - df_fields.empend("total_inode_count", "Total inodes", Gfx::TextAlignment::CenterRight); - df_fields.empend("block_size", "Block size", Gfx::TextAlignment::CenterRight); - fs_table_view.set_model(GUI::SortingProxyModel::create(GUI::JsonArrayModel::create("/proc/df", move(df_fields)))); - - fs_table_view.set_column_painting_delegate(3, make<ProgressBarPaintingDelegate>()); - - fs_table_view.model()->update(); - }; - return fs_widget; -} - -NonnullRefPtr<GUI::Widget> build_pci_devices_tab() -{ - auto pci_widget = GUI::LazyWidget::construct(); - - pci_widget->on_first_show = [](GUI::LazyWidget& self) { - self.set_layout<GUI::VerticalBoxLayout>(); - self.layout()->set_margins({ 4, 4, 4, 4 }); - auto& pci_table_view = self.add<GUI::TableView>(); - - auto db = PCIDB::Database::open(); - - Vector<GUI::JsonArrayModel::FieldSpec> pci_fields; - pci_fields.empend( - "Address", Gfx::TextAlignment::CenterLeft, - [](const JsonObject& object) { - auto seg = object.get("seg").to_u32(); - auto bus = object.get("bus").to_u32(); - auto slot = object.get("slot").to_u32(); - auto function = object.get("function").to_u32(); - return String::formatted("{:04x}:{:02x}:{:02x}.{}", seg, bus, slot, function); - }); - pci_fields.empend( - "Class", Gfx::TextAlignment::CenterLeft, - [db](const JsonObject& object) { - auto class_id = object.get("class").to_u32(); - String class_name = db->get_class(class_id); - return class_name == "" ? String::formatted("{:04x}", class_id) : class_name; - }); - pci_fields.empend( - "Vendor", Gfx::TextAlignment::CenterLeft, - [db](const JsonObject& object) { - auto vendor_id = object.get("vendor_id").to_u32(); - String vendor_name = db->get_vendor(vendor_id); - return vendor_name == "" ? String::formatted("{:02x}", vendor_id) : vendor_name; - }); - pci_fields.empend( - "Device", Gfx::TextAlignment::CenterLeft, - [db](const JsonObject& object) { - auto vendor_id = object.get("vendor_id").to_u32(); - auto device_id = object.get("device_id").to_u32(); - String device_name = db->get_device(vendor_id, device_id); - return device_name == "" ? String::formatted("{:02x}", device_id) : device_name; - }); - pci_fields.empend( - "Revision", Gfx::TextAlignment::CenterRight, - [](const JsonObject& object) { - auto revision_id = object.get("revision_id").to_u32(); - return String::formatted("{:02x}", revision_id); - }); - - pci_table_view.set_model(GUI::SortingProxyModel::create(GUI::JsonArrayModel::create("/proc/pci", move(pci_fields)))); - pci_table_view.model()->update(); - }; - - return pci_widget; -} - -NonnullRefPtr<GUI::Widget> build_devices_tab() -{ - auto devices_widget = GUI::LazyWidget::construct(); - - devices_widget->on_first_show = [](GUI::LazyWidget& self) { - self.set_layout<GUI::VerticalBoxLayout>(); - self.layout()->set_margins({ 4, 4, 4, 4 }); - - auto& devices_table_view = self.add<GUI::TableView>(); - devices_table_view.set_model(GUI::SortingProxyModel::create(DevicesModel::create())); - devices_table_view.model()->update(); - }; - - return devices_widget; -} - -NonnullRefPtr<GUI::Widget> build_graphs_tab() -{ - auto graphs_container = GUI::LazyWidget::construct(); - - graphs_container->on_first_show = [](GUI::LazyWidget& self) { - self.set_fill_with_background_color(true); - self.set_background_role(ColorRole::Button); - self.set_layout<GUI::VerticalBoxLayout>(); - self.layout()->set_margins({ 4, 4, 4, 4 }); - - auto& cpu_graph_group_box = self.add<GUI::GroupBox>("CPU usage"); - cpu_graph_group_box.set_layout<GUI::HorizontalBoxLayout>(); - cpu_graph_group_box.layout()->set_margins({ 6, 16, 6, 6 }); - cpu_graph_group_box.set_fixed_height(120); - Vector<GraphWidget*> cpu_graphs; - for (size_t i = 0; i < ProcessModel::the().cpus().size(); i++) { - auto& cpu_graph = cpu_graph_group_box.add<GraphWidget>(); - cpu_graph.set_max(100); - cpu_graph.set_background_color(Color::White); - cpu_graph.set_value_format(0, { - .line_color = Color::Blue, - .background_color = Color::from_rgb(0xaaaaff), - .text_formatter = [](int value) { - return String::formatted("Total: {}%", value); - }, - }); - cpu_graph.set_value_format(1, { - .line_color = Color::Red, - .background_color = Color::from_rgb(0xffaaaa), - .text_formatter = [](int value) { - return String::formatted("Kernel: {}%", value); - }, - }); - cpu_graphs.append(&cpu_graph); - } - ProcessModel::the().on_cpu_info_change = [cpu_graphs](const NonnullOwnPtrVector<ProcessModel::CpuInfo>& cpus) { - for (size_t i = 0; i < cpus.size(); i++) - cpu_graphs[i]->add_value({ (int)cpus[i].total_cpu_percent, (int)cpus[i].total_cpu_percent_kernel }); - }; - - auto& memory_graph_group_box = self.add<GUI::GroupBox>("Memory usage"); - memory_graph_group_box.set_layout<GUI::VerticalBoxLayout>(); - memory_graph_group_box.layout()->set_margins({ 6, 16, 6, 6 }); - memory_graph_group_box.set_fixed_height(120); - auto& memory_graph = memory_graph_group_box.add<GraphWidget>(); - memory_graph.set_background_color(Color::White); - memory_graph.set_stack_values(true); - memory_graph.set_value_format(0, { - .line_color = Color::from_rgb(0x619910), - .background_color = Color::from_rgb(0xbbffbb), - .text_formatter = [&memory_graph](int value) { - return String::formatted("Committed: {} KiB", value); - }, - }); - memory_graph.set_value_format(1, { - .line_color = Color::Blue, - .background_color = Color::from_rgb(0xaaaaff), - .text_formatter = [&memory_graph](int value) { - return String::formatted("Allocated: {} KiB", value); - }, - }); - memory_graph.set_value_format(2, { - .line_color = Color::Red, - .background_color = Color::from_rgb(0xffaaaa), - .text_formatter = [&memory_graph](int value) { - return String::formatted("Kernel heap: {} KiB", value); - }, - }); - - self.add<MemoryStatsWidget>(memory_graph); - }; - return graphs_container; -} - -NonnullRefPtr<GUI::Widget> build_processors_tab() -{ - auto processors_widget = GUI::LazyWidget::construct(); - - processors_widget->on_first_show = [](GUI::LazyWidget& self) { - self.set_layout<GUI::VerticalBoxLayout>(); - self.layout()->set_margins({ 4, 4, 4, 4 }); - - Vector<GUI::JsonArrayModel::FieldSpec> processors_field; - processors_field.empend("processor", "Processor", Gfx::TextAlignment::CenterRight); - processors_field.empend("cpuid", "CPUID", Gfx::TextAlignment::CenterLeft); - processors_field.empend("brandstr", "Brand", Gfx::TextAlignment::CenterLeft); - processors_field.empend("Features", Gfx::TextAlignment::CenterLeft, [](auto& object) { - StringBuilder builder; - auto features = object.get("features").as_array(); - for (auto& feature : features.values()) { - builder.append(feature.to_string()); - builder.append(' '); - } - return GUI::Variant(builder.to_string()); - }); - processors_field.empend("family", "Family", Gfx::TextAlignment::CenterRight); - processors_field.empend("model", "Model", Gfx::TextAlignment::CenterRight); - processors_field.empend("stepping", "Stepping", Gfx::TextAlignment::CenterRight); - processors_field.empend("type", "Type", Gfx::TextAlignment::CenterRight); - - auto& processors_table_view = self.add<GUI::TableView>(); - processors_table_view.set_model(GUI::JsonArrayModel::create("/proc/cpuinfo", move(processors_field))); - processors_table_view.model()->update(); - }; - - return processors_widget; -} diff --git a/Applications/Terminal/CMakeLists.txt b/Applications/Terminal/CMakeLists.txt deleted file mode 100644 index 1fb969546e..0000000000 --- a/Applications/Terminal/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -compile_gml(TerminalSettingsWindow.gml TerminalSettingsWindowGML.h terminal_settings_window_gml) - -set(SOURCES - TerminalSettingsWindowGML.h - main.cpp -) - -serenity_app(Terminal ICON app-terminal) -target_link_libraries(Terminal LibGUI LibVT) diff --git a/Applications/Terminal/TerminalSettingsWindow.gml b/Applications/Terminal/TerminalSettingsWindow.gml deleted file mode 100644 index 07b8400c56..0000000000 --- a/Applications/Terminal/TerminalSettingsWindow.gml +++ /dev/null @@ -1,63 +0,0 @@ -@GUI::Widget { - fill_with_background_color: true - - layout: @GUI::VerticalBoxLayout { - margins: [4, 4, 4, 4] - } - - @GUI::GroupBox { - title: "Bell mode" - shrink_to_fit: true - - layout: @GUI::VerticalBoxLayout { - margins: [6, 16, 6, 6] - } - - @GUI::RadioButton { - name: "beep_bell_radio" - text: "System beep" - } - - @GUI::RadioButton { - name: "visual_bell_radio" - text: "Visual bell" - } - - @GUI::RadioButton { - name: "no_bell_radio" - text: "No bell" - } - } - - @GUI::GroupBox { - title: "Background opacity" - shrink_to_fit: true - - layout: @GUI::VerticalBoxLayout { - margins: [6, 16, 6, 6] - } - - @GUI::OpacitySlider { - name: "background_opacity_slider" - min: 0 - max: 255 - orientation: "Horizontal" - } - } - - @GUI::GroupBox { - title: "Scrollback size (lines)" - shrink_to_fit: true - - layout: @GUI::VerticalBoxLayout { - margins: [6, 16, 6, 6] - } - - @GUI::SpinBox { - name: "history_size_spinbox" - min: 0 - max: 40960 - orientation: "Horizontal" - } - } -} diff --git a/Applications/Terminal/main.cpp b/Applications/Terminal/main.cpp deleted file mode 100644 index 9267b9465f..0000000000 --- a/Applications/Terminal/main.cpp +++ /dev/null @@ -1,525 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 <AK/URL.h> -#include <Applications/Terminal/TerminalSettingsWindowGML.h> -#include <LibCore/ArgsParser.h> -#include <LibDesktop/Launcher.h> -#include <LibGUI/Action.h> -#include <LibGUI/ActionGroup.h> -#include <LibGUI/Application.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Button.h> -#include <LibGUI/CheckBox.h> -#include <LibGUI/Event.h> -#include <LibGUI/FontPicker.h> -#include <LibGUI/GroupBox.h> -#include <LibGUI/Icon.h> -#include <LibGUI/Menu.h> -#include <LibGUI/MenuBar.h> -#include <LibGUI/OpacitySlider.h> -#include <LibGUI/RadioButton.h> -#include <LibGUI/SpinBox.h> -#include <LibGUI/TextBox.h> -#include <LibGUI/Widget.h> -#include <LibGUI/Window.h> -#include <LibGfx/Font.h> -#include <LibGfx/Palette.h> -#include <LibVT/TerminalWidget.h> -#include <assert.h> -#include <errno.h> -#include <fcntl.h> -#include <pwd.h> -#include <serenity.h> -#include <signal.h> -#include <spawn.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/ioctl.h> -#include <sys/select.h> -#include <sys/wait.h> -#include <unistd.h> - -static void utmp_update(const char* tty, pid_t pid, bool create) -{ - if (!tty) - return; - int utmpupdate_pid = fork(); - if (utmpupdate_pid < 0) { - perror("fork"); - return; - } - if (utmpupdate_pid == 0) { - // Be careful here! Because fork() only clones one thread it's - // possible that we deadlock on anything involving a mutex, - // including the heap! So resort to low-level APIs - char pid_str[32]; - snprintf(pid_str, sizeof(pid_str), "%d", pid); - execl("/bin/utmpupdate", "/bin/utmpupdate", "-f", "Terminal", "-p", pid_str, (create ? "-c" : "-d"), tty, nullptr); - } else { - wait_again: - int status = 0; - if (waitpid(utmpupdate_pid, &status, 0) < 0) { - int err = errno; - if (err == EINTR) - goto wait_again; - perror("waitpid"); - return; - } - if (WIFEXITED(status) && WEXITSTATUS(status) != 0) - dbgln("Terminal: utmpupdate exited with status {}", WEXITSTATUS(status)); - else if (WIFSIGNALED(status)) - dbgln("Terminal: utmpupdate exited due to unhandled signal {}", WTERMSIG(status)); - } -} - -static pid_t run_command(int ptm_fd, String command) -{ - pid_t pid = fork(); - if (pid < 0) { - perror("fork"); - dbgln("run_command: could not fork to run '{}'", command); - return pid; - } - - if (pid == 0) { - const char* tty_name = ptsname(ptm_fd); - if (!tty_name) { - perror("ptsname"); - exit(1); - } - close(ptm_fd); - int pts_fd = open(tty_name, O_RDWR); - if (pts_fd < 0) { - perror("open"); - exit(1); - } - - if (setsid() < 0) { - perror("setsid"); - } - - close(0); - close(1); - close(2); - - int rc = dup2(pts_fd, 0); - if (rc < 0) { - perror("dup2"); - exit(1); - } - rc = dup2(pts_fd, 1); - if (rc < 0) { - perror("dup2"); - exit(1); - } - rc = dup2(pts_fd, 2); - if (rc < 0) { - perror("dup2"); - exit(1); - } - rc = close(pts_fd); - if (rc < 0) { - perror("close"); - exit(1); - } - rc = ioctl(0, TIOCSCTTY); - if (rc < 0) { - perror("ioctl(TIOCSCTTY)"); - exit(1); - } - - String shell = "/bin/Shell"; - auto* pw = getpwuid(getuid()); - if (pw && pw->pw_shell) { - shell = pw->pw_shell; - } - endpwent(); - - const char* args[4] = { shell.characters(), nullptr, nullptr, nullptr }; - if (!command.is_empty()) { - args[1] = "-c"; - args[2] = command.characters(); - } - const char* envs[] = { "PROMPT=\\X\\u@\\h:\\w\\a\\e[33;1m\\h\\e[0m \\e[34;1m\\w\\e[0m \\p ", "TERM=xterm", "PAGER=more", "PATH=/bin:/usr/bin:/usr/local/bin", nullptr }; - rc = execve(shell.characters(), const_cast<char**>(args), const_cast<char**>(envs)); - if (rc < 0) { - perror("execve"); - exit(1); - } - ASSERT_NOT_REACHED(); - } - - return pid; -} - -static RefPtr<GUI::Window> create_settings_window(TerminalWidget& terminal) -{ - auto window = GUI::Window::construct(terminal.window()); - window->set_title("Terminal settings"); - window->set_minimizable(false); - window->set_resizable(false); - window->resize(200, 210); - window->set_modal(true); - - auto& settings = window->set_main_widget<GUI::Widget>(); - settings.load_from_gml(terminal_settings_window_gml); - - auto& beep_bell_radio = *settings.find_descendant_of_type_named<GUI::RadioButton>("beep_bell_radio"); - auto& visual_bell_radio = *settings.find_descendant_of_type_named<GUI::RadioButton>("visual_bell_radio"); - auto& no_bell_radio = *settings.find_descendant_of_type_named<GUI::RadioButton>("no_bell_radio"); - - switch (terminal.bell_mode()) { - case TerminalWidget::BellMode::Visible: - visual_bell_radio.set_checked(true); - break; - case TerminalWidget::BellMode::AudibleBeep: - beep_bell_radio.set_checked(true); - break; - case TerminalWidget::BellMode::Disabled: - no_bell_radio.set_checked(true); - break; - } - - beep_bell_radio.on_checked = [&terminal](bool) { - terminal.set_bell_mode(TerminalWidget::BellMode::AudibleBeep); - }; - visual_bell_radio.on_checked = [&terminal](bool) { - terminal.set_bell_mode(TerminalWidget::BellMode::Visible); - }; - no_bell_radio.on_checked = [&terminal](bool) { - terminal.set_bell_mode(TerminalWidget::BellMode::Disabled); - }; - - auto& slider = *settings.find_descendant_of_type_named<GUI::OpacitySlider>("background_opacity_slider"); - slider.on_change = [&terminal](int value) { - terminal.set_opacity(value); - }; - slider.set_value(terminal.opacity()); - - auto& history_size_spinbox = *settings.find_descendant_of_type_named<GUI::SpinBox>("history_size_spinbox"); - history_size_spinbox.set_value(terminal.max_history_size()); - history_size_spinbox.on_change = [&terminal](int value) { - terminal.set_max_history_size(value); - }; - - return window; -} - -static RefPtr<GUI::Window> create_find_window(TerminalWidget& terminal) -{ - auto window = GUI::Window::construct(); - window->set_title("Find in Terminal"); - window->set_resizable(false); - window->resize(300, 90); - window->set_modal(true); - - auto& search = window->set_main_widget<GUI::Widget>(); - search.set_fill_with_background_color(true); - search.set_background_role(ColorRole::Button); - search.set_layout<GUI::VerticalBoxLayout>(); - search.layout()->set_margins({ 4, 4, 4, 4 }); - - auto& find = search.add<GUI::Widget>(); - find.set_layout<GUI::HorizontalBoxLayout>(); - find.layout()->set_margins({ 4, 4, 4, 4 }); - find.set_fixed_height(30); - - auto& find_textbox = find.add<GUI::TextBox>(); - find_textbox.set_fixed_width(230); - find_textbox.set_focus(true); - if (terminal.has_selection()) { - String selected_text = terminal.selected_text(); - selected_text.replace("\n", " ", true); - find_textbox.set_text(selected_text); - } - auto& find_backwards = find.add<GUI::Button>(); - find_backwards.set_fixed_width(25); - find_backwards.set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/upward-triangle.png")); - auto& find_forwards = find.add<GUI::Button>(); - find_forwards.set_fixed_width(25); - find_forwards.set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/downward-triangle.png")); - - find_textbox.on_return_pressed = [&]() { - find_backwards.click(); - }; - - auto& match_case = search.add<GUI::CheckBox>("Case sensitive"); - auto& wrap_around = search.add<GUI::CheckBox>("Wrap around"); - - find_backwards.on_click = [&](auto) { - auto needle = find_textbox.text(); - if (needle.is_empty()) { - return; - } - - auto found_range = terminal.find_previous(needle, terminal.normalized_selection().start(), match_case.is_checked(), wrap_around.is_checked()); - - if (found_range.is_valid()) { - terminal.scroll_to_row(found_range.start().row()); - terminal.set_selection(found_range); - } - }; - find_forwards.on_click = [&](auto) { - auto needle = find_textbox.text(); - if (needle.is_empty()) { - return; - } - - auto found_range = terminal.find_next(needle, terminal.normalized_selection().end(), match_case.is_checked(), wrap_around.is_checked()); - - if (found_range.is_valid()) { - terminal.scroll_to_row(found_range.start().row()); - terminal.set_selection(found_range); - } - }; - - return window; -} - -int main(int argc, char** argv) -{ - if (pledge("stdio tty rpath accept cpath wpath shared_buffer proc exec unix fattr sigaction", nullptr) < 0) { - perror("pledge"); - return 1; - } - - struct sigaction act; - memset(&act, 0, sizeof(act)); - act.sa_flags = SA_NOCLDWAIT; - act.sa_handler = SIG_IGN; - int rc = sigaction(SIGCHLD, &act, nullptr); - if (rc < 0) { - perror("sigaction"); - return 1; - } - - auto app = GUI::Application::construct(argc, argv); - - if (pledge("stdio tty rpath accept cpath wpath shared_buffer proc exec unix", nullptr) < 0) { - perror("pledge"); - return 1; - } - - const char* command_to_execute = nullptr; - - Core::ArgsParser args_parser; - args_parser.add_option(command_to_execute, "Execute this command inside the terminal", nullptr, 'e', "command"); - - args_parser.parse(argc, argv); - - int ptm_fd = posix_openpt(O_RDWR | O_CLOEXEC); - if (ptm_fd < 0) { - perror("posix_openpt"); - return 1; - } - if (grantpt(ptm_fd) < 0) { - perror("grantpt"); - return 1; - } - if (unlockpt(ptm_fd) < 0) { - perror("unlockpt"); - return 1; - } - - RefPtr<Core::ConfigFile> config = Core::ConfigFile::get_for_app("Terminal"); - - pid_t shell_pid = 0; - - if (command_to_execute) - shell_pid = run_command(ptm_fd, command_to_execute); - else - shell_pid = run_command(ptm_fd, config->read_entry("Startup", "Command", "")); - - auto* pts_name = ptsname(ptm_fd); - utmp_update(pts_name, shell_pid, true); - - auto app_icon = GUI::Icon::default_icon("app-terminal"); - - auto window = GUI::Window::construct(); - window->set_title("Terminal"); - window->set_background_color(Color::Black); - window->set_double_buffering_enabled(false); - - auto& terminal = window->set_main_widget<TerminalWidget>(ptm_fd, true, config); - terminal.on_command_exit = [&] { - app->quit(0); - }; - terminal.on_title_change = [&](auto& title) { - window->set_title(title); - }; - terminal.apply_size_increments_to_window(*window); - window->show(); - window->set_icon(app_icon.bitmap_for_size(16)); - - auto bell = config->read_entry("Window", "Bell", "Visible"); - if (bell == "AudibleBeep") { - terminal.set_bell_mode(TerminalWidget::BellMode::AudibleBeep); - } else if (bell == "Disabled") { - terminal.set_bell_mode(TerminalWidget::BellMode::Disabled); - } else { - terminal.set_bell_mode(TerminalWidget::BellMode::Visible); - } - - RefPtr<GUI::Window> settings_window; - RefPtr<GUI::Window> find_window; - - auto new_opacity = config->read_num_entry("Window", "Opacity", 255); - terminal.set_opacity(new_opacity); - window->set_has_alpha_channel(new_opacity < 255); - - auto new_scrollback_size = config->read_num_entry("Terminal", "MaxHistorySize", terminal.max_history_size()); - terminal.set_max_history_size(new_scrollback_size); - - auto open_settings_action = GUI::Action::create("Settings...", Gfx::Bitmap::load_from_file("/res/icons/16x16/gear.png"), - [&](const GUI::Action&) { - if (!settings_window) { - settings_window = create_settings_window(terminal); - settings_window->on_close_request = [&] { - settings_window->remove_from_parent(); - settings_window = nullptr; - return GUI::Window::CloseRequestDecision::Close; - }; - } - if (!settings_window->is_visible()) { - settings_window->center_within(*window); - settings_window->show(); - } - settings_window->move_to_front(); - }); - - terminal.context_menu().add_separator(); - auto pick_font_action = GUI::Action::create("Terminal font...", Gfx::Bitmap::load_from_file("/res/icons/16x16/app-font-editor.png"), - [&](auto&) { - auto picker = GUI::FontPicker::construct(window, &terminal.font(), true); - if (picker->exec() == GUI::Dialog::ExecOK) { - terminal.set_font_and_resize_to_fit(*picker->font()); - window->resize(terminal.size()); - config->write_entry("Text", "Font", picker->font()->qualified_name()); - config->sync(); - } - }); - - terminal.context_menu().add_action(pick_font_action); - - terminal.context_menu().add_separator(); - terminal.context_menu().add_action(open_settings_action); - - auto menubar = GUI::MenuBar::construct(); - - auto& app_menu = menubar->add_menu("Terminal"); - app_menu.add_action(GUI::Action::create("Open new terminal", { Mod_Ctrl | Mod_Shift, Key_N }, Gfx::Bitmap::load_from_file("/res/icons/16x16/app-terminal.png"), [&](auto&) { - pid_t child; - const char* argv[] = { "Terminal", nullptr }; - if ((errno = posix_spawn(&child, "/bin/Terminal", nullptr, nullptr, const_cast<char**>(argv), environ))) { - perror("posix_spawn"); - } else { - if (disown(child) < 0) - perror("disown"); - } - })); - - app_menu.add_action(open_settings_action); - app_menu.add_separator(); - app_menu.add_action(GUI::CommonActions::make_quit_action([](auto&) { - dbgln("Terminal: Quit menu activated!"); - GUI::Application::the()->quit(); - })); - - auto& edit_menu = menubar->add_menu("Edit"); - edit_menu.add_action(terminal.copy_action()); - edit_menu.add_action(terminal.paste_action()); - edit_menu.add_separator(); - edit_menu.add_action(GUI::Action::create("Find...", { Mod_Ctrl | Mod_Shift, Key_F }, Gfx::Bitmap::load_from_file("/res/icons/16x16/find.png"), - [&](auto&) { - if (!find_window) { - find_window = create_find_window(terminal); - find_window->on_close_request = [&] { - find_window = nullptr; - return GUI::Window::CloseRequestDecision::Close; - }; - } - find_window->show(); - find_window->move_to_front(); - })); - - auto& view_menu = menubar->add_menu("View"); - view_menu.add_action(terminal.clear_including_history_action()); - view_menu.add_separator(); - view_menu.add_action(pick_font_action); - - auto& help_menu = menubar->add_menu("Help"); - help_menu.add_action(GUI::CommonActions::make_help_action([](auto&) { - Desktop::Launcher::open(URL::create_with_file_protocol("/usr/share/man/man1/Terminal.md"), "/bin/Help"); - })); - help_menu.add_action(GUI::CommonActions::make_about_action("Terminal", app_icon, window)); - - app->set_menubar(move(menubar)); - - if (unveil("/res", "r") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/bin", "r") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/bin/Terminal", "x") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/bin/utmpupdate", "x") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/etc/FileIconProvider.ini", "r") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/tmp/portal/launch", "rw") < 0) { - perror("unveil"); - return 1; - } - - if (unveil(config->file_name().characters(), "rwc")) { - perror("unveil"); - return 1; - } - - unveil(nullptr, nullptr); - - config->sync(); - int result = app->exec(); - dbgln("Exiting terminal, updating utmp"); - utmp_update(pts_name, 0, false); - return result; -} diff --git a/Applications/TextEditor/CMakeLists.txt b/Applications/TextEditor/CMakeLists.txt deleted file mode 100644 index 07eccd212e..0000000000 --- a/Applications/TextEditor/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -compile_gml(TextEditorWindow.gml TextEditorWindowGML.h text_editor_window_gml) - -set(SOURCES - main.cpp - TextEditorWidget.cpp - TextEditorWindowGML.h -) - -serenity_app(TextEditor ICON app-text-editor) -target_link_libraries(TextEditor LibWeb LibMarkdown LibGUI LibShell LibRegex LibDesktop) diff --git a/Applications/TextEditor/TextEditorWidget.cpp b/Applications/TextEditor/TextEditorWidget.cpp deleted file mode 100644 index 982bcc282c..0000000000 --- a/Applications/TextEditor/TextEditorWidget.cpp +++ /dev/null @@ -1,667 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "TextEditorWidget.h" -#include <AK/JsonObject.h> -#include <AK/JsonValue.h> -#include <AK/Optional.h> -#include <AK/StringBuilder.h> -#include <AK/URL.h> -#include <Applications/TextEditor/TextEditorWindowGML.h> -#include <LibCore/File.h> -#include <LibCore/MimeData.h> -#include <LibDesktop/Launcher.h> -#include <LibGUI/Action.h> -#include <LibGUI/ActionGroup.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Button.h> -#include <LibGUI/CppSyntaxHighlighter.h> -#include <LibGUI/FilePicker.h> -#include <LibGUI/FontPicker.h> -#include <LibGUI/GMLSyntaxHighlighter.h> -#include <LibGUI/INISyntaxHighlighter.h> -#include <LibGUI/JSSyntaxHighlighter.h> -#include <LibGUI/Menu.h> -#include <LibGUI/MenuBar.h> -#include <LibGUI/MessageBox.h> -#include <LibGUI/RegularEditingEngine.h> -#include <LibGUI/ShellSyntaxHighlighter.h> -#include <LibGUI/Splitter.h> -#include <LibGUI/StatusBar.h> -#include <LibGUI/TextBox.h> -#include <LibGUI/TextEditor.h> -#include <LibGUI/ToolBar.h> -#include <LibGUI/ToolBarContainer.h> -#include <LibGUI/VimEditingEngine.h> -#include <LibGfx/Font.h> -#include <LibMarkdown/Document.h> -#include <LibWeb/OutOfProcessWebView.h> -#include <string.h> - -TextEditorWidget::TextEditorWidget() -{ - load_from_gml(text_editor_window_gml); - - auto& toolbar = *find_descendant_of_type_named<GUI::ToolBar>("toolbar"); - - m_editor = *find_descendant_of_type_named<GUI::TextEditor>("editor"); - m_editor->set_ruler_visible(true); - m_editor->set_automatic_indentation_enabled(true); - m_editor->set_line_wrapping_enabled(true); - m_editor->set_editing_engine(make<GUI::RegularEditingEngine>()); - - m_editor->on_change = [this] { - update_preview(); - - // Do not mark as dirty on the first change (When document is first opened.) - if (m_document_opening) { - m_document_opening = false; - return; - } - - bool was_dirty = m_document_dirty; - m_document_dirty = true; - if (!was_dirty) - update_title(); - }; - - m_page_view = *find_descendant_of_type_named<Web::OutOfProcessWebView>("webview"); - m_page_view->on_link_hover = [this](auto& url) { - if (url.is_valid()) - m_statusbar->set_text(url.to_string()); - else - update_statusbar_cursor_position(); - }; - m_page_view->on_link_click = [&](auto& url, auto&, unsigned) { - if (!Desktop::Launcher::open(url)) { - GUI::MessageBox::show( - window(), - String::formatted("The link to '{}' could not be opened.", url), - "Failed to open link", - GUI::MessageBox::Type::Error); - } - }; - - m_find_replace_widget = *find_descendant_of_type_named<GUI::Widget>("find_replace_widget"); - - m_find_widget = *find_descendant_of_type_named<GUI::Widget>("find_widget"); - - m_replace_widget = *find_descendant_of_type_named<GUI::Widget>("replace_widget"); - - m_find_textbox = m_find_widget->add<GUI::TextBox>(); - m_replace_textbox = m_replace_widget->add<GUI::TextBox>(); - - m_find_next_action = GUI::Action::create("Find next", { Mod_Ctrl, Key_G }, Gfx::Bitmap::load_from_file("/res/icons/16x16/find-next.png"), [&](auto&) { - auto needle = m_find_textbox->text(); - if (needle.is_empty()) { - dbgln("find_next(\"\")"); - return; - } - - if (m_find_use_regex) - m_editor->document().update_regex_matches(needle); - - auto found_range = m_editor->document().find_next(needle, m_editor->normalized_selection().end(), GUI::TextDocument::SearchShouldWrap::Yes, m_find_use_regex); - dbgln("find_next('{}') returned {}", needle, found_range); - if (found_range.is_valid()) { - m_editor->set_selection(found_range); - } else { - GUI::MessageBox::show(window(), - String::formatted("Not found: \"{}\"", needle), - "Not found", - GUI::MessageBox::Type::Information); - } - }); - - m_find_regex_action = GUI::Action::create("Find regex", { Mod_Ctrl | Mod_Shift, Key_R }, [&](auto&) { - m_find_regex_button->set_checked(!m_find_regex_button->is_checked()); - m_find_use_regex = m_find_regex_button->is_checked(); - }); - - m_find_previous_action = GUI::Action::create("Find previous", { Mod_Ctrl | Mod_Shift, Key_G }, [&](auto&) { - auto needle = m_find_textbox->text(); - if (needle.is_empty()) { - dbgln("find_prev(\"\")"); - return; - } - - auto selection_start = m_editor->normalized_selection().start(); - if (!selection_start.is_valid()) - selection_start = m_editor->normalized_selection().end(); - - if (m_find_use_regex) - m_editor->document().update_regex_matches(needle); - - auto found_range = m_editor->document().find_previous(needle, selection_start, GUI::TextDocument::SearchShouldWrap::Yes, m_find_use_regex); - - dbgln("find_prev(\"{}\") returned {}", needle, found_range); - if (found_range.is_valid()) { - m_editor->set_selection(found_range); - } else { - GUI::MessageBox::show(window(), - String::formatted("Not found: \"{}\"", needle), - "Not found", - GUI::MessageBox::Type::Information); - } - }); - - m_replace_next_action = GUI::Action::create("Replace next", { Mod_Ctrl, Key_F1 }, [&](auto&) { - auto needle = m_find_textbox->text(); - auto substitute = m_replace_textbox->text(); - - if (needle.is_empty()) - return; - - auto selection_start = m_editor->normalized_selection().start(); - if (!selection_start.is_valid()) - selection_start = m_editor->normalized_selection().start(); - - if (m_find_use_regex) - m_editor->document().update_regex_matches(needle); - - auto found_range = m_editor->document().find_next(needle, selection_start, GUI::TextDocument::SearchShouldWrap::Yes, m_find_use_regex); - - if (found_range.is_valid()) { - m_editor->set_selection(found_range); - m_editor->insert_at_cursor_or_replace_selection(substitute); - } else { - GUI::MessageBox::show(window(), - String::formatted("Not found: \"{}\"", needle), - "Not found", - GUI::MessageBox::Type::Information); - } - }); - - m_replace_previous_action = GUI::Action::create("Replace previous", { Mod_Ctrl | Mod_Shift, Key_F1 }, [&](auto&) { - auto needle = m_find_textbox->text(); - auto substitute = m_replace_textbox->text(); - if (needle.is_empty()) - return; - - auto selection_start = m_editor->normalized_selection().start(); - if (!selection_start.is_valid()) - selection_start = m_editor->normalized_selection().start(); - - if (m_find_use_regex) - m_editor->document().update_regex_matches(needle); - - auto found_range = m_editor->document().find_previous(needle, selection_start); - - if (found_range.is_valid()) { - m_editor->set_selection(found_range); - m_editor->insert_at_cursor_or_replace_selection(substitute); - } else { - GUI::MessageBox::show(window(), - String::formatted("Not found: \"{}\"", needle), - "Not found", - GUI::MessageBox::Type::Information); - } - }); - - m_replace_all_action = GUI::Action::create("Replace all", { Mod_Ctrl, Key_F2 }, [&](auto&) { - auto needle = m_find_textbox->text(); - auto substitute = m_replace_textbox->text(); - if (needle.is_empty()) - return; - if (m_find_use_regex) - m_editor->document().update_regex_matches(needle); - - auto found_range = m_editor->document().find_next(needle, {}, GUI::TextDocument::SearchShouldWrap::Yes, m_find_use_regex); - while (found_range.is_valid()) { - m_editor->set_selection(found_range); - m_editor->insert_at_cursor_or_replace_selection(substitute); - found_range = m_editor->document().find_next(needle, {}, GUI::TextDocument::SearchShouldWrap::Yes, m_find_use_regex); - } - }); - - m_find_previous_button = *find_descendant_of_type_named<GUI::Button>("find_previous_button"); - m_find_previous_button->set_action(*m_find_previous_action); - - m_find_next_button = *find_descendant_of_type_named<GUI::Button>("find_next_button"); - m_find_next_button->set_action(*m_find_next_action); - - m_find_textbox->on_return_pressed = [this] { - m_find_next_button->click(); - }; - - m_find_regex_button = m_find_widget->add<GUI::Button>(".*"); - m_find_regex_button->set_fixed_width(20); - m_find_regex_button->set_action(*m_find_regex_action); - - m_find_textbox->on_escape_pressed = [this] { - m_find_replace_widget->set_visible(false); - m_editor->set_focus(true); - }; - - m_replace_previous_button = *find_descendant_of_type_named<GUI::Button>("replace_previous_button"); - m_replace_previous_button->set_action(*m_replace_previous_action); - - m_replace_next_button = *find_descendant_of_type_named<GUI::Button>("replace_next_button"); - m_replace_next_button->set_action(*m_replace_next_action); - - m_replace_all_button = *find_descendant_of_type_named<GUI::Button>("replace_all_button"); - m_replace_all_button->set_action(*m_replace_all_action); - - m_replace_textbox->on_return_pressed = [this] { - m_replace_next_button->click(); - }; - - m_replace_textbox->on_escape_pressed = [this] { - m_find_replace_widget->set_visible(false); - m_editor->set_focus(true); - }; - - m_vim_emulation_setting_action = GUI::Action::create_checkable("Vim emulation", { Mod_Ctrl | Mod_Shift | Mod_Alt, Key_V }, [&](auto& action) { - if (action.is_checked()) - m_editor->set_editing_engine(make<GUI::VimEditingEngine>()); - else - m_editor->set_editing_engine(make<GUI::RegularEditingEngine>()); - }); - m_vim_emulation_setting_action->set_checked(false); - - m_find_replace_action = GUI::Action::create("Find/Replace...", { Mod_Ctrl, Key_F }, Gfx::Bitmap::load_from_file("/res/icons/16x16/find.png"), [this](auto&) { - m_find_replace_widget->set_visible(true); - m_find_widget->set_visible(true); - m_replace_widget->set_visible(true); - m_find_textbox->set_focus(true); - - if (m_editor->has_selection()) { - auto selected_text = m_editor->document().text_in_range(m_editor->normalized_selection()); - m_find_textbox->set_text(selected_text); - } - m_find_textbox->select_all(); - }); - - m_editor->add_custom_context_menu_action(*m_find_replace_action); - m_editor->add_custom_context_menu_action(*m_find_next_action); - m_editor->add_custom_context_menu_action(*m_find_previous_action); - - m_statusbar = *find_descendant_of_type_named<GUI::StatusBar>("statusbar"); - - m_editor->on_cursor_change = [this] { update_statusbar_cursor_position(); }; - - m_new_action = GUI::Action::create("New", { Mod_Ctrl, Key_N }, Gfx::Bitmap::load_from_file("/res/icons/16x16/new.png"), [this](const GUI::Action&) { - if (m_document_dirty) { - auto save_document_first_result = GUI::MessageBox::show(window(), "Save changes to current document first?", "Warning", GUI::MessageBox::Type::Warning, GUI::MessageBox::InputType::YesNoCancel); - if (save_document_first_result == GUI::Dialog::ExecResult::ExecYes) - m_save_action->activate(); - if (save_document_first_result == GUI::Dialog::ExecResult::ExecCancel) - return; - } - - m_document_dirty = false; - m_editor->set_text(StringView()); - set_path(LexicalPath()); - update_title(); - }); - - m_open_action = GUI::CommonActions::make_open_action([this](auto&) { - Optional<String> open_path = GUI::FilePicker::get_open_filepath(window()); - - if (!open_path.has_value()) - return; - - if (m_document_dirty) { - auto save_document_first_result = GUI::MessageBox::show(window(), "Save changes to current document first?", "Warning", GUI::MessageBox::Type::Warning, GUI::MessageBox::InputType::YesNoCancel); - if (save_document_first_result == GUI::Dialog::ExecResult::ExecYes) - m_save_action->activate(); - if (save_document_first_result == GUI::Dialog::ExecResult::ExecCancel) - return; - } - - open_sesame(open_path.value()); - }); - - m_save_as_action = GUI::CommonActions::make_save_as_action([&](auto&) { - Optional<String> save_path = GUI::FilePicker::get_save_filepath(window(), m_name.is_null() ? "Untitled" : m_name, m_extension.is_null() ? "txt" : m_extension); - if (!save_path.has_value()) - return; - - if (!m_editor->write_to_file(save_path.value())) { - GUI::MessageBox::show(window(), "Unable to save file.\n", "Error", GUI::MessageBox::Type::Error); - return; - } - - m_document_dirty = false; - set_path(LexicalPath(save_path.value())); - dbgln("Wrote document to {}", save_path.value()); - }); - - m_save_action = GUI::CommonActions::make_save_action([&](auto&) { - if (!m_path.is_empty()) { - if (!m_editor->write_to_file(m_path)) { - GUI::MessageBox::show(window(), "Unable to save file.\n", "Error", GUI::MessageBox::Type::Error); - } else { - m_document_dirty = false; - update_title(); - } - return; - } - - m_save_as_action->activate(); - }); - - m_line_wrapping_setting_action = GUI::Action::create_checkable("Line wrapping", [&](auto& action) { - m_editor->set_line_wrapping_enabled(action.is_checked()); - }); - m_line_wrapping_setting_action->set_checked(m_editor->is_line_wrapping_enabled()); - - auto menubar = GUI::MenuBar::construct(); - auto& app_menu = menubar->add_menu("Text Editor"); - app_menu.add_action(*m_new_action); - app_menu.add_action(*m_open_action); - app_menu.add_action(*m_save_action); - app_menu.add_action(*m_save_as_action); - app_menu.add_separator(); - app_menu.add_action(GUI::CommonActions::make_quit_action([this](auto&) { - if (!request_close()) - return; - GUI::Application::the()->quit(); - })); - - auto& edit_menu = menubar->add_menu("Edit"); - edit_menu.add_action(m_editor->undo_action()); - edit_menu.add_action(m_editor->redo_action()); - edit_menu.add_separator(); - edit_menu.add_action(m_editor->cut_action()); - edit_menu.add_action(m_editor->copy_action()); - edit_menu.add_action(m_editor->paste_action()); - edit_menu.add_action(m_editor->delete_action()); - edit_menu.add_separator(); - edit_menu.add_action(*m_vim_emulation_setting_action); - edit_menu.add_separator(); - edit_menu.add_action(*m_find_replace_action); - edit_menu.add_action(*m_find_next_action); - edit_menu.add_action(*m_find_regex_action); - edit_menu.add_action(*m_find_previous_action); - edit_menu.add_action(*m_replace_next_action); - edit_menu.add_action(*m_replace_previous_action); - edit_menu.add_action(*m_replace_all_action); - - m_no_preview_action = GUI::Action::create_checkable( - "No preview", [this](auto&) { - set_preview_mode(PreviewMode::None); - }); - - m_markdown_preview_action = GUI::Action::create_checkable( - "Markdown preview", [this](auto&) { - set_preview_mode(PreviewMode::Markdown); - }, - this); - - m_html_preview_action = GUI::Action::create_checkable( - "HTML preview", [this](auto&) { - set_preview_mode(PreviewMode::HTML); - }, - this); - - m_preview_actions.add_action(*m_no_preview_action); - m_preview_actions.add_action(*m_markdown_preview_action); - m_preview_actions.add_action(*m_html_preview_action); - m_preview_actions.set_exclusive(true); - - auto& view_menu = menubar->add_menu("View"); - view_menu.add_action(GUI::Action::create("Editor font...", Gfx::Bitmap::load_from_file("/res/icons/16x16/app-font-editor.png"), - [&](auto&) { - auto picker = GUI::FontPicker::construct(window(), &m_editor->font(), true); - if (picker->exec() == GUI::Dialog::ExecOK) { - dbgln("setting font {}", picker->font()->qualified_name()); - m_editor->set_font(picker->font()); - } - })); - - view_menu.add_separator(); - view_menu.add_action(*m_line_wrapping_setting_action); - view_menu.add_separator(); - view_menu.add_action(*m_no_preview_action); - view_menu.add_action(*m_markdown_preview_action); - view_menu.add_action(*m_html_preview_action); - view_menu.add_separator(); - - syntax_actions.set_exclusive(true); - - auto& syntax_menu = view_menu.add_submenu("Syntax"); - m_plain_text_highlight = GUI::Action::create_checkable("Plain text", [&](auto&) { - m_editor->set_syntax_highlighter({}); - m_editor->update(); - }); - m_plain_text_highlight->set_checked(true); - syntax_actions.add_action(*m_plain_text_highlight); - syntax_menu.add_action(*m_plain_text_highlight); - - m_cpp_highlight = GUI::Action::create_checkable("C++", [&](auto&) { - m_editor->set_syntax_highlighter(make<GUI::CppSyntaxHighlighter>()); - m_editor->update(); - }); - syntax_actions.add_action(*m_cpp_highlight); - syntax_menu.add_action(*m_cpp_highlight); - - m_js_highlight = GUI::Action::create_checkable("JavaScript", [&](auto&) { - m_editor->set_syntax_highlighter(make<GUI::JSSyntaxHighlighter>()); - m_editor->update(); - }); - syntax_actions.add_action(*m_js_highlight); - syntax_menu.add_action(*m_js_highlight); - - m_gml_highlight = GUI::Action::create_checkable("GML", [&](auto&) { - m_editor->set_syntax_highlighter(make<GUI::GMLSyntaxHighlighter>()); - m_editor->update(); - }); - syntax_actions.add_action(*m_gml_highlight); - syntax_menu.add_action(*m_gml_highlight); - - m_ini_highlight = GUI::Action::create_checkable("INI File", [&](auto&) { - m_editor->set_syntax_highlighter(make<GUI::IniSyntaxHighlighter>()); - m_editor->update(); - }); - syntax_actions.add_action(*m_ini_highlight); - syntax_menu.add_action(*m_ini_highlight); - - m_shell_highlight = GUI::Action::create_checkable("Shell File", [&](auto&) { - m_editor->set_syntax_highlighter(make<GUI::ShellSyntaxHighlighter>()); - m_editor->update(); - }); - syntax_actions.add_action(*m_shell_highlight); - syntax_menu.add_action(*m_shell_highlight); - - auto& help_menu = menubar->add_menu("Help"); - help_menu.add_action(GUI::CommonActions::make_about_action("Text Editor", GUI::Icon::default_icon("app-text-editor"), window())); - - GUI::Application::the()->set_menubar(move(menubar)); - - toolbar.add_action(*m_new_action); - toolbar.add_action(*m_open_action); - toolbar.add_action(*m_save_action); - - toolbar.add_separator(); - - toolbar.add_action(m_editor->cut_action()); - toolbar.add_action(m_editor->copy_action()); - toolbar.add_action(m_editor->paste_action()); - toolbar.add_action(m_editor->delete_action()); - - toolbar.add_separator(); - - toolbar.add_action(m_editor->undo_action()); - toolbar.add_action(m_editor->redo_action()); -} - -TextEditorWidget::~TextEditorWidget() -{ -} - -void TextEditorWidget::set_path(const LexicalPath& lexical_path) -{ - m_path = lexical_path.string(); - m_name = lexical_path.title(); - m_extension = lexical_path.extension(); - - if (m_extension == "c" || m_extension == "cc" || m_extension == "cxx" || m_extension == "cpp" || m_extension == "h") { - m_cpp_highlight->activate(); - } else if (m_extension == "js" || m_extension == "json") { - m_js_highlight->activate(); - } else if (m_extension == "gml") { - m_gml_highlight->activate(); - } else if (m_extension == "ini") { - m_ini_highlight->activate(); - } else { - m_plain_text_highlight->activate(); - } - - if (m_auto_detect_preview_mode) { - if (m_extension == "md") - set_preview_mode(PreviewMode::Markdown); - else if (m_extension == "html") - set_preview_mode(PreviewMode::HTML); - else - set_preview_mode(PreviewMode::None); - } - - update_title(); -} - -void TextEditorWidget::update_title() -{ - StringBuilder builder; - if (m_path.is_empty()) - builder.append("Untitled"); - else - builder.append(m_path); - if (m_document_dirty) - builder.append(" (*)"); - builder.append(" - Text Editor"); - window()->set_title(builder.to_string()); -} - -void TextEditorWidget::open_sesame(const String& path) -{ - auto file = Core::File::construct(path); - if (!file->open(Core::IODevice::ReadOnly) && file->error() != ENOENT) { - GUI::MessageBox::show(window(), String::formatted("Opening \"{}\" failed: {}", path, strerror(errno)), "Error", GUI::MessageBox::Type::Error); - return; - } - - m_editor->set_text(file->read_all()); - m_document_dirty = false; - m_document_opening = true; - - set_path(LexicalPath(path)); - - m_editor->set_focus(true); -} - -bool TextEditorWidget::request_close() -{ - if (!m_document_dirty) - return true; - auto result = GUI::MessageBox::show(window(), "The document has been modified. Would you like to save?", "Unsaved changes", GUI::MessageBox::Type::Warning, GUI::MessageBox::InputType::YesNoCancel); - - if (result == GUI::MessageBox::ExecYes) { - m_save_action->activate(); - return true; - } - - if (result == GUI::MessageBox::ExecNo) - return true; - - return false; -} - -void TextEditorWidget::drop_event(GUI::DropEvent& event) -{ - event.accept(); - window()->move_to_front(); - - if (event.mime_data().has_urls()) { - auto urls = event.mime_data().urls(); - if (urls.is_empty()) - return; - if (urls.size() > 1) { - GUI::MessageBox::show(window(), "TextEditor can only open one file at a time!", "One at a time please!", GUI::MessageBox::Type::Error); - return; - } - open_sesame(urls.first().path()); - } -} - -void TextEditorWidget::set_preview_mode(PreviewMode mode) -{ - if (m_preview_mode == mode) - return; - m_preview_mode = mode; - - if (m_preview_mode == PreviewMode::HTML) { - m_html_preview_action->set_checked(true); - m_page_view->set_visible(true); - update_html_preview(); - } else if (m_preview_mode == PreviewMode::Markdown) { - m_markdown_preview_action->set_checked(true); - m_page_view->set_visible(true); - update_markdown_preview(); - } else { - m_no_preview_action->set_checked(true); - m_page_view->set_visible(false); - } -} - -void TextEditorWidget::update_preview() -{ - switch (m_preview_mode) { - case PreviewMode::Markdown: - update_markdown_preview(); - break; - case PreviewMode::HTML: - update_html_preview(); - break; - default: - break; - } -} - -void TextEditorWidget::update_markdown_preview() -{ - auto document = Markdown::Document::parse(m_editor->text()); - if (document) { - auto html = document->render_to_html(); - auto current_scroll_pos = m_page_view->visible_content_rect(); - m_page_view->load_html(html, URL::create_with_file_protocol(m_path)); - m_page_view->scroll_into_view(current_scroll_pos, true, true); - } -} - -void TextEditorWidget::update_html_preview() -{ - auto current_scroll_pos = m_page_view->visible_content_rect(); - m_page_view->load_html(m_editor->text(), URL::create_with_file_protocol(m_path)); - m_page_view->scroll_into_view(current_scroll_pos, true, true); -} - -void TextEditorWidget::update_statusbar_cursor_position() -{ - StringBuilder builder; - builder.appendff("Line: {}, Column: {}", m_editor->cursor().line() + 1, m_editor->cursor().column()); - m_statusbar->set_text(builder.to_string()); -} diff --git a/Applications/TextEditor/TextEditorWidget.h b/Applications/TextEditor/TextEditorWidget.h deleted file mode 100644 index 763087b9e4..0000000000 --- a/Applications/TextEditor/TextEditorWidget.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 <AK/Function.h> -#include <AK/LexicalPath.h> -#include <LibGUI/ActionGroup.h> -#include <LibGUI/Application.h> -#include <LibGUI/Icon.h> -#include <LibGUI/TextEditor.h> -#include <LibGUI/Widget.h> -#include <LibGUI/Window.h> -#include <LibWeb/Forward.h> - -class TextEditorWidget final : public GUI::Widget { - C_OBJECT(TextEditorWidget) -public: - virtual ~TextEditorWidget() override; - void open_sesame(const String& path); - bool request_close(); - - GUI::TextEditor& editor() { return *m_editor; } - - enum class PreviewMode { - None, - Markdown, - HTML, - }; - - void set_preview_mode(PreviewMode); - void set_auto_detect_preview_mode(bool value) { m_auto_detect_preview_mode = value; } - - void update_title(); - -private: - TextEditorWidget(); - void set_path(const LexicalPath& file); - void update_preview(); - void update_markdown_preview(); - void update_html_preview(); - void update_statusbar_cursor_position(); - - virtual void drop_event(GUI::DropEvent&) override; - - RefPtr<GUI::TextEditor> m_editor; - String m_path; - String m_name; - String m_extension; - RefPtr<GUI::Action> m_new_action; - RefPtr<GUI::Action> m_open_action; - RefPtr<GUI::Action> m_save_action; - RefPtr<GUI::Action> m_save_as_action; - RefPtr<GUI::Action> m_find_replace_action; - RefPtr<GUI::Action> m_line_wrapping_setting_action; - RefPtr<GUI::Action> m_vim_emulation_setting_action; - - RefPtr<GUI::Action> m_find_next_action; - RefPtr<GUI::Action> m_find_regex_action; - RefPtr<GUI::Action> m_find_previous_action; - RefPtr<GUI::Action> m_replace_next_action; - RefPtr<GUI::Action> m_replace_previous_action; - RefPtr<GUI::Action> m_replace_all_action; - - GUI::ActionGroup m_preview_actions; - RefPtr<GUI::Action> m_no_preview_action; - RefPtr<GUI::Action> m_markdown_preview_action; - RefPtr<GUI::Action> m_html_preview_action; - - RefPtr<GUI::StatusBar> m_statusbar; - - RefPtr<GUI::TextBox> m_find_textbox; - RefPtr<GUI::TextBox> m_replace_textbox; - RefPtr<GUI::Button> m_find_previous_button; - RefPtr<GUI::Button> m_find_next_button; - RefPtr<GUI::Button> m_find_regex_button; - RefPtr<GUI::Button> m_replace_previous_button; - RefPtr<GUI::Button> m_replace_next_button; - RefPtr<GUI::Button> m_replace_all_button; - RefPtr<GUI::Widget> m_find_replace_widget; - RefPtr<GUI::Widget> m_find_widget; - RefPtr<GUI::Widget> m_replace_widget; - - GUI::ActionGroup syntax_actions; - RefPtr<GUI::Action> m_plain_text_highlight; - RefPtr<GUI::Action> m_cpp_highlight; - RefPtr<GUI::Action> m_js_highlight; - RefPtr<GUI::Action> m_gml_highlight; - RefPtr<GUI::Action> m_ini_highlight; - RefPtr<GUI::Action> m_shell_highlight; - - RefPtr<Web::OutOfProcessWebView> m_page_view; - - bool m_document_dirty { false }; - bool m_document_opening { false }; - bool m_auto_detect_preview_mode { false }; - bool m_find_use_regex { false }; - - PreviewMode m_preview_mode { PreviewMode::None }; -}; diff --git a/Applications/TextEditor/TextEditorWindow.gml b/Applications/TextEditor/TextEditorWindow.gml deleted file mode 100644 index 433d01abff..0000000000 --- a/Applications/TextEditor/TextEditorWindow.gml +++ /dev/null @@ -1,88 +0,0 @@ -@GUI::Widget { - name: "main" - fill_with_background_color: true - - layout: @GUI::VerticalBoxLayout { - spacing: 2 - } - - @GUI::ToolBarContainer { - @GUI::ToolBar { - name: "toolbar" - } - } - - @GUI::HorizontalSplitter { - @GUI::TextEditor { - name: "editor" - } - - @Web::OutOfProcessWebView { - name: "webview" - visible: false - } - } - - @GUI::Widget { - name: "find_replace_widget" - visible: false - fill_with_background_color: true - fixed_height: 48 - - layout: @GUI::VerticalBoxLayout { - margins: [2, 2, 2, 4] - } - - @GUI::Widget { - name: "find_widget" - fill_with_background_color: true - fixed_height: 22 - - layout: @GUI::HorizontalBoxLayout { - } - - @GUI::Button { - name: "find_previous_button" - text: "Find previous" - fixed_width: 150 - } - - @GUI::Button { - name: "find_next_button" - text: "Find next" - fixed_width: 150 - } - } - - @GUI::Widget { - name: "replace_widget" - fill_with_background_color: true - fixed_height: 22 - - layout: @GUI::HorizontalBoxLayout { - } - - @GUI::Button { - name: "replace_previous_button" - text: "Replace previous" - fixed_width: 100 - } - - @GUI::Button { - name: "replace_next_button" - text: "Replace next" - fixed_width: 100 - } - - @GUI::Button { - name: "replace_all_button" - text: "Replace all" - fixed_width: 100 - } - } - } - - @GUI::StatusBar { - name: "statusbar" - } -} diff --git a/Applications/TextEditor/main.cpp b/Applications/TextEditor/main.cpp deleted file mode 100644 index 8e9c8fdfb8..0000000000 --- a/Applications/TextEditor/main.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "TextEditorWidget.h" -#include <LibCore/ArgsParser.h> -#include <LibGfx/Bitmap.h> -#include <stdio.h> - -int main(int argc, char** argv) -{ - if (pledge("stdio thread rpath accept cpath wpath shared_buffer unix fattr", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto app = GUI::Application::construct(argc, argv); - - if (pledge("stdio thread rpath accept cpath wpath shared_buffer unix", nullptr) < 0) { - perror("pledge"); - return 1; - } - - const char* preview_mode = "auto"; - const char* file_to_edit = nullptr; - Core::ArgsParser parser; - parser.add_option(preview_mode, "Preview mode, one of 'none', 'html', 'markdown', 'auto'", "preview-mode", '\0', "mode"); - parser.add_positional_argument(file_to_edit, "File to edit", "file", Core::ArgsParser::Required::No); - - parser.parse(argc, argv); - - StringView preview_mode_view = preview_mode; - - auto app_icon = GUI::Icon::default_icon("app-text-editor"); - - auto window = GUI::Window::construct(); - window->resize(640, 400); - - auto& text_widget = window->set_main_widget<TextEditorWidget>(); - - text_widget.editor().set_focus(true); - - window->on_close_request = [&]() -> GUI::Window::CloseRequestDecision { - if (text_widget.request_close()) - return GUI::Window::CloseRequestDecision::Close; - return GUI::Window::CloseRequestDecision::StayOpen; - }; - - if (preview_mode_view == "auto") { - text_widget.set_auto_detect_preview_mode(true); - } else if (preview_mode_view == "markdown") { - text_widget.set_preview_mode(TextEditorWidget::PreviewMode::Markdown); - } else if (preview_mode_view == "html") { - text_widget.set_preview_mode(TextEditorWidget::PreviewMode::HTML); - } else if (preview_mode_view == "none") { - text_widget.set_preview_mode(TextEditorWidget::PreviewMode::None); - } else { - warnln("Invalid mode '{}'", preview_mode); - return 1; - } - - if (file_to_edit) - text_widget.open_sesame(file_to_edit); - else - text_widget.update_title(); - - window->show(); - window->set_icon(app_icon.bitmap_for_size(16)); - - return app->exec(); -} diff --git a/Applications/ThemeEditor/CMakeLists.txt b/Applications/ThemeEditor/CMakeLists.txt deleted file mode 100644 index 94ca0e1f3e..0000000000 --- a/Applications/ThemeEditor/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -set(SOURCES - PreviewWidget.cpp - main.cpp -) - -serenity_app(ThemeEditor ICON app-theme-editor) -target_link_libraries(ThemeEditor LibGUI) diff --git a/Applications/ThemeEditor/PreviewWidget.cpp b/Applications/ThemeEditor/PreviewWidget.cpp deleted file mode 100644 index a57da75936..0000000000 --- a/Applications/ThemeEditor/PreviewWidget.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (c) 2020, 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 "PreviewWidget.h" -#include <AK/StringView.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Button.h> -#include <LibGUI/CheckBox.h> -#include <LibGUI/Painter.h> -#include <LibGUI/RadioButton.h> -#include <LibGUI/StatusBar.h> -#include <LibGUI/TextEditor.h> -#include <LibGfx/Bitmap.h> -#include <LibGfx/WindowTheme.h> - -namespace ThemeEditor { - -class MiniWidgetGallery final : public GUI::Widget { - C_OBJECT(MiniWidgetGallery); - -public: - void set_preview_palette(const Gfx::Palette& palette) - { - set_palette(palette); - Function<void(GUI::Widget&)> recurse = [&](GUI::Widget& parent_widget) { - parent_widget.for_each_child_widget([&](auto& widget) { - widget.set_palette(palette); - recurse(widget); - return IterationDecision::Continue; - }); - }; - recurse(*this); - } - -private: - MiniWidgetGallery() - { - set_fill_with_background_color(true); - m_button = add<GUI::Button>(); - m_button->set_text("Button"); - m_checkbox = add<GUI::CheckBox>(); - m_checkbox->set_text("Check box"); - m_radio = add<GUI::RadioButton>(); - m_radio->set_text("Radio button"); - m_statusbar = add<GUI::StatusBar>(); - m_statusbar->set_text("Status bar"); - m_editor = add<GUI::TextEditor>(); - m_editor->set_text("Text editor\nwith multiple\nlines."); - } - - virtual void resize_event(GUI::ResizeEvent&) override - { - m_editor->set_relative_rect(10, 70, 200, 140); - m_button->set_relative_rect(10, 10, 200, 20); - m_checkbox->set_relative_rect(10, 30, 200, 20); - m_radio->set_relative_rect(10, 50, 200, 20); - m_statusbar->set_relative_rect(0, height() - 16, width(), 16); - } - - RefPtr<GUI::TextEditor> m_editor; - RefPtr<GUI::Button> m_button; - RefPtr<GUI::CheckBox> m_checkbox; - RefPtr<GUI::RadioButton> m_radio; - RefPtr<GUI::StatusBar> m_statusbar; -}; - -PreviewWidget::PreviewWidget(const Gfx::Palette& preview_palette) - : m_preview_palette(preview_palette) -{ - m_active_window_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/window.png"); - m_inactive_window_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/window.png"); - - m_close_bitmap = Gfx::Bitmap::load_from_file("/res/icons/16x16/window-close.png"); - m_maximize_bitmap = Gfx::Bitmap::load_from_file("/res/icons/16x16/upward-triangle.png"); - m_minimize_bitmap = Gfx::Bitmap::load_from_file("/res/icons/16x16/downward-triangle.png"); - - m_gallery = add<MiniWidgetGallery>(); - set_greedy_for_hits(true); -} - -PreviewWidget::~PreviewWidget() -{ -} - -void PreviewWidget::set_preview_palette(const Gfx::Palette& palette) -{ - m_preview_palette = palette; - m_gallery->set_preview_palette(palette); - update(); -} - -void PreviewWidget::paint_event(GUI::PaintEvent& event) -{ - GUI::Frame::paint_event(event); - GUI::Painter painter(*this); - - painter.add_clip_rect(event.rect()); - painter.add_clip_rect(frame_inner_rect()); - - painter.fill_rect(frame_inner_rect(), m_preview_palette.desktop_background()); - - struct Button { - Gfx::IntRect rect; - RefPtr<Gfx::Bitmap> bitmap; - }; - - auto paint_window = [&](auto& title, const Gfx::IntRect& rect, auto state, const Gfx::Bitmap& icon) { - int window_button_width = m_preview_palette.window_title_button_width(); - int window_button_height = m_preview_palette.window_title_button_height(); - auto title_bar_text_rect = Gfx::WindowTheme::current().title_bar_text_rect(Gfx::WindowTheme::WindowType::Normal, rect, m_preview_palette); - int pos = title_bar_text_rect.right() + 1; - - Vector<Button> buttons; - buttons.append(Button { {}, m_close_bitmap }); - buttons.append(Button { {}, m_maximize_bitmap }); - buttons.append(Button { {}, m_minimize_bitmap }); - - for (auto& button : buttons) { - pos -= window_button_width; - Gfx::IntRect rect { pos, 0, window_button_width, window_button_height }; - rect.center_vertically_within(title_bar_text_rect); - button.rect = rect; - } - - auto frame_rect = Gfx::WindowTheme::current().frame_rect_for_window(Gfx::WindowTheme::WindowType::Normal, rect, m_preview_palette); - Gfx::PainterStateSaver saver(painter); - painter.translate(frame_rect.location()); - Gfx::WindowTheme::current().paint_normal_frame(painter, state, rect, title, icon, m_preview_palette, buttons.last().rect); - - for (auto& button : buttons) { - Gfx::StylePainter::paint_button(painter, button.rect, m_preview_palette, Gfx::ButtonStyle::Normal, false); - auto bitmap_rect = button.bitmap->rect(); - bitmap_rect.center_within(button.rect); - painter.blit(bitmap_rect.location(), *button.bitmap, button.bitmap->rect()); - } - }; - - Gfx::IntRect active_rect { 0, 0, 320, 240 }; - active_rect.center_within(frame_inner_rect()); - Gfx::IntRect inactive_rect = active_rect.translated(-20, -20); - - paint_window("Inactive window", inactive_rect, Gfx::WindowTheme::WindowState::Inactive, *m_active_window_icon); - paint_window("Active window", active_rect, Gfx::WindowTheme::WindowState::Active, *m_inactive_window_icon); -} - -void PreviewWidget::resize_event(GUI::ResizeEvent&) -{ - Gfx::IntRect gallery_rect { 0, 0, 320, 240 }; - gallery_rect.center_within(rect()); - m_gallery->set_relative_rect(gallery_rect); -} - -} diff --git a/Applications/ThemeEditor/PreviewWidget.h b/Applications/ThemeEditor/PreviewWidget.h deleted file mode 100644 index 41ef25e0a8..0000000000 --- a/Applications/ThemeEditor/PreviewWidget.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2020, 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/Frame.h> -#include <LibGfx/Palette.h> - -namespace ThemeEditor { - -class MiniWidgetGallery; - -class PreviewWidget final : public GUI::Frame { - C_OBJECT(PreviewWidget); - -public: - virtual ~PreviewWidget() override; - - const Gfx::Palette& preview_palette() const { return m_preview_palette; } - void set_preview_palette(const Gfx::Palette&); - -private: - explicit PreviewWidget(const Gfx::Palette&); - - virtual void paint_event(GUI::PaintEvent&) override; - virtual void resize_event(GUI::ResizeEvent&) override; - - Gfx::Palette m_preview_palette; - - RefPtr<Gfx::Bitmap> m_active_window_icon; - RefPtr<Gfx::Bitmap> m_inactive_window_icon; - - RefPtr<MiniWidgetGallery> m_gallery; - - RefPtr<Gfx::Bitmap> m_close_bitmap; - RefPtr<Gfx::Bitmap> m_maximize_bitmap; - RefPtr<Gfx::Bitmap> m_minimize_bitmap; -}; - -} diff --git a/Applications/ThemeEditor/main.cpp b/Applications/ThemeEditor/main.cpp deleted file mode 100644 index d4a5936770..0000000000 --- a/Applications/ThemeEditor/main.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2020, 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 "PreviewWidget.h" -#include <LibGUI/Application.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/ColorInput.h> -#include <LibGUI/ComboBox.h> -#include <LibGUI/Icon.h> -#include <LibGUI/Model.h> -#include <LibGUI/Window.h> - -class ColorRoleModel final : public GUI::Model { -public: - virtual int row_count(const GUI::ModelIndex&) const { return m_color_roles.size(); } - virtual int column_count(const GUI::ModelIndex&) const { return 1; } - virtual GUI::Variant data(const GUI::ModelIndex& index, GUI::ModelRole role = GUI::ModelRole::Display) const - { - if (role == GUI::ModelRole::Display) - return Gfx::to_string(m_color_roles[(size_t)index.row()]); - return {}; - } - virtual void update() { did_update(); } - - explicit ColorRoleModel(const Vector<Gfx::ColorRole>& color_roles) - : m_color_roles(color_roles) - { - } - - Gfx::ColorRole color_role(const GUI::ModelIndex& index) const - { - return m_color_roles[index.row()]; - } - - Gfx::ColorRole color_role(size_t index) const - { - return m_color_roles[index]; - } - -private: - const Vector<Gfx::ColorRole>& m_color_roles; -}; - -int main(int argc, char** argv) -{ - - if (pledge("stdio thread rpath accept cpath wpath shared_buffer unix fattr", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto app = GUI::Application::construct(argc, argv); - - if (pledge("stdio thread rpath accept shared_buffer", nullptr) < 0) { - perror("pledge"); - return 1; - } - - if (unveil("/res", "r") < 0) { - perror("unveil"); - return 1; - } - - if (unveil(nullptr, nullptr) < 0) { - perror("unveil"); - return 1; - } - - auto app_icon = GUI::Icon::default_icon("app-theme-editor"); - - Gfx::Palette preview_palette = app->palette(); - - auto window = GUI::Window::construct(); - auto& main_widget = window->set_main_widget<GUI::Widget>(); - main_widget.set_fill_with_background_color(true); - main_widget.set_layout<GUI::VerticalBoxLayout>(); - - auto& preview_widget = main_widget.add<ThemeEditor::PreviewWidget>(app->palette()); - preview_widget.set_fixed_size(480, 360); - - auto& horizontal_container = main_widget.add<GUI::Widget>(); - horizontal_container.set_layout<GUI::HorizontalBoxLayout>(); - horizontal_container.set_fixed_size(480, 20); - - auto& combo_box = horizontal_container.add<GUI::ComboBox>(); - auto& color_input = horizontal_container.add<GUI::ColorInput>(); - - Vector<Gfx::ColorRole> color_roles; -#define __ENUMERATE_COLOR_ROLE(role) color_roles.append(Gfx::ColorRole::role); - ENUMERATE_COLOR_ROLES(__ENUMERATE_COLOR_ROLE) -#undef __ENUMERATE_COLOR_ROLE - - combo_box.set_only_allow_values_from_model(true); - combo_box.set_model(adopt(*new ColorRoleModel(color_roles))); - combo_box.on_change = [&](auto&, auto& index) { - auto role = static_cast<const ColorRoleModel*>(index.model())->color_role(index); - color_input.set_color(preview_palette.color(role)); - }; - - combo_box.set_selected_index((size_t)Gfx::ColorRole::Window - 1); - - color_input.on_change = [&] { - auto role = static_cast<const ColorRoleModel*>(combo_box.model())->color_role(combo_box.selected_index()); - preview_palette.set_color(role, color_input.color()); - preview_widget.set_preview_palette(preview_palette); - }; - - window->resize(480, 500); - window->show(); - window->set_title("Theme Editor"); - window->set_icon(app_icon.bitmap_for_size(16)); - return app->exec(); -} diff --git a/Applications/Welcome/BackgroundWidget.cpp b/Applications/Welcome/BackgroundWidget.cpp deleted file mode 100644 index c64f2fc93b..0000000000 --- a/Applications/Welcome/BackgroundWidget.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "BackgroundWidget.h" -#include <LibGUI/Painter.h> -#include <LibGfx/Color.h> -#include <LibGfx/Palette.h> - -BackgroundWidget::BackgroundWidget() -{ -} - -BackgroundWidget::~BackgroundWidget() -{ -} - -void BackgroundWidget::paint_event(GUI::PaintEvent& event) -{ - GUI::Frame::paint_event(event); - - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - painter.fill_rect_with_gradient(event.rect(), Color::from_rgb(0xccdddd), Color::from_rgb(0xdcdcde)); -} - -void BackgroundWidget::resize_event(GUI::ResizeEvent& event) -{ - GUI::Widget::resize_event(event); -} diff --git a/Applications/Welcome/BackgroundWidget.h b/Applications/Welcome/BackgroundWidget.h deleted file mode 100644 index fea3871edc..0000000000 --- a/Applications/Welcome/BackgroundWidget.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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/Frame.h> - -class BackgroundWidget : public GUI::Frame { - C_OBJECT(BackgroundWidget) -public: - virtual ~BackgroundWidget() override; - -private: - BackgroundWidget(); - - virtual void paint_event(GUI::PaintEvent&) override; - virtual void resize_event(GUI::ResizeEvent&) override; -}; diff --git a/Applications/Welcome/CMakeLists.txt b/Applications/Welcome/CMakeLists.txt deleted file mode 100644 index e9d1ef3380..0000000000 --- a/Applications/Welcome/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -set(SOURCES - BackgroundWidget.cpp - main.cpp - TextWidget.cpp - UnuncheckableButton.cpp -) - -serenity_bin(Welcome) -target_link_libraries(Welcome LibGUI) diff --git a/Applications/Welcome/TextWidget.cpp b/Applications/Welcome/TextWidget.cpp deleted file mode 100644 index 263d4514f8..0000000000 --- a/Applications/Welcome/TextWidget.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "TextWidget.h" -#include <AK/Optional.h> -#include <AK/String.h> -#include <AK/StringBuilder.h> -#include <AK/Vector.h> -#include <LibGUI/Painter.h> -#include <LibGfx/Font.h> -#include <LibGfx/Palette.h> - -TextWidget::TextWidget(const StringView& text) - : m_text(text) -{ - set_frame_thickness(0); - set_frame_shadow(Gfx::FrameShadow::Plain); - set_frame_shape(Gfx::FrameShape::NoFrame); -} - -TextWidget::~TextWidget() -{ -} - -void TextWidget::set_text(const StringView& text) -{ - if (text == m_text) - return; - m_text = text; - wrap_and_set_height(); - update(); -} - -void TextWidget::paint_event(GUI::PaintEvent& event) -{ - GUI::Frame::paint_event(event); - - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - - int indent = 0; - if (frame_thickness() > 0) - indent = font().glyph_width('x') / 2; - - for (size_t i = 0; i < m_lines.size(); i++) { - auto& line = m_lines[i]; - - auto text_rect = frame_inner_rect(); - text_rect.move_by(indent, i * m_line_height); - if (!line.is_empty()) - text_rect.set_width(text_rect.width() - indent * 2); - - if (is_enabled()) { - painter.draw_text(text_rect, line, m_text_alignment, palette().color(foreground_role()), Gfx::TextElision::None); - } else { - painter.draw_text(text_rect.translated(1, 1), line, font(), text_alignment(), Color::White, Gfx::TextElision::Right); - painter.draw_text(text_rect, line, font(), text_alignment(), Color::from_rgb(0x808080), Gfx::TextElision::Right); - } - } -} - -void TextWidget::resize_event(GUI::ResizeEvent& event) -{ - wrap_and_set_height(); - GUI::Widget::resize_event(event); -} - -void TextWidget::wrap_and_set_height() -{ - Vector<String> words; - Optional<size_t> start; - for (size_t i = 0; i < m_text.length(); i++) { - auto ch = m_text[i]; - - if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') { - if (start.has_value()) - words.append(m_text.substring(start.value(), i - start.value())); - start.clear(); - } else if (!start.has_value()) { - start = i; - } - } - if (start.has_value()) - words.append(m_text.substring(start.value(), m_text.length() - start.value())); - - auto rect = frame_inner_rect(); - if (frame_thickness() > 0) - rect.set_width(rect.width() - font().glyph_width('x')); - - StringBuilder builder; - Vector<String> lines; - int line_width = 0; - for (auto& word : words) { - int word_width = font().width(word); - if (line_width != 0) - word_width += font().glyph_width('x'); - - if (line_width + word_width > rect.width()) { - lines.append(builder.to_string()); - builder.clear(); - line_width = 0; - } - - if (line_width != 0) - builder.append(' '); - builder.append(word); - line_width += word_width; - } - auto last_line = builder.to_string(); - if (!last_line.is_empty()) { - lines.append(last_line); - } - - m_lines = lines; - - set_fixed_height(m_lines.size() * m_line_height + frame_thickness() * 2); -} diff --git a/Applications/Welcome/TextWidget.h b/Applications/Welcome/TextWidget.h deleted file mode 100644 index c4353096ff..0000000000 --- a/Applications/Welcome/TextWidget.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 <AK/String.h> -#include <AK/Vector.h> -#include <LibGUI/Frame.h> -#include <LibGfx/TextAlignment.h> - -class TextWidget : public GUI::Frame { - C_OBJECT(TextWidget); - -public: - virtual ~TextWidget() override; - - String text() const { return m_text; } - void set_text(const StringView&); - - Gfx::TextAlignment text_alignment() const { return m_text_alignment; } - void set_text_alignment(Gfx::TextAlignment text_alignment) { m_text_alignment = text_alignment; } - - bool should_wrap() const { return m_should_wrap; } - void set_should_wrap(bool should_wrap) { m_should_wrap = should_wrap; } - - int line_height() const { return m_line_height; } - void set_line_height(int line_height) { m_line_height = line_height; } - - void wrap_and_set_height(); - -private: - explicit TextWidget(const StringView& text = {}); - - virtual void paint_event(GUI::PaintEvent&) override; - virtual void resize_event(GUI::ResizeEvent&) override; - - String m_text; - Vector<String> m_lines; - Gfx::TextAlignment m_text_alignment { Gfx::TextAlignment::Center }; - bool m_should_wrap { false }; - int m_line_height { 0 }; -}; diff --git a/Applications/Welcome/UnuncheckableButton.cpp b/Applications/Welcome/UnuncheckableButton.cpp deleted file mode 100644 index 29194fff81..0000000000 --- a/Applications/Welcome/UnuncheckableButton.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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 "UnuncheckableButton.h" - -UnuncheckableButton::UnuncheckableButton() -{ -} - -UnuncheckableButton::~UnuncheckableButton() -{ -} diff --git a/Applications/Welcome/UnuncheckableButton.h b/Applications/Welcome/UnuncheckableButton.h deleted file mode 100644 index b91a41ef9c..0000000000 --- a/Applications/Welcome/UnuncheckableButton.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * 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/Button.h> - -class UnuncheckableButton : public GUI::Button { - C_OBJECT(UnuncheckableButton) -public: - virtual ~UnuncheckableButton() override; - - virtual bool is_uncheckable() const override { return false; } - -private: - UnuncheckableButton(); -}; diff --git a/Applications/Welcome/main.cpp b/Applications/Welcome/main.cpp deleted file mode 100644 index 101b35fd97..0000000000 --- a/Applications/Welcome/main.cpp +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (c) 2018-2020, 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 "BackgroundWidget.h" -#include "TextWidget.h" -#include "UnuncheckableButton.h" -#include <AK/ByteBuffer.h> -#include <AK/Optional.h> -#include <AK/String.h> -#include <AK/StringBuilder.h> -#include <AK/Vector.h> -#include <LibCore/File.h> -#include <LibGUI/Application.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Button.h> -#include <LibGUI/ImageWidget.h> -#include <LibGUI/Label.h> -#include <LibGUI/MessageBox.h> -#include <LibGUI/StackWidget.h> -#include <LibGUI/Window.h> -#include <LibGfx/Bitmap.h> -#include <LibGfx/Font.h> -#include <LibGfx/FontDatabase.h> -#include <stdio.h> -#include <unistd.h> - -struct ContentPage { - String menu_name; - String title; - String icon = String::empty(); - Vector<String> content; -}; - -static Optional<Vector<ContentPage>> parse_welcome_file(const String& path) -{ - auto file = Core::File::construct(path); - if (!file->open(Core::IODevice::ReadOnly)) - return {}; - - Vector<ContentPage> pages; - StringBuilder current_output_line; - bool started = false; - ContentPage current; - while (file->can_read_line()) { - auto line = file->read_line(); - if (line.is_empty()) { - if (!current_output_line.to_string().is_empty()) - current.content.append(current_output_line.to_string()); - current_output_line.clear(); - continue; - } - switch (line[0]) { - case '*': - dbgln("menu_item line:\t{}", line); - if (started) - pages.append(current); - else - started = true; - - current = {}; - current.menu_name = line.substring(2, line.length() - 2); - break; - case '$': - dbgln("icon line: \t{}", line); - current.icon = line.substring(2, line.length() - 2); - break; - case '>': - dbgln("title line:\t{}", line); - current.title = line.substring(2, line.length() - 2); - break; - case '#': - dbgln("comment line:\t{}", line); - break; - default: - dbgln("content line:\t", line); - if (current_output_line.length() != 0) - current_output_line.append(' '); - current_output_line.append(line); - break; - } - } - - if (started) { - current.content.append(current_output_line.to_string()); - pages.append(current); - } - - return pages; -} - -int main(int argc, char** argv) -{ - if (pledge("stdio shared_buffer rpath unix cpath fattr", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto app = GUI::Application::construct(argc, argv); - - if (pledge("stdio shared_buffer rpath", nullptr) < 0) { - perror("pledge"); - return 1; - } - - if (unveil("/res", "r") < 0) { - perror("unveil"); - return 1; - } - - unveil(nullptr, nullptr); - - Optional<Vector<ContentPage>> _pages = parse_welcome_file("/res/welcome.txt"); - if (!_pages.has_value()) { - GUI::MessageBox::show(nullptr, "Could not open Welcome file.", "Welcome", GUI::MessageBox::Type::Error); - return 1; - } - auto pages = _pages.value(); - - auto window = GUI::Window::construct(); - window->set_title("Welcome"); - window->resize(640, 360); - window->center_on_screen(); - - auto& background = window->set_main_widget<BackgroundWidget>(); - background.set_fill_with_background_color(false); - background.set_layout<GUI::VerticalBoxLayout>(); - background.layout()->set_margins({ 16, 8, 16, 8 }); - background.layout()->set_spacing(8); - - // - // header - // - - auto& header = background.add<GUI::Label>(); - header.set_font(Gfx::Font::load_from_file("/res/fonts/PebbletonBold14.font")); - header.set_text("Welcome to SerenityOS!"); - header.set_text_alignment(Gfx::TextAlignment::CenterLeft); - header.set_fixed_height(30); - - // - // main section - // - - auto& main_section = background.add<GUI::Widget>(); - main_section.set_layout<GUI::HorizontalBoxLayout>(); - main_section.layout()->set_margins({ 0, 0, 0, 0 }); - main_section.layout()->set_spacing(8); - - auto& menu = main_section.add<GUI::Widget>(); - menu.set_layout<GUI::VerticalBoxLayout>(); - menu.layout()->set_margins({ 0, 0, 0, 0 }); - menu.layout()->set_spacing(4); - menu.set_fixed_width(100); - - auto& stack = main_section.add<GUI::StackWidget>(); - - bool first = true; - for (auto& page : pages) { - auto& content = stack.add<GUI::Widget>(); - content.set_layout<GUI::VerticalBoxLayout>(); - content.layout()->set_margins({ 0, 0, 0, 0 }); - content.layout()->set_spacing(8); - - auto& title_box = content.add<GUI::Widget>(); - title_box.set_layout<GUI::HorizontalBoxLayout>(); - title_box.layout()->set_spacing(4); - title_box.set_fixed_height(16); - - if (!page.icon.is_empty()) { - auto& icon = title_box.add<GUI::ImageWidget>(); - icon.set_fixed_size(16, 16); - icon.load_from_file(page.icon); - } - - auto& content_title = title_box.add<GUI::Label>(); - content_title.set_font(Gfx::FontDatabase::default_bold_font()); - content_title.set_text(page.title); - content_title.set_text_alignment(Gfx::TextAlignment::CenterLeft); - content_title.set_fixed_height(10); - - for (auto& paragraph : page.content) { - auto& content_text = content.add<TextWidget>(); - content_text.set_font(Gfx::FontDatabase::default_font()); - content_text.set_text(paragraph); - content_text.set_text_alignment(Gfx::TextAlignment::TopLeft); - content_text.set_line_height(12); - content_text.wrap_and_set_height(); - } - - auto& menu_option = menu.add<UnuncheckableButton>(); - menu_option.set_font(Gfx::FontDatabase::default_font()); - menu_option.set_text(page.menu_name); - menu_option.set_text_alignment(Gfx::TextAlignment::CenterLeft); - menu_option.set_fixed_height(20); - menu_option.set_checkable(true); - menu_option.set_exclusive(true); - - if (first) - menu_option.set_checked(true); - - menu_option.on_click = [content = &content, &stack](auto) { - stack.set_active_widget(content); - content->invalidate_layout(); - }; - - first = false; - } - - window->show(); - return app->exec(); -} |