| 1 | /* |
| 2 | SPDX-FileCopyrightText: 2013 Aleix Pol Gonzalez <aleixpol@blue-systems.com> |
| 3 | |
| 4 | SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL |
| 5 | */ |
| 6 | |
| 7 | #include "kurlhandler_p.h" |
| 8 | |
| 9 | #include <kguiaddons_debug.h> |
| 10 | |
| 11 | #include <QCoreApplication> |
| 12 | #include <QDebug> |
| 13 | #include <QDesktopServices> |
| 14 | #include <QLocale> |
| 15 | #include <QProcess> |
| 16 | #include <QStandardPaths> |
| 17 | #include <QUrl> |
| 18 | |
| 19 | static const char s_khelpcenter_exec[] = "khelpcenter" ; |
| 20 | |
| 21 | static bool openWithKHelpCenter(const QUrl &url) |
| 22 | { |
| 23 | const QString helpcenter = QStandardPaths::findExecutable(executableName: QString::fromLatin1(ba: s_khelpcenter_exec)); |
| 24 | if (!helpcenter.isEmpty()) { |
| 25 | QUrl u(url); |
| 26 | if (u.path() == QLatin1Char('/')) { |
| 27 | const QString appName = QCoreApplication::applicationName(); |
| 28 | u.setPath(path: appName); |
| 29 | } |
| 30 | |
| 31 | QProcess::startDetached(program: helpcenter, arguments: QStringList(u.toString())); |
| 32 | return true; |
| 33 | } |
| 34 | |
| 35 | return false; |
| 36 | } |
| 37 | |
| 38 | KUrlHandler::KUrlHandler(QObject *parent) |
| 39 | : QObject(parent) |
| 40 | { |
| 41 | } |
| 42 | |
| 43 | void KUrlHandler::openHelp(const QUrl &url) const |
| 44 | { |
| 45 | if (openWithKHelpCenter(url)) { |
| 46 | return; |
| 47 | } |
| 48 | |
| 49 | const QUrl docUrl = concatDocsUrl(url); |
| 50 | if (docUrl.isValid()) { |
| 51 | QDesktopServices::openUrl(url: docUrl); |
| 52 | } else { |
| 53 | qCWarning(KGUIADDONS_LOG) << "Could not find a suitable handler for" << url.toString(); |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | QUrl KUrlHandler::concatDocsUrl(const QUrl &url) const |
| 58 | { |
| 59 | if (QCoreApplication::organizationDomain() != QLatin1String("kde.org" )) { |
| 60 | return {}; |
| 61 | } |
| 62 | |
| 63 | // KHelpCenter is not available and it's a KDE application, open the docs at docs.kde.org |
| 64 | // with the default web browser on the system |
| 65 | |
| 66 | QString path = url.path(); |
| 67 | const QString fragment = url.fragment(); |
| 68 | const QString common = QLatin1String("https://docs.kde.org/index.php?branch=stable5&language=" ) + QLocale().name(); |
| 69 | |
| 70 | const QString appName = QCoreApplication::applicationName(); |
| 71 | |
| 72 | // Special case for KCModules |
| 73 | if (appName == QLatin1String("systemsettings" ) && path.startsWith(s: QLatin1String("/kcontrol" ))) { |
| 74 | // E.g. change "/kcontrol/fonts/index.html" to "&application=kcontrol/fonts&path=index.html" |
| 75 | // docs.kde.org will resolve the url and add the proper package name, e.g. plasma-workspace: |
| 76 | // https://docs.kde.org/stable5/en/plasma-workspace/kcontrol/fonts/index.html |
| 77 | QString kcmAppName(path); |
| 78 | kcmAppName.remove(i: 0, len: 1); // Remove leading "/" |
| 79 | const int idx = kcmAppName.indexOf(s: QLatin1String("/index.html" )); |
| 80 | if (idx > 0) { |
| 81 | kcmAppName.truncate(pos: idx); |
| 82 | } |
| 83 | |
| 84 | // Some KCModules have a valid fragment, e.g. kcontrol/powerdevil/index.html#advanced-settings |
| 85 | const QString tail = QLatin1String("index.html" ) + (!fragment.isEmpty() ? QLatin1Char('#') + fragment : QString{}); |
| 86 | |
| 87 | return QUrl(common + QLatin1String("&application=" ) + kcmAppName + QLatin1String("&path=" ) + tail); |
| 88 | } |
| 89 | |
| 90 | // E.g. "help:/" and appName is "okular", e.g. opening Help -> Okular HandBook |
| 91 | if (path == QLatin1Char('/')) { |
| 92 | return QUrl(common + QLatin1String("&application=" ) + appName + QLatin1String("&path=" ) + QLatin1String("index.html" )); |
| 93 | } |
| 94 | |
| 95 | // E.g. "help:/okular/configure.html", don't repeat "appName"; e.g. clicking Help button in |
| 96 | // the "Settings -> Configure Okular" dialog |
| 97 | const QString redundant = QLatin1Char('/') + appName + QLatin1Char('/'); |
| 98 | if (path.startsWith(s: redundant)) { |
| 99 | path.remove(i: 0, len: redundant.size()); |
| 100 | |
| 101 | if (!fragment.isEmpty()) { |
| 102 | // E.g. "help:/kinfocenter/index.html#kcm_memory", it's actually "kinfocenter/kcm_memory.html" |
| 103 | if (path == QLatin1String("index.html" )) { |
| 104 | qCWarning(KGUIADDONS_LOG) << "X-DocPath entry in a .desktop file in" << appName << "is:" << appName + QLatin1String("/index.html#" ) + fragment |
| 105 | << ", however it should be:" << appName + QLatin1Char('/') + fragment + QLatin1String(".html" ); |
| 106 | |
| 107 | path = fragment + QLatin1String(".html" ); |
| 108 | } else { |
| 109 | // E.g. "help:/okular/signatures.html#adding_digital_signatures" |
| 110 | path += QLatin1Char('#') + fragment; |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | return QUrl(common + QLatin1String("&application=" ) + appName + QLatin1String("&path=" ) + path); |
| 115 | } |
| 116 | |
| 117 | return {}; |
| 118 | } |
| 119 | |
| 120 | Q_GLOBAL_STATIC(KUrlHandler, s_handler) |
| 121 | |
| 122 | static void initializeGlobalSettings() |
| 123 | { |
| 124 | QDesktopServices::setUrlHandler(QStringLiteral("help" ), receiver: s_handler, method: "openHelp" ); |
| 125 | } |
| 126 | |
| 127 | Q_COREAPP_STARTUP_FUNCTION(initializeGlobalSettings) |
| 128 | |
| 129 | #include "moc_kurlhandler_p.cpp" |
| 130 | |