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

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 "bookmarkstoolbar.h"
0019 #include "bookmarkstoolbarbutton.h"
0020 #include "bookmarkstools.h"
0021 #include "bookmarksmodel.h"
0022 #include "bookmarkitem.h"
0023 #include "bookmarks.h"
0024 #include "mainapplication.h"
0025 #include "iconprovider.h"
0026 #include "qztools.h"
0027 
0028 #include <QDragEnterEvent>
0029 #include <QHBoxLayout>
0030 #include <QMimeData>
0031 #include <QTimer>
0032 #include <QFrame>
0033 #include <QInputDialog>
0034 
0035 BookmarksToolbar::BookmarksToolbar(BrowserWindow* window, QWidget* parent)
0036     : QWidget(parent)
0037     , m_window(window)
0038     , m_bookmarks(mApp->bookmarks())
0039     , m_clickedBookmark(nullptr)
0040     , m_dropRow(-1)
0041 {
0042     setObjectName("bookmarksbar");
0043     setAcceptDrops(true);
0044     setContextMenuPolicy(Qt::CustomContextMenu);
0045 
0046     m_layout = new QHBoxLayout(this);
0047     auto contentsMargin = style()->pixelMetric(QStyle::PM_ToolBarItemMargin, nullptr, this)
0048                         + style()->pixelMetric(QStyle::PM_ToolBarFrameWidth, nullptr, this);
0049     m_layout->setContentsMargins(contentsMargin, contentsMargin, contentsMargin, contentsMargin);
0050     m_layout->setSpacing(style()->pixelMetric(QStyle::PM_ToolBarItemSpacing, nullptr, this));
0051     setLayout(m_layout);
0052 
0053     m_updateTimer = new QTimer(this);
0054     m_updateTimer->setInterval(300);
0055     m_updateTimer->setSingleShot(true);
0056     connect(m_updateTimer, &QTimer::timeout, this, &BookmarksToolbar::refresh);
0057 
0058     connect(m_bookmarks, &Bookmarks::bookmarkAdded, this, &BookmarksToolbar::bookmarksChanged);
0059     connect(m_bookmarks, &Bookmarks::bookmarkRemoved, this, &BookmarksToolbar::bookmarksChanged);
0060     connect(m_bookmarks, &Bookmarks::bookmarkChanged, this, &BookmarksToolbar::bookmarksChanged);
0061     connect(m_bookmarks, &Bookmarks::showOnlyIconsInToolbarChanged, this, &BookmarksToolbar::showOnlyIconsChanged);
0062     connect(m_bookmarks, &Bookmarks::showOnlyTextInToolbarChanged, this, &BookmarksToolbar::showOnlyTextChanged);
0063     connect(this, &QWidget::customContextMenuRequested, this, &BookmarksToolbar::contextMenuRequested);
0064 
0065     refresh();
0066 }
0067 
0068 void BookmarksToolbar::contextMenuRequested(const QPoint &pos)
0069 {
0070     BookmarksToolbarButton* button = buttonAt(pos);
0071     m_clickedBookmark = button ? button->bookmark() : nullptr;
0072 
0073     QMenu menu;
0074     QAction* actNewTab = menu.addAction(IconProvider::newTabIcon(), tr("Open in new tab"));
0075     QAction* actNewWindow = menu.addAction(IconProvider::newWindowIcon(), tr("Open in new window"));
0076     QAction* actNewPrivateWindow = menu.addAction(IconProvider::privateBrowsingIcon(), tr("Open in new private window"));
0077     menu.addSeparator();
0078     QAction* actNewFolder = menu.addAction(QIcon::fromTheme(QSL("folder-new")), tr("New Folder"));
0079     QAction* actEdit = menu.addAction(tr("Edit"));
0080     QAction* actDelete = menu.addAction(QIcon::fromTheme(QSL("edit-delete")), tr("Delete"));
0081     menu.addSeparator();
0082     m_actShowOnlyIcons = menu.addAction(tr("Show Only Icons"));
0083     m_actShowOnlyIcons->setCheckable(true);
0084     m_actShowOnlyIcons->setChecked(m_bookmarks->showOnlyIconsInToolbar());
0085     connect(m_actShowOnlyIcons, &QAction::toggled, m_bookmarks, &Bookmarks::setShowOnlyIconsInToolbar);
0086     m_actShowOnlyText = menu.addAction(tr("Show Only Text"));
0087     m_actShowOnlyText->setCheckable(true);
0088     m_actShowOnlyText->setChecked(m_bookmarks->showOnlyTextInToolbar());
0089     connect(m_actShowOnlyText, &QAction::toggled, m_bookmarks, &Bookmarks::setShowOnlyTextInToolbar);
0090 
0091     connect(actNewTab, &QAction::triggered, this, &BookmarksToolbar::openBookmarkInNewTab);
0092     connect(actNewWindow, &QAction::triggered, this, &BookmarksToolbar::openBookmarkInNewWindow);
0093     connect(actNewPrivateWindow, &QAction::triggered, this, &BookmarksToolbar::openBookmarkInNewPrivateWindow);
0094     connect(actNewFolder, &QAction::triggered, this, &BookmarksToolbar::createNewFolder);
0095     connect(actEdit, &QAction::triggered, this, &BookmarksToolbar::editBookmark);
0096     connect(actDelete, &QAction::triggered, this, &BookmarksToolbar::deleteBookmark);
0097 
0098     actEdit->setEnabled(m_clickedBookmark && m_bookmarks->canBeModified(m_clickedBookmark));
0099     actDelete->setEnabled(m_clickedBookmark && m_bookmarks->canBeModified(m_clickedBookmark));
0100     actNewTab->setEnabled(m_clickedBookmark && m_clickedBookmark->isUrl());
0101     actNewWindow->setEnabled(m_clickedBookmark && m_clickedBookmark->isUrl());
0102     actNewPrivateWindow->setEnabled(m_clickedBookmark && m_clickedBookmark->isUrl());
0103 
0104     menu.exec(mapToGlobal(pos));
0105 
0106     if (button) {
0107         // Clear mouseover state after closing menu
0108         button->update();
0109     }
0110 
0111     m_clickedBookmark = nullptr;
0112     m_actShowOnlyIcons = nullptr;
0113     m_actShowOnlyText = nullptr;
0114 }
0115 
0116 void BookmarksToolbar::refresh()
0117 {
0118     clear();
0119 
0120     BookmarkItem* folder = mApp->bookmarks()->toolbarFolder();
0121 
0122     const auto children = folder->children();
0123     for (BookmarkItem* child : children) {
0124         addItem(child);
0125     }
0126 
0127     m_layout->addStretch();
0128 }
0129 
0130 void BookmarksToolbar::bookmarksChanged()
0131 {
0132     m_updateTimer->start();
0133 }
0134 
0135 void BookmarksToolbar::showOnlyIconsChanged(bool state)
0136 {
0137     if (state && m_actShowOnlyText) {
0138         m_actShowOnlyText->setChecked(false);
0139     }
0140 
0141     for (int i = 0; i < m_layout->count(); ++i) {
0142         auto* b = qobject_cast<BookmarksToolbarButton*>(m_layout->itemAt(i)->widget());
0143         if (b) {
0144             b->setShowOnlyIcon(state);
0145         }
0146     }
0147 }
0148 
0149 void BookmarksToolbar::showOnlyTextChanged(bool state)
0150 {
0151     if (state && m_actShowOnlyIcons) {
0152         m_actShowOnlyIcons->setChecked(false);
0153     }
0154 
0155     for (int i = 0; i < m_layout->count(); ++i) {
0156         auto* b = qobject_cast<BookmarksToolbarButton*>(m_layout->itemAt(i)->widget());
0157         if (b) {
0158             b->setShowOnlyText(state);
0159         }
0160     }
0161 }
0162 
0163 void BookmarksToolbar::openBookmarkInNewTab()
0164 {
0165     if (m_clickedBookmark) {
0166         BookmarksTools::openBookmarkInNewTab(m_window, m_clickedBookmark);
0167     }
0168 }
0169 
0170 void BookmarksToolbar::openBookmarkInNewWindow()
0171 {
0172     if (m_clickedBookmark) {
0173         BookmarksTools::openBookmarkInNewWindow(m_clickedBookmark);
0174     }
0175 }
0176 
0177 void BookmarksToolbar::openBookmarkInNewPrivateWindow()
0178 {
0179     if (m_clickedBookmark) {
0180         BookmarksTools::openBookmarkInNewPrivateWindow(m_clickedBookmark);
0181     }
0182 }
0183 
0184 void BookmarksToolbar::createNewFolder() 
0185 {
0186     QString name = QInputDialog::getText(nullptr, tr("New Folder"), tr("Enter Folder Name:"));
0187     
0188     if (!name.isEmpty()) {
0189         BookmarkItem* parent;
0190         
0191         if (m_clickedBookmark && m_clickedBookmark->isFolder()) {
0192             parent = m_clickedBookmark;
0193         } else {
0194             parent = m_bookmarks->toolbarFolder();
0195         }
0196         
0197         auto folder = new BookmarkItem(BookmarkItem::Folder);
0198         folder->setTitle(name);
0199         m_bookmarks->addBookmark(parent, folder);
0200     }
0201 }
0202 
0203 void BookmarksToolbar::editBookmark()
0204 {
0205     if (m_clickedBookmark) {
0206         BookmarksTools::editBookmarkDialog(this, m_clickedBookmark);
0207         m_bookmarks->changeBookmark(m_clickedBookmark);
0208     }
0209 }
0210 
0211 void BookmarksToolbar::deleteBookmark()
0212 {
0213     if (m_clickedBookmark) {
0214         m_bookmarks->removeBookmark(m_clickedBookmark);
0215     }
0216 }
0217 
0218 void BookmarksToolbar::clear()
0219 {
0220     int count = m_layout->count();
0221 
0222     for (int i = 0; i < count; ++i) {
0223         QLayoutItem* item = m_layout->takeAt(0);
0224         delete item->widget();
0225         delete item;
0226     }
0227 
0228     Q_ASSERT(m_layout->isEmpty());
0229 }
0230 
0231 void BookmarksToolbar::addItem(BookmarkItem* item)
0232 {
0233     Q_ASSERT(item);
0234 
0235     auto* button = new BookmarksToolbarButton(item, this);
0236     button->setMainWindow(m_window);
0237     button->setShowOnlyIcon(m_bookmarks->showOnlyIconsInToolbar());
0238     button->setShowOnlyText(m_bookmarks->showOnlyTextInToolbar());
0239     m_layout->addWidget(button);
0240 }
0241 
0242 BookmarksToolbarButton* BookmarksToolbar::buttonAt(const QPoint &pos)
0243 {
0244     return qobject_cast<BookmarksToolbarButton*>(QApplication::widgetAt(mapToGlobal(pos)));
0245 }
0246 
0247 QSize BookmarksToolbar::minimumSizeHint() const
0248 {
0249     QSize size = QWidget::minimumSizeHint();
0250     size.setHeight(qMax(20, size.height()));
0251     return size;
0252 }
0253 
0254 void BookmarksToolbar::dropEvent(QDropEvent* e)
0255 {
0256     int row = m_dropRow;
0257     clearDropIndicator();
0258 
0259     const QMimeData* mime = e->mimeData();
0260 
0261     if (!mime->hasUrls() && !mime->hasFormat(BookmarksButtonMimeData::mimeType())) {
0262         QWidget::dropEvent(e);
0263         return;
0264     }
0265 
0266     BookmarkItem* parent = m_bookmarks->toolbarFolder();
0267     BookmarkItem* bookmark = nullptr;
0268 
0269     if (mime->hasFormat(BookmarksButtonMimeData::mimeType())) {
0270         const auto* bookmarkMime = static_cast<const BookmarksButtonMimeData*>(mime);
0271         bookmark = bookmarkMime->item();
0272         const int initialIndex = bookmark->parent()->children().indexOf(bookmark);
0273         BookmarksToolbarButton* current = buttonAt(m_dropPos);
0274         if (initialIndex < m_layout->indexOf(current)) {
0275             row -= 1;
0276         }
0277     } else {
0278         const QUrl url = mime->urls().at(0);
0279         const QString title = mime->hasText() ? mime->text() : QString::fromUtf8(url.toEncoded(QUrl::RemoveScheme));
0280 
0281         bookmark = new BookmarkItem(BookmarkItem::Url);
0282         bookmark->setTitle(title);
0283         bookmark->setUrl(url);
0284     }
0285 
0286     if (row >= 0) {
0287         m_bookmarks->insertBookmark(parent, row, bookmark);
0288     } else {
0289         m_bookmarks->addBookmark(parent, bookmark);
0290     }
0291 }
0292 
0293 void BookmarksToolbar::dragEnterEvent(QDragEnterEvent* e)
0294 {
0295     const QMimeData* mime = e->mimeData();
0296 
0297     if ((mime->hasUrls() && mime->hasText()) || mime->hasFormat(BookmarksButtonMimeData::mimeType())) {
0298         e->acceptProposedAction();
0299         return;
0300     }
0301 
0302     QWidget::dragEnterEvent(e);
0303 }
0304 
0305 void BookmarksToolbar::dragMoveEvent(QDragMoveEvent *e)
0306 {
0307     int eventX = e->position().toPoint().x();
0308     BookmarksToolbarButton* button = buttonAt(e->position().toPoint());
0309     m_dropPos = e->position().toPoint();
0310     m_dropRow = m_layout->indexOf(button);
0311     if (button) {
0312         bool res = eventX - button->x() < button->x() + button->width() -eventX;
0313         m_dropRow = res ? m_dropRow : m_dropRow + 1;
0314     } else {
0315         m_dropRow = -1;
0316     }
0317 
0318     update();
0319 }
0320 
0321 void BookmarksToolbar::dragLeaveEvent(QDragLeaveEvent *e)
0322 {
0323     Q_UNUSED(e);
0324     clearDropIndicator();
0325 }
0326 
0327 void BookmarksToolbar::clearDropIndicator()
0328 {
0329     m_dropRow = -1;
0330     update();
0331 }
0332 
0333 void BookmarksToolbar::paintEvent(QPaintEvent *p)
0334 {
0335     QWidget::paintEvent(p);
0336 
0337     // Draw drop indicator
0338     if (m_dropRow != -1) {
0339         BookmarksToolbarButton* button = buttonAt(m_dropPos);
0340         if (button) {
0341             if (button->bookmark()->isFolder()) {
0342                 return;
0343             }
0344             const QRect tr = QRect(button->x(), 0, button->width(), height());
0345             QRect r;
0346 
0347             if (m_dropRow == m_layout->indexOf(button)) {
0348                 r = QRect(qMax(0, tr.left() - 2), tr.top(), 3, tr.height());
0349             } else {
0350                 r = QRect(tr.right() + 0, tr.top(), 3, tr.height());
0351             }
0352 
0353             QzTools::paintDropIndicator(this, r);
0354         }
0355     }
0356 }