File indexing completed on 2024-05-12 04:57:57
0001 /* ============================================================ 0002 * Falkon - Qt web browser 0003 * Copyright (C) 2010-2018 David Rosca <nowrep@gmail.com> 0004 * 0005 * This program is free software: you can redistribute it and/or modify 0006 * it under the terms of the GNU General Public License as published by 0007 * the Free Software Foundation, either version 3 of the License, or 0008 * (at your option) any later version. 0009 * 0010 * This program is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0013 * GNU General Public License for more details. 0014 * 0015 * You should have received a copy of the GNU General Public License 0016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0017 * ============================================================ */ 0018 #include "bookmarks.h" 0019 #include "bookmarkitem.h" 0020 #include "bookmarksmodel.h" 0021 #include "bookmarkstools.h" 0022 #include "autosaver.h" 0023 #include "datapaths.h" 0024 #include "settings.h" 0025 #include "qztools.h" 0026 0027 #include <QSaveFile> 0028 #include <QJsonDocument> 0029 #include <QMetaType> 0030 0031 static const int bookmarksVersion = 1; 0032 0033 Bookmarks::Bookmarks(QObject* parent) 0034 : QObject(parent) 0035 , m_autoSaver(nullptr) 0036 { 0037 m_autoSaver = new AutoSaver(this); 0038 connect(m_autoSaver, &AutoSaver::save, this, &Bookmarks::saveSettings); 0039 0040 init(); 0041 loadSettings(); 0042 } 0043 0044 Bookmarks::~Bookmarks() 0045 { 0046 m_autoSaver->saveIfNecessary(); 0047 delete m_root; 0048 } 0049 0050 void Bookmarks::loadSettings() 0051 { 0052 Settings settings; 0053 settings.beginGroup(QSL("Bookmarks")); 0054 m_showOnlyIconsInToolbar = settings.value(QSL("showOnlyIconsInToolbar"), false).toBool(); 0055 m_showOnlyTextInToolbar = settings.value(QSL("showOnlyTextInToolbar"), false).toBool(); 0056 settings.endGroup(); 0057 } 0058 0059 bool Bookmarks::showOnlyIconsInToolbar() const 0060 { 0061 return m_showOnlyIconsInToolbar; 0062 } 0063 0064 bool Bookmarks::showOnlyTextInToolbar() const 0065 { 0066 return m_showOnlyTextInToolbar; 0067 } 0068 0069 BookmarkItem* Bookmarks::rootItem() const 0070 { 0071 return m_root; 0072 } 0073 0074 BookmarkItem* Bookmarks::toolbarFolder() const 0075 { 0076 return m_folderToolbar; 0077 } 0078 0079 BookmarkItem* Bookmarks::menuFolder() const 0080 { 0081 return m_folderMenu; 0082 } 0083 0084 BookmarkItem* Bookmarks::unsortedFolder() const 0085 { 0086 return m_folderUnsorted; 0087 } 0088 0089 BookmarkItem* Bookmarks::lastUsedFolder() const 0090 { 0091 return m_lastFolder; 0092 } 0093 0094 BookmarksModel* Bookmarks::model() const 0095 { 0096 return m_model; 0097 } 0098 0099 bool Bookmarks::isBookmarked(const QUrl &url) 0100 { 0101 return !searchBookmarks(url).isEmpty(); 0102 } 0103 0104 bool Bookmarks::canBeModified(BookmarkItem* item) const 0105 { 0106 Q_ASSERT(item); 0107 0108 return item != m_root && 0109 item != m_folderToolbar && 0110 item != m_folderMenu && 0111 item != m_folderUnsorted; 0112 } 0113 0114 QList<BookmarkItem*> Bookmarks::searchBookmarks(const QUrl &url) const 0115 { 0116 QList<BookmarkItem*> items; 0117 search(&items, m_root, url); 0118 return items; 0119 } 0120 0121 QList<BookmarkItem*> Bookmarks::searchBookmarks(const QString &string, int limit, Qt::CaseSensitivity sensitive) const 0122 { 0123 QList<BookmarkItem*> items; 0124 search(&items, m_root, string, limit, sensitive); 0125 return items; 0126 } 0127 0128 QList<BookmarkItem*> Bookmarks::searchKeyword(const QString &keyword) const 0129 { 0130 QList<BookmarkItem*> items; 0131 searchKeyword(&items, m_root, keyword); 0132 return items; 0133 } 0134 0135 void Bookmarks::addBookmark(BookmarkItem* parent, BookmarkItem* item) 0136 { 0137 Q_ASSERT(parent); 0138 Q_ASSERT(parent->isFolder()); 0139 Q_ASSERT(item); 0140 0141 insertBookmark(parent, parent->children().count(), item); 0142 } 0143 0144 void Bookmarks::insertBookmark(BookmarkItem* parent, int row, BookmarkItem* item) 0145 { 0146 Q_ASSERT(parent); 0147 Q_ASSERT(parent->isFolder()); 0148 Q_ASSERT(item); 0149 0150 m_lastFolder = parent; 0151 m_model->addBookmark(parent, row, item); 0152 Q_EMIT bookmarkAdded(item); 0153 0154 m_autoSaver->changeOccurred(); 0155 } 0156 0157 bool Bookmarks::removeBookmark(BookmarkItem* item) 0158 { 0159 if (!canBeModified(item)) { 0160 return false; 0161 } 0162 0163 m_model->removeBookmark(item); 0164 Q_EMIT bookmarkRemoved(item); 0165 0166 m_autoSaver->changeOccurred(); 0167 return true; 0168 } 0169 0170 void Bookmarks::changeBookmark(BookmarkItem* item) 0171 { 0172 Q_ASSERT(item); 0173 Q_EMIT bookmarkChanged(item); 0174 0175 m_autoSaver->changeOccurred(); 0176 } 0177 0178 void Bookmarks::setShowOnlyIconsInToolbar(bool state) 0179 { 0180 m_showOnlyIconsInToolbar = state; 0181 Q_EMIT showOnlyIconsInToolbarChanged(state); 0182 m_autoSaver->changeOccurred(); 0183 } 0184 0185 void Bookmarks::setShowOnlyTextInToolbar(bool state) 0186 { 0187 m_showOnlyTextInToolbar = state; 0188 Q_EMIT showOnlyTextInToolbarChanged(state); 0189 m_autoSaver->changeOccurred(); 0190 } 0191 0192 void Bookmarks::saveSettings() 0193 { 0194 Settings settings; 0195 settings.beginGroup(QSL("Bookmarks")); 0196 settings.setValue(QSL("showOnlyIconsInToolbar"), m_showOnlyIconsInToolbar); 0197 settings.setValue(QSL("showOnlyTextInToolbar"), m_showOnlyTextInToolbar); 0198 settings.endGroup(); 0199 0200 saveBookmarks(); 0201 } 0202 0203 void Bookmarks::init() 0204 { 0205 m_root = new BookmarkItem(BookmarkItem::Root); 0206 0207 m_folderToolbar = new BookmarkItem(BookmarkItem::Folder, m_root); 0208 m_folderToolbar->setTitle(tr("Bookmarks Toolbar")); 0209 m_folderToolbar->setDescription(tr("Bookmarks located in Bookmarks Toolbar")); 0210 0211 m_folderMenu = new BookmarkItem(BookmarkItem::Folder, m_root); 0212 m_folderMenu->setTitle(tr("Bookmarks Menu")); 0213 m_folderMenu->setDescription(tr("Bookmarks located in Bookmarks Menu")); 0214 0215 m_folderUnsorted = new BookmarkItem(BookmarkItem::Folder, m_root); 0216 m_folderUnsorted->setTitle(tr("Unsorted Bookmarks")); 0217 m_folderUnsorted->setDescription(tr("All other bookmarks")); 0218 0219 if (BookmarksTools::migrateBookmarksIfNecessary(this)) { 0220 // Bookmarks migrated just now, let's save them ASAP 0221 saveBookmarks(); 0222 } 0223 else { 0224 // Bookmarks don't need to be migrated, just load them as usual 0225 loadBookmarks(); 0226 } 0227 0228 m_lastFolder = m_folderUnsorted; 0229 m_model = new BookmarksModel(m_root, this, this); 0230 } 0231 0232 void Bookmarks::loadBookmarks() 0233 { 0234 const QString bookmarksFile = DataPaths::currentProfilePath() + QLatin1String("/bookmarks.json"); 0235 const QString backupFile = bookmarksFile + QLatin1String(".old"); 0236 0237 QJsonParseError err; 0238 QJsonDocument json = QJsonDocument::fromJson(QzTools::readAllFileByteContents(bookmarksFile), &err); 0239 const QVariant res = json.toVariant(); 0240 0241 if (err.error != QJsonParseError::NoError || res.typeId() != QMetaType::QVariantMap) { 0242 if (QFile(bookmarksFile).exists()) { 0243 qWarning() << "Bookmarks::init() Error parsing bookmarks! Using default bookmarks!"; 0244 qWarning() << "Bookmarks::init() Your bookmarks have been backed up in" << backupFile; 0245 0246 // Backup the user bookmarks 0247 QFile::remove(backupFile); 0248 QFile::copy(bookmarksFile, backupFile); 0249 } 0250 0251 // Load default bookmarks 0252 json = QJsonDocument::fromJson(QzTools::readAllFileByteContents(QSL(":data/bookmarks.json")), &err); 0253 const QVariant data = json.toVariant(); 0254 0255 Q_ASSERT(err.error == QJsonParseError::NoError); 0256 Q_ASSERT(data.typeId() == QMetaType::QVariantMap); 0257 0258 loadBookmarksFromMap(data.toMap().value(QSL("roots")).toMap()); 0259 0260 // Don't forget to save the bookmarks 0261 m_autoSaver->changeOccurred(); 0262 } 0263 else { 0264 loadBookmarksFromMap(res.toMap().value(QSL("roots")).toMap()); 0265 } 0266 } 0267 0268 void Bookmarks::saveBookmarks() 0269 { 0270 QVariantMap bookmarksMap; 0271 0272 #define WRITE_FOLDER(name, mapName, folder) \ 0273 QVariantMap mapName; \ 0274 mapName.insert(QSL("children"), writeBookmarks(folder)); \ 0275 mapName.insert(QSL("expanded"), folder->isExpanded()); \ 0276 mapName.insert(QSL("expanded_sidebar"), folder->isSidebarExpanded()); \ 0277 mapName.insert(QSL("name"), folder->title()); \ 0278 mapName.insert(QSL("description"), folder->description()); \ 0279 mapName.insert(QSL("type"), QSL("folder")); \ 0280 bookmarksMap.insert(name, mapName); 0281 0282 WRITE_FOLDER(QSL("bookmark_bar"), toolbarMap, m_folderToolbar) 0283 WRITE_FOLDER(QSL("bookmark_menu"), menuMap, m_folderMenu) 0284 WRITE_FOLDER(QSL("other"), unsortedMap, m_folderUnsorted) 0285 #undef WRITE_FOLDER 0286 0287 QVariantMap map; 0288 map.insert(QSL("version"), bookmarksVersion); 0289 map.insert(QSL("roots"), bookmarksMap); 0290 0291 const QJsonDocument json = QJsonDocument::fromVariant(map); 0292 const QByteArray data = json.toJson(); 0293 0294 if (data.isEmpty()) { 0295 qWarning() << "Bookmarks::saveBookmarks() Error serializing bookmarks!"; 0296 return; 0297 } 0298 0299 QSaveFile file(DataPaths::currentProfilePath() + QLatin1String("/bookmarks.json")); 0300 if (!file.open(QFile::WriteOnly)) { 0301 qWarning() << "Bookmarks::saveBookmarks() Error opening bookmarks file for writing!"; 0302 return; 0303 } 0304 0305 file.write(data); 0306 file.commit(); 0307 } 0308 0309 void Bookmarks::loadBookmarksFromMap(const QVariantMap &map) 0310 { 0311 #define READ_FOLDER(name, folder) \ 0312 readBookmarks(map.value(name).toMap().value(QSL("children")).toList(), folder); \ 0313 folder->setExpanded(map.value(name).toMap().value(QSL("expanded")).toBool()); \ 0314 folder->setSidebarExpanded(map.value(name).toMap().value(QSL("expanded_sidebar")).toBool()); 0315 0316 READ_FOLDER(QSL("bookmark_bar"), m_folderToolbar) 0317 READ_FOLDER(QSL("bookmark_menu"), m_folderMenu) 0318 READ_FOLDER(QSL("other"), m_folderUnsorted) 0319 #undef READ_FOLDER 0320 } 0321 0322 void Bookmarks::readBookmarks(const QVariantList &list, BookmarkItem* parent) 0323 { 0324 Q_ASSERT(parent); 0325 0326 for (const QVariant &entry : list) { 0327 const QVariantMap map = entry.toMap(); 0328 BookmarkItem::Type type = BookmarkItem::typeFromString(map.value(QSL("type")).toString()); 0329 0330 if (type == BookmarkItem::Invalid) { 0331 continue; 0332 } 0333 0334 auto* item = new BookmarkItem(type, parent); 0335 0336 switch (type) { 0337 case BookmarkItem::Url: 0338 item->setUrl(QUrl::fromEncoded(map.value(QSL("url")).toByteArray())); 0339 item->setTitle(map.value(QSL("name")).toString()); 0340 item->setDescription(map.value(QSL("description")).toString()); 0341 item->setKeyword(map.value(QSL("keyword")).toString()); 0342 item->setVisitCount(map.value(QSL("visit_count")).toInt()); 0343 break; 0344 0345 case BookmarkItem::Folder: 0346 item->setTitle(map.value(QSL("name")).toString()); 0347 item->setDescription(map.value(QSL("description")).toString()); 0348 item->setExpanded(map.value(QSL("expanded")).toBool()); 0349 item->setSidebarExpanded(map.value(QSL("expanded_sidebar")).toBool()); 0350 break; 0351 0352 default: 0353 break; 0354 } 0355 0356 if (map.contains(QSL("children"))) { 0357 readBookmarks(map.value(QSL("children")).toList(), item); 0358 } 0359 } 0360 } 0361 0362 QVariantList Bookmarks::writeBookmarks(BookmarkItem* parent) 0363 { 0364 Q_ASSERT(parent); 0365 0366 QVariantList list; 0367 0368 const auto children = parent->children(); 0369 for (BookmarkItem* child : children) { 0370 QVariantMap map; 0371 map.insert(QSL("type"), BookmarkItem::typeToString(child->type())); 0372 0373 switch (child->type()) { 0374 case BookmarkItem::Url: 0375 map.insert(QSL("url"), child->urlString()); 0376 map.insert(QSL("name"), child->title()); 0377 map.insert(QSL("description"), child->description()); 0378 map.insert(QSL("keyword"), child->keyword()); 0379 map.insert(QSL("visit_count"), child->visitCount()); 0380 break; 0381 0382 case BookmarkItem::Folder: 0383 map.insert(QSL("name"), child->title()); 0384 map.insert(QSL("description"), child->description()); 0385 map.insert(QSL("expanded"), child->isExpanded()); 0386 map.insert(QSL("expanded_sidebar"), child->isSidebarExpanded()); 0387 break; 0388 0389 default: 0390 break; 0391 } 0392 0393 if (!child->children().isEmpty()) { 0394 map.insert(QSL("children"), writeBookmarks(child)); 0395 } 0396 0397 list.append(map); 0398 } 0399 0400 return list; 0401 } 0402 0403 void Bookmarks::search(QList<BookmarkItem*>* items, BookmarkItem* parent, const QUrl &url) const 0404 { 0405 Q_ASSERT(items); 0406 Q_ASSERT(parent); 0407 0408 switch (parent->type()) { 0409 case BookmarkItem::Root: 0410 case BookmarkItem::Folder: { 0411 const auto children = parent->children(); 0412 for (BookmarkItem* child : children) { 0413 search(items, child, url); 0414 } 0415 break; 0416 } 0417 0418 case BookmarkItem::Url: 0419 if (parent->url() == url) { 0420 items->append(parent); 0421 } 0422 break; 0423 0424 default: 0425 break; 0426 } 0427 } 0428 0429 void Bookmarks::search(QList<BookmarkItem*>* items, BookmarkItem* parent, const QString &string, int limit, Qt::CaseSensitivity sensitive) const 0430 { 0431 Q_ASSERT(items); 0432 Q_ASSERT(parent); 0433 0434 if (limit == items->count()) { 0435 return; 0436 } 0437 0438 switch (parent->type()) { 0439 case BookmarkItem::Root: 0440 case BookmarkItem::Folder: { 0441 const auto children = parent->children(); 0442 for (BookmarkItem* child : children) { 0443 search(items, child, string, limit, sensitive); 0444 } 0445 break; 0446 } 0447 0448 case BookmarkItem::Url: 0449 if (parent->title().contains(string, sensitive) || 0450 parent->urlString().contains(string, sensitive) || 0451 parent->description().contains(string, sensitive) || 0452 parent->keyword().compare(string, sensitive) == 0 0453 ) { 0454 items->append(parent); 0455 } 0456 break; 0457 0458 default: 0459 break; 0460 } 0461 } 0462 0463 void Bookmarks::searchKeyword(QList<BookmarkItem*>* items, BookmarkItem* parent, const QString &keyword) const 0464 { 0465 Q_ASSERT(items); 0466 Q_ASSERT(parent); 0467 0468 switch (parent->type()) { 0469 case BookmarkItem::Root: 0470 case BookmarkItem::Folder: { 0471 const auto children = parent->children(); 0472 for (BookmarkItem* child : children) 0473 searchKeyword(items, child, keyword); 0474 break; 0475 } 0476 0477 case BookmarkItem::Url: 0478 if (parent->keyword() == keyword) 0479 items->append(parent); 0480 break; 0481 0482 default: 0483 break; 0484 } 0485 }