File indexing completed on 2024-05-12 04:57:58

0001 /* ============================================================
0002 * Falkon - Qt web browser
0003 * Copyright (C) 2014-2017 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 "bookmarkstools.h"
0019 #include "bookmarkitem.h"
0020 #include "bookmarks.h"
0021 #include "mainapplication.h"
0022 #include "iconprovider.h"
0023 #include "enhancedmenu.h"
0024 #include "tabwidget.h"
0025 #include "qzsettings.h"
0026 #include "browserwindow.h"
0027 #include "sqldatabase.h"
0028 
0029 #include <iostream>
0030 #include <QDialogButtonBox>
0031 #include <QBoxLayout>
0032 #include <QFormLayout>
0033 #include <QLabel>
0034 #include <QLineEdit>
0035 #include <QPlainTextEdit>
0036 #include <QStyle>
0037 #include <QDialog>
0038 #include <QMessageBox>
0039 
0040 // BookmarksFoldersMenu
0041 BookmarksFoldersMenu::BookmarksFoldersMenu(QWidget* parent)
0042     : QMenu(parent)
0043     , m_selectedFolder(nullptr)
0044 {
0045     init();
0046 }
0047 
0048 BookmarkItem* BookmarksFoldersMenu::selectedFolder() const
0049 {
0050     return m_selectedFolder;
0051 }
0052 
0053 void BookmarksFoldersMenu::folderChoosed()
0054 {
0055     if (auto* act = qobject_cast<QAction*>(sender())) {
0056         BookmarkItem* folder = static_cast<BookmarkItem*>(act->data().value<void*>());
0057         Q_EMIT folderSelected(folder);
0058     }
0059 }
0060 
0061 void BookmarksFoldersMenu::init()
0062 {
0063 #define ADD_MENU(name) \
0064     BookmarkItem* f_##name = mApp->bookmarks()->name(); \
0065     QMenu* m_##name = addMenu(f_##name->icon(), f_##name->title()); \
0066     createMenu(m_##name, f_##name);
0067 
0068     ADD_MENU(toolbarFolder)
0069     ADD_MENU(menuFolder)
0070     ADD_MENU(unsortedFolder)
0071 #undef ADD_MENU
0072 }
0073 
0074 void BookmarksFoldersMenu::createMenu(QMenu* menu, BookmarkItem* parent)
0075 {
0076     QAction* act = menu->addAction(tr("Choose %1").arg(parent->title()));
0077     act->setData(QVariant::fromValue<void*>(static_cast<void*>(parent)));
0078     connect(act, &QAction::triggered, this, &BookmarksFoldersMenu::folderChoosed);
0079 
0080     menu->addSeparator();
0081 
0082     const auto children = parent->children();
0083     for (BookmarkItem* child : children) {
0084         if (child->isFolder()) {
0085             QMenu* m = menu->addMenu(child->icon(), child->title());
0086             createMenu(m, child);
0087         }
0088     }
0089 }
0090 
0091 
0092 // BookmarksFoldersButton
0093 BookmarksFoldersButton::BookmarksFoldersButton(QWidget* parent, BookmarkItem* folder)
0094     : QPushButton(parent)
0095     , m_menu(new BookmarksFoldersMenu(this))
0096     , m_selectedFolder(folder ? folder : mApp->bookmarks()->lastUsedFolder())
0097 {
0098     init();
0099 
0100     connect(m_menu, &BookmarksFoldersMenu::folderSelected, this, &BookmarksFoldersButton::setSelectedFolder);
0101 }
0102 
0103 BookmarkItem* BookmarksFoldersButton::selectedFolder() const
0104 {
0105     return m_selectedFolder;
0106 }
0107 
0108 void BookmarksFoldersButton::setSelectedFolder(BookmarkItem* folder)
0109 {
0110     Q_ASSERT(folder);
0111     Q_ASSERT(folder->isFolder());
0112 
0113     m_selectedFolder = folder;
0114     setText(folder->title());
0115     setIcon(folder->icon());
0116 
0117     if (sender()) {
0118         Q_EMIT selectedFolderChanged(folder);
0119     }
0120 }
0121 
0122 void BookmarksFoldersButton::init()
0123 {
0124     setMenu(m_menu);
0125     setSelectedFolder(m_selectedFolder);
0126 }
0127 
0128 
0129 // BookmarksTools
0130 bool BookmarksTools::addBookmarkDialog(QWidget* parent, const QUrl &url, const QString &title, BookmarkItem* folder)
0131 {
0132     if (url.isEmpty() || title.isEmpty()) {
0133         return false;
0134     }
0135 
0136     auto* dialog = new QDialog(parent);
0137     auto* layout = new QBoxLayout(QBoxLayout::TopToBottom, dialog);
0138     auto* label = new QLabel(dialog);
0139     auto* edit = new QLineEdit(dialog);
0140     auto* folderButton = new BookmarksFoldersButton(dialog, folder);
0141 
0142     auto* box = new QDialogButtonBox(dialog);
0143     box->addButton(QDialogButtonBox::Ok);
0144     box->addButton(QDialogButtonBox::Cancel);
0145     QObject::connect(box, &QDialogButtonBox::rejected, dialog, &QDialog::reject);
0146     QObject::connect(box, &QDialogButtonBox::accepted, dialog, &QDialog::accept);
0147 
0148     layout->addWidget(label);
0149     layout->addWidget(edit);
0150     layout->addWidget(folderButton);
0151     layout->addWidget(box);
0152 
0153     label->setText(Bookmarks::tr("Choose name and location of this bookmark."));
0154     edit->setText(title);
0155     edit->setCursorPosition(0);
0156     dialog->setWindowIcon(IconProvider::iconForUrl(url));
0157     dialog->setWindowTitle(Bookmarks::tr("Add New Bookmark"));
0158 
0159     QSize size = dialog->size();
0160     size.setWidth(350);
0161     dialog->resize(size);
0162     dialog->exec();
0163 
0164     if (dialog->result() == QDialog::Rejected || edit->text().isEmpty()) {
0165         delete dialog;
0166         return false;
0167     }
0168 
0169     auto* bookmark = new BookmarkItem(BookmarkItem::Url);
0170     bookmark->setTitle(edit->text());
0171     bookmark->setUrl(url);
0172     mApp->bookmarks()->addBookmark(folderButton->selectedFolder(), bookmark);
0173 
0174     delete dialog;
0175     return true;
0176 }
0177 
0178 bool BookmarksTools::bookmarkAllTabsDialog(QWidget* parent, TabWidget* tabWidget, BookmarkItem* folder)
0179 {
0180     Q_ASSERT(tabWidget);
0181 
0182     auto* dialog = new QDialog(parent);
0183     auto* layout = new QBoxLayout(QBoxLayout::TopToBottom, dialog);
0184     auto* label = new QLabel(dialog);
0185     auto* folderButton = new BookmarksFoldersButton(dialog, folder);
0186 
0187     auto* box = new QDialogButtonBox(dialog);
0188     box->addButton(QDialogButtonBox::Ok);
0189     box->addButton(QDialogButtonBox::Cancel);
0190     QObject::connect(box, &QDialogButtonBox::rejected, dialog, &QDialog::reject);
0191     QObject::connect(box, &QDialogButtonBox::accepted, dialog, &QDialog::accept);
0192 
0193     layout->addWidget(label);
0194     layout->addWidget(folderButton);
0195     layout->addWidget(box);
0196 
0197     label->setText(Bookmarks::tr("Choose folder for bookmarks:"));
0198     dialog->setWindowTitle(Bookmarks::tr("Bookmark All Tabs"));
0199 
0200     QSize size = dialog->size();
0201     size.setWidth(350);
0202     dialog->resize(size);
0203     dialog->exec();
0204 
0205     if (dialog->result() == QDialog::Rejected) {
0206         return false;
0207     }
0208 
0209     const auto allTabs = tabWidget->allTabs(false);
0210     for (WebTab* tab : allTabs) {
0211         if (!tab->url().isEmpty()) {
0212             auto* bookmark = new BookmarkItem(BookmarkItem::Url);
0213             bookmark->setTitle(tab->title());
0214             bookmark->setUrl(tab->url());
0215             mApp->bookmarks()->addBookmark(folderButton->selectedFolder(), bookmark);
0216         }
0217     }
0218 
0219     delete dialog;
0220     return true;
0221 }
0222 
0223 bool BookmarksTools::editBookmarkDialog(QWidget* parent, BookmarkItem *item)
0224 {
0225     auto* dialog = new QDialog(parent);
0226     auto* layout = new QFormLayout(dialog);
0227 
0228     auto* title = new QLineEdit;
0229     auto* address = new QLineEdit;
0230     auto* keyword = new QLineEdit;
0231     auto* description = new QPlainTextEdit;
0232 
0233     auto* box = new QDialogButtonBox(dialog);
0234     box->addButton(QDialogButtonBox::Ok);
0235     box->addButton(QDialogButtonBox::Cancel);
0236     QObject::connect(box, &QDialogButtonBox::accepted, dialog, &QDialog::accept);
0237     QObject::connect(box, &QDialogButtonBox::rejected, dialog, &QDialog::reject);
0238 
0239     layout->addRow(Bookmarks::tr("Title:"), title);
0240     title->setText(item->title());
0241     if (!item->isFolder()) {
0242         layout->addRow(Bookmarks::tr("Address:"), address);
0243         address->setText(item->urlString());
0244         layout->addRow(Bookmarks::tr("Keyword:"), keyword);
0245         keyword->setText(item->keyword());
0246     }
0247     layout->addRow(Bookmarks::tr("Description:"), description);
0248     description->document()->setPlainText(item->description());
0249     layout->addWidget(box);
0250 
0251     dialog->setWindowIcon(item->icon());
0252     dialog->setWindowTitle(Bookmarks::tr("Edit Bookmark"));
0253 
0254     dialog->exec();
0255 
0256     if (dialog->result() == QDialog::Rejected) {
0257         delete dialog;
0258         return false;
0259     }
0260 
0261     item->setTitle(title->text());
0262     if (!item->isFolder()) {
0263         item->setUrl(QUrl::fromEncoded(address->text().toUtf8()));
0264         item->setKeyword(keyword->text());
0265     }
0266     item->setDescription(description->toPlainText());
0267 
0268     delete dialog;
0269     return true;
0270 }
0271 
0272 void BookmarksTools::openBookmark(BrowserWindow* window, BookmarkItem* item)
0273 {
0274     Q_ASSERT(window);
0275 
0276     if (!item || !item->isUrl()) {
0277         return;
0278     }
0279 
0280     if (item->isFolder()) {
0281         openFolderInTabs(window, item);
0282     }
0283     else if (item->isUrl()) {
0284         item->updateVisitCount();
0285         window->loadAddress(item->url());
0286     }
0287 }
0288 
0289 void BookmarksTools::openBookmarkInNewTab(BrowserWindow* window, BookmarkItem* item)
0290 {
0291     Q_ASSERT(window);
0292 
0293     if (!item) {
0294         return;
0295     }
0296 
0297     if (item->isFolder()) {
0298         openFolderInTabs(window, item);
0299     }
0300     else if (item->isUrl()) {
0301         item->updateVisitCount();
0302         window->tabWidget()->addView(item->url(), item->title(), qzSettings->newTabPosition);
0303     }
0304 }
0305 
0306 void BookmarksTools::openBookmarkInNewWindow(BookmarkItem* item)
0307 {
0308     if (!item->isUrl()) {
0309         return;
0310     }
0311 
0312     item->updateVisitCount();
0313     mApp->createWindow(Qz::BW_NewWindow, item->url());
0314 }
0315 
0316 void BookmarksTools::openBookmarkInNewPrivateWindow(BookmarkItem* item)
0317 {
0318     if (!item->isUrl()) {
0319         return;
0320     }
0321 
0322     item->updateVisitCount();
0323     mApp->startPrivateBrowsing(item->url());
0324 }
0325 
0326 void BookmarksTools::openFolderInTabs(BrowserWindow* window, BookmarkItem* folder)
0327 {
0328     Q_ASSERT(window);
0329     Q_ASSERT(folder->isFolder());
0330 
0331     const auto children = folder->children();
0332 
0333     bool showWarning = folder->children().size() > 10;
0334     if (!showWarning) {
0335         for (BookmarkItem* child : children) {
0336             if (child->isFolder()) {
0337                 showWarning = true;
0338                 break;
0339             }
0340         }
0341     }
0342 
0343     if (showWarning) {
0344         const auto button = QMessageBox::warning(window, Bookmarks::tr("Confirmation"),
0345                                                  Bookmarks::tr("Are you sure you want to open all bookmarks from '%1' folder in tabs?").arg(folder->title()),
0346                                                  QMessageBox::Yes | QMessageBox::No);
0347         if (button != QMessageBox::Yes) {
0348             return;
0349         }
0350     }
0351 
0352     for (BookmarkItem* child : children) {
0353         if (child->isUrl()) {
0354             openBookmarkInNewTab(window, child);
0355         }
0356         else if (child->isFolder()) {
0357             openFolderInTabs(window, child);
0358         }
0359     }
0360 }
0361 
0362 void BookmarksTools::addActionToMenu(QObject* receiver, Menu* menu, BookmarkItem* item)
0363 {
0364     Q_ASSERT(menu);
0365     Q_ASSERT(item);
0366 
0367     switch (item->type()) {
0368     case BookmarkItem::Url:
0369         addUrlToMenu(receiver, menu, item);
0370         break;
0371     case BookmarkItem::Folder:
0372         addFolderToMenu(receiver, menu, item);
0373         break;
0374     case BookmarkItem::Separator:
0375         addSeparatorToMenu(menu, item);
0376         break;
0377     default:
0378         break;
0379     }
0380 }
0381 
0382 void BookmarksTools::addFolderToMenu(QObject* receiver, Menu* menu, BookmarkItem* folder)
0383 {
0384     Q_ASSERT(menu);
0385     Q_ASSERT(folder);
0386     Q_ASSERT(folder->isFolder());
0387 
0388     Menu* m = new Menu(menu);
0389     QString title = QFontMetrics(m->font()).elidedText(folder->title(), Qt::ElideRight, 250);
0390     m->setTitle(title);
0391     m->setIcon(folder->icon());
0392 
0393     addFolderContentsToMenu(receiver, m, folder);
0394 
0395     QAction* act = menu->addMenu(m);
0396     act->setData(QVariant::fromValue<void*>(static_cast<void*>(folder)));
0397     act->setIconVisibleInMenu(true);
0398 }
0399 
0400 void BookmarksTools::addUrlToMenu(QObject* receiver, Menu* menu, BookmarkItem* bookmark)
0401 {
0402     Q_ASSERT(menu);
0403     Q_ASSERT(bookmark);
0404     Q_ASSERT(bookmark->isUrl());
0405 
0406     auto* act = new Action(menu);
0407     QString title = QFontMetrics(act->font()).elidedText(bookmark->title(), Qt::ElideRight, 250);
0408     act->setText(title);
0409     act->setData(QVariant::fromValue<void*>(static_cast<void*>(bookmark)));
0410     act->setIconVisibleInMenu(true);
0411 
0412     QObject::connect(act, SIGNAL(triggered()), receiver, SLOT(bookmarkActivated()));
0413     QObject::connect(act, SIGNAL(ctrlTriggered()), receiver, SLOT(bookmarkCtrlActivated()));
0414     QObject::connect(act, SIGNAL(shiftTriggered()), receiver, SLOT(bookmarkShiftActivated()));
0415 
0416     menu->addAction(act);
0417 }
0418 
0419 void BookmarksTools::addSeparatorToMenu(Menu* menu, BookmarkItem* separator)
0420 {
0421     Q_UNUSED(separator)
0422 
0423     Q_ASSERT(menu);
0424     Q_ASSERT(separator->isSeparator());
0425 
0426     menu->addSeparator();
0427 }
0428 
0429 void BookmarksTools::addFolderContentsToMenu(QObject *receiver, Menu *menu, BookmarkItem *folder)
0430 {
0431     QObject::connect(menu, SIGNAL(aboutToShow()), receiver, SLOT(menuAboutToShow()));
0432     QObject::connect(menu, SIGNAL(menuMiddleClicked(Menu*)), receiver, SLOT(menuMiddleClicked(Menu*)));
0433 
0434     const auto children = folder->children();
0435     for (BookmarkItem* child : children) {
0436         addActionToMenu(receiver, menu, child);
0437     }
0438 
0439     if (menu->isEmpty()) {
0440         menu->addAction(Bookmarks::tr("Empty"))->setDisabled(true);
0441     }
0442 }
0443 
0444 bool BookmarksTools::migrateBookmarksIfNecessary(Bookmarks* bookmarks)
0445 {
0446     QSqlQuery query(SqlDatabase::instance()->database());
0447     query.exec(QSL("SELECT name FROM sqlite_master WHERE type='table' AND name='folders'"));
0448 
0449     if (!query.next()) {
0450         return false;
0451     }
0452 
0453     std::cout << "Bookmarks: Migrating your bookmarks from SQLite to JSON..." << std::endl;
0454 
0455     QHash<QString, BookmarkItem*> folders;
0456     folders.insert(QSL("bookmarksToolbar"), bookmarks->toolbarFolder());
0457     folders.insert(QSL("bookmarksMenu"), bookmarks->menuFolder());
0458     folders.insert(QSL("unsorted"), bookmarks->unsortedFolder());
0459 
0460     query.exec(QSL("SELECT name, subfolder FROM folders"));
0461     while (query.next()) {
0462         const QString title = query.value(0).toString();
0463         bool subfolder = query.value(1).toString() == QLatin1String("yes");
0464 
0465         BookmarkItem* parent = subfolder ? bookmarks->toolbarFolder() : bookmarks->unsortedFolder();
0466         auto* folder = new BookmarkItem(BookmarkItem::Folder, parent);
0467         folder->setTitle(title);
0468         folders.insert(folder->title(), folder);
0469     }
0470 
0471     query.exec(QSL("SELECT title, folder, url FROM bookmarks ORDER BY position ASC"));
0472     while (query.next()) {
0473         const QString title = query.value(0).toString();
0474         const QString folder = query.value(1).toString();
0475         const QUrl url = query.value(2).toUrl();
0476 
0477         BookmarkItem* parent = folders.value(folder);
0478         if (!parent) {
0479             parent = bookmarks->unsortedFolder();
0480         }
0481         Q_ASSERT(parent);
0482 
0483         auto* bookmark = new BookmarkItem(BookmarkItem::Url, parent);
0484         bookmark->setTitle(title);
0485         bookmark->setUrl(url);
0486     }
0487 
0488     query.exec(QSL("DROP TABLE folders"));
0489     query.exec(QSL("DROP TABLE bookmarks"));
0490     query.exec(QSL("VACUUM"));
0491 
0492     std::cout << "Bookmarks: Bookmarks successfully migrated!" << std::endl;
0493     return true;
0494 }