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 setFocusPolicy(Qt::NoFocus);
33
34 connect(m_placesModel, &KFilePlacesModel::reloaded, this, [this] {
35 updateSelection(m_selectedUrl);
36 });
37
38 m_placesMenu = new QMenu(this);
39 m_placesMenu->installEventFilter(this);
40 connect(m_placesMenu, &QMenu::aboutToShow, this, &KUrlNavigatorPlacesSelector::updateMenu);
41 connect(m_placesMenu, &QMenu::triggered, this, [this](QAction *action) {
42 activatePlace(action, &KUrlNavigatorPlacesSelector::placeActivated);
43 });
44
45 setMenu(m_placesMenu);
46
47 setAcceptDrops(true);
48}
49
50KUrlNavigatorPlacesSelector::~KUrlNavigatorPlacesSelector()
51{
52}
53
54void KUrlNavigatorPlacesSelector::updateMenu()
55{
56 m_placesMenu->clear();
57
58 // Submenus have to be deleted explicitly (QTBUG-11070)
59 for (QObject *obj : QObjectList(m_placesMenu->children())) {
60 delete qobject_cast<QMenu *>(obj); // Noop for nullptr
61 }
62
63 QString previousGroup;
64 QMenu *subMenu = nullptr;
65
66 const int rowCount = m_placesModel->rowCount();
67 for (int i = 0; i < rowCount; ++i) {
68 QModelIndex index = m_placesModel->index(i, 0);
69 if (m_placesModel->isHidden(index)) {
70 continue;
71 }
72
73 QAction *placeAction = new QAction(m_placesModel->icon(index), m_placesModel->text(index), m_placesMenu);
74 placeAction->setData(i);
75
76 const QString &groupName = index.data(KFilePlacesModel::GroupRole).toString();
77 if (previousGroup.isEmpty()) { // Skip first group heading.
78 previousGroup = groupName;
79 }
80
81 // Put all subsequent categories into a submenu.
82 if (previousGroup != groupName) {
83 QAction *subMenuAction = new QAction(groupName, m_placesMenu);
84 subMenu = new QMenu(m_placesMenu);
85 subMenu->installEventFilter(this);
86 subMenuAction->setMenu(subMenu);
87
88 m_placesMenu->addAction(subMenuAction);
89
90 previousGroup = groupName;
91 }
92
93 if (subMenu) {
94 subMenu->addAction(placeAction);
95 } else {
96 m_placesMenu->addAction(placeAction);
97 }
98
99 if (i == m_selectedItem) {
100 setIcon(m_placesModel->icon(index));
101 }
102 }
103
104 const QModelIndex index = m_placesModel->index(m_selectedItem, 0);
105 if (QAction *teardown = m_placesModel->teardownActionForIndex(index)) {
106 m_placesMenu->addSeparator();
107
108 teardown->setParent(m_placesMenu);
109 m_placesMenu->addAction(teardown);
110 }
111}
112
113void KUrlNavigatorPlacesSelector::updateSelection(const QUrl &url)
114{
115 const QModelIndex index = m_placesModel->closestItem(url);
116 if (index.isValid()) {
117 m_selectedItem = index.row();
118 m_selectedUrl = url;
119 setIcon(m_placesModel->icon(index));
120 } else {
121 m_selectedItem = -1;
122 // No bookmark has been found which matches to the given Url.
123 // Show the protocol's icon as pixmap for indication, if available:
124 QIcon icon;
125 if (!url.scheme().isEmpty()) {
126 if (const QString iconName = KProtocolInfo::icon(url.scheme()); !iconName.isEmpty()) {
127 icon = QIcon::fromTheme(iconName);
128 }
129 }
130 if (icon.isNull()) {
131 icon = QIcon::fromTheme(QStringLiteral("folder"));
132 }
133 setIcon(icon);
134 }
135}
136
137QUrl KUrlNavigatorPlacesSelector::selectedPlaceUrl() const
138{
139 const QModelIndex index = m_placesModel->index(m_selectedItem, 0);
140 return index.isValid() ? m_placesModel->url(index) : QUrl();
141}
142
143QString KUrlNavigatorPlacesSelector::selectedPlaceText() const
144{
145 const QModelIndex index = m_placesModel->index(m_selectedItem, 0);
146 return index.isValid() ? m_placesModel->text(index) : QString();
147}
148
149QSize KUrlNavigatorPlacesSelector::sizeHint() const
150{
151 const int height = KUrlNavigatorButtonBase::sizeHint().height();
152 return QSize(height, height);
153}
154
155void KUrlNavigatorPlacesSelector::paintEvent(QPaintEvent *event)
156{
157 Q_UNUSED(event);
158 QPainter painter(this);
159 drawHoverBackground(&painter);
160
161 // draw icon
162 const QPixmap pixmap = icon().pixmap(QSize(22, 22).expandedTo(iconSize()), QIcon::Normal);
163 style()->drawItemPixmap(&painter, rect(), Qt::AlignCenter, pixmap);
164}
165
166void KUrlNavigatorPlacesSelector::dragEnterEvent(QDragEnterEvent *event)
167{
168 if (event->mimeData()->hasUrls()) {
169 setDisplayHintEnabled(DraggedHint, true);
170 event->acceptProposedAction();
171
172 update();
173 }
174}
175
176void KUrlNavigatorPlacesSelector::dragLeaveEvent(QDragLeaveEvent *event)
177{
178 KUrlNavigatorButtonBase::dragLeaveEvent(event);
179
180 setDisplayHintEnabled(DraggedHint, false);
181 update();
182}
183
184void KUrlNavigatorPlacesSelector::dropEvent(QDropEvent *event)
185{
186 setDisplayHintEnabled(DraggedHint, false);
187 update();
188
189 QMimeDatabase db;
190 const QList<QUrl> urlList = KUrlMimeData::urlsFromMimeData(event->mimeData());
191 for (const QUrl &url : urlList) {
193 if (mimetype.inherits(QStringLiteral("inode/directory"))) {
194 m_placesModel->addPlace(url.fileName(), url);
195 }
196 }
197}
198
199void KUrlNavigatorPlacesSelector::mouseReleaseEvent(QMouseEvent *event)
200{
201 if (event->button() == Qt::MiddleButton && geometry().contains(event->pos())) {
202 Q_EMIT tabRequested(KFilePlacesModel::convertedUrl(m_placesModel->url(m_placesModel->index(m_selectedItem, 0))));
203 event->accept();
204 return;
205 }
206
207 KUrlNavigatorButtonBase::mouseReleaseEvent(event);
208}
209
210void KUrlNavigatorPlacesSelector::activatePlace(QAction *action, ActivationSignal activationSignal)
211{
212 Q_ASSERT(action != nullptr);
213 if (action->data().toString() == QLatin1String("teardownAction")) {
214 QModelIndex index = m_placesModel->index(m_selectedItem, 0);
215 m_placesModel->requestTeardown(index);
216 return;
217 }
218
219 QModelIndex index = m_placesModel->index(action->data().toInt(), 0);
220
221 m_lastClickedIndex = QPersistentModelIndex();
222 m_lastActivationSignal = nullptr;
223
224 if (m_placesModel->setupNeeded(index)) {
225 connect(m_placesModel, &KFilePlacesModel::setupDone, this, &KUrlNavigatorPlacesSelector::onStorageSetupDone);
226
227 m_lastClickedIndex = index;
228 m_lastActivationSignal = activationSignal;
229 m_placesModel->requestSetup(index);
230 return;
231 } else if (index.isValid()) {
232 if (activationSignal == &KUrlNavigatorPlacesSelector::placeActivated) {
233 m_selectedItem = index.row();
234 setIcon(m_placesModel->icon(index));
235 }
236
237 const QUrl url = KFilePlacesModel::convertedUrl(m_placesModel->url(index));
238 /*Q_EMIT*/ std::invoke(activationSignal, this, url);
239 }
240}
241
242void KUrlNavigatorPlacesSelector::onStorageSetupDone(const QModelIndex &index, bool success)
243{
244 disconnect(m_placesModel, &KFilePlacesModel::setupDone, this, &KUrlNavigatorPlacesSelector::onStorageSetupDone);
245
246 if (m_lastClickedIndex == index) {
247 if (success) {
248 if (m_lastActivationSignal == &KUrlNavigatorPlacesSelector::placeActivated) {
249 m_selectedItem = index.row();
250 setIcon(m_placesModel->icon(index));
251 }
252
253 const QUrl url = KFilePlacesModel::convertedUrl(m_placesModel->url(index));
254 /*Q_EMIT*/ std::invoke(m_lastActivationSignal, this, url);
255 }
256 m_lastClickedIndex = QPersistentModelIndex();
257 m_lastActivationSignal = nullptr;
258 }
259}
260
261bool KUrlNavigatorPlacesSelector::eventFilter(QObject *watched, QEvent *event)
262{
263 if (auto *menu = qobject_cast<QMenu *>(watched)) {
264 if (event->type() == QEvent::MouseButtonRelease) {
265 QMouseEvent *me = static_cast<QMouseEvent *>(event);
266 if (me->button() == Qt::MiddleButton) {
267 if (QAction *action = menu->activeAction()) {
268 m_placesMenu->close(); // always close top menu
269
270 activatePlace(action, &KUrlNavigatorPlacesSelector::tabRequested);
271 return true;
272 }
273 }
274 }
275 }
276
277 return KUrlNavigatorButtonBase::eventFilter(watched, event);
278}
279
280} // namespace KDEPrivate
281
282#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 Tue Mar 26 2024 11:18:52 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.