KIO

kurlnavigatorplacesselector.cpp
1/*
2 SPDX-FileCopyrightText: 2006 Peter Penz <peter.penz@gmx.at>
3 SPDX-FileCopyrightText: 2007 Kevin Ottens <ervin@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "kurlnavigatorplacesselector_p.h"
9
10#include <KProtocolInfo>
11#include <KUrlMimeData>
12#include <kfileplacesmodel.h>
13
14#include <QDragEnterEvent>
15#include <QDragLeaveEvent>
16#include <QDropEvent>
17#include <QMenu>
18#include <QMimeData>
19#include <QMimeDatabase>
20#include <QMouseEvent>
21#include <QPainter>
22#include <QPixmap>
23#include <QStyle>
24
25namespace KDEPrivate
26{
27KUrlNavigatorPlacesSelector::KUrlNavigatorPlacesSelector(KUrlNavigator *parent, KFilePlacesModel *placesModel)
28 : KUrlNavigatorButtonBase(parent)
29 , m_selectedItem(-1)
30 , m_placesModel(placesModel)
31{
32 connect(m_placesModel, &KFilePlacesModel::reloaded, this, [this] {
33 updateSelection(m_selectedUrl);
34 });
35
36 m_placesMenu = new QMenu(this);
37 m_placesMenu->installEventFilter(this);
38 connect(m_placesMenu, &QMenu::aboutToShow, this, &KUrlNavigatorPlacesSelector::updateMenu);
39 connect(m_placesMenu, &QMenu::triggered, this, [this](QAction *action) {
40 activatePlace(action, &KUrlNavigatorPlacesSelector::placeActivated);
41 });
42
43 setMenu(m_placesMenu);
44
45 setAcceptDrops(true);
46}
47
48KUrlNavigatorPlacesSelector::~KUrlNavigatorPlacesSelector()
49{
50}
51
52void KUrlNavigatorPlacesSelector::updateMenu()
53{
54 m_placesMenu->clear();
55
56 // Submenus have to be deleted explicitly (QTBUG-11070)
57 for (QObject *obj : QObjectList(m_placesMenu->children())) {
58 delete qobject_cast<QMenu *>(obj); // Noop for nullptr
59 }
60
61 QString previousGroup;
62 QMenu *subMenu = nullptr;
63
64 const int rowCount = m_placesModel->rowCount();
65 for (int i = 0; i < rowCount; ++i) {
66 QModelIndex index = m_placesModel->index(i, 0);
67 if (m_placesModel->isHidden(index)) {
68 continue;
69 }
70
71 QAction *placeAction = new QAction(m_placesModel->icon(index), m_placesModel->text(index), m_placesMenu);
72 placeAction->setData(i);
73
74 const QString &groupName = index.data(KFilePlacesModel::GroupRole).toString();
75 if (previousGroup.isEmpty()) { // Skip first group heading.
76 previousGroup = groupName;
77 }
78
79 // Put all subsequent categories into a submenu.
80 if (previousGroup != groupName) {
81 QAction *subMenuAction = new QAction(groupName, m_placesMenu);
82 subMenu = new QMenu(m_placesMenu);
83 subMenu->installEventFilter(this);
84 subMenuAction->setMenu(subMenu);
85
86 m_placesMenu->addAction(subMenuAction);
87
88 previousGroup = groupName;
89 }
90
91 if (subMenu) {
92 subMenu->addAction(placeAction);
93 } else {
94 m_placesMenu->addAction(placeAction);
95 }
96
97 if (i == m_selectedItem) {
98 setIcon(m_placesModel->icon(index));
99 }
100 }
101
102 const QModelIndex index = m_placesModel->index(m_selectedItem, 0);
103 if (QAction *teardown = m_placesModel->teardownActionForIndex(index)) {
104 m_placesMenu->addSeparator();
105
106 teardown->setParent(m_placesMenu);
107 m_placesMenu->addAction(teardown);
108 }
109}
110
111void KUrlNavigatorPlacesSelector::updateSelection(const QUrl &url)
112{
113 const QModelIndex index = m_placesModel->closestItem(url);
114 if (index.isValid()) {
115 m_selectedItem = index.row();
116 m_selectedUrl = url;
117 setIcon(m_placesModel->icon(index));
118 } else {
119 m_selectedItem = -1;
120 // No bookmark has been found which matches to the given Url.
121 // Show the protocol's icon as pixmap for indication, if available:
122 QIcon icon;
123 if (!url.scheme().isEmpty()) {
124 if (const QString iconName = KProtocolInfo::icon(url.scheme()); !iconName.isEmpty()) {
125 icon = QIcon::fromTheme(iconName);
126 }
127 }
128 if (icon.isNull()) {
129 icon = QIcon::fromTheme(QStringLiteral("folder"));
130 }
131 setIcon(icon);
132 }
133}
134
135QUrl KUrlNavigatorPlacesSelector::selectedPlaceUrl() const
136{
137 const QModelIndex index = m_placesModel->index(m_selectedItem, 0);
138 return index.isValid() ? m_placesModel->url(index) : QUrl();
139}
140
141QString KUrlNavigatorPlacesSelector::selectedPlaceText() const
142{
143 const QModelIndex index = m_placesModel->index(m_selectedItem, 0);
144 return index.isValid() ? m_placesModel->text(index) : QString();
145}
146
147QSize KUrlNavigatorPlacesSelector::sizeHint() const
148{
149 const int height = KUrlNavigatorButtonBase::sizeHint().height();
150 return QSize(height, height);
151}
152
153void KUrlNavigatorPlacesSelector::paintEvent(QPaintEvent *event)
154{
155 Q_UNUSED(event);
156 QPainter painter(this);
157 drawHoverBackground(&painter);
158
159 // draw icon
160 const QPixmap pixmap = icon().pixmap(QSize(22, 22).expandedTo(iconSize()), QIcon::Normal);
161 style()->drawItemPixmap(&painter, rect(), Qt::AlignCenter, pixmap);
162}
163
164void KUrlNavigatorPlacesSelector::dragEnterEvent(QDragEnterEvent *event)
165{
166 if (event->mimeData()->hasUrls()) {
167 setDisplayHintEnabled(DraggedHint, true);
168 event->acceptProposedAction();
169
170 update();
171 }
172}
173
174void KUrlNavigatorPlacesSelector::dragLeaveEvent(QDragLeaveEvent *event)
175{
176 KUrlNavigatorButtonBase::dragLeaveEvent(event);
177
178 setDisplayHintEnabled(DraggedHint, false);
179 update();
180}
181
182void KUrlNavigatorPlacesSelector::dropEvent(QDropEvent *event)
183{
184 setDisplayHintEnabled(DraggedHint, false);
185 update();
186
187 QMimeDatabase db;
188 const QList<QUrl> urlList = KUrlMimeData::urlsFromMimeData(event->mimeData());
189 for (const QUrl &url : urlList) {
191 if (mimetype.inherits(QStringLiteral("inode/directory"))) {
192 m_placesModel->addPlace(url.fileName(), url);
193 }
194 }
195}
196
197void KUrlNavigatorPlacesSelector::mouseReleaseEvent(QMouseEvent *event)
198{
199 if (event->button() == Qt::MiddleButton && geometry().contains(event->pos())) {
200 Q_EMIT tabRequested(KFilePlacesModel::convertedUrl(m_placesModel->url(m_placesModel->index(m_selectedItem, 0))));
201 event->accept();
202 return;
203 }
204
205 KUrlNavigatorButtonBase::mouseReleaseEvent(event);
206}
207
208void KUrlNavigatorPlacesSelector::activatePlace(QAction *action, ActivationSignal activationSignal)
209{
210 Q_ASSERT(action != nullptr);
211 if (action->data().toString() == QLatin1String("teardownAction")) {
212 QModelIndex index = m_placesModel->index(m_selectedItem, 0);
213 m_placesModel->requestTeardown(index);
214 return;
215 }
216
217 QModelIndex index = m_placesModel->index(action->data().toInt(), 0);
218
219 m_lastClickedIndex = QPersistentModelIndex();
220 m_lastActivationSignal = nullptr;
221
222 if (m_placesModel->setupNeeded(index)) {
223 connect(m_placesModel, &KFilePlacesModel::setupDone, this, &KUrlNavigatorPlacesSelector::onStorageSetupDone);
224
225 m_lastClickedIndex = index;
226 m_lastActivationSignal = activationSignal;
227 m_placesModel->requestSetup(index);
228 return;
229 } else if (index.isValid()) {
230 if (activationSignal == &KUrlNavigatorPlacesSelector::placeActivated) {
231 m_selectedItem = index.row();
232 setIcon(m_placesModel->icon(index));
233 }
234
235 const QUrl url = KFilePlacesModel::convertedUrl(m_placesModel->url(index));
236 /*Q_EMIT*/ std::invoke(activationSignal, this, url);
237 }
238}
239
240void KUrlNavigatorPlacesSelector::onStorageSetupDone(const QModelIndex &index, bool success)
241{
242 disconnect(m_placesModel, &KFilePlacesModel::setupDone, this, &KUrlNavigatorPlacesSelector::onStorageSetupDone);
243
244 if (m_lastClickedIndex == index) {
245 if (success) {
246 if (m_lastActivationSignal == &KUrlNavigatorPlacesSelector::placeActivated) {
247 m_selectedItem = index.row();
248 setIcon(m_placesModel->icon(index));
249 }
250
251 const QUrl url = KFilePlacesModel::convertedUrl(m_placesModel->url(index));
252 /*Q_EMIT*/ std::invoke(m_lastActivationSignal, this, url);
253 }
254 m_lastClickedIndex = QPersistentModelIndex();
255 m_lastActivationSignal = nullptr;
256 }
257}
258
259bool KUrlNavigatorPlacesSelector::eventFilter(QObject *watched, QEvent *event)
260{
261 if (auto *menu = qobject_cast<QMenu *>(watched)) {
262 if (event->type() == QEvent::MouseButtonRelease) {
263 QMouseEvent *me = static_cast<QMouseEvent *>(event);
264 if (me->button() == Qt::MiddleButton) {
265 if (QAction *action = menu->activeAction()) {
266 m_placesMenu->close(); // always close top menu
267
268 activatePlace(action, &KUrlNavigatorPlacesSelector::tabRequested);
269 return true;
270 }
271 }
272 }
273 }
274
275 return KUrlNavigatorButtonBase::eventFilter(watched, event);
276}
277
278} // namespace KDEPrivate
279
280#include "moc_kurlnavigatorplacesselector_p.cpp"
This class is a list view model.
static QUrl convertedUrl(const QUrl &url)
Converts the URL, which contains "virtual" URLs for system-items like "timeline:/lastmonth" into a Qu...
void reloaded()
Called once the model has been reloaded.
@ GroupRole
The name of the group, for example "Remote" or "Devices".
void setupDone(const QModelIndex &index, bool success)
Emitted after the Solid setup ends.
static QString icon(const QString &protocol)
Returns the name of the icon, associated with the specified protocol.
Widget that allows to navigate through the paths of an URL.
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
void update(Part *part, const QByteArray &data, qint64 dataSize)
KIOCORE_EXPORT MimetypeJob * mimetype(const QUrl &url, JobFlags flags=DefaultFlags)
Find MIME type for one file or directory.
KCOREADDONS_EXPORT QList< QUrl > urlsFromMimeData(const QMimeData *mimeData, DecodeOptions decodeOptions=PreferKdeUrls, MetaDataMap *metaData=nullptr)
QVariant data() const const
void setData(const QVariant &data)
void setMenu(QMenu *menu)
MouseButtonRelease
QPixmap pixmap(QWindow *window, const QSize &size, Mode mode, State state) const const
QIcon fromTheme(const QString &name)
bool isNull() const const
QAction * addAction(const QIcon &icon, const QString &text, Functor functor, const QKeySequence &shortcut)
void aboutToShow()
void triggered(QAction *action)
QMimeType mimeTypeForUrl(const QUrl &url) const const
QVariant data(int role) const const
bool isValid() const const
int row() const const
bool inherits(const char *className) const const
void installEventFilter(QObject *filterObj)
Qt::MouseButton button() const const
int height() const const
bool isEmpty() const const
AlignCenter
MiddleButton
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QString fileName(ComponentFormattingOptions options) const const
QString scheme() const const
int toInt(bool *ok) const const
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:16:28 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.