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#ifdef WITH_QTDBUS
49 void createDBusStrategy()
50 {
51 strategy = std::make_unique<OpenFileManagerWindowDBusStrategy>();
52 }
53#endif
54#if defined(Q_OS_WINDOWS)
55 void createWindowsShellStrategy()
56 {
57 strategy = std::make_unique<OpenFileManagerWindowWindowsShellStrategy>();
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 connect(d->strategy.get(), &AbstractOpenFileManagerWindowStrategy::finished, this, [this](int result) {
86 if (result == KJob::NoError) {
87 emitResult();
88 } else {
89#ifdef WITH_QTDBUS
90 // DBus strategy failed, fall back to KRun strategy
91 d->strategy = std::make_unique<OpenFileManagerWindowKRunStrategy>(this);
92 d->strategy->start(d->highlightUrls, d->startupId);
93
94 connect(d->strategy.get(), &KIO::AbstractOpenFileManagerWindowStrategy::finished, this, [this](int result) {
95 setError(result);
96 emitResult();
97 });
98#else
100 emitResult();
101#endif
102 }
103 });
104}
105
106OpenFileManagerWindowJob::~OpenFileManagerWindowJob() = default;
107
108QList<QUrl> OpenFileManagerWindowJob::highlightUrls() const
109{
110 return d->highlightUrls;
111}
112
113void OpenFileManagerWindowJob::setHighlightUrls(const QList<QUrl> &highlightUrls)
114{
115 d->highlightUrls = highlightUrls;
116}
117
118QByteArray OpenFileManagerWindowJob::startupId() const
119{
120 return d->startupId;
121}
122
123void OpenFileManagerWindowJob::setStartupId(const QByteArray &startupId)
124{
125 d->startupId = startupId;
126}
127
128void OpenFileManagerWindowJob::start()
129{
130 if (d->highlightUrls.isEmpty()) {
131 setError(NoValidUrlsError);
132 emitResult();
133 return;
134 }
135
136 d->strategy->start(d->highlightUrls, d->startupId);
137}
138
140{
141 auto *job = new OpenFileManagerWindowJob();
142 job->setHighlightUrls(urls);
143 job->setStartupId(asn);
144 job->start();
145
146 return job;
147}
148
149#ifdef WITH_QTDBUS
150void OpenFileManagerWindowDBusStrategy::start(const QList<QUrl> &urls, const QByteArray &asn)
151{
152 // see the spec at: https://www.freedesktop.org/wiki/Specifications/file-manager-interface/
153
154 auto runWithToken = [this, urls](const QByteArray &asn) {
155 QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.FileManager1"),
156 QStringLiteral("/org/freedesktop/FileManager1"),
157 QStringLiteral("org.freedesktop.FileManager1"),
158 QStringLiteral("ShowItems"));
159
160 msg << QUrl::toStringList(urls) << QString::fromUtf8(asn);
161
163 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this);
164 QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [urls, asn, this](QDBusPendingCallWatcher *watcher) {
165 QDBusPendingReply<void> reply = *watcher;
166 watcher->deleteLater();
167
168 Q_EMIT finished(reply.isError() ? KJob::UserDefinedError : KJob::NoError);
169 });
170 };
171
172 if (asn.isEmpty()) {
173#if HAVE_WAYLAND
175 auto window = qGuiApp->focusWindow();
176 if (!window && !qGuiApp->allWindows().isEmpty()) {
177 window = qGuiApp->allWindows().constFirst();
178 }
179 const int launchedSerial = KWaylandExtras::lastInputSerial(window);
181 KWaylandExtras::self(),
183 this,
184 [launchedSerial, runWithToken](int serial, const QString &token) {
185 if (serial == launchedSerial) {
186 runWithToken(token.toUtf8());
187 }
188 },
190 KWaylandExtras::requestXdgActivationToken(window, launchedSerial, {});
191 } else {
192 runWithToken({});
193 }
194#else
195 runWithToken({});
196#endif
197 } else {
198 runWithToken(asn);
199 }
200}
201#endif
202
203void OpenFileManagerWindowKRunStrategy::start(const QList<QUrl> &urls, const QByteArray &asn)
204{
205 KIO::OpenUrlJob *urlJob = new KIO::OpenUrlJob(urls.at(0).adjusted(QUrl::RemoveFilename), QStringLiteral("inode/directory"));
206 urlJob->setUiDelegate(m_job->uiDelegate());
207 urlJob->setStartupId(asn);
208 QObject::connect(urlJob, &KJob::result, this, [this](KJob *urlJob) {
209 if (urlJob->error()) {
210 Q_EMIT finished(OpenFileManagerWindowJob::LaunchFailedError);
211 } else {
212 Q_EMIT finished(KJob::NoError);
213 }
214 });
215 urlJob->start();
216}
217
218#if defined(Q_OS_WINDOWS)
219void OpenFileManagerWindowWindowsShellStrategy::start(const QList<QUrl> &urls, const QByteArray &asn)
220{
221 Q_UNUSED(asn);
222 LPITEMIDLIST dir = ILCreateFromPathW(QDir::toNativeSeparators(urls.at(0).adjusted(QUrl::RemoveFilename).toLocalFile()).toStdWString().data());
223
224 std::vector<LPCITEMIDLIST> items;
225 for (const auto &url : urls) {
226 LPITEMIDLIST item = ILCreateFromPathW(QDir::toNativeSeparators(url.toLocalFile()).toStdWString().data());
227 items.push_back(item);
228 }
229
230 auto result = SHOpenFolderAndSelectItems(dir, items.size(), items.data(), 0);
231 if (SUCCEEDED(result)) {
232 Q_EMIT finished(KJob::NoError);
233 } else {
234 Q_EMIT finished(OpenFileManagerWindowJob::LaunchFailedError);
235 }
236 ILFree(dir);
237 for (auto &item : items) {
238 ILFree(const_cast<LPITEMIDLIST>(item));
239 }
240}
241#endif
242} // namespace KIO
243
244#include "moc_openfilemanagerwindowjob.cpp"
Open a File Manager Window.
OpenFileManagerWindowJob(QObject *parent=nullptr)
Creates an OpenFileManagerWindowJob.
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 Dec 6 2024 12:09:38 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.