File indexing completed on 2024-03-24 05:01:11
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