KIO

openfilemanagerwindowjob.cpp
1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2016 Kai Uwe Broulik <kde@privat.broulik.de>
4 SPDX-FileCopyrightText: 2023 g10 Code GmbH
5 SPDX-FileContributor: Sune Stolborg Vuorela <sune@vuorela.dk>
6
7 SPDX-License-Identifier: LGPL-2.0-or-later
8*/
9
10#include "openfilemanagerwindowjob.h"
11#include "openfilemanagerwindowjob_p.h"
12
13#ifdef WITH_QTDBUS
14#include <QDBusConnection>
15#include <QDBusMessage>
16#include <QDBusPendingCallWatcher>
17#include <QDBusPendingReply>
18#endif
19#if defined(Q_OS_WINDOWS)
20#include <QDir>
21#include <shlobj.h>
22#include <vector>
23#endif
24#include <QGuiApplication>
25
26#include <KWindowSystem>
27
28#include "config-kiogui.h"
29#if HAVE_WAYLAND
30#include <KWaylandExtras>
31#endif
32
33#include <KIO/OpenUrlJob>
34
35namespace KIO
36{
37class OpenFileManagerWindowJobPrivate
38{
39public:
40 OpenFileManagerWindowJobPrivate(OpenFileManagerWindowJob *qq)
41 : q(qq)
42 , strategy(nullptr)
43 {
44 }
45
46 ~OpenFileManagerWindowJobPrivate() = default;
47
48#if USE_DBUS
49 void createDBusStrategy()
50 {
51 strategy = std::make_unique<OpenFileManagerWindowDBusStrategy>(q);
52 }
53#endif
54#if defined(Q_OS_WINDOWS)
55 void createWindowsShellStrategy()
56 {
57 strategy = std::make_unique<OpenFileManagerWindowWindowsShellStrategy>(q);
58 }
59#endif
60
61 void createKRunStrategy()
62 {
63 strategy = std::make_unique<OpenFileManagerWindowKRunStrategy>(q);
64 }
65
66 OpenFileManagerWindowJob *const q;
67 QList<QUrl> highlightUrls;
68 QByteArray startupId;
69
70 std::unique_ptr<AbstractOpenFileManagerWindowStrategy> strategy;
71};
72
74 : KJob(parent)
75 , d(new OpenFileManagerWindowJobPrivate(this))
76{
77#ifdef WITH_QTDBUS
78 d->createDBusStrategy();
79#elif defined(Q_OS_WINDOWS)
80 d->createWindowsShellStrategy();
81#else
82 d->createKRunStrategy();
83#endif
84}
85
87
89{
90 return d->highlightUrls;
91}
92
94{
95 d->highlightUrls = highlightUrls;
96}
97
99{
100 return d->startupId;
101}
102
104{
105 d->startupId = startupId;
106}
107
109{
110 if (d->highlightUrls.isEmpty()) {
112 emitResult();
113 return;
114 }
115
116 d->strategy->start(d->highlightUrls, d->startupId);
117}
118
120{
121 auto *job = new OpenFileManagerWindowJob();
122 job->setHighlightUrls(urls);
123 job->setStartupId(asn);
124 job->start();
125
126 return job;
127}
128
129#ifdef WITH_QTDBUS
130void OpenFileManagerWindowDBusStrategy::start(const QList<QUrl> &urls, const QByteArray &asn)
131{
132 // see the spec at: https://www.freedesktop.org/wiki/Specifications/file-manager-interface/
133
134 auto runWithToken = [this, urls](const QByteArray &asn) {
135 QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.FileManager1"),
136 QStringLiteral("/org/freedesktop/FileManager1"),
137 QStringLiteral("org.freedesktop.FileManager1"),
138 QStringLiteral("ShowItems"));
139
140 msg << QUrl::toStringList(urls) << QString::fromUtf8(asn);
141
143 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, m_job);
145 QDBusPendingReply<void> reply = *watcher;
146 watcher->deleteLater();
147
148 if (reply.isError()) {
149 // Try the KRun strategy as fallback, also calls emitResult inside
150 m_job->d->createKRunStrategy();
151 m_job->d->strategy->start(urls, asn);
152 return;
153 }
154
155 emitResultProxy();
156 });
157 };
158
159 if (asn.isEmpty()) {
160#if HAVE_WAYLAND
162 auto window = qGuiApp->focusWindow();
163 if (!window && !qGuiApp->allWindows().isEmpty()) {
164 window = qGuiApp->allWindows().constFirst();
165 }
166 const int launchedSerial = KWaylandExtras::lastInputSerial(window);
168 KWaylandExtras::self(),
170 m_job,
171 [launchedSerial, runWithToken](int serial, const QString &token) {
172 if (serial == launchedSerial) {
173 runWithToken(token.toUtf8());
174 }
175 },
177 KWaylandExtras::requestXdgActivationToken(window, launchedSerial, {});
178 } else {
179 runWithToken({});
180 }
181#else
182 runWithToken({});
183#endif
184 } else {
185 runWithToken(asn);
186 }
187}
188#endif
189
190void OpenFileManagerWindowKRunStrategy::start(const QList<QUrl> &urls, const QByteArray &asn)
191{
192 KIO::OpenUrlJob *urlJob = new KIO::OpenUrlJob(urls.at(0).adjusted(QUrl::RemoveFilename), QStringLiteral("inode/directory"));
193 urlJob->setUiDelegate(m_job->uiDelegate());
194 urlJob->setStartupId(asn);
195 QObject::connect(urlJob, &KJob::result, m_job, [this](KJob *urlJob) {
196 if (urlJob->error()) {
197 emitResultProxy(OpenFileManagerWindowJob::LaunchFailedError);
198 } else {
199 emitResultProxy();
200 }
201 });
202 urlJob->start();
203}
204
205#if defined(Q_OS_WINDOWS)
206void OpenFileManagerWindowWindowsShellStrategy::start(const QList<QUrl> &urls, const QByteArray &asn)
207{
208 Q_UNUSED(asn);
209 LPITEMIDLIST dir = ILCreateFromPathW(QDir::toNativeSeparators(urls.at(0).adjusted(QUrl::RemoveFilename).toLocalFile()).toStdWString().data());
210
211 std::vector<LPCITEMIDLIST> items;
212 for (const auto &url : urls) {
213 LPITEMIDLIST item = ILCreateFromPathW(QDir::toNativeSeparators(url.toLocalFile()).toStdWString().data());
214 items.push_back(item);
215 }
216
217 auto result = SHOpenFolderAndSelectItems(dir, items.size(), items.data(), 0);
218 if (SUCCEEDED(result)) {
219 emitResultProxy();
220 } else {
222 }
223 ILFree(dir);
224 for (auto &item : items) {
225 ILFree(const_cast<LPITEMIDLIST>(item));
226 }
227}
228#endif
229} // namespace KIO
230
231#include "moc_openfilemanagerwindowjob.cpp"
~OpenFileManagerWindowJob() override
Destroys the OpenFileManagerWindowJob.
@ LaunchFailedError
Failed to launch the file manager.
@ NoValidUrlsError
No valid URLs to highlight have been specified.
OpenFileManagerWindowJob(QObject *parent=nullptr)
Creates an OpenFileManagerWindowJob.
void start() override
Starts the job.
QList< QUrl > highlightUrls() const
The files and/or folders to highlight.
void setHighlightUrls(const QList< QUrl > &highlightUrls)
Set the files and/or folders to highlight.
void setStartupId(const QByteArray &startupId)
Sets the platform-specific startup id of the file manager launch.
QByteArray startupId() const
The Startup ID.
OpenUrlJob finds out the right way to "open" a URL.
Definition openurljob.h:42
void setStartupId(const QByteArray &startupId)
Sets the platform-specific startup id of the application launch.
void emitResult()
int error() const
void result(KJob *job)
void setError(int errorCode)
virtual Q_SCRIPTABLE void start()=0
void setUiDelegate(KJobUiDelegate *delegate)
void xdgActivationTokenArrived(int serial, const QString &token)
static Q_INVOKABLE void requestXdgActivationToken(QWindow *win, uint32_t serial, const QString &app_id)
static Q_INVOKABLE quint32 lastInputSerial(QWindow *window)
static bool isPlatformWayland()
A namespace for KIO globals.
OpenFileManagerWindowJob * highlightInFileManager(const QList< QUrl > &urls, const QByteArray &asn)
Convenience method for creating a job to highlight a certain file or folder.
QWidget * window(QObject *job)
KIOCORE_EXPORT QString dir(const QString &fileClass)
Returns the most recently used directory associated with this file-class.
bool isEmpty() const const
QDBusPendingCall asyncCall(const QDBusMessage &message, int timeout) const const
QDBusConnection sessionBus()
QDBusMessage createMethodCall(const QString &service, const QString &path, const QString &interface, const QString &method)
void finished(QDBusPendingCallWatcher *self)
bool isError() const const
QString toNativeSeparators(const QString &pathName)
const_reference at(qsizetype i) const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
QString fromUtf8(QByteArrayView str)
std::wstring toStdWString() const const
QByteArray toUtf8() const const
SingleShotConnection
RemoveFilename
QStringList toStringList(const QList< QUrl > &urls, FormattingOptions options)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jun 7 2024 11:58:22 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.