diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 46944af3..b148623d 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -1,77 +1,77 @@ configure_file(config-kiogui.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kiogui.h) set(kiogui_SRCS applicationlauncherjob.cpp commandlauncherjob.cpp faviconrequestjob.cpp openurljob.cpp - openurljobhandlerinterface.cpp + openwithhandlerinterface.cpp kprocessrunner.cpp ) ecm_qt_declare_logging_category(kiogui_SRCS HEADER kiogui_debug.h IDENTIFIER KIO_GUI CATEGORY_NAME kf5.kio.gui DESCRIPTION "KIOGui (KIO)" EXPORT KIO ) ecm_qt_declare_logging_category(kiogui_SRCS HEADER favicons_debug.h IDENTIFIER FAVICONS_LOG CATEGORY_NAME kf5.kio.favicons DESCRIPTION "FavIcons (KIO)" EXPORT KIO ) add_library(KF5KIOGui ${kiogui_SRCS}) generate_export_header(KF5KIOGui BASE_NAME KIOGui) add_library(KF5::KIOGui ALIAS KF5KIOGui) target_include_directories(KF5KIOGui INTERFACE "$") target_link_libraries(KF5KIOGui PUBLIC KF5::KIOCore KF5::WindowSystem Qt5::Gui PRIVATE KF5::I18n ) set_target_properties(KF5KIOGui PROPERTIES VERSION ${KIO_VERSION_STRING} SOVERSION ${KIO_SOVERSION} EXPORT_NAME KIOGui ) # Headers prefixed with KIO/ ecm_generate_headers(KIOGui_CamelCase_HEADERS HEADER_NAMES ApplicationLauncherJob CommandLauncherJob FavIconRequestJob OpenUrlJob PREFIX KIO REQUIRED_HEADERS KIO_namespaced_gui_HEADERS ) install(FILES ${KIOGui_CamelCase_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KIOGui/KIO COMPONENT Devel) install(TARGETS KF5KIOGui EXPORT KF5KIOTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES ${KIO_namespaced_gui_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KIOGui/kio COMPONENT Devel) install(FILES ${KIOGui_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/kiogui_export.h DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KIOGui COMPONENT Devel) # make available to ecm_add_qch in parent folder set(KIOGui_QCH_SOURCES ${KIOGui_HEADERS} ${KIO_namespaced_gui_HEADERS} PARENT_SCOPE) include(ECMGeneratePriFile) ecm_generate_pri_file(BASE_NAME KIOGui LIB_NAME KF5KIOGui DEPS "KIOCore" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}/KIOGui) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) diff --git a/src/gui/openurljob.cpp b/src/gui/openurljob.cpp index 900a241d..b7bdfa9c 100644 --- a/src/gui/openurljob.cpp +++ b/src/gui/openurljob.cpp @@ -1,651 +1,651 @@ /* This file is part of the KDE libraries Copyright (c) 2020 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "openurljob.h" -#include "openurljobhandlerinterface.h" +#include "openwithhandlerinterface.h" #include "global.h" #include "job.h" // for buildErrorString #include "commandlauncherjob.h" #include "desktopexecparser.h" #include "untrustedprogramhandlerinterface.h" #include "kiogui_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -static KIO::OpenUrlJobHandlerInterface *s_openUrlJobHandler = nullptr; +static KIO::OpenWithHandlerInterface *s_openWithHandler = nullptr; namespace KIO { -// Hidden API because in KF6 we'll just check if the job's uiDelegate implements OpenUrlJobHandlerInterface. -KIOGUI_EXPORT void setDefaultOpenUrlJobHandler(KIO::OpenUrlJobHandlerInterface *iface) { s_openUrlJobHandler = iface; } +// Hidden API because in KF6 we'll just check if the job's uiDelegate implements OpenWithHandlerInterface. +KIOGUI_EXPORT void setDefaultOpenWithHandler(KIO::OpenWithHandlerInterface *iface) { s_openWithHandler = iface; } } class KIO::OpenUrlJobPrivate { public: explicit OpenUrlJobPrivate(const QUrl &url, OpenUrlJob *qq) : m_url(url), q(qq) { q->setCapabilities(KJob::Killable); } void emitAccessDenied(); void runUrlWithMimeType(); QString externalBrowser() const; bool runExternalBrowser(const QString &exe); void useSchemeHandler(); void determineLocalMimeType(); void statFile(); void scanFileWithGet(); QUrl m_url; KIO::OpenUrlJob * const q; QString m_suggestedFileName; QByteArray m_startupId; QString m_mimeTypeName; KService::Ptr m_preferredService; bool m_deleteTemporaryFile = false; bool m_runExecutables = false; bool m_externalBrowserEnabled = true; bool m_followRedirections = true; private: void executeCommand(); bool handleExecutables(const QMimeType &mimeType); void runLink(const QString &filePath, const QString &urlStr, const QString &optionalServiceName); void showOpenWithDialog(); void startService(const KService::Ptr &service, const QList &urls); void startService(const KService::Ptr &service) { startService(service, {m_url}); } }; KIO::OpenUrlJob::OpenUrlJob(const QUrl &url, QObject *parent) : KCompositeJob(parent), d(new OpenUrlJobPrivate(url, this)) { } KIO::OpenUrlJob::OpenUrlJob(const QUrl &url, const QString &mimeType, QObject *parent) : KCompositeJob(parent), d(new OpenUrlJobPrivate(url, this)) { d->m_mimeTypeName = mimeType; } KIO::OpenUrlJob::~OpenUrlJob() { } void KIO::OpenUrlJob::setDeleteTemporaryFile(bool b) { d->m_deleteTemporaryFile = b; } void KIO::OpenUrlJob::setSuggestedFileName(const QString &suggestedFileName) { d->m_suggestedFileName = suggestedFileName; } void KIO::OpenUrlJob::setStartupId(const QByteArray &startupId) { d->m_startupId = startupId; } void KIO::OpenUrlJob::setRunExecutables(bool allow) { d->m_runExecutables = allow; } void KIO::OpenUrlJob::setEnableExternalBrowser(bool b) { d->m_externalBrowserEnabled = b; } void KIO::OpenUrlJob::setFollowRedirections(bool b) { d->m_followRedirections = b; } static bool checkNeedPortalSupport() { return !(QStandardPaths::locate(QStandardPaths::RuntimeLocation, QLatin1String("flatpak-info")).isEmpty() || qEnvironmentVariableIsSet("SNAP")); } void KIO::OpenUrlJob::start() { if (!d->m_url.isValid() || d->m_url.scheme().isEmpty()) { const QString error = !d->m_url.isValid() ? d->m_url.errorString() : d->m_url.toDisplayString(); setError(KIO::ERR_MALFORMED_URL); setErrorText(i18n("Malformed URL\n%1", error)); emitResult(); return; } if (!KUrlAuthorized::authorizeUrlAction(QStringLiteral("open"), QUrl(), d->m_url)) { d->emitAccessDenied(); return; } if (d->m_externalBrowserEnabled && checkNeedPortalSupport()) { // Use the function from QDesktopServices as it handles portals correctly // Note that it falls back to "normal way" if the portal service isn't running. if (!QDesktopServices::openUrl(d->m_url)) { // Is this an actual error, or USER_CANCELED? setError(KJob::UserDefinedError); setErrorText(i18n("Failed to open %1", d->m_url.toDisplayString())); } emitResult(); return; } // If we know the mimetype, proceed if (!d->m_mimeTypeName.isEmpty()) { d->runUrlWithMimeType(); return; } if (d->m_externalBrowserEnabled && d->m_url.scheme().startsWith(QLatin1String("http"))) { const QString externalBrowser = d->externalBrowser(); if (!externalBrowser.isEmpty() && d->runExternalBrowser(externalBrowser)) { return; } } if (KIO::DesktopExecParser::hasSchemeHandler(d->m_url)) { d->useSchemeHandler(); return; } if (!KProtocolManager::supportsListing(d->m_url)) { // No support for listing => it can't be a directory (example: http) d->scanFileWithGet(); return; } // It may be a directory or a file, let's use stat to find out d->statFile(); } bool KIO::OpenUrlJob::doKill() { return true; } QString KIO::OpenUrlJobPrivate::externalBrowser() const { if (!m_externalBrowserEnabled) { return QString(); } const QString browserApp = KConfigGroup(KSharedConfig::openConfig(), "General").readEntry("BrowserApplication"); if (!browserApp.isEmpty()) { return browserApp; } // If a default browser isn't set in kdeglobals, fall back to mimeapps.list KSharedConfig::Ptr profile = KSharedConfig::openConfig(QStringLiteral("mimeapps.list"), KConfig::NoGlobals, QStandardPaths::GenericConfigLocation); const KConfigGroup defaultApps(profile, "Default Applications"); QString externalBrowser = defaultApps.readEntry("x-scheme-handler/https"); if (externalBrowser.isEmpty()) { externalBrowser = defaultApps.readEntry("x-scheme-handler/http"); } return externalBrowser; } bool KIO::OpenUrlJobPrivate::runExternalBrowser(const QString &exec) { if (exec.startsWith(QLatin1Char('!'))) { // Literal command const QString command = exec.midRef(1) + QLatin1String(" %u"); KService::Ptr service(new KService(QString(), command, QString())); startService(service); return true; } else { // Name of desktop file KService::Ptr service = KService::serviceByStorageId(exec); if (service) { startService(service); return true; } } return false; } void KIO::OpenUrlJobPrivate::useSchemeHandler() { // look for an application associated with x-scheme-handler/ const KService::Ptr service = KApplicationTrader::preferredService(QLatin1String("x-scheme-handler/") + m_url.scheme()); if (service) { startService(service); return; } // fallback, look for associated helper protocol Q_ASSERT(KProtocolInfo::isHelperProtocol(m_url.scheme())); const auto exec = KProtocolInfo::exec(m_url.scheme()); if (exec.isEmpty()) { // use default mimetype opener for file m_mimeTypeName = KProtocolManager::defaultMimetype(m_url); runUrlWithMimeType(); } else { KService::Ptr service(new KService(QString(), exec, QString())); startService(service); } } void KIO::OpenUrlJobPrivate::statFile() { Q_ASSERT(m_mimeTypeName.isEmpty()); KIO::StatJob *job = KIO::statDetails(m_url, KIO::StatJob::SourceSide, KIO::StatBasic, KIO::HideProgressInfo); job->setUiDelegate(nullptr); QObject::connect(job, &KJob::result, q, [=]() { const int errCode = job->error(); if (errCode) { // ERR_NO_CONTENT is not an error, but an indication no further // actions needs to be taken. if (errCode != KIO::ERR_NO_CONTENT) { q->setError(errCode); q->setErrorText(KIO::buildErrorString(errCode, job->errorText())); } q->emitResult(); return; } if (m_followRedirections) { // Update our URL in case of a redirection m_url = job->url(); } const KIO::UDSEntry entry = job->statResult(); const QString localPath = entry.stringValue(KIO::UDSEntry::UDS_LOCAL_PATH); if (!localPath.isEmpty()) { m_url = QUrl::fromLocalFile(localPath); } // mimetype already known? (e.g. print:/manager) m_mimeTypeName = entry.stringValue(KIO::UDSEntry::UDS_MIME_TYPE); if (!m_mimeTypeName.isEmpty()) { runUrlWithMimeType(); return; } if (entry.isDir()) { m_mimeTypeName = QStringLiteral("inode/directory"); runUrlWithMimeType(); } else { // it's a file // Start the timer. Once we get the timer event this // protocol server is back in the pool and we can reuse it. // This gives better performance than starting a new slave QTimer::singleShot(0, q, [this] { scanFileWithGet(); }); } }); } void KIO::OpenUrlJobPrivate::startService(const KService::Ptr &service, const QList &urls) { KIO::ApplicationLauncherJob *job = new KIO::ApplicationLauncherJob(service, q); job->setUrls(urls); job->setRunFlags(m_deleteTemporaryFile ? KIO::ApplicationLauncherJob::DeleteTemporaryFiles : KIO::ApplicationLauncherJob::RunFlags{}); job->setSuggestedFileName(m_suggestedFileName); job->setStartupId(m_startupId); job->setUiDelegate(q->uiDelegate()); q->addSubjob(job); job->start(); } static QMimeType fixupMimeType(const QString &mimeType, const QString &fileName) { QMimeDatabase db; QMimeType mime = db.mimeTypeForName(mimeType); if ((!mime.isValid() || mime.isDefault()) && !fileName.isEmpty()) { mime = db.mimeTypeForFile(fileName, QMimeDatabase::MatchExtension); } return mime; } void KIO::OpenUrlJobPrivate::scanFileWithGet() { Q_ASSERT(m_mimeTypeName.isEmpty()); // First, let's check for well-known extensions // Not over HTTP and not when there is a query in the URL, in any case. if (!m_url.hasQuery() && !m_url.scheme().startsWith(QLatin1String("http"))) { QMimeDatabase db; QMimeType mime = db.mimeTypeForUrl(m_url); if (!mime.isDefault()) { //qDebug() << "Scanfile: MIME TYPE is " << mime.name(); m_mimeTypeName = mime.name(); runUrlWithMimeType(); return; } } // No mimetype found, and the URL is not local (or fast mode not allowed). // We need to apply the 'KIO' method, i.e. either asking the server or // getting some data out of the file, to know what mimetype it is. if (!KProtocolManager::supportsReading(m_url)) { qCWarning(KIO_GUI) << "#### NO SUPPORT FOR READING!"; q->setError(KIO::ERR_CANNOT_READ); q->setErrorText(m_url.toDisplayString()); q->emitResult(); return; } //qDebug() << this << "Scanning file" << url; KIO::TransferJob *job = KIO::get(m_url, KIO::NoReload /*reload*/, KIO::HideProgressInfo); job->setUiDelegate(nullptr); QObject::connect(job, &KJob::result, q, [=]() { const int errCode = job->error(); if (errCode) { // ERR_NO_CONTENT is not an error, but an indication no further // actions needs to be taken. if (errCode != KIO::ERR_NO_CONTENT) { q->setError(errCode); q->setErrorText(job->errorText()); } q->emitResult(); } // if the job succeeded, we certainly hope it emitted mimetype()... }); QObject::connect(job, QOverload::of(&KIO::TransferJob::mimetype), q, [=](KIO::Job *, const QString &mimetype) { if (m_followRedirections) { // Update our URL in case of a redirection m_url = job->url(); } if (mimetype.isEmpty()) { qCWarning(KIO_GUI) << "get() didn't emit a mimetype! Probably a kioslave bug, please check the implementation of" << m_url.scheme(); } m_mimeTypeName = mimetype; // If the current mime-type is the default mime-type, then attempt to // determine the "real" mimetype from the file name (bug #279675) const QMimeType mime = fixupMimeType(m_mimeTypeName, m_suggestedFileName.isEmpty() ? m_url.fileName() : m_suggestedFileName); const QString mimeName = mime.name(); if (mime.isValid() && mimeName != m_mimeTypeName) { m_mimeTypeName = mimeName; } if (m_suggestedFileName.isEmpty()) { m_suggestedFileName = job->queryMetaData(QStringLiteral("content-disposition-filename")); } job->putOnHold(); KIO::Scheduler::publishSlaveOnHold(); runUrlWithMimeType(); }); } void KIO::OpenUrlJobPrivate::runLink(const QString &filePath, const QString &urlStr, const QString &optionalServiceName) { if (urlStr.isEmpty()) { q->setError(KJob::UserDefinedError); q->setErrorText(i18n("The desktop entry file\n%1\nis of type Link but has no URL=... entry.", filePath)); q->emitResult(); return; } m_url = QUrl::fromUserInput(urlStr); m_mimeTypeName.clear(); // X-KDE-LastOpenedWith holds the service desktop entry name that // should be preferred for opening this URL if possible. // This is used by the Recent Documents menu for instance. if (!optionalServiceName.isEmpty()) { m_preferredService = KService::serviceByDesktopName(optionalServiceName); } // Restart from scratch with the target of the link q->start(); } void KIO::OpenUrlJobPrivate::emitAccessDenied() { q->setError(KIO::ERR_ACCESS_DENIED); q->setErrorText(KIO::buildErrorString(KIO::ERR_ACCESS_DENIED, m_url.toDisplayString())); q->emitResult(); } // was: KRun::isExecutable. Feel free to make public if needed. static bool isExecutableMime(const QMimeType &mimeType) { return (mimeType.inherits(QLatin1String("application/x-desktop")) || mimeType.inherits(QLatin1String("application/x-executable")) || /* See https://bugs.freedesktop.org/show_bug.cgi?id=97226 */ mimeType.inherits(QLatin1String("application/x-sharedlib")) || mimeType.inherits(QLatin1String("application/x-ms-dos-executable")) || mimeType.inherits(QLatin1String("application/x-shellscript"))); } // Helper function that returns whether a file has the execute bit set or not. static bool hasExecuteBit(const QString &fileName) { return QFileInfo(fileName).isExecutable(); } namespace KIO { extern KIO::UntrustedProgramHandlerInterface *defaultUntrustedProgramHandler(); } // Return true if handled in any way (success or error) // Return false if the caller should proceed bool KIO::OpenUrlJobPrivate::handleExecutables(const QMimeType &mimeType) { if (!KAuthorized::authorize(QStringLiteral("shell_access"))) { emitAccessDenied(); return true; // handled } // Check whether file is an executable script #ifdef Q_OS_WIN const bool isNativeBinary = !mimeType.inherits(QStringLiteral("text/plain")); #else const bool isNativeBinary = !mimeType.inherits(QStringLiteral("text/plain")) && !mimeType.inherits(QStringLiteral("application/x-ms-dos-executable")); #endif if (!m_url.isLocalFile() || !m_runExecutables) { if (isNativeBinary) { // Show warning for executables that aren't scripts q->setError(KJob::UserDefinedError); q->setErrorText(i18n("The file \"%1\" is an executable program. " "For safety it will not be started.", m_url.toDisplayString())); q->emitResult(); return true; // handled } // Let scripts be open as text files, if remote, or no exec allowed return false; } const QString localPath = m_url.toLocalFile(); // For executables that aren't scripts and without execute bit, // show prompt asking user if he wants to run the program. if (!hasExecuteBit(localPath)) { if (!isNativeBinary) { // Don't try to run scripts/exes without execute bit, instead // open them with default application return false; } KIO::UntrustedProgramHandlerInterface *untrustedProgramHandler = defaultUntrustedProgramHandler(); if (!untrustedProgramHandler) { // No way to ask the user to make it executable q->setError(KJob::UserDefinedError); q->setErrorText(i18n("The program \"%1\" needs to have executable permission before it can be launched.", localPath)); q->emitResult(); return true; } QObject::connect(untrustedProgramHandler, &KIO::UntrustedProgramHandlerInterface::result, q, [=](bool result) { if (result) { QString errorString; if (untrustedProgramHandler->setExecuteBit(localPath, errorString)) { executeCommand(); } else { q->setError(KJob::UserDefinedError); q->setErrorText(i18n("Unable to make file \"%1\" executable.\n%2.", localPath, errorString)); q->emitResult(); } } else { q->setError(KIO::ERR_USER_CANCELED); q->emitResult(); } }); untrustedProgramHandler->showUntrustedProgramWarning(q, m_url.fileName()); return true; } // Local executable with execute bit, proceed executeCommand(); return true; // handled } void KIO::OpenUrlJobPrivate::executeCommand() { // Execute the URL as a command. This is how we start scripts and executables KIO::CommandLauncherJob *job = new KIO::CommandLauncherJob(m_url.toLocalFile()); job->setUiDelegate(q->uiDelegate()); job->setStartupId(m_startupId); job->setWorkingDirectory(m_url.adjusted(QUrl::RemoveFilename).toLocalFile()); q->addSubjob(job); job->start(); // TODO implement deleting the file if tempFile==true // CommandLauncherJob doesn't support that, unlike ApplicationLauncherJob // We'd have to do it in KProcessRunner. } void KIO::OpenUrlJobPrivate::runUrlWithMimeType() { // Tell the app, in case it wants us to stop here Q_EMIT q->mimeTypeFound(m_mimeTypeName); if (q->error() == KJob::KilledJobError) { q->emitResult(); return; } // Support for preferred service setting, see setPreferredService if (m_preferredService && m_preferredService->hasMimeType(m_mimeTypeName)) { startService(m_preferredService); return; } // Local desktop file if (m_url.isLocalFile() && m_mimeTypeName == QLatin1String("application/x-desktop")) { if (m_url.fileName() == QLatin1String(".directory")) { // We cannot execute a .directory file. Open with a text editor instead. m_mimeTypeName = QStringLiteral("text/plain"); } else { const QString filePath = m_url.toLocalFile(); KDesktopFile cfg(filePath); KConfigGroup cfgGroup = cfg.desktopGroup(); if (!cfgGroup.hasKey("Type")) { q->setError(KJob::UserDefinedError); q->setErrorText(i18n("The desktop entry file %1 has no Type=... entry.", filePath)); q->emitResult(); return; } if ((cfg.hasApplicationType() || cfg.readType() == QLatin1String("Service")) // for kio_settings && !cfgGroup.readEntry("Exec").isEmpty() && m_runExecutables) { KService::Ptr service(new KService(filePath)); startService(service, {}); return; } else if (cfg.hasLinkType()) { runLink(filePath, cfg.readUrl(), cfg.desktopGroup().readEntry("X-KDE-LastOpenedWith")); return; } } } // Scripts and executables QMimeDatabase db; const QMimeType mimeType = db.mimeTypeForName(m_mimeTypeName); if (isExecutableMime(mimeType)) { if (handleExecutables(mimeType)) { return; } } // General case: look up associated application KService::Ptr service = KApplicationTrader::preferredService(m_mimeTypeName); if (service) { startService(service); } else { showOpenWithDialog(); } } void KIO::OpenUrlJobPrivate::showOpenWithDialog() { if (!KAuthorized::authorizeAction(QStringLiteral("openwith"))) { q->setError(KJob::UserDefinedError); q->setErrorText(i18n("You are not authorized to select an application to open this file.")); q->emitResult(); return; } - if (!s_openUrlJobHandler || QOperatingSystemVersion::currentType() == QOperatingSystemVersion::Windows) { + if (!s_openWithHandler || QOperatingSystemVersion::currentType() == QOperatingSystemVersion::Windows) { // As KDE on windows doesn't know about the windows default applications, offers will be empty in nearly all cases. // So we use QDesktopServices::openUrl to let windows decide how to open the file. // It's also our fallback if there's no handler to show an open-with dialog. if (!QDesktopServices::openUrl(m_url)) { q->setError(KJob::UserDefinedError); q->setErrorText(i18n("Failed to open the file.")); } q->emitResult(); return; } - QObject::connect(s_openUrlJobHandler, &KIO::OpenUrlJobHandlerInterface::canceled, q, [this]() { + QObject::connect(s_openWithHandler, &KIO::OpenWithHandlerInterface::canceled, q, [this]() { q->setError(KIO::ERR_USER_CANCELED); q->emitResult(); }); - QObject::connect(s_openUrlJobHandler, &KIO::OpenUrlJobHandlerInterface::serviceSelected, q, [this](const KService::Ptr &service) { + QObject::connect(s_openWithHandler, &KIO::OpenWithHandlerInterface::serviceSelected, q, [this](const KService::Ptr &service) { startService(service); }); - s_openUrlJobHandler->promptUserForApplication(q, {m_url}, m_mimeTypeName); + s_openWithHandler->promptUserForApplication(q, {m_url}, m_mimeTypeName); } void KIO::OpenUrlJob::slotResult(KJob *job) { // This is only used for the final application/launcher job, so we're done when it's done const int errCode = job->error(); if (errCode) { setError(errCode); setErrorText(KIO::buildErrorString(errCode, job->errorText())); } emitResult(); } diff --git a/src/gui/openurljobhandlerinterface.cpp b/src/gui/openwithhandlerinterface.cpp similarity index 76% rename from src/gui/openurljobhandlerinterface.cpp rename to src/gui/openwithhandlerinterface.cpp index e7cb521f..64fdd600 100644 --- a/src/gui/openurljobhandlerinterface.cpp +++ b/src/gui/openwithhandlerinterface.cpp @@ -1,41 +1,41 @@ /* This file is part of the KDE libraries Copyright (c) 2020 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "openurljobhandlerinterface.h" +#include "openwithhandlerinterface.h" #include #include #include "kiocoredebug.h" using namespace KIO; -class KIO::OpenUrlJobHandlerInterfacePrivate {}; +class KIO::OpenWithHandlerInterfacePrivate {}; -OpenUrlJobHandlerInterface::OpenUrlJobHandlerInterface() = default; +OpenWithHandlerInterface::OpenWithHandlerInterface() = default; -OpenUrlJobHandlerInterface::~OpenUrlJobHandlerInterface() = default; +OpenWithHandlerInterface::~OpenWithHandlerInterface() = default; -void OpenUrlJobHandlerInterface::promptUserForApplication(OpenUrlJob *job, const QList &urls, const QString &mimeType) +void OpenWithHandlerInterface::promptUserForApplication(OpenUrlJob *job, const QList &urls, const QString &mimeType) { Q_UNUSED(job) Q_UNUSED(urls) Q_UNUSED(mimeType) Q_EMIT canceled(); } diff --git a/src/gui/openurljobhandlerinterface.h b/src/gui/openwithhandlerinterface.h similarity index 83% rename from src/gui/openurljobhandlerinterface.h rename to src/gui/openwithhandlerinterface.h index 4d69e607..d56d3c36 100644 --- a/src/gui/openurljobhandlerinterface.h +++ b/src/gui/openwithhandlerinterface.h @@ -1,91 +1,91 @@ /* This file is part of the KDE libraries Copyright (c) 2020 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef OPENURLJOBHANDLERINTERFACE_H -#define OPENURLJOBHANDLERINTERFACE_H +#ifndef OPENWITHHANDLERINTERFACE_H +#define OPENWITHHANDLERINTERFACE_H #include #include #include class QString; namespace KIO { class OpenUrlJob; -class OpenUrlJobHandlerInterfacePrivate; +class OpenWithHandlerInterfacePrivate; /** - * @class OpenUrlJobHandlerInterface openurljobhandlerinterface.h - * @brief The OpenUrlJobHandlerInterface class allows OpenUrlJob to + * @class OpenWithHandlerInterface openwithhandlerinterface.h + * @brief The OpenWithHandlerInterface class allows OpenUrlJob to * prompt the user about which application to use to open URLs that do not * have an associated application (via the "Open With" dialog). * * This extension mechanism for jobs is similar to KIO::JobUiDelegateExtension * and UntrustedProgramHandlerInterface. * * @since 5.71 */ -class KIOGUI_EXPORT OpenUrlJobHandlerInterface : public QObject +class KIOGUI_EXPORT OpenWithHandlerInterface : public QObject { Q_OBJECT protected: /** * Constructor */ - OpenUrlJobHandlerInterface(); + OpenWithHandlerInterface(); /** * Destructor */ - ~OpenUrlJobHandlerInterface() override; + ~OpenWithHandlerInterface() override; public: /** * Show the "Open With" dialog. * @param job the job calling this. Useful to get all its properties * @param urls the URLs to open * @param mimeType the mimeType of the URLs, if known. Can be empty otherwise. * * Implementations of this method must emit either serviceSelected or canceled. * * The default implementation in this base class simply emits canceled(). * Any application using KIO::JobUiDelegate (from KIOWidgets) will benefit from an * automatically registered subclass which implements this method using KOpenWithDialog. */ virtual void promptUserForApplication(KIO::OpenUrlJob *job, const QList &urls, const QString &mimeType); Q_SIGNALS: /** * Emitted by promptUserForApplication() once the user chooses an application. * @param service the application chosen by the user */ void serviceSelected(const KService::Ptr &service); /** * Emitted by promptUserForApplication() if the user canceled the application selection dialog. */ void canceled(); private: - QScopedPointer d; + QScopedPointer d; }; } -#endif // OPENURLJOBHANDLERINTERFACE_H +#endif // OPENWITHHANDLERINTERFACE_H diff --git a/src/widgets/CMakeLists.txt b/src/widgets/CMakeLists.txt index c626e126..ee013a89 100644 --- a/src/widgets/CMakeLists.txt +++ b/src/widgets/CMakeLists.txt @@ -1,227 +1,227 @@ project(KIOWidgets) #include (ConfigureChecks.cmake) find_package(ACL) set(HAVE_LIBACL ${ACL_FOUND}) set(HAVE_POSIX_ACL ${ACL_FOUND}) set_package_properties(ACL PROPERTIES DESCRIPTION "LibACL" URL "ftp://oss.sgi.com/projects/xfs/cmd_tars" TYPE RECOMMENDED PURPOSE "Support for manipulating access control lists") configure_file(config-kiowidgets.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kiowidgets.h) set(kiowidgets_SRCS accessmanager.cpp accessmanagerreply_p.cpp fileundomanager.cpp kacleditwidget.cpp kpropertiesdialog.cpp kurlrequesterdialog.cpp kurlcombobox.cpp kfileitemactions.cpp delegateanimationhandler.cpp imagefilter.cpp kfileitemdelegate.cpp kdesktopfileactions.cpp kopenwithdialog.cpp kfile.cpp pastedialog.cpp paste.cpp clipboardupdater.cpp kabstractfileitemactionplugin.cpp koverlayiconplugin.cpp kbuildsycocaprogressdialog.cpp kurlrequester.cpp krun.cpp sslui.cpp kurlpixmapprovider.cpp pixmaploader.cpp thumbsequencecreator.cpp thumbcreator.cpp kshellcompletion.cpp kurlcompletion.cpp kurifilter.cpp dropjob.cpp openfilemanagerwindowjob.cpp pastejob.cpp previewjob.cpp renamedialog.cpp ksslcertificatebox.cpp kdynamicjobtracker.cpp ksslinfodialog.cpp joburlcache.cpp skipdialog.cpp jobuidelegate.cpp kdirlister.cpp kdirmodel.cpp executablefileopendialog.cpp dndpopupmenuplugin.cpp kurifiltersearchprovideractions.cpp renamefiledialog.cpp widgetsuntrustedprogramhandler.cpp - widgetsopenurljobhandler.cpp + widgetsopenwithhandler.cpp ) if (WIN32) list(APPEND kiowidgets_SRCS krun_win.cpp ) else() list(APPEND kiowidgets_SRCS kautomount.cpp ) endif() ecm_qt_declare_logging_category(kiowidgets_SRCS HEADER kio_widgets_debug.h IDENTIFIER KIO_WIDGETS CATEGORY_NAME kf5.kio.widgets DESCRIPTION "KIOWidgets (KIO)" EXPORT KIO ) ecm_qt_export_logging_category( IDENTIFIER category CATEGORY_NAME kf5.kio.kdirmodel DESCRIPTION "KDirModel (KIO)" EXPORT KIO ) qt5_add_dbus_adaptor(kiowidgets_SRCS org.kde.kio.FileUndoManager.xml fileundomanager_p.h KIO::FileUndoManagerPrivate fileundomanager_adaptor KIOFileUndoManagerAdaptor) qt5_add_dbus_interface(kiowidgets_SRCS org.kde.kuiserver.xml kuiserver_interface) ki18n_wrap_ui(kiowidgets_SRCS checksumswidget.ui certificateparty.ui sslinfo.ui kpropertiesdesktopadvbase.ui kpropertiesdesktopbase.ui ) add_library(KF5KIOWidgets ${kiowidgets_SRCS}) add_library(KF5::KIOWidgets ALIAS KF5KIOWidgets) ecm_generate_export_header(KF5KIOWidgets BASE_NAME KIOWidgets GROUP_BASE_NAME KF VERSION ${KF5_VERSION} DEPRECATED_BASE_VERSION 0 DEPRECATION_VERSIONS 4.0 4.1 4.3 4.4 4.5 4.6 4.7 5.0 5.4 5.6 5.25 5.31 5.32 5.64 5.66 5.71 EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT} ) # TODO: add support for EXCLUDE_DEPRECATED_BEFORE_AND_AT to all KIO libs # needs fixing of undeprecated API being still implemented using own deprecated API target_include_directories(KF5KIOWidgets INTERFACE "$") target_link_libraries(KF5KIOWidgets PUBLIC KF5::KIOGui KF5::KIOCore KF5::JobWidgets KF5::Service Qt5::Network # SSL KF5::Completion # KUrlCompletion uses KCompletion KF5::WidgetsAddons # keditlistwidget PRIVATE Qt5::Concurrent Qt5::DBus KF5::I18n KF5::IconThemes # KIconLoader KF5::WindowSystem # KStartupInfo KF5::ConfigWidgets # KColorScheme ) if(ACL_FOUND) target_link_libraries(KF5KIOWidgets PRIVATE ${ACL_LIBS}) endif() set_target_properties(KF5KIOWidgets PROPERTIES VERSION ${KIO_VERSION_STRING} SOVERSION ${KIO_SOVERSION} EXPORT_NAME KIOWidgets ) # Headers not prefixed with KIO/ ecm_generate_headers(KIOWidgets_HEADERS HEADER_NAMES KPropertiesDialog KUrlRequesterDialog KUrlComboBox KFileItemActions KFileItemDelegate KAutoMount KDesktopFileActions KOpenWithDialog KAbstractFileItemActionPlugin KOverlayIconPlugin KBuildSycocaProgressDialog KFile KUrlRequester KRun KUrlPixmapProvider KSslCertificateBox KSslInfoDialog KDirLister KDirModel KShellCompletion KUrlCompletion KUriFilter REQUIRED_HEADERS KIOWidgets_HEADERS ) # Headers prefixed with KIO/ ecm_generate_headers(KIOWidgets_CamelCase_HEADERS HEADER_NAMES AccessManager SslUi ThumbSequenceCreator ThumbCreator DropJob DndPopupMenuPlugin OpenFileManagerWindowJob PasteJob PreviewJob RenameDialog SkipDialog JobUiDelegate FileUndoManager Paste PixmapLoader KUriFilterSearchProviderActions # KF6: fix and move to non-KIO prefixed install folder RenameFileDialog PREFIX KIO REQUIRED_HEADERS KIO_namespaced_widgets_HEADERS ) install(FILES ${KIOWidgets_CamelCase_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KIOWidgets/KIO COMPONENT Devel) install(TARGETS KF5KIOWidgets EXPORT KF5KIOTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES org.kde.kio.FileUndoManager.xml DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR} RENAME kf5_org.kde.kio.FileUndoManager.xml) install(FILES ${KIO_namespaced_widgets_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KIOWidgets/kio COMPONENT Devel) install(FILES ${KIOWidgets_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/kiowidgets_export.h DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KIOWidgets COMPONENT Devel) install(FILES kfileitemactionplugin.desktop kpropertiesdialogplugin.desktop kurifilterplugin.desktop konqpopupmenuplugin.desktop kiodndpopupmenuplugin.desktop DESTINATION ${KDE_INSTALL_KSERVICETYPES5DIR} ) # make available to ecm_add_qch in parent folder set(KIOWidgets_QCH_SOURCES ${KIOWidgets_HEADERS} ${KIO_namespaced_widgets_HEADERS} PARENT_SCOPE) include(ECMGeneratePriFile) ecm_generate_pri_file(BASE_NAME KIOWidgets LIB_NAME KF5KIOWidgets DEPS "KIOGui KIOCore KBookmarks KXmlGui Solid" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}/KIOWidgets) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) diff --git a/src/widgets/jobuidelegate.cpp b/src/widgets/jobuidelegate.cpp index 56def164..e3d9a178 100644 --- a/src/widgets/jobuidelegate.cpp +++ b/src/widgets/jobuidelegate.cpp @@ -1,453 +1,453 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Stephan Kulow David Faure Copyright (C) 2006 Kevin Ottens Copyright (C) 2013 Dawit Alemayehu This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "jobuidelegate.h" #include #include "kio_widgets_debug.h" #include "kiogui_export.h" #include "widgetsuntrustedprogramhandler.h" -#include "widgetsopenurljobhandler.h" +#include "widgetsopenwithhandler.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kio/scheduler.h" class Q_DECL_HIDDEN KIO::JobUiDelegate::Private { public: }; namespace KIO { KIOGUI_EXPORT void setDefaultUntrustedProgramHandler(KIO::UntrustedProgramHandlerInterface *iface); -KIOGUI_EXPORT void setDefaultOpenUrlJobHandler(KIO::OpenUrlJobHandlerInterface *iface); +KIOGUI_EXPORT void setDefaultOpenWithHandler(KIO::OpenWithHandlerInterface *iface); } KIO::JobUiDelegate::JobUiDelegate() : d(new Private()) { // KF6 TODO: remove, inherit from WidgetsUntrustedProgramHandler instead static WidgetsUntrustedProgramHandler s_handler; KIO::setDefaultUntrustedProgramHandler(&s_handler); - static WidgetsOpenUrlJobHandler s_openUrlHandler; - KIO::setDefaultOpenUrlJobHandler(&s_openUrlHandler); + static WidgetsOpenWithHandler s_openUrlHandler; + KIO::setDefaultOpenWithHandler(&s_openUrlHandler); } KIO::JobUiDelegate::~JobUiDelegate() { delete d; } /* Returns the top most window associated with widget. Unlike QWidget::window(), this function does its best to find and return the main application window associated with the given widget. If widget itself is a dialog or its parent is a dialog, and that dialog has a parent widget then this function will iterate through all those widgets to find the top most window, which most of the time is the main window of the application. By contrast, QWidget::window() would simply return the first file dialog it encountered since it is the "next ancestor widget that has (or could have) a window-system frame". */ static QWidget *topLevelWindow(QWidget *widget) { QWidget *w = widget; while (w && w->parentWidget()) { w = w->parentWidget(); } return (w ? w->window() : nullptr); } class JobUiDelegateStatic : public QObject { Q_OBJECT public: void registerWindow(QWidget *wid) { if (!wid) { return; } QWidget *window = topLevelWindow(wid); QObject *obj = static_cast(window); if (!m_windowList.contains(obj)) { // We must store the window Id because by the time // the destroyed signal is emitted we can no longer // access QWidget::winId() (already destructed) WId windowId = window->winId(); m_windowList.insert(obj, windowId); connect(window, &QObject::destroyed, this, &JobUiDelegateStatic::slotUnregisterWindow); QDBusInterface(QStringLiteral("org.kde.kded5"), QStringLiteral("/kded"), QStringLiteral("org.kde.kded5")). call(QDBus::NoBlock, QStringLiteral("registerWindowId"), qlonglong(windowId)); } } public Q_SLOTS: void slotUnregisterWindow(QObject *obj) { if (!obj) { return; } QMap::Iterator it = m_windowList.find(obj); if (it == m_windowList.end()) { return; } WId windowId = it.value(); disconnect(it.key(), &QObject::destroyed, this, &JobUiDelegateStatic::slotUnregisterWindow); m_windowList.erase(it); QDBusInterface(QStringLiteral("org.kde.kded5"), QStringLiteral("/kded"), QStringLiteral("org.kde.kded5")). call(QDBus::NoBlock, QStringLiteral("unregisterWindowId"), qlonglong(windowId)); } private: QMap m_windowList; }; Q_GLOBAL_STATIC(JobUiDelegateStatic, s_static) KIO::JobUiDelegate::JobUiDelegate(KJobUiDelegate::Flags flags, QWidget *window) : KDialogJobUiDelegate(flags, window), d(new Private()) { s_static()->registerWindow(window); } void KIO::JobUiDelegate::setWindow(QWidget *window) { KDialogJobUiDelegate::setWindow(window); s_static()->registerWindow(window); } void KIO::JobUiDelegate::unregisterWindow(QWidget *window) { s_static()->slotUnregisterWindow(window); } KIO::RenameDialog_Result KIO::JobUiDelegate::askFileRename(KJob *job, const QString &caption, const QUrl &src, const QUrl &dest, KIO::RenameDialog_Options options, QString &newDest, KIO::filesize_t sizeSrc, KIO::filesize_t sizeDest, const QDateTime &ctimeSrc, const QDateTime &ctimeDest, const QDateTime &mtimeSrc, const QDateTime &mtimeDest) { //qDebug() << "job=" << job; // We now do it in process, so that opening the rename dialog // doesn't start uiserver for nothing if progressId=0 (e.g. F2 in konq) KIO::RenameDialog dlg(KJobWidgets::window(job), caption, src, dest, options, sizeSrc, sizeDest, ctimeSrc, ctimeDest, mtimeSrc, mtimeDest); dlg.setWindowModality(Qt::WindowModal); connect(job, &KJob::finished, &dlg, &QDialog::reject); // #192976 KIO::RenameDialog_Result res = static_cast(dlg.exec()); if (res == Result_AutoRename) { newDest = dlg.autoDestUrl().path(); } else { newDest = dlg.newDestUrl().path(); } return res; } KIO::SkipDialog_Result KIO::JobUiDelegate::askSkip(KJob *job, KIO::SkipDialog_Options options, const QString &error_text) { KIO::SkipDialog dlg(KJobWidgets::window(job), options, error_text); dlg.setWindowModality(Qt::WindowModal); connect(job, &KJob::finished, &dlg, &QDialog::reject); // #192976 return static_cast(dlg.exec()); } bool KIO::JobUiDelegate::askDeleteConfirmation(const QList &urls, DeletionType deletionType, ConfirmationType confirmationType) { QString keyName; bool ask = (confirmationType == ForceConfirmation); if (!ask) { KSharedConfigPtr kioConfig = KSharedConfig::openConfig(QStringLiteral("kiorc"), KConfig::NoGlobals); // The default value for confirmations is true for delete and false // for trash. If you change this, please also update: // dolphin/src/settings/general/confirmationssettingspage.cpp bool defaultValue = true; switch (deletionType) { case Delete: keyName = QStringLiteral("ConfirmDelete"); break; case Trash: keyName = QStringLiteral("ConfirmTrash"); defaultValue = false; break; case EmptyTrash: keyName = QStringLiteral("ConfirmEmptyTrash"); break; } ask = kioConfig->group("Confirmations").readEntry(keyName, defaultValue); } if (ask) { QStringList prettyList; prettyList.reserve(urls.size()); for (const QUrl &url : urls) { if (url.scheme() == QLatin1String("trash")) { QString path = url.path(); // HACK (#98983): remove "0-foo". Note that it works better than // displaying KFileItem::name(), for files under a subdir. path.remove(QRegularExpression(QStringLiteral("^/[0-9]*-"))); prettyList.append(path); } else { prettyList.append(url.toDisplayString(QUrl::PreferLocalFile)); } } int result; QWidget *widget = window(); const KMessageBox::Options options(KMessageBox::Notify | KMessageBox::WindowModal); switch (deletionType) { case Delete: if (prettyList.count() == 1) { result = KMessageBox::warningContinueCancel( widget, xi18nc("@info", "Do you really want to permanently delete this item?%1This action cannot be undone.", prettyList.first()), i18n("Delete Permanently"), KStandardGuiItem::del(), KStandardGuiItem::cancel(), keyName, options); } else { result = KMessageBox::warningContinueCancelList( widget, xi18ncp("@info", "Do you really want to permanently delete this item?This action cannot be undone.", "Do you really want to permanently delete these %1 items?This action cannot be undone.", prettyList.count()), prettyList, i18n("Delete Permanently"), KStandardGuiItem::del(), KStandardGuiItem::cancel(), keyName, options); } break; case EmptyTrash: result = KMessageBox::warningContinueCancel( widget, xi18nc("@info", "Do you want to permanently delete all items from the Trash?This action cannot be undone."), i18n("Delete Permanently"), KGuiItem(i18nc("@action:button", "Empty Trash"), QIcon::fromTheme(QStringLiteral("user-trash"))), KStandardGuiItem::cancel(), keyName, options); break; case Trash: default: if (prettyList.count() == 1) { result = KMessageBox::warningContinueCancel( widget, xi18nc("@info", "Do you really want to move this item to the Trash?%1", prettyList.first()), i18n("Move to Trash"), KGuiItem(i18n("Move to Trash"), QStringLiteral("user-trash")), KStandardGuiItem::cancel(), keyName, options); } else { result = KMessageBox::warningContinueCancelList( widget, i18np("Do you really want to move this item to the Trash?", "Do you really want to move these %1 items to the Trash?", prettyList.count()), prettyList, i18n("Move to Trash"), KGuiItem(i18n("Move to Trash"), QStringLiteral("user-trash")), KStandardGuiItem::cancel(), keyName, options); } } if (!keyName.isEmpty()) { // Check kmessagebox setting... erase & copy to konquerorrc. KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup notificationGroup(config, "Notification Messages"); if (!notificationGroup.readEntry(keyName, true)) { notificationGroup.writeEntry(keyName, true); notificationGroup.sync(); KSharedConfigPtr kioConfig = KSharedConfig::openConfig(QStringLiteral("kiorc"), KConfig::NoGlobals); kioConfig->group("Confirmations").writeEntry(keyName, false); } } return (result == KMessageBox::Continue); } return true; } int KIO::JobUiDelegate::requestMessageBox(KIO::JobUiDelegate::MessageBoxType type, const QString &text, const QString &caption, const QString &buttonYes, const QString &buttonNo, const QString &iconYes, const QString &iconNo, const QString &dontAskAgainName, const KIO::MetaData &metaData) { int result = -1; //qDebug() << type << text << "caption=" << caption; KConfig config(QStringLiteral("kioslaverc")); KMessageBox::setDontShowAgainConfig(&config); const KGuiItem buttonYesGui(buttonYes, iconYes); const KGuiItem buttonNoGui(buttonNo, iconNo); KMessageBox::Options options(KMessageBox::Notify | KMessageBox::WindowModal); switch (type) { case QuestionYesNo: result = KMessageBox::questionYesNo( window(), text, caption, buttonYesGui, buttonNoGui, dontAskAgainName, options); break; case WarningYesNo: result = KMessageBox::warningYesNo( window(), text, caption, buttonYesGui, buttonNoGui, dontAskAgainName, options | KMessageBox::Dangerous); break; case WarningYesNoCancel: result = KMessageBox::warningYesNoCancel( window(), text, caption, buttonYesGui, buttonNoGui, KStandardGuiItem::cancel(), dontAskAgainName, options); break; case WarningContinueCancel: result = KMessageBox::warningContinueCancel( window(), text, caption, buttonYesGui, KStandardGuiItem::cancel(), dontAskAgainName, options); break; case Information: KMessageBox::information(window(), text, caption, dontAskAgainName, options); result = 1; // whatever break; case SSLMessageBox: { QPointer kid(new KSslInfoDialog(window())); //### this is boilerplate code and appears in khtml_part.cpp almost unchanged! #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) const QStringList sl = metaData.value(QStringLiteral("ssl_peer_chain")).split(QLatin1Char('\x01'), QString::SkipEmptyParts); #else const QStringList sl = metaData.value(QStringLiteral("ssl_peer_chain")).split(QLatin1Char('\x01'), Qt::SkipEmptyParts); #endif QList certChain; bool decodedOk = true; for (const QString &s : sl) { certChain.append(QSslCertificate(s.toLatin1())); //or is it toLocal8Bit or whatever? if (certChain.last().isNull()) { decodedOk = false; break; } } if (decodedOk) { result = 1; // whatever kid->setSslInfo(certChain, metaData.value(QStringLiteral("ssl_peer_ip")), text, // the URL metaData.value(QStringLiteral("ssl_protocol_version")), metaData.value(QStringLiteral("ssl_cipher")), metaData.value(QStringLiteral("ssl_cipher_used_bits")).toInt(), metaData.value(QStringLiteral("ssl_cipher_bits")).toInt(), KSslInfoDialog::certificateErrorsFromString(metaData.value(QStringLiteral("ssl_cert_errors")))); kid->exec(); } else { result = -1; KMessageBox::information(window(), i18n("The peer SSL certificate chain appears to be corrupt."), i18n("SSL"), QString(), options); } // KSslInfoDialog deletes itself (Qt::WA_DeleteOnClose). delete kid; break; } case WarningContinueCancelDetailed: { const QString details = metaData.value(QStringLiteral("privilege_conf_details")); result = KMessageBox::warningContinueCancelDetailed( window(), text, caption, KStandardGuiItem::cont(), KStandardGuiItem::cancel(), dontAskAgainName, options | KMessageBox::Dangerous, details); break; } default: qCWarning(KIO_WIDGETS) << "Unknown type" << type; result = 0; break; } KMessageBox::setDontShowAgainConfig(nullptr); return result; } KIO::ClipboardUpdater *KIO::JobUiDelegate::createClipboardUpdater(Job *job, ClipboardUpdaterMode mode) { if (qobject_cast(qApp)) { return new KIO::ClipboardUpdater(job, mode); } return nullptr; } void KIO::JobUiDelegate::updateUrlInClipboard(const QUrl &src, const QUrl &dest) { if (qobject_cast(qApp)) { KIO::ClipboardUpdater::update(src, dest); } } class KIOWidgetJobUiDelegateFactory : public KIO::JobUiDelegateFactory { public: KJobUiDelegate *createDelegate() const override { return new KIO::JobUiDelegate; } }; Q_GLOBAL_STATIC(KIOWidgetJobUiDelegateFactory, globalUiDelegateFactory) Q_GLOBAL_STATIC(KIO::JobUiDelegate, globalUiDelegate) // Simply linking to this library, creates a GUI job delegate and delegate extension for all KIO jobs static void registerJobUiDelegate() { KIO::setDefaultJobUiDelegateFactory(globalUiDelegateFactory()); KIO::setDefaultJobUiDelegateExtension(globalUiDelegate()); } Q_CONSTRUCTOR_FUNCTION(registerJobUiDelegate) #include "jobuidelegate.moc" diff --git a/src/widgets/widgetsopenurljobhandler.cpp b/src/widgets/widgetsopenwithhandler.cpp similarity index 84% rename from src/widgets/widgetsopenurljobhandler.cpp rename to src/widgets/widgetsopenwithhandler.cpp index 803f3a2f..f4147438 100644 --- a/src/widgets/widgetsopenurljobhandler.cpp +++ b/src/widgets/widgetsopenwithhandler.cpp @@ -1,55 +1,55 @@ /* This file is part of the KDE libraries Copyright (c) 2020 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kopenwithdialog.h" #include "openurljob.h" -#include "widgetsopenurljobhandler.h" +#include "widgetsopenwithhandler.h" #include #include #include #include -KIO::WidgetsOpenUrlJobHandler::WidgetsOpenUrlJobHandler() - : KIO::OpenUrlJobHandlerInterface() +KIO::WidgetsOpenWithHandler::WidgetsOpenWithHandler() + : KIO::OpenWithHandlerInterface() { } -KIO::WidgetsOpenUrlJobHandler::~WidgetsOpenUrlJobHandler() = default; +KIO::WidgetsOpenWithHandler::~WidgetsOpenWithHandler() = default; -void KIO::WidgetsOpenUrlJobHandler::promptUserForApplication(KIO::OpenUrlJob *job, const QList &urls, const QString &mimeType) +void KIO::WidgetsOpenWithHandler::promptUserForApplication(KIO::OpenUrlJob *job, const QList &urls, const QString &mimeType) { QWidget *parentWidget = job ? KJobWidgets::window(job) : qApp->activeWindow(); KOpenWithDialog *dialog = new KOpenWithDialog(urls, mimeType, QString(), QString(), parentWidget); dialog->setAttribute(Qt::WA_DeleteOnClose); connect(dialog, &QDialog::accepted, this, [=]() { KService::Ptr service = dialog->service(); if (!service) { service = KService::Ptr(new KService(QString() /*name*/, dialog->text(), QString() /*icon*/)); } Q_EMIT serviceSelected(service); }); connect(dialog, &QDialog::rejected, this, [this]() { Q_EMIT canceled(); }); dialog->show(); } diff --git a/src/widgets/widgetsopenurljobhandler.h b/src/widgets/widgetsopenwithhandler.h similarity index 79% rename from src/widgets/widgetsopenurljobhandler.h rename to src/widgets/widgetsopenwithhandler.h index 30d6b780..94d97832 100644 --- a/src/widgets/widgetsopenurljobhandler.h +++ b/src/widgets/widgetsopenwithhandler.h @@ -1,50 +1,50 @@ /* This file is part of the KDE libraries Copyright (c) 2020 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef WIDGETSOPENURLJOBHANDLER_H -#define WIDGETSOPENURLJOBHANDLER_H +#ifndef WIDGETSOPENWITHHANDLER_H +#define WIDGETSOPENWITHHANDLER_H -#include "openurljobhandlerinterface.h" +#include "openwithhandlerinterface.h" class QDialog; class QWidget; namespace KIO { -// TODO KF6: Make KIO::JobUiDelegate inherit from WidgetsOpenUrlJobHandler +// TODO KF6: Make KIO::JobUiDelegate inherit from WidgetsOpenWithHandler // (or even merge the two classes) // so that setDelegate(new KIO::JobUiDelegate) provides both dialog boxes on error // and the open-with dialog. -class WidgetsOpenUrlJobHandler : public OpenUrlJobHandlerInterface +class WidgetsOpenWithHandler : public OpenWithHandlerInterface { public: - WidgetsOpenUrlJobHandler(); - ~WidgetsOpenUrlJobHandler() override; + WidgetsOpenWithHandler(); + ~WidgetsOpenWithHandler() override; void promptUserForApplication(KIO::OpenUrlJob *job, const QList &urls, const QString &mimeType) override; private: // Note: no d pointer because not exported at this point }; } -#endif // WIDGETSOPENURLJOBHANDLER_H +#endif // WIDGETSOPENWITHHANDLER_H