mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 09:22:44 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			335 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			335 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2020, Emanuel Sprung <emanuel.sprung@gmail.com>
 | |
|  * Copyright (c) 2022, Jakob-Niklas See <git@nwex.de>
 | |
|  *
 | |
|  * SPDX-License-Identifier: BSD-2-Clause
 | |
|  */
 | |
| 
 | |
| #include <Applications/Browser/BookmarksBarWidget.h>
 | |
| #include <Applications/Browser/Browser.h>
 | |
| #include <Applications/Browser/EditBookmarkGML.h>
 | |
| #include <LibGUI/Action.h>
 | |
| #include <LibGUI/BoxLayout.h>
 | |
| #include <LibGUI/Button.h>
 | |
| #include <LibGUI/Dialog.h>
 | |
| #include <LibGUI/Event.h>
 | |
| #include <LibGUI/JsonArrayModel.h>
 | |
| #include <LibGUI/Menu.h>
 | |
| #include <LibGUI/Model.h>
 | |
| #include <LibGUI/TextBox.h>
 | |
| #include <LibGUI/Widget.h>
 | |
| #include <LibGUI/Window.h>
 | |
| #include <LibGfx/Palette.h>
 | |
| 
 | |
| namespace Browser {
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| class BookmarkEditor final : public GUI::Dialog {
 | |
|     C_OBJECT(BookmarkEditor)
 | |
| 
 | |
| public:
 | |
|     static Vector<JsonValue>
 | |
|     edit_bookmark(Window* parent_window, StringView title, StringView url)
 | |
|     {
 | |
|         auto editor = BookmarkEditor::construct(parent_window, title, url);
 | |
|         editor->set_title("Edit Bookmark");
 | |
|         editor->set_icon(g_icon_bag.bookmark_filled);
 | |
| 
 | |
|         if (editor->exec() == ExecResult::OK) {
 | |
|             return Vector<JsonValue> { editor->title(), editor->url() };
 | |
|         }
 | |
| 
 | |
|         return {};
 | |
|     }
 | |
| 
 | |
| private:
 | |
|     BookmarkEditor(Window* parent_window, StringView title, StringView url)
 | |
|         : Dialog(parent_window)
 | |
|     {
 | |
|         auto& widget = set_main_widget<GUI::Widget>();
 | |
|         if (!widget.load_from_gml(edit_bookmark_gml))
 | |
|             VERIFY_NOT_REACHED();
 | |
| 
 | |
|         set_resizable(false);
 | |
|         resize(260, 85);
 | |
| 
 | |
|         m_title_textbox = *widget.find_descendant_of_type_named<GUI::TextBox>("title_textbox");
 | |
|         m_title_textbox->set_text(title);
 | |
|         m_title_textbox->set_focus(true);
 | |
|         m_title_textbox->select_all();
 | |
| 
 | |
|         m_url_textbox = *widget.find_descendant_of_type_named<GUI::TextBox>("url_textbox");
 | |
|         m_url_textbox->set_text(url);
 | |
| 
 | |
|         auto& ok_button = *widget.find_descendant_of_type_named<GUI::Button>("ok_button");
 | |
|         ok_button.on_click = [this](auto) {
 | |
|             done(ExecResult::OK);
 | |
|         };
 | |
|         ok_button.set_default(true);
 | |
| 
 | |
|         auto& cancel_button = *widget.find_descendant_of_type_named<GUI::Button>("cancel_button");
 | |
|         cancel_button.on_click = [this](auto) {
 | |
|             done(ExecResult::Cancel);
 | |
|         };
 | |
|     }
 | |
| 
 | |
|     String title() const
 | |
|     {
 | |
|         return m_title_textbox->text();
 | |
|     }
 | |
| 
 | |
|     String url() const
 | |
|     {
 | |
|         return m_url_textbox->text();
 | |
|     }
 | |
| 
 | |
|     RefPtr<GUI::TextBox> m_title_textbox;
 | |
|     RefPtr<GUI::TextBox> m_url_textbox;
 | |
| };
 | |
| 
 | |
| }
 | |
| 
 | |
| static BookmarksBarWidget* s_the;
 | |
| 
 | |
| BookmarksBarWidget& BookmarksBarWidget::the()
 | |
| {
 | |
|     return *s_the;
 | |
| }
 | |
| 
 | |
| BookmarksBarWidget::BookmarksBarWidget(String const& bookmarks_file, bool enabled)
 | |
| {
 | |
|     s_the = this;
 | |
|     set_layout<GUI::HorizontalBoxLayout>();
 | |
|     layout()->set_spacing(0);
 | |
|     layout()->set_margins(2);
 | |
| 
 | |
|     set_fixed_height(20);
 | |
| 
 | |
|     if (!enabled)
 | |
|         set_visible(false);
 | |
| 
 | |
|     m_additional = GUI::Button::construct();
 | |
|     m_additional->set_tooltip("Show hidden bookmarks");
 | |
|     m_additional->set_menu(m_additional_menu);
 | |
|     m_additional->set_menu_position(GUI::Button::MenuPosition::BottomLeft);
 | |
|     auto bitmap_or_error = Gfx::Bitmap::try_load_from_file("/res/icons/16x16/overflow-menu.png"sv);
 | |
|     if (!bitmap_or_error.is_error())
 | |
|         m_additional->set_icon(bitmap_or_error.release_value());
 | |
|     m_additional->set_button_style(Gfx::ButtonStyle::Coolbar);
 | |
|     m_additional->set_fixed_size(22, 20);
 | |
|     m_additional->set_focus_policy(GUI::FocusPolicy::TabFocus);
 | |
| 
 | |
|     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, OpenInNewTab::No);
 | |
|         },
 | |
|         this);
 | |
|     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, OpenInNewTab::Yes);
 | |
|         },
 | |
|         this));
 | |
|     m_context_menu->add_separator();
 | |
|     m_context_menu->add_action(GUI::Action::create(
 | |
|         "&Edit...", [this](auto&) {
 | |
|             edit_bookmark(m_context_menu_url);
 | |
|         },
 | |
|         this));
 | |
|     m_context_menu->add_action(GUI::CommonActions::make_delete_action(
 | |
|         [this](auto&) {
 | |
|             remove_bookmark(m_context_menu_url);
 | |
|         },
 | |
|         this));
 | |
| 
 | |
|     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()->invalidate();
 | |
| }
 | |
| 
 | |
| 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(g_icon_bag.filetype_html);
 | |
|         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.set_allowed_mouse_buttons_for_pressing(GUI::MouseButton::Primary | GUI::MouseButton::Middle);
 | |
| 
 | |
|         button.on_click = [title, url, this](auto) {
 | |
|             if (on_bookmark_click)
 | |
|                 on_bookmark_click(url, OpenInNewTab::No);
 | |
|         };
 | |
| 
 | |
|         button.on_middle_mouse_click = [title, url, this](auto) {
 | |
|             if (on_bookmark_click)
 | |
|                 on_bookmark_click(url, OpenInNewTab::Yes);
 | |
|         };
 | |
| 
 | |
|         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() + m_additional->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");
 | |
|         m_additional->set_menu(m_additional_menu);
 | |
|         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(), g_icon_bag.filetype_html, [&](auto&) { bookmark.on_click(0); }));
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| bool BookmarksBarWidget::contains_bookmark(String const& 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(String const& 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());
 | |
| 
 | |
|             auto const item_removed = json_model.remove(item_index);
 | |
|             if (item_removed)
 | |
|                 json_model.store();
 | |
| 
 | |
|             return item_removed;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| bool BookmarksBarWidget::add_bookmark(String const& url, String const& 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;
 | |
| }
 | |
| 
 | |
| bool BookmarksBarWidget::edit_bookmark(String const& 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 values = BookmarkEditor::edit_bookmark(window(), item_title, item_url);
 | |
|             bool item_replaced = false;
 | |
| 
 | |
|             if (!values.is_empty()) {
 | |
|                 auto& json_model = *static_cast<GUI::JsonArrayModel*>(model());
 | |
|                 item_replaced = json_model.set(item_index, move(values));
 | |
| 
 | |
|                 if (item_replaced)
 | |
|                     json_model.store();
 | |
|             }
 | |
| 
 | |
|             return item_replaced;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| }
 | 
