Falkon Develop
Cross-platform Qt-based web browser
bookmarks.cpp
Go to the documentation of this file.
1/* ============================================================
2* Falkon - Qt web browser
3* Copyright (C) 2010-2018 David Rosca <nowrep@gmail.com>
4*
5* This program is free software: you can redistribute it and/or modify
6* it under the terms of the GNU General Public License as published by
7* the Free Software Foundation, either version 3 of the License, or
8* (at your option) any later version.
9*
10* This program is distributed in the hope that it will be useful,
11* but WITHOUT ANY WARRANTY; without even the implied warranty of
12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13* GNU General Public License for more details.
14*
15* You should have received a copy of the GNU General Public License
16* along with this program. If not, see <http://www.gnu.org/licenses/>.
17* ============================================================ */
18#include "bookmarks.h"
19#include "bookmarkitem.h"
20#include "bookmarksmodel.h"
21#include "bookmarkstools.h"
22#include "autosaver.h"
23#include "datapaths.h"
24#include "settings.h"
25#include "qztools.h"
26
27#include <QSaveFile>
28#include <QJsonDocument>
29#include <QMetaType>
30
31static const int bookmarksVersion = 1;
32
33Bookmarks::Bookmarks(QObject* parent)
34 : QObject(parent)
35 , m_autoSaver(nullptr)
36{
37 m_autoSaver = new AutoSaver(this);
38 connect(m_autoSaver, &AutoSaver::save, this, &Bookmarks::saveSettings);
39
40 init();
42}
43
45{
46 m_autoSaver->saveIfNecessary();
47 delete m_root;
48}
49
51{
52 Settings settings;
53 settings.beginGroup(QSL("Bookmarks"));
54 m_showOnlyIconsInToolbar = settings.value(QSL("showOnlyIconsInToolbar"), false).toBool();
55 m_showOnlyTextInToolbar = settings.value(QSL("showOnlyTextInToolbar"), false).toBool();
56 settings.endGroup();
57}
58
60{
61 return m_showOnlyIconsInToolbar;
62}
63
65{
66 return m_showOnlyTextInToolbar;
67}
68
70{
71 return m_root;
72}
73
75{
76 return m_folderToolbar;
77}
78
80{
81 return m_folderMenu;
82}
83
85{
86 return m_folderUnsorted;
87}
88
90{
91 return m_lastFolder;
92}
93
95{
96 return m_model;
97}
98
99bool Bookmarks::isBookmarked(const QUrl &url)
100{
101 return !searchBookmarks(url).isEmpty();
102}
103
105{
106 Q_ASSERT(item);
107
108 return item != m_root &&
109 item != m_folderToolbar &&
110 item != m_folderMenu &&
111 item != m_folderUnsorted;
112}
113
114QList<BookmarkItem*> Bookmarks::searchBookmarks(const QUrl &url) const
115{
116 QList<BookmarkItem*> items;
117 search(&items, m_root, url);
118 return items;
119}
120
121QList<BookmarkItem*> Bookmarks::searchBookmarks(const QString &string, int limit, Qt::CaseSensitivity sensitive) const
122{
123 QList<BookmarkItem*> items;
124 search(&items, m_root, string, limit, sensitive);
125 return items;
126}
127
128QList<BookmarkItem*> Bookmarks::searchKeyword(const QString &keyword) const
129{
130 QList<BookmarkItem*> items;
131 searchKeyword(&items, m_root, keyword);
132 return items;
133}
134
136{
137 Q_ASSERT(parent);
138 Q_ASSERT(parent->isFolder());
139 Q_ASSERT(item);
140
141 insertBookmark(parent, parent->children().count(), item);
142}
143
145{
146 Q_ASSERT(parent);
147 Q_ASSERT(parent->isFolder());
148 Q_ASSERT(item);
149
150 m_lastFolder = parent;
151 m_model->addBookmark(parent, row, item);
152 Q_EMIT bookmarkAdded(item);
153
154 m_autoSaver->changeOccurred();
155}
156
158{
159 if (!canBeModified(item)) {
160 return false;
161 }
162
163 m_model->removeBookmark(item);
164 Q_EMIT bookmarkRemoved(item);
165
166 m_autoSaver->changeOccurred();
167 return true;
168}
169
171{
172 Q_ASSERT(item);
173 Q_EMIT bookmarkChanged(item);
174
175 m_autoSaver->changeOccurred();
176}
177
179{
180 m_showOnlyIconsInToolbar = state;
182 m_autoSaver->changeOccurred();
183}
184
186{
187 m_showOnlyTextInToolbar = state;
189 m_autoSaver->changeOccurred();
190}
191
192void Bookmarks::saveSettings()
193{
194 Settings settings;
195 settings.beginGroup(QSL("Bookmarks"));
196 settings.setValue(QSL("showOnlyIconsInToolbar"), m_showOnlyIconsInToolbar);
197 settings.setValue(QSL("showOnlyTextInToolbar"), m_showOnlyTextInToolbar);
198 settings.endGroup();
199
200 saveBookmarks();
201}
202
203void Bookmarks::init()
204{
205 m_root = new BookmarkItem(BookmarkItem::Root);
206
207 m_folderToolbar = new BookmarkItem(BookmarkItem::Folder, m_root);
208 m_folderToolbar->setTitle(tr("Bookmarks Toolbar"));
209 m_folderToolbar->setDescription(tr("Bookmarks located in Bookmarks Toolbar"));
210
211 m_folderMenu = new BookmarkItem(BookmarkItem::Folder, m_root);
212 m_folderMenu->setTitle(tr("Bookmarks Menu"));
213 m_folderMenu->setDescription(tr("Bookmarks located in Bookmarks Menu"));
214
215 m_folderUnsorted = new BookmarkItem(BookmarkItem::Folder, m_root);
216 m_folderUnsorted->setTitle(tr("Unsorted Bookmarks"));
217 m_folderUnsorted->setDescription(tr("All other bookmarks"));
218
220 // Bookmarks migrated just now, let's save them ASAP
221 saveBookmarks();
222 }
223 else {
224 // Bookmarks don't need to be migrated, just load them as usual
225 loadBookmarks();
226 }
227
228 m_lastFolder = m_folderUnsorted;
229 m_model = new BookmarksModel(m_root, this, this);
230}
231
232void Bookmarks::loadBookmarks()
233{
234 const QString bookmarksFile = DataPaths::currentProfilePath() + QLatin1String("/bookmarks.json");
235 const QString backupFile = bookmarksFile + QLatin1String(".old");
236
237 QJsonParseError err;
238 QJsonDocument json = QJsonDocument::fromJson(QzTools::readAllFileByteContents(bookmarksFile), &err);
239 const QVariant res = json.toVariant();
240
241 if (err.error != QJsonParseError::NoError || res.typeId() != QMetaType::QVariantMap) {
242 if (QFile(bookmarksFile).exists()) {
243 qWarning() << "Bookmarks::init() Error parsing bookmarks! Using default bookmarks!";
244 qWarning() << "Bookmarks::init() Your bookmarks have been backed up in" << backupFile;
245
246 // Backup the user bookmarks
247 QFile::remove(backupFile);
248 QFile::copy(bookmarksFile, backupFile);
249 }
250
251 // Load default bookmarks
252 json = QJsonDocument::fromJson(QzTools::readAllFileByteContents(QSL(":data/bookmarks.json")), &err);
253 const QVariant data = json.toVariant();
254
255 Q_ASSERT(err.error == QJsonParseError::NoError);
256 Q_ASSERT(data.typeId() == QMetaType::QVariantMap);
257
258 loadBookmarksFromMap(data.toMap().value(QSL("roots")).toMap());
259
260 // Don't forget to save the bookmarks
261 m_autoSaver->changeOccurred();
262 }
263 else {
264 loadBookmarksFromMap(res.toMap().value(QSL("roots")).toMap());
265 }
266}
267
268void Bookmarks::saveBookmarks()
269{
270 QVariantMap bookmarksMap;
271
272#define WRITE_FOLDER(name, mapName, folder) \
273 QVariantMap mapName; \
274 mapName.insert(QSL("children"), writeBookmarks(folder)); \
275 mapName.insert(QSL("expanded"), folder->isExpanded()); \
276 mapName.insert(QSL("expanded_sidebar"), folder->isSidebarExpanded()); \
277 mapName.insert(QSL("name"), folder->title()); \
278 mapName.insert(QSL("description"), folder->description()); \
279 mapName.insert(QSL("type"), QSL("folder")); \
280 bookmarksMap.insert(name, mapName);
281
282 WRITE_FOLDER(QSL("bookmark_bar"), toolbarMap, m_folderToolbar)
283 WRITE_FOLDER(QSL("bookmark_menu"), menuMap, m_folderMenu)
284 WRITE_FOLDER(QSL("other"), unsortedMap, m_folderUnsorted)
285#undef WRITE_FOLDER
286
287 QVariantMap map;
288 map.insert(QSL("version"), bookmarksVersion);
289 map.insert(QSL("roots"), bookmarksMap);
290
291 const QJsonDocument json = QJsonDocument::fromVariant(map);
292 const QByteArray data = json.toJson();
293
294 if (data.isEmpty()) {
295 qWarning() << "Bookmarks::saveBookmarks() Error serializing bookmarks!";
296 return;
297 }
298
299 QSaveFile file(DataPaths::currentProfilePath() + QLatin1String("/bookmarks.json"));
300 if (!file.open(QFile::WriteOnly)) {
301 qWarning() << "Bookmarks::saveBookmarks() Error opening bookmarks file for writing!";
302 return;
303 }
304
305 file.write(data);
306 file.commit();
307}
308
309void Bookmarks::loadBookmarksFromMap(const QVariantMap &map)
310{
311#define READ_FOLDER(name, folder) \
312 readBookmarks(map.value(name).toMap().value(QSL("children")).toList(), folder); \
313 folder->setExpanded(map.value(name).toMap().value(QSL("expanded")).toBool()); \
314 folder->setSidebarExpanded(map.value(name).toMap().value(QSL("expanded_sidebar")).toBool());
315
316 READ_FOLDER(QSL("bookmark_bar"), m_folderToolbar)
317 READ_FOLDER(QSL("bookmark_menu"), m_folderMenu)
318 READ_FOLDER(QSL("other"), m_folderUnsorted)
319#undef READ_FOLDER
320}
321
322void Bookmarks::readBookmarks(const QVariantList &list, BookmarkItem* parent)
323{
324 Q_ASSERT(parent);
325
326 for (const QVariant &entry : list) {
327 const QVariantMap map = entry.toMap();
328 BookmarkItem::Type type = BookmarkItem::typeFromString(map.value(QSL("type")).toString());
329
330 if (type == BookmarkItem::Invalid) {
331 continue;
332 }
333
334 auto* item = new BookmarkItem(type, parent);
335
336 switch (type) {
338 item->setUrl(QUrl::fromEncoded(map.value(QSL("url")).toByteArray()));
339 item->setTitle(map.value(QSL("name")).toString());
340 item->setDescription(map.value(QSL("description")).toString());
341 item->setKeyword(map.value(QSL("keyword")).toString());
342 item->setVisitCount(map.value(QSL("visit_count")).toInt());
343 break;
344
346 item->setTitle(map.value(QSL("name")).toString());
347 item->setDescription(map.value(QSL("description")).toString());
348 item->setExpanded(map.value(QSL("expanded")).toBool());
349 item->setSidebarExpanded(map.value(QSL("expanded_sidebar")).toBool());
350 break;
351
352 default:
353 break;
354 }
355
356 if (map.contains(QSL("children"))) {
357 readBookmarks(map.value(QSL("children")).toList(), item);
358 }
359 }
360}
361
362QVariantList Bookmarks::writeBookmarks(BookmarkItem* parent)
363{
364 Q_ASSERT(parent);
365
366 QVariantList list;
367
368 const auto children = parent->children();
369 for (BookmarkItem* child : children) {
370 QVariantMap map;
371 map.insert(QSL("type"), BookmarkItem::typeToString(child->type()));
372
373 switch (child->type()) {
375 map.insert(QSL("url"), child->urlString());
376 map.insert(QSL("name"), child->title());
377 map.insert(QSL("description"), child->description());
378 map.insert(QSL("keyword"), child->keyword());
379 map.insert(QSL("visit_count"), child->visitCount());
380 break;
381
383 map.insert(QSL("name"), child->title());
384 map.insert(QSL("description"), child->description());
385 map.insert(QSL("expanded"), child->isExpanded());
386 map.insert(QSL("expanded_sidebar"), child->isSidebarExpanded());
387 break;
388
389 default:
390 break;
391 }
392
393 if (!child->children().isEmpty()) {
394 map.insert(QSL("children"), writeBookmarks(child));
395 }
396
397 list.append(map);
398 }
399
400 return list;
401}
402
403void Bookmarks::search(QList<BookmarkItem*>* items, BookmarkItem* parent, const QUrl &url) const
404{
405 Q_ASSERT(items);
406 Q_ASSERT(parent);
407
408 switch (parent->type()) {
411 const auto children = parent->children();
412 for (BookmarkItem* child : children) {
413 search(items, child, url);
414 }
415 break;
416 }
417
419 if (parent->url() == url) {
420 items->append(parent);
421 }
422 break;
423
424 default:
425 break;
426 }
427}
428
429void Bookmarks::search(QList<BookmarkItem*>* items, BookmarkItem* parent, const QString &string, int limit, Qt::CaseSensitivity sensitive) const
430{
431 Q_ASSERT(items);
432 Q_ASSERT(parent);
433
434 if (limit == items->count()) {
435 return;
436 }
437
438 switch (parent->type()) {
441 const auto children = parent->children();
442 for (BookmarkItem* child : children) {
443 search(items, child, string, limit, sensitive);
444 }
445 break;
446 }
447
449 if (parent->title().contains(string, sensitive) ||
450 parent->urlString().contains(string, sensitive) ||
451 parent->description().contains(string, sensitive) ||
452 parent->keyword().compare(string, sensitive) == 0
453 ) {
454 items->append(parent);
455 }
456 break;
457
458 default:
459 break;
460 }
461}
462
463void Bookmarks::searchKeyword(QList<BookmarkItem*>* items, BookmarkItem* parent, const QString &keyword) const
464{
465 Q_ASSERT(items);
466 Q_ASSERT(parent);
467
468 switch (parent->type()) {
471 const auto children = parent->children();
472 for (BookmarkItem* child : children)
473 searchKeyword(items, child, keyword);
474 break;
475 }
476
478 if (parent->keyword() == keyword)
479 items->append(parent);
480 break;
481
482 default:
483 break;
484 }
485}
#define WRITE_FOLDER(name, mapName, folder)
#define READ_FOLDER(name, folder)
void changeOccurred()
Definition: autosaver.cpp:39
void save()
void saveIfNecessary()
Definition: autosaver.cpp:31
QString keyword() const
bool isFolder() const
void setDescription(const QString &description)
QString urlString() const
QString description() const
QUrl url() const
static Type typeFromString(const QString &string)
static QString typeToString(Type type)
QList< BookmarkItem * > children() const
QString title() const
Type type() const
void setTitle(const QString &title)
BookmarkItem * menuFolder() const
Definition: bookmarks.cpp:79
BookmarkItem * rootItem() const
Definition: bookmarks.cpp:69
void changeBookmark(BookmarkItem *item)
Definition: bookmarks.cpp:170
void bookmarkRemoved(BookmarkItem *item)
void showOnlyIconsInToolbarChanged(bool show)
void addBookmark(BookmarkItem *parent, BookmarkItem *item)
Definition: bookmarks.cpp:135
void setShowOnlyTextInToolbar(bool state)
Definition: bookmarks.cpp:185
bool showOnlyTextInToolbar() const
Definition: bookmarks.cpp:64
QList< BookmarkItem * > searchBookmarks(const QUrl &url) const
Definition: bookmarks.cpp:114
bool showOnlyIconsInToolbar() const
Definition: bookmarks.cpp:59
void bookmarkAdded(BookmarkItem *item)
void bookmarkChanged(BookmarkItem *item)
void loadSettings()
Definition: bookmarks.cpp:50
BookmarkItem * unsortedFolder() const
Definition: bookmarks.cpp:84
bool canBeModified(BookmarkItem *item) const
Definition: bookmarks.cpp:104
bool removeBookmark(BookmarkItem *item)
Definition: bookmarks.cpp:157
BookmarkItem * lastUsedFolder() const
Definition: bookmarks.cpp:89
bool isBookmarked(const QUrl &url)
Definition: bookmarks.cpp:99
BookmarkItem * toolbarFolder() const
Definition: bookmarks.cpp:74
void insertBookmark(BookmarkItem *parent, int row, BookmarkItem *item)
Definition: bookmarks.cpp:144
Bookmarks(QObject *parent=nullptr)
Definition: bookmarks.cpp:33
void setShowOnlyIconsInToolbar(bool state)
Definition: bookmarks.cpp:178
QList< BookmarkItem * > searchKeyword(const QString &keyword) const
Definition: bookmarks.cpp:128
BookmarksModel * model() const
Definition: bookmarks.cpp:94
void showOnlyTextInToolbarChanged(bool show)
void removeBookmark(BookmarkItem *item)
void addBookmark(BookmarkItem *parent, int row, BookmarkItem *item)
static bool migrateBookmarksIfNecessary(Bookmarks *bookmarks)
static QString currentProfilePath()
Definition: datapaths.cpp:95
static QByteArray readAllFileByteContents(const QString &filename)
Definition: qztools.cpp:103
void beginGroup(const QString &prefix)
Definition: settings.cpp:79
void endGroup()
Definition: settings.cpp:84
QVariant value(const QString &key, const QVariant &defaultValue=QVariant())
Definition: settings.cpp:74
void setValue(const QString &key, const QVariant &defaultValue=QVariant())
Definition: settings.cpp:69
State state
#define QSL(x)
Definition: qzcommon.h:40