File indexing completed on 2025-01-19 03:41:34
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 2016 Kai Uwe Broulik <kde@privat.broulik.de> 0004 SPDX-FileCopyrightText: 2023 g10 Code GmbH 0005 SPDX-FileContributor: Sune Stolborg Vuorela <sune@vuorela.dk> 0006 0007 SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 0010 #include "openfilemanagerwindowjob.h" 0011 #include "openfilemanagerwindowjob_p.h" 0012 0013 #if USE_DBUS 0014 #include <QDBusConnection> 0015 #include <QDBusMessage> 0016 #include <QDBusPendingCallWatcher> 0017 #include <QDBusPendingReply> 0018 #endif 0019 #if defined(Q_OS_WINDOWS) 0020 #include <QDir> 0021 #include <shlobj.h> 0022 #include <vector> 0023 #endif 0024 #include <QGuiApplication> 0025 0026 #include <KWindowSystem> 0027 0028 #include "config-kiogui.h" 0029 #if HAVE_WAYLAND 0030 #include <KWaylandExtras> 0031 #endif 0032 0033 #include <KIO/OpenUrlJob> 0034 0035 namespace KIO 0036 { 0037 class OpenFileManagerWindowJobPrivate 0038 { 0039 public: 0040 OpenFileManagerWindowJobPrivate(OpenFileManagerWindowJob *qq) 0041 : q(qq) 0042 , strategy(nullptr) 0043 { 0044 } 0045 0046 ~OpenFileManagerWindowJobPrivate() = default; 0047 0048 #if USE_DBUS 0049 void createDBusStrategy() 0050 { 0051 strategy = std::make_unique<OpenFileManagerWindowDBusStrategy>(q); 0052 } 0053 #endif 0054 #if defined(Q_OS_WINDOWS) 0055 void createWindowsShellStrategy() 0056 { 0057 strategy = std::make_unique<OpenFileManagerWindowWindowsShellStrategy>(q); 0058 } 0059 #endif 0060 0061 void createKRunStrategy() 0062 { 0063 strategy = std::make_unique<OpenFileManagerWindowKRunStrategy>(q); 0064 } 0065 0066 OpenFileManagerWindowJob *const q; 0067 QList<QUrl> highlightUrls; 0068 QByteArray startupId; 0069 0070 std::unique_ptr<AbstractOpenFileManagerWindowStrategy> strategy; 0071 }; 0072 0073 OpenFileManagerWindowJob::OpenFileManagerWindowJob(QObject *parent) 0074 : KJob(parent) 0075 , d(new OpenFileManagerWindowJobPrivate(this)) 0076 { 0077 #if USE_DBUS 0078 d->createDBusStrategy(); 0079 #elif defined(Q_OS_WINDOWS) 0080 d->createWindowsShellStrategy(); 0081 #else 0082 d->createKRunStrategy(); 0083 #endif 0084 } 0085 0086 OpenFileManagerWindowJob::~OpenFileManagerWindowJob() = default; 0087 0088 QList<QUrl> OpenFileManagerWindowJob::highlightUrls() const 0089 { 0090 return d->highlightUrls; 0091 } 0092 0093 void OpenFileManagerWindowJob::setHighlightUrls(const QList<QUrl> &highlightUrls) 0094 { 0095 d->highlightUrls = highlightUrls; 0096 } 0097 0098 QByteArray OpenFileManagerWindowJob::startupId() const 0099 { 0100 return d->startupId; 0101 } 0102 0103 void OpenFileManagerWindowJob::setStartupId(const QByteArray &startupId) 0104 { 0105 d->startupId = startupId; 0106 } 0107 0108 void OpenFileManagerWindowJob::start() 0109 { 0110 if (d->highlightUrls.isEmpty()) { 0111 setError(NoValidUrlsError); 0112 emitResult(); 0113 return; 0114 } 0115 0116 d->strategy->start(d->highlightUrls, d->startupId); 0117 } 0118 0119 OpenFileManagerWindowJob *highlightInFileManager(const QList<QUrl> &urls, const QByteArray &asn) 0120 { 0121 auto *job = new OpenFileManagerWindowJob(); 0122 job->setHighlightUrls(urls); 0123 job->setStartupId(asn); 0124 job->start(); 0125 0126 return job; 0127 } 0128 0129 #if USE_DBUS 0130 void OpenFileManagerWindowDBusStrategy::start(const QList<QUrl> &urls, const QByteArray &asn) 0131 { 0132 // see the spec at: https://www.freedesktop.org/wiki/Specifications/file-manager-interface/ 0133 0134 auto runWithToken = [this, urls](const QByteArray &asn) { 0135 QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.FileManager1"), 0136 QStringLiteral("/org/freedesktop/FileManager1"), 0137 QStringLiteral("org.freedesktop.FileManager1"), 0138 QStringLiteral("ShowItems")); 0139 0140 msg << QUrl::toStringList(urls) << QString::fromUtf8(asn); 0141 0142 QDBusPendingReply<void> reply = QDBusConnection::sessionBus().asyncCall(msg); 0143 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, m_job); 0144 QObject::connect(watcher, &QDBusPendingCallWatcher::finished, m_job, [=](QDBusPendingCallWatcher *watcher) { 0145 QDBusPendingReply<void> reply = *watcher; 0146 watcher->deleteLater(); 0147 0148 if (reply.isError()) { 0149 // Try the KRun strategy as fallback, also calls emitResult inside 0150 m_job->d->createKRunStrategy(); 0151 m_job->d->strategy->start(urls, asn); 0152 return; 0153 } 0154 0155 emitResultProxy(); 0156 }); 0157 }; 0158 0159 if (asn.isEmpty()) { 0160 #if HAVE_WAYLAND 0161 if (KWindowSystem::isPlatformWayland()) { 0162 auto window = qGuiApp->focusWindow(); 0163 if (!window && !qGuiApp->allWindows().isEmpty()) { 0164 window = qGuiApp->allWindows().constFirst(); 0165 } 0166 const int launchedSerial = KWaylandExtras::lastInputSerial(window); 0167 QObject::connect( 0168 KWaylandExtras::self(), 0169 &KWaylandExtras::xdgActivationTokenArrived, 0170 m_job, 0171 [launchedSerial, runWithToken](int serial, const QString &token) { 0172 if (serial == launchedSerial) { 0173 runWithToken(token.toUtf8()); 0174 } 0175 }, 0176 Qt::SingleShotConnection); 0177 KWaylandExtras::requestXdgActivationToken(window, launchedSerial, {}); 0178 } else { 0179 runWithToken({}); 0180 } 0181 #else 0182 runWithToken({}); 0183 #endif 0184 } else { 0185 runWithToken(asn); 0186 } 0187 } 0188 #endif 0189 0190 void OpenFileManagerWindowKRunStrategy::start(const QList<QUrl> &urls, const QByteArray &asn) 0191 { 0192 KIO::OpenUrlJob *urlJob = new KIO::OpenUrlJob(urls.at(0).adjusted(QUrl::RemoveFilename), QStringLiteral("inode/directory")); 0193 urlJob->setUiDelegate(m_job->uiDelegate()); 0194 urlJob->setStartupId(asn); 0195 QObject::connect(urlJob, &KJob::result, m_job, [this](KJob *urlJob) { 0196 if (urlJob->error()) { 0197 emitResultProxy(OpenFileManagerWindowJob::LaunchFailedError); 0198 } else { 0199 emitResultProxy(); 0200 } 0201 }); 0202 urlJob->start(); 0203 } 0204 0205 #if defined(Q_OS_WINDOWS) 0206 void OpenFileManagerWindowWindowsShellStrategy::start(const QList<QUrl> &urls, const QByteArray &asn) 0207 { 0208 Q_UNUSED(asn); 0209 LPITEMIDLIST dir = ILCreateFromPathW(QDir::toNativeSeparators(urls.at(0).adjusted(QUrl::RemoveFilename).toLocalFile()).toStdWString().data()); 0210 0211 std::vector<LPCITEMIDLIST> items; 0212 for (const auto &url : urls) { 0213 LPITEMIDLIST item = ILCreateFromPathW(QDir::toNativeSeparators(url.toLocalFile()).toStdWString().data()); 0214 items.push_back(item); 0215 } 0216 0217 auto result = SHOpenFolderAndSelectItems(dir, items.size(), items.data(), 0); 0218 if (SUCCEEDED(result)) { 0219 emitResultProxy(); 0220 } else { 0221 emitResultProxy(OpenFileManagerWindowJob::LaunchFailedError); 0222 } 0223 ILFree(dir); 0224 for (auto &item : items) { 0225 ILFree(const_cast<LPITEMIDLIST>(item)); 0226 } 0227 } 0228 #endif 0229 } // namespace KIO 0230 0231 #include "moc_openfilemanagerwindowjob.cpp"