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

0001 /* ============================================================
0002 * Falkon - Qt web browser
0003 * Copyright (C) 2014-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 "bookmarkstoolbarbutton.h"
0019 #include "bookmarkstools.h"
0020 #include "bookmarksmodel.h"
0021 #include "bookmarkitem.h"
0022 #include "bookmarks.h"
0023 #include "mainapplication.h"
0024 #include "enhancedmenu.h"
0025 
0026 #include <QStyle>
0027 #include <QPainter>
0028 #include <QMouseEvent>
0029 #include <QStyleOptionButton>
0030 #include <QDrag>
0031 #include <QMimeData>
0032 #include <QtGuiVersion>
0033 
0034 #define MAX_WIDTH 150
0035 #define SEPARATOR_WIDTH 8
0036 #define PADDING 5
0037 
0038 BookmarksToolbarButton::BookmarksToolbarButton(BookmarkItem* bookmark, QWidget* parent)
0039     : QPushButton(parent)
0040     , m_bookmark(bookmark)
0041     , m_window(nullptr)
0042     , m_showOnlyIcon(false)
0043 {
0044     init();
0045 
0046     if (m_bookmark->isFolder()) {
0047         setAcceptDrops(true);
0048     }
0049 }
0050 
0051 BookmarkItem* BookmarksToolbarButton::bookmark() const
0052 {
0053     return m_bookmark;
0054 }
0055 
0056 void BookmarksToolbarButton::setMainWindow(BrowserWindow* window)
0057 {
0058     m_window = window;
0059 }
0060 
0061 bool BookmarksToolbarButton::showOnlyIcon() const
0062 {
0063     return m_showOnlyIcon;
0064 }
0065 
0066 void BookmarksToolbarButton::setShowOnlyIcon(bool show)
0067 {
0068     m_showOnlyIcon = show;
0069     updateGeometry();
0070     update();
0071 }
0072 
0073 bool BookmarksToolbarButton::showOnlyText() const
0074 {
0075     return m_showOnlyText;
0076 }
0077 
0078 void BookmarksToolbarButton::setShowOnlyText(bool show)
0079 {
0080     m_showOnlyText = show;
0081     updateGeometry();
0082     update();
0083 }
0084 
0085 QSize BookmarksToolbarButton::sizeHint() const
0086 {
0087     int width = PADDING * 2;
0088     if (!m_showOnlyText) {
0089         width += 16;
0090     }
0091 
0092     if (m_bookmark->isSeparator()) {
0093         width = SEPARATOR_WIDTH;
0094     }
0095     else if (!m_showOnlyIcon) {
0096         width += PADDING * 2 + fontMetrics().horizontalAdvance(m_bookmark->title());
0097 
0098         if (menu()) {
0099             width += PADDING + 8;
0100         }
0101     }
0102 
0103     QSize s = QPushButton::sizeHint();
0104     s.setWidth(qMin(width, MAX_WIDTH));
0105     return s;
0106 }
0107 
0108 QSize BookmarksToolbarButton::minimumSizeHint() const
0109 {
0110     int width = PADDING * 2;
0111     if (!m_showOnlyText) {
0112         width += 16;
0113     }
0114 
0115     if (m_bookmark->isSeparator()) {
0116         width = SEPARATOR_WIDTH;
0117     }
0118     else if (!m_showOnlyIcon && menu()) {
0119         width += PADDING + 8;
0120     }
0121 
0122     QSize s = QPushButton::minimumSizeHint();
0123     s.setWidth(width);
0124     return s;
0125 }
0126 
0127 void BookmarksToolbarButton::createMenu()
0128 {
0129     if (!menu()->isEmpty()) {
0130         return;
0131     }
0132 
0133     Menu* m = qobject_cast<Menu*>(menu());
0134     Q_ASSERT(m);
0135 
0136     BookmarksTools::addFolderContentsToMenu(this, m, m_bookmark);
0137 }
0138 
0139 void BookmarksToolbarButton::menuAboutToShow()
0140 {
0141     Q_ASSERT(qobject_cast<Menu*>(sender()));
0142     Menu *menu = static_cast<Menu*>(sender());
0143 
0144     const auto menuActions = menu->actions();
0145     for (QAction *action : menuActions) {
0146         BookmarkItem *item = static_cast<BookmarkItem*>(action->data().value<void*>());
0147         if (item && item->type() == BookmarkItem::Url && action->icon().isNull()) {
0148             action->setIcon(item->icon());
0149         }
0150     }
0151 }
0152 
0153 void BookmarksToolbarButton::menuMiddleClicked(Menu* menu)
0154 {
0155     BookmarkItem* item = static_cast<BookmarkItem*>(menu->menuAction()->data().value<void*>());
0156     Q_ASSERT(item);
0157     openFolder(item);
0158 }
0159 
0160 void BookmarksToolbarButton::bookmarkActivated(BookmarkItem* item)
0161 {
0162     if (auto* action = qobject_cast<QAction*>(sender())) {
0163         item = static_cast<BookmarkItem*>(action->data().value<void*>());
0164     }
0165 
0166     Q_ASSERT(item);
0167     openBookmark(item);
0168 }
0169 
0170 void BookmarksToolbarButton::bookmarkCtrlActivated(BookmarkItem* item)
0171 {
0172     if (auto* action = qobject_cast<QAction*>(sender())) {
0173         item = static_cast<BookmarkItem*>(action->data().value<void*>());
0174     }
0175 
0176     Q_ASSERT(item);
0177     openBookmarkInNewTab(item);
0178 }
0179 
0180 void BookmarksToolbarButton::bookmarkShiftActivated(BookmarkItem* item)
0181 {
0182     if (auto* action = qobject_cast<QAction*>(sender())) {
0183         item = static_cast<BookmarkItem*>(action->data().value<void*>());
0184     }
0185 
0186     Q_ASSERT(item);
0187     openBookmarkInNewWindow(item);
0188 }
0189 
0190 void BookmarksToolbarButton::openFolder(BookmarkItem* item)
0191 {
0192     Q_ASSERT(item->isFolder());
0193 
0194     if (m_window) {
0195         BookmarksTools::openFolderInTabs(m_window, item);
0196     }
0197 }
0198 
0199 void BookmarksToolbarButton::openBookmark(BookmarkItem* item)
0200 {
0201     Q_ASSERT(item->isUrl());
0202 
0203     if (m_window) {
0204         BookmarksTools::openBookmark(m_window, item);
0205     }
0206 }
0207 
0208 void BookmarksToolbarButton::openBookmarkInNewTab(BookmarkItem* item)
0209 {
0210     Q_ASSERT(item->isUrl());
0211 
0212     if (m_window) {
0213         BookmarksTools::openBookmarkInNewTab(m_window, item);
0214     }
0215 }
0216 
0217 void BookmarksToolbarButton::openBookmarkInNewWindow(BookmarkItem* item)
0218 {
0219     Q_ASSERT(item->isUrl());
0220 
0221     BookmarksTools::openBookmarkInNewWindow(item);
0222 }
0223 
0224 void BookmarksToolbarButton::init()
0225 {
0226     Q_ASSERT(m_bookmark);
0227 
0228     setFocusPolicy(Qt::NoFocus);
0229     setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
0230     setToolTip(createTooltip());
0231 
0232     if (m_bookmark->isFolder()) {
0233         Menu* m = new Menu(this);
0234         setMenu(m);
0235         createMenu();
0236     }
0237 }
0238 
0239 QString BookmarksToolbarButton::createTooltip() const
0240 {
0241     if (!m_bookmark->description().isEmpty()) {
0242         if (!m_bookmark->urlString().isEmpty()) {
0243             return QSL("%1\n%2").arg(m_bookmark->description(), m_bookmark->urlString());
0244         }
0245         return m_bookmark->description();
0246     }
0247 
0248     if (!m_bookmark->title().isEmpty() && !m_bookmark->url().isEmpty()) {
0249         return QSL("%1\n%2").arg(m_bookmark->title(), m_bookmark->urlString());
0250     }
0251 
0252     if (!m_bookmark->title().isEmpty()) {
0253         return m_bookmark->title();
0254     }
0255 
0256     return m_bookmark->urlString();
0257 }
0258 
0259 void BookmarksToolbarButton::enterEvent(QEnterEvent* event)
0260 {
0261     QPushButton::enterEvent(event);
0262 
0263     update();
0264 }
0265 
0266 void BookmarksToolbarButton::leaveEvent(QEvent* event)
0267 {
0268     QPushButton::leaveEvent(event);
0269 
0270     update();
0271 }
0272 
0273 void BookmarksToolbarButton::mousePressEvent(QMouseEvent* event)
0274 {
0275     if (m_bookmark && m_bookmark->isFolder()) {
0276         if (event->buttons() == Qt::LeftButton && event->modifiers() == Qt::ControlModifier) {
0277             openFolder(m_bookmark);
0278             return;
0279         }
0280     }
0281 
0282     m_dragStartPosition = event->position().toPoint();
0283 
0284     QPushButton::mousePressEvent(event);
0285 }
0286 
0287 void BookmarksToolbarButton::mouseReleaseEvent(QMouseEvent* event)
0288 {
0289     if (m_bookmark && rect().contains(event->position().toPoint())) {
0290         Qt::MouseButton button = event->button();
0291         Qt::KeyboardModifiers modifiers = event->modifiers();
0292 
0293         if (m_bookmark->isUrl()) {
0294             if (button == Qt::LeftButton && modifiers == Qt::NoModifier) {
0295                 bookmarkActivated(m_bookmark);
0296             }
0297             else if (button == Qt::LeftButton && modifiers == Qt::ShiftModifier) {
0298                 bookmarkShiftActivated(m_bookmark);
0299             }
0300             else if (button == Qt::MiddleButton || modifiers == Qt::ControlModifier) {
0301                 bookmarkCtrlActivated(m_bookmark);
0302             }
0303         }
0304         else if (m_bookmark->isFolder() && button == Qt::MiddleButton) {
0305             openFolder(m_bookmark);
0306         }
0307     }
0308 
0309     QPushButton::mouseReleaseEvent(event);
0310 }
0311 
0312 void BookmarksToolbarButton::mouseMoveEvent(QMouseEvent *event)
0313 {
0314     if ((event->position().toPoint() - m_dragStartPosition).manhattanLength() < QApplication::startDragDistance()) {
0315         QPushButton::mouseMoveEvent(event);
0316         return;
0317     }
0318 
0319     setDown(false);
0320 
0321     auto *drag = new QDrag(this);
0322     auto* mime = new BookmarksButtonMimeData;
0323     mime->setBookmarkItem(m_bookmark);
0324     drag->setMimeData(mime);
0325     drag->setPixmap(grab());
0326     drag->exec();
0327 }
0328 
0329 void BookmarksToolbarButton::paintEvent(QPaintEvent* event)
0330 {
0331     Q_UNUSED(event)
0332 
0333     QPainter p(this);
0334 
0335     // Just draw separator
0336     if (m_bookmark->isSeparator()) {
0337         QStyleOption opt;
0338         opt.initFrom(this);
0339         opt.state |= QStyle::State_Horizontal;
0340         style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &opt, &p);
0341         return;
0342     }
0343 
0344     QStyleOptionButton option;
0345     initStyleOption(&option);
0346 
0347     // We are manually drawing the arrow
0348     option.features &= ~QStyleOptionButton::HasMenu;
0349 
0350     // Draw button base (only under mouse, this is autoraise button)
0351     if (isDown() || hitButton(mapFromGlobal(QCursor::pos()))) {
0352         option.state |= QStyle::State_AutoRaise | QStyle::State_Raised;
0353         style()->drawPrimitive(QStyle::PE_PanelButtonTool, &option, &p, this);
0354     }
0355 
0356     const int shiftX = isDown() ? style()->pixelMetric(QStyle::PM_ButtonShiftHorizontal, &option, this) : 0;
0357     const int shiftY = isDown() ? style()->pixelMetric(QStyle::PM_ButtonShiftVertical, &option, this) : 0;
0358 
0359     const int height = option.rect.height();
0360     const int center = height / 2 + option.rect.top() + shiftY;
0361 
0362     const int iconSize = 16;
0363     const int iconYPos = center - iconSize / 2;
0364 
0365     int leftPosition = PADDING + shiftX;
0366     int rightPosition = option.rect.right() - PADDING;
0367 
0368     // Draw icon
0369     if (!m_showOnlyText) {
0370         QRect iconRect(leftPosition, iconYPos, iconSize, iconSize);
0371         p.drawPixmap(QStyle::visualRect(option.direction, option.rect, iconRect), m_bookmark->icon().pixmap(iconSize));
0372         leftPosition = iconRect.right() + PADDING;
0373     }
0374 
0375     // Draw menu arrow
0376     if (!m_showOnlyIcon && menu()) {
0377         const int arrowSize = 8;
0378         QStyleOption opt;
0379         opt.initFrom(this);
0380         const QRect rect = QRect(rightPosition - 8, center - arrowSize / 2, arrowSize, arrowSize);
0381         opt.rect = QStyle::visualRect(option.direction, option.rect, rect);
0382         opt.state &= ~QStyle::State_MouseOver;
0383         style()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &opt, &p, this);
0384         rightPosition = rect.left() - PADDING;
0385     }
0386 
0387     // Draw text
0388     if (!m_showOnlyIcon) {
0389         const int textWidth = rightPosition - leftPosition;
0390         const int textYPos = center - fontMetrics().height() / 2;
0391         const QString txt = fontMetrics().elidedText(m_bookmark->title(), Qt::ElideRight, textWidth);
0392         QRect textRect(leftPosition, textYPos, textWidth, fontMetrics().height());
0393         style()->drawItemText(&p, QStyle::visualRect(option.direction, option.rect, textRect),
0394                               Qt::TextSingleLine | Qt::AlignCenter, option.palette, true, txt);
0395     }
0396 }
0397 
0398 void BookmarksToolbarButton::dragEnterEvent(QDragEnterEvent *event)
0399 {
0400     const QMimeData* mime = event->mimeData();
0401     if ((mime->hasUrls() && mime->hasText()) || mime->hasFormat(BookmarksButtonMimeData::mimeType())) {
0402         event->acceptProposedAction();
0403         setDown(true);
0404         return;
0405     }
0406 
0407     QPushButton::dragEnterEvent(event);
0408 }
0409 
0410 void BookmarksToolbarButton::dragLeaveEvent(QDragLeaveEvent *event)
0411 {
0412     Q_UNUSED(event);
0413     setDown(false);
0414 }
0415 
0416 void BookmarksToolbarButton::dropEvent(QDropEvent *event)
0417 {
0418     setDown(false);
0419 
0420     const QMimeData* mime = event->mimeData();
0421     if (!mime->hasUrls() && !mime->hasFormat(BookmarksButtonMimeData::mimeType())) {
0422         QPushButton::dropEvent(event);
0423         return;
0424     }
0425 
0426     BookmarkItem* bookmark = nullptr;
0427 
0428     if (mime->hasFormat(BookmarksButtonMimeData::mimeType())) {
0429         const auto* bookmarkMime = static_cast<const BookmarksButtonMimeData*>(mime);
0430         bookmark = bookmarkMime->item();
0431 
0432         if (m_bookmark == bookmark) {
0433             return;
0434         }
0435     } else {
0436         const QUrl url = mime->urls().at(0);
0437         const QString title = mime->hasText() ? mime->text() : QString::fromUtf8(url.toEncoded(QUrl::RemoveScheme));
0438 
0439         bookmark = new BookmarkItem(BookmarkItem::Url);
0440         bookmark->setTitle(title);
0441         bookmark->setUrl(url);
0442     }
0443 
0444     mApp->bookmarks()->addBookmark(m_bookmark, bookmark);
0445 }