File indexing completed on 2024-04-28 08:50:37

0001 //  -*- c-basic-offset:4; indent-tabs-mode:nil -*-
0002 /* This file is part of the KDE project
0003    SPDX-FileCopyrightText: 1999 Kurt Granroth <granroth@kde.org>
0004    SPDX-FileCopyrightText: 1998, 1999 Torben Weis <weis@kde.org>
0005 
0006    SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "konqbookmarkbar.h"
0010 
0011 #include <QApplication>
0012 #include <QDropEvent>
0013 #include <QEvent>
0014 #include <QMenu>
0015 
0016 #include <kwidgetsaddons_version.h>
0017 #include <ktoolbar.h>
0018 #include <kactionmenu.h>
0019 #include <kconfig.h>
0020 #include "konqdebug.h"
0021 #include <kconfiggroup.h>
0022 #include <kio/global.h>
0023 #include <kbookmarkmanager.h>
0024 
0025 #include "konqbookmarkmenu.h"
0026 
0027 #if QT_VERSION_MAJOR < 6
0028 #include "kbookmarkimporter.h"
0029 #include "kbookmarkdombuilder.h"
0030 #endif
0031 
0032 #include "kbookmarkaction.h"
0033 #include "konqpixmapprovider.h"
0034 
0035 class KBookmarkBarPrivate
0036 {
0037 public:
0038     QList<QAction *> m_actions;
0039     int m_sepIndex;
0040     QList<int> widgetPositions; //right edge, bottom edge
0041     QString tempLabel;
0042     bool m_filteredToolbar;
0043     bool m_contextMenu;
0044 
0045     KBookmarkBarPrivate() :
0046         m_sepIndex(-1)
0047     {
0048         // see KBookmarkSettings::readSettings in kio
0049         KConfig config(QStringLiteral("kbookmarkrc"), KConfig::NoGlobals);
0050         KConfigGroup cg(&config, "Bookmarks");
0051         m_filteredToolbar = cg.readEntry("FilteredToolbar", false);
0052         m_contextMenu = cg.readEntry("ContextMenuActions", true);
0053     }
0054 };
0055 
0056 KBookmarkBar::KBookmarkBar(KBookmarkManager *mgr,
0057                            KBookmarkOwner *_owner, KToolBar *_toolBar,
0058                            QObject *parent)
0059     : QObject(parent), m_pOwner(_owner), m_toolBar(_toolBar),
0060       m_pManager(mgr), d(new KBookmarkBarPrivate)
0061 {
0062     m_toolBar->setAcceptDrops(true);
0063     m_toolBar->installEventFilter(this);   // for drops
0064 
0065     if (d->m_contextMenu) {
0066         m_toolBar->setContextMenuPolicy(Qt::CustomContextMenu);
0067         connect(m_toolBar, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenu(QPoint)));
0068     }
0069 
0070 #if QT_VERSION_MAJOR < 6
0071     connect(mgr, SIGNAL(changed(QString,QString)),
0072             SLOT(slotBookmarksChanged(QString)));
0073     connect(mgr, SIGNAL(configChanged()),
0074             SLOT(slotConfigChanged()));
0075 #else
0076     connect(mgr, SIGNAL(changed(QString)),
0077             SLOT(slotBookmarksChanged(QString)));
0078 #endif
0079 
0080 
0081     KBookmarkGroup toolbar = getToolbar();
0082     fillBookmarkBar(toolbar);
0083     m_toolBarSeparator = new QAction(this);
0084 }
0085 
0086 QString KBookmarkBar::parentAddress()
0087 {
0088     if (d->m_filteredToolbar) {
0089         return QLatin1String("");
0090     } else {
0091         return m_pManager->toolbar().address();
0092     }
0093 }
0094 
0095 KBookmarkGroup KBookmarkBar::getToolbar()
0096 {
0097     if (d->m_filteredToolbar) {
0098         return m_pManager->root();
0099     } else {
0100         return m_pManager->toolbar();
0101     }
0102 }
0103 
0104 KBookmarkBar::~KBookmarkBar()
0105 {
0106     //clear();
0107     qDeleteAll(d->m_actions);
0108     qDeleteAll(m_lstSubMenus);
0109     delete d;
0110 }
0111 
0112 void KBookmarkBar::clear()
0113 {
0114     if (m_toolBar) {
0115         m_toolBar->clear();
0116     }
0117     qDeleteAll(d->m_actions);
0118     d->m_actions.clear();
0119     qDeleteAll(m_lstSubMenus);
0120     m_lstSubMenus.clear();
0121 }
0122 
0123 void KBookmarkBar::slotBookmarksChanged(const QString &group)
0124 {
0125     KBookmarkGroup tb = getToolbar(); // heavy for non cached toolbar version
0126     qCDebug(KONQUEROR_LOG) << "KBookmarkBar::slotBookmarksChanged( " << group << " )";
0127 
0128     if (tb.isNull()) {
0129         return;
0130     }
0131 
0132     if (d->m_filteredToolbar) {
0133         clear();
0134         fillBookmarkBar(tb);
0135     } else if (KBookmark::commonParent(group, tb.address()) == group) { // Is group a parent of tb.address?
0136         clear();
0137         fillBookmarkBar(tb);
0138     } else {
0139         // Iterate recursively into child menus
0140         for (QList<KBookmarkMenu *>::ConstIterator smit = m_lstSubMenus.constBegin(), smend = m_lstSubMenus.constEnd();
0141                 smit != smend; ++smit) {
0142             (*smit)->slotBookmarksChanged(group);
0143         }
0144     }
0145 }
0146 
0147 void KBookmarkBar::slotConfigChanged()
0148 {
0149     KConfig config(QStringLiteral("kbookmarkrc"), KConfig::NoGlobals);
0150     KConfigGroup cg(&config, "Bookmarks");
0151     d->m_filteredToolbar = cg.readEntry("FilteredToolbar", false);
0152     d->m_contextMenu = cg.readEntry("ContextMenuActions", true);
0153     clear();
0154     fillBookmarkBar(getToolbar());
0155 }
0156 
0157 void KBookmarkBar::fillBookmarkBar(const KBookmarkGroup &parent)
0158 {
0159     if (parent.isNull()) {
0160         return;
0161     }
0162 
0163     for (KBookmark bm = parent.first(); !bm.isNull(); bm = parent.next(bm)) {
0164         // Filtered special cases
0165         if (d->m_filteredToolbar) {
0166             if (bm.isGroup() && !bm.showInToolbar()) {
0167                 fillBookmarkBar(bm.toGroup());
0168             }
0169 
0170             if (!bm.showInToolbar()) {
0171                 continue;
0172             }
0173         }
0174 
0175         if (!bm.isGroup()) {
0176             if (bm.isSeparator()) {
0177                 if (m_toolBar) {
0178                     m_toolBar->addSeparator();
0179                 }
0180             } else {
0181                 auto host = bm.url().adjusted(QUrl::RemovePath | QUrl::RemoveQuery);
0182                 bm.setIcon(KonqPixmapProvider::self()->iconNameFor(host));
0183                 QAction *action = new KBookmarkAction(bm, m_pOwner, nullptr);
0184                 if (m_toolBar) {
0185                     m_toolBar->addAction(action);
0186                 }
0187                 d->m_actions.append(action);
0188                     connect(KonqPixmapProvider::self(), &KonqPixmapProvider::changed, action, [host, action]() {
0189                         action->setIcon(KonqPixmapProvider::self()->iconForUrl(host));
0190                     });
0191                 KonqPixmapProvider::self()->downloadHostIcon(host);
0192             }
0193         } else {
0194             KBookmarkActionMenu *action = new KBookmarkActionMenu(bm, nullptr);
0195             action->setPopupMode(QToolButton::InstantPopup);
0196             if (m_toolBar) {
0197                 m_toolBar->addAction(action);
0198             }
0199             d->m_actions.append(action);
0200             KBookmarkMenu *menu = new Konqueror::KonqBookmarkMenu(m_pManager, m_pOwner, action, bm.address(), m_actionCollection);
0201             m_lstSubMenus.append(menu);
0202         }
0203     }
0204 }
0205 
0206 void KBookmarkBar::removeTempSep()
0207 {
0208     if (m_toolBarSeparator) {
0209         m_toolBar->removeAction(m_toolBarSeparator);
0210     }
0211 
0212 }
0213 
0214 /**
0215  * Handle a QDragMoveEvent event on a toolbar drop
0216  * @return true if the event should be accepted, false if the event should be ignored
0217  * @param pos the current QDragMoveEvent position
0218  * @param the toolbar
0219  * @param actions the list of actions plugged into the bar
0220  *        returned action was dropped on
0221  */
0222 bool KBookmarkBar::handleToolbarDragMoveEvent(const QPoint &p, const QList<QAction *> &actions, const QString &text)
0223 {
0224     if (d->m_filteredToolbar) {
0225         return false;
0226     }
0227     int pos = m_toolBar->orientation() == Qt::Horizontal ? p.x() : p.y();
0228     Q_ASSERT(actions.isEmpty() || (m_toolBar == qobject_cast<KToolBar *>(actions.first()->associatedWidgets().value(0))));
0229     m_toolBar->setUpdatesEnabled(false);
0230     removeTempSep();
0231 
0232     bool foundWidget = false;
0233     // Right To Left
0234     // only relevant if the toolbar is Horizontal!
0235     bool rtl = QApplication::isRightToLeft() && m_toolBar->orientation() == Qt::Horizontal;
0236     m_toolBarSeparator->setText(text);
0237 
0238     // Empty toolbar
0239     if (actions.isEmpty()) {
0240         d->m_sepIndex = 0;
0241         m_toolBar->addAction(m_toolBarSeparator);
0242         m_toolBar->setUpdatesEnabled(true);
0243         return true;
0244     }
0245 
0246     // else find the toolbar button
0247     for (int i = 0; i < d->widgetPositions.count(); ++i) {
0248         if (rtl ^ (pos <= d->widgetPositions[i])) {
0249             foundWidget = true;
0250             d->m_sepIndex = i;
0251             break;
0252         }
0253     }
0254 
0255     QString address;
0256 
0257     if (foundWidget) { // found the containing button
0258         int leftOrTop = d->m_sepIndex == 0 ? 0 : d->widgetPositions[d->m_sepIndex - 1];
0259         int rightOrBottom = d->widgetPositions[d->m_sepIndex];
0260         if (rtl ^ (pos >= (leftOrTop + rightOrBottom) / 2)) {
0261             // if in second half of button then
0262             // we jump to next index
0263             d->m_sepIndex++;
0264         }
0265         if (d->m_sepIndex != actions.count()) {
0266             QAction *before = m_toolBar->actions()[d->m_sepIndex];
0267             m_toolBar->insertAction(before, m_toolBarSeparator);
0268         } else {
0269             m_toolBar->addAction(m_toolBarSeparator);
0270         }
0271         m_toolBar->setUpdatesEnabled(true);
0272         return true;
0273     } else { // (!foundWidget)
0274         // if !b and not past last button, we didn't find button
0275         if (rtl ^ (pos <= d->widgetPositions[d->widgetPositions.count() - 1])) {
0276             m_toolBar->setUpdatesEnabled(true);
0277             return false;
0278         } else { // location is beyond last action, assuming we want to add in the end
0279             d->m_sepIndex = actions.count();
0280             m_toolBar->addAction(m_toolBarSeparator);
0281             m_toolBar->setUpdatesEnabled(true);
0282             return true;
0283         }
0284     }
0285 }
0286 
0287 void KBookmarkBar::contextMenu(const QPoint &pos)
0288 {
0289     KBookmarkActionInterface *action = dynamic_cast<KBookmarkActionInterface *>(m_toolBar->actionAt(pos));
0290     if (!action) {
0291         //Show default (ktoolbar) menu
0292         m_toolBar->setContextMenuPolicy(Qt::DefaultContextMenu);
0293         //Recreate event with the same position
0294         QContextMenuEvent evt(QContextMenuEvent::Other, pos);
0295         QCoreApplication::sendEvent(m_toolBar, &evt);
0296         //Reassign custom context menu
0297         m_toolBar->setContextMenuPolicy(Qt::CustomContextMenu);
0298     } else {
0299         QMenu *menu = new Konqueror::KonqBookmarkContextMenu(action->bookmark(), m_pManager, m_pOwner);
0300         menu->setAttribute(Qt::WA_DeleteOnClose);
0301         menu->popup(m_toolBar->mapToGlobal(pos));
0302     }
0303 }
0304 
0305 // TODO    *** drop improvements ***
0306 // open submenus on drop interactions
0307 bool KBookmarkBar::eventFilter(QObject *, QEvent *e)
0308 {
0309     if (d->m_filteredToolbar) {
0310         return false;    // todo: make this limit the actions
0311     }
0312 
0313     if (e->type() == QEvent::DragLeave) {
0314         removeTempSep();
0315     } else if (e->type() == QEvent::Drop) {
0316         removeTempSep();
0317 
0318         QDropEvent *dev = static_cast<QDropEvent *>(e);
0319         QDomDocument doc;
0320         QList<KBookmark> list = KBookmark::List::fromMimeData(dev->mimeData(), doc);
0321         if (list.isEmpty()) {
0322             return false;
0323         }
0324         if (list.count() > 1)
0325             qCWarning(KONQUEROR_LOG) << "Sorry, currently you can only drop one address "
0326                            "onto the bookmark bar!";
0327         KBookmark toInsert = list.first();
0328 
0329         KBookmarkGroup parentBookmark = getToolbar();
0330 
0331         if (d->m_sepIndex == 0) {
0332             KBookmark newBookmark = parentBookmark.addBookmark(toInsert.fullText(), toInsert.url(), KIO::iconNameForUrl(toInsert.url()));
0333 
0334             parentBookmark.moveBookmark(newBookmark, KBookmark());
0335             m_pManager->emitChanged(parentBookmark);
0336             return true;
0337         } else {
0338             KBookmark after = parentBookmark.first();
0339 
0340             for (int i = 0; i < d->m_sepIndex - 1; ++i) {
0341                 after = parentBookmark.next(after);
0342             }
0343             KBookmark newBookmark = parentBookmark.addBookmark(toInsert.fullText(), toInsert.url(), KIO::iconNameForUrl(toInsert.url()));
0344 
0345             parentBookmark.moveBookmark(newBookmark, after);
0346             m_pManager->emitChanged(parentBookmark);
0347             return true;
0348         }
0349     } else if (e->type() == QEvent::DragMove || e->type() == QEvent::DragEnter) {
0350         QDragMoveEvent *dme = static_cast<QDragMoveEvent *>(e);
0351         if (!KBookmark::List::canDecode(dme->mimeData())) {
0352             return false;
0353         }
0354 
0355         //cache text, save positions (inserting the temporary widget changes the positions)
0356         if (e->type() == QEvent::DragEnter) {
0357             QDomDocument doc;
0358             const QList<KBookmark> list = KBookmark::List::fromMimeData(dme->mimeData(), doc);
0359             if (list.isEmpty()) {
0360                 return false;
0361             }
0362             d->tempLabel  = list.first().url().toDisplayString(QUrl::PreferLocalFile);
0363 
0364             d->widgetPositions.clear();
0365 
0366             for (int i = 0; i < m_toolBar->actions().count(); ++i)
0367                 if (QWidget *button = m_toolBar->widgetForAction(m_toolBar->actions()[i])) {
0368                     if (m_toolBar->orientation() == Qt::Horizontal) {
0369                         if (QApplication::isLeftToRight()) {
0370                             d->widgetPositions.push_back(button->geometry().right());
0371                         } else {
0372                             d->widgetPositions.push_back(button->geometry().left());
0373                         }
0374                     } else {
0375                         d->widgetPositions.push_back(button->geometry().bottom());
0376                     }
0377                 }
0378         }
0379 
0380         bool accept = handleToolbarDragMoveEvent(dme->pos(), d->m_actions, d->tempLabel);
0381         if (accept) {
0382             dme->accept();
0383             return true; //Really?
0384         }
0385     }
0386     return false;
0387 }
0388