Serenity Operating System
1/*
2 * Copyright (c) 2020, Emanuel Sprung <emanuel.sprung@gmail.com>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "BookmarksBarWidget.h"
28#include <LibGUI/Action.h>
29#include <LibGUI/BoxLayout.h>
30#include <LibGUI/Button.h>
31#include <LibGUI/Event.h>
32#include <LibGUI/JsonArrayModel.h>
33#include <LibGUI/Menu.h>
34#include <LibGUI/Model.h>
35#include <LibGUI/Widget.h>
36#include <LibGUI/Window.h>
37#include <LibGfx/Palette.h>
38
39BookmarksBarWidget::BookmarksBarWidget(const String& bookmarks_file, bool enabled)
40{
41 set_layout<GUI::HorizontalBoxLayout>();
42 layout()->set_spacing(0);
43
44 set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed);
45 set_preferred_size(0, 20);
46
47 if (!enabled)
48 set_visible(false);
49
50 m_additional = GUI::Button::construct();
51 m_additional->set_button_style(Gfx::ButtonStyle::CoolBar);
52 m_additional->set_text(">");
53 m_additional->set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fixed);
54 m_additional->set_preferred_size(14, 20);
55 m_additional->on_click = [&] {
56 if (m_additional_menu) {
57 m_additional_menu->popup(m_additional->relative_position().translated(relative_position().translated(m_additional->window()->position())));
58 }
59 };
60
61 m_separator = GUI::Widget::construct();
62
63 Vector<GUI::JsonArrayModel::FieldSpec> fields;
64 fields.empend("title", "Title", Gfx::TextAlignment::CenterLeft);
65 fields.empend("url", "Url", Gfx::TextAlignment::CenterRight);
66 set_model(GUI::JsonArrayModel::create(bookmarks_file, move(fields)));
67 model()->update();
68}
69
70BookmarksBarWidget::~BookmarksBarWidget()
71{
72}
73
74void BookmarksBarWidget::set_model(RefPtr<GUI::Model> model)
75{
76 if (model == m_model)
77 return;
78 m_model = move(model);
79 m_model->on_update = [&]() {
80 did_update_model();
81 };
82}
83
84void BookmarksBarWidget::resize_event(GUI::ResizeEvent& event)
85{
86 Widget::resize_event(event);
87 update_content_size();
88}
89
90void BookmarksBarWidget::did_update_model()
91{
92 for (auto* child : child_widgets()) {
93 child->remove_from_parent();
94 }
95 child_widgets().clear();
96
97 m_bookmarks.clear();
98
99 int width = 0;
100 for (int item_index = 0; item_index < model()->row_count(); ++item_index) {
101
102 auto title = model()->data(model()->index(item_index, 0)).to_string();
103 auto url = model()->data(model()->index(item_index, 1)).to_string();
104
105 Gfx::Rect rect { width, 0, font().width(title) + 32, height() };
106
107 auto& button = add<GUI::Button>();
108 m_bookmarks.append(button);
109
110 button.set_button_style(Gfx::ButtonStyle::CoolBar);
111 button.set_text(title);
112 button.set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fixed);
113 button.set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-html.png"));
114 button.set_preferred_size(font().width(title) + 32, 20);
115 button.set_relative_rect(rect);
116
117 button.on_click = [title, url, this] {
118 if (on_bookmark_click)
119 on_bookmark_click(title, url);
120 };
121
122 width += rect.width();
123 }
124
125 add_child(*m_separator);
126 add_child(*m_additional);
127
128 update_content_size();
129 update();
130}
131
132void BookmarksBarWidget::update_content_size()
133{
134 int x_position = 0;
135 m_last_visible_index = -1;
136
137 for (size_t i = 0; i < m_bookmarks.size(); ++i) {
138 auto& bookmark = m_bookmarks.at(i);
139 if (x_position + bookmark.width() > width()) {
140 m_last_visible_index = i;
141 break;
142 }
143 bookmark.set_x(x_position);
144 bookmark.set_visible(true);
145 x_position += bookmark.width();
146 }
147
148 if (m_last_visible_index < 0) {
149 m_additional->set_visible(false);
150 } else {
151 // hide all items > m_last_visible_index and create new bookmarks menu for them
152 m_additional->set_visible(true);
153 m_additional_menu = GUI::Menu::construct("Additional Bookmarks");
154 for (size_t i = m_last_visible_index; i < m_bookmarks.size(); ++i) {
155 auto& bookmark = m_bookmarks.at(i);
156 bookmark.set_visible(false);
157 m_additional_menu->add_action(GUI::Action::create(bookmark.text(),
158 Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-html.png"),
159 [&](auto&) {
160 bookmark.on_click();
161 }));
162 }
163 }
164}
165
166bool BookmarksBarWidget::contains_bookmark(const String& url)
167{
168 for (int item_index = 0; item_index < model()->row_count(); ++item_index) {
169
170 auto item_title = model()->data(model()->index(item_index, 0)).to_string();
171 auto item_url = model()->data(model()->index(item_index, 1)).to_string();
172 if (item_url == url) {
173 return true;
174 }
175 }
176 return false;
177}
178
179bool BookmarksBarWidget::remove_bookmark(const String& url)
180{
181 for (int item_index = 0; item_index < model()->row_count(); ++item_index) {
182
183 auto item_title = model()->data(model()->index(item_index, 0)).to_string();
184 auto item_url = model()->data(model()->index(item_index, 1)).to_string();
185 if (item_url == url) {
186 auto& json_model = *static_cast<GUI::JsonArrayModel*>(model());
187 json_model.remove(item_index);
188 return true;
189 }
190 }
191
192 return false;
193}
194bool BookmarksBarWidget::add_bookmark(const String& url, const String& title)
195{
196 Vector<JsonValue> values;
197 values.append(title);
198 values.append(url);
199
200 auto& json_model = *static_cast<GUI::JsonArrayModel*>(model());
201 if (json_model.add(move(values))) {
202 json_model.store();
203 return true;
204 }
205 return false;
206}