File indexing completed on 2023-12-03 10:57:30
0001 /* 0002 SPDX-FileCopyrightText: 2006 Peter Penz <peter.penz@gmx.at> 0003 SPDX-FileCopyrightText: 2007 Kevin Ottens <ervin@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "kurlnavigatorplacesselector_p.h" 0009 0010 #include <KProtocolInfo> 0011 #include <KUrlMimeData> 0012 #include <kfileplacesmodel.h> 0013 0014 #include <QDragEnterEvent> 0015 #include <QDragLeaveEvent> 0016 #include <QDropEvent> 0017 #include <QMenu> 0018 #include <QMimeData> 0019 #include <QMimeDatabase> 0020 #include <QMouseEvent> 0021 #include <QPainter> 0022 #include <QPixmap> 0023 #include <QStyle> 0024 0025 namespace KDEPrivate 0026 { 0027 KUrlNavigatorPlacesSelector::KUrlNavigatorPlacesSelector(KUrlNavigator *parent, KFilePlacesModel *placesModel) 0028 : KUrlNavigatorButtonBase(parent) 0029 , m_selectedItem(-1) 0030 , m_placesModel(placesModel) 0031 { 0032 setFocusPolicy(Qt::NoFocus); 0033 0034 connect(m_placesModel, &KFilePlacesModel::reloaded, this, [this] { 0035 updateSelection(m_selectedUrl); 0036 }); 0037 0038 m_placesMenu = new QMenu(this); 0039 m_placesMenu->installEventFilter(this); 0040 connect(m_placesMenu, &QMenu::aboutToShow, this, &KUrlNavigatorPlacesSelector::updateMenu); 0041 connect(m_placesMenu, &QMenu::triggered, this, [this](QAction *action) { 0042 activatePlace(action, &KUrlNavigatorPlacesSelector::placeActivated); 0043 }); 0044 0045 setMenu(m_placesMenu); 0046 0047 setAcceptDrops(true); 0048 } 0049 0050 KUrlNavigatorPlacesSelector::~KUrlNavigatorPlacesSelector() 0051 { 0052 } 0053 0054 void KUrlNavigatorPlacesSelector::updateMenu() 0055 { 0056 m_placesMenu->clear(); 0057 0058 // Submenus have to be deleted explicitly (QTBUG-11070) 0059 for (QObject *obj : QObjectList(m_placesMenu->children())) { 0060 delete qobject_cast<QMenu *>(obj); // Noop for nullptr 0061 } 0062 0063 QString previousGroup; 0064 QMenu *subMenu = nullptr; 0065 0066 const int rowCount = m_placesModel->rowCount(); 0067 for (int i = 0; i < rowCount; ++i) { 0068 QModelIndex index = m_placesModel->index(i, 0); 0069 if (m_placesModel->isHidden(index)) { 0070 continue; 0071 } 0072 0073 QAction *placeAction = new QAction(m_placesModel->icon(index), m_placesModel->text(index), m_placesMenu); 0074 placeAction->setData(i); 0075 0076 const QString &groupName = index.data(KFilePlacesModel::GroupRole).toString(); 0077 if (previousGroup.isEmpty()) { // Skip first group heading. 0078 previousGroup = groupName; 0079 } 0080 0081 // Put all subsequent categories into a submenu. 0082 if (previousGroup != groupName) { 0083 QAction *subMenuAction = new QAction(groupName, m_placesMenu); 0084 subMenu = new QMenu(m_placesMenu); 0085 subMenu->installEventFilter(this); 0086 subMenuAction->setMenu(subMenu); 0087 0088 m_placesMenu->addAction(subMenuAction); 0089 0090 previousGroup = groupName; 0091 } 0092 0093 if (subMenu) { 0094 subMenu->addAction(placeAction); 0095 } else { 0096 m_placesMenu->addAction(placeAction); 0097 } 0098 0099 if (i == m_selectedItem) { 0100 setIcon(m_placesModel->icon(index)); 0101 } 0102 } 0103 0104 const QModelIndex index = m_placesModel->index(m_selectedItem, 0); 0105 if (QAction *teardown = m_placesModel->teardownActionForIndex(index)) { 0106 m_placesMenu->addSeparator(); 0107 0108 teardown->setParent(m_placesMenu); 0109 m_placesMenu->addAction(teardown); 0110 } 0111 } 0112 0113 void KUrlNavigatorPlacesSelector::updateSelection(const QUrl &url) 0114 { 0115 const QModelIndex index = m_placesModel->closestItem(url); 0116 if (index.isValid()) { 0117 m_selectedItem = index.row(); 0118 m_selectedUrl = url; 0119 setIcon(m_placesModel->icon(index)); 0120 } else { 0121 m_selectedItem = -1; 0122 // No bookmark has been found which matches to the given Url. 0123 // Show the protocol's icon as pixmap for indication, if available: 0124 QIcon icon; 0125 if (!url.scheme().isEmpty()) { 0126 if (const QString iconName = KProtocolInfo::icon(url.scheme()); !iconName.isEmpty()) { 0127 icon = QIcon::fromTheme(iconName); 0128 } 0129 } 0130 if (icon.isNull()) { 0131 icon = QIcon::fromTheme(QStringLiteral("folder")); 0132 } 0133 setIcon(icon); 0134 } 0135 } 0136 0137 QUrl KUrlNavigatorPlacesSelector::selectedPlaceUrl() const 0138 { 0139 const QModelIndex index = m_placesModel->index(m_selectedItem, 0); 0140 return index.isValid() ? m_placesModel->url(index) : QUrl(); 0141 } 0142 0143 QString KUrlNavigatorPlacesSelector::selectedPlaceText() const 0144 { 0145 const QModelIndex index = m_placesModel->index(m_selectedItem, 0); 0146 return index.isValid() ? m_placesModel->text(index) : QString(); 0147 } 0148 0149 QSize KUrlNavigatorPlacesSelector::sizeHint() const 0150 { 0151 const int height = KUrlNavigatorButtonBase::sizeHint().height(); 0152 return QSize(height, height); 0153 } 0154 0155 void KUrlNavigatorPlacesSelector::paintEvent(QPaintEvent *event) 0156 { 0157 Q_UNUSED(event); 0158 QPainter painter(this); 0159 drawHoverBackground(&painter); 0160 0161 // draw icon 0162 const QPixmap pixmap = icon().pixmap(QSize(22, 22).expandedTo(iconSize()), QIcon::Normal); 0163 style()->drawItemPixmap(&painter, rect(), Qt::AlignCenter, pixmap); 0164 } 0165 0166 void KUrlNavigatorPlacesSelector::dragEnterEvent(QDragEnterEvent *event) 0167 { 0168 if (event->mimeData()->hasUrls()) { 0169 setDisplayHintEnabled(DraggedHint, true); 0170 event->acceptProposedAction(); 0171 0172 update(); 0173 } 0174 } 0175 0176 void KUrlNavigatorPlacesSelector::dragLeaveEvent(QDragLeaveEvent *event) 0177 { 0178 KUrlNavigatorButtonBase::dragLeaveEvent(event); 0179 0180 setDisplayHintEnabled(DraggedHint, false); 0181 update(); 0182 } 0183 0184 void KUrlNavigatorPlacesSelector::dropEvent(QDropEvent *event) 0185 { 0186 setDisplayHintEnabled(DraggedHint, false); 0187 update(); 0188 0189 QMimeDatabase db; 0190 const QList<QUrl> urlList = KUrlMimeData::urlsFromMimeData(event->mimeData()); 0191 for (const QUrl &url : urlList) { 0192 QMimeType mimetype = db.mimeTypeForUrl(url); 0193 if (mimetype.inherits(QStringLiteral("inode/directory"))) { 0194 m_placesModel->addPlace(url.fileName(), url); 0195 } 0196 } 0197 } 0198 0199 void KUrlNavigatorPlacesSelector::mouseReleaseEvent(QMouseEvent *event) 0200 { 0201 if (event->button() == Qt::MiddleButton && geometry().contains(event->pos())) { 0202 Q_EMIT tabRequested(KFilePlacesModel::convertedUrl(m_placesModel->url(m_placesModel->index(m_selectedItem, 0)))); 0203 event->accept(); 0204 return; 0205 } 0206 0207 KUrlNavigatorButtonBase::mouseReleaseEvent(event); 0208 } 0209 0210 void KUrlNavigatorPlacesSelector::activatePlace(QAction *action, ActivationSignal activationSignal) 0211 { 0212 Q_ASSERT(action != nullptr); 0213 if (action->data().toString() == QLatin1String("teardownAction")) { 0214 QModelIndex index = m_placesModel->index(m_selectedItem, 0); 0215 m_placesModel->requestTeardown(index); 0216 return; 0217 } 0218 0219 QModelIndex index = m_placesModel->index(action->data().toInt(), 0); 0220 0221 m_lastClickedIndex = QPersistentModelIndex(); 0222 m_lastActivationSignal = nullptr; 0223 0224 if (m_placesModel->setupNeeded(index)) { 0225 connect(m_placesModel, &KFilePlacesModel::setupDone, this, &KUrlNavigatorPlacesSelector::onStorageSetupDone); 0226 0227 m_lastClickedIndex = index; 0228 m_lastActivationSignal = activationSignal; 0229 m_placesModel->requestSetup(index); 0230 return; 0231 } else if (index.isValid()) { 0232 if (activationSignal == &KUrlNavigatorPlacesSelector::placeActivated) { 0233 m_selectedItem = index.row(); 0234 setIcon(m_placesModel->icon(index)); 0235 } 0236 0237 const QUrl url = KFilePlacesModel::convertedUrl(m_placesModel->url(index)); 0238 /*Q_EMIT*/ std::invoke(activationSignal, this, url); 0239 } 0240 } 0241 0242 void KUrlNavigatorPlacesSelector::onStorageSetupDone(const QModelIndex &index, bool success) 0243 { 0244 disconnect(m_placesModel, &KFilePlacesModel::setupDone, this, &KUrlNavigatorPlacesSelector::onStorageSetupDone); 0245 0246 if (m_lastClickedIndex == index) { 0247 if (success) { 0248 if (m_lastActivationSignal == &KUrlNavigatorPlacesSelector::placeActivated) { 0249 m_selectedItem = index.row(); 0250 setIcon(m_placesModel->icon(index)); 0251 } 0252 0253 const QUrl url = KFilePlacesModel::convertedUrl(m_placesModel->url(index)); 0254 /*Q_EMIT*/ std::invoke(m_lastActivationSignal, this, url); 0255 } 0256 m_lastClickedIndex = QPersistentModelIndex(); 0257 m_lastActivationSignal = nullptr; 0258 } 0259 } 0260 0261 bool KUrlNavigatorPlacesSelector::eventFilter(QObject *watched, QEvent *event) 0262 { 0263 if (auto *menu = qobject_cast<QMenu *>(watched)) { 0264 if (event->type() == QEvent::MouseButtonRelease) { 0265 QMouseEvent *me = static_cast<QMouseEvent *>(event); 0266 if (me->button() == Qt::MiddleButton) { 0267 if (QAction *action = menu->activeAction()) { 0268 m_placesMenu->close(); // always close top menu 0269 0270 activatePlace(action, &KUrlNavigatorPlacesSelector::tabRequested); 0271 return true; 0272 } 0273 } 0274 } 0275 } 0276 0277 return KUrlNavigatorButtonBase::eventFilter(watched, event); 0278 } 0279 0280 } // namespace KDEPrivate 0281 0282 #include "moc_kurlnavigatorplacesselector_p.cpp"