File indexing completed on 2024-05-05 17:33:03

0001 /*
0002  *   SPDX-FileCopyrightText: 2012 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
0003  *
0004  *   SPDX-License-Identifier: LGPL-2.0-or-later
0005  */
0006 
0007 // #define QT_QML_DEBUG
0008 
0009 #include "DiscoverObject.h"
0010 #include "DiscoverVersion.h"
0011 #include <DiscoverBackendsFactory.h>
0012 #include <KAboutData>
0013 #include <KCrash>
0014 #include <KDBusService>
0015 #include <KLocalizedString>
0016 #include <KWindowSystem>
0017 #include <QApplication>
0018 #include <QCommandLineParser>
0019 #include <QStandardPaths>
0020 #include <QSurfaceFormat>
0021 #include <QTextStream>
0022 #include <QWindow>
0023 #if WITH_QTWEBVIEW
0024 #include <QtWebView>
0025 #endif
0026 
0027 #include <QProcessEnvironment>
0028 
0029 typedef QHash<QString, DiscoverObject::CompactMode> StringCompactMode;
0030 Q_GLOBAL_STATIC_WITH_ARGS(StringCompactMode,
0031                           s_decodeCompactMode,
0032                           (StringCompactMode{
0033                               {QLatin1String("auto"), DiscoverObject::Auto},
0034                               {QLatin1String("compact"), DiscoverObject::Compact},
0035                               {QLatin1String("full"), DiscoverObject::Full},
0036                           }))
0037 
0038 QCommandLineParser *createParser()
0039 {
0040     // clang-format off
0041     QCommandLineParser *parser = new QCommandLineParser;
0042     parser->addOption(QCommandLineOption(QStringLiteral("application"), i18n("Directly open the specified application by its appstream:// URI."), QStringLiteral("name")));
0043     parser->addOption(QCommandLineOption(QStringLiteral("mime"), i18n("Open with a search for programs that can deal with the given mimetype."), QStringLiteral("name")));
0044     parser->addOption(QCommandLineOption(QStringLiteral("category"), i18n("Display a list of entries with a category."), QStringLiteral("name")));
0045     parser->addOption(QCommandLineOption(QStringLiteral("mode"), i18n("Open Discover in a said mode. Modes correspond to the toolbar buttons."), QStringLiteral("name")));
0046     parser->addOption(QCommandLineOption(QStringLiteral("listmodes"), i18n("List all the available modes.")));
0047     parser->addOption(QCommandLineOption(QStringLiteral("compact"), i18n("Compact Mode (auto/compact/full)."), QStringLiteral("mode"), QStringLiteral("auto")));
0048     parser->addOption(QCommandLineOption(QStringLiteral("local-filename"), i18n("Local package file to install"), QStringLiteral("package")));
0049     parser->addOption(QCommandLineOption(QStringLiteral("listbackends"), i18n("List all the available backends.")));
0050     parser->addOption(QCommandLineOption(QStringLiteral("search"), i18n("Search string."), QStringLiteral("text")));
0051     parser->addOption(QCommandLineOption(QStringLiteral("feedback"), i18n("Lists the available options for user feedback")));
0052     parser->addOption(QCommandLineOption(QStringLiteral("test"), QStringLiteral("Test file"), QStringLiteral("file.qml")));
0053     parser->addPositionalArgument(QStringLiteral("urls"), i18n("Supports appstream: url scheme"));
0054     // clang-format on
0055     DiscoverBackendsFactory::setupCommandLine(parser);
0056     KAboutData::applicationData().setupCommandLine(parser);
0057     return parser;
0058 }
0059 
0060 void processArgs(QCommandLineParser *parser, DiscoverObject *mainWindow)
0061 {
0062     if (parser->isSet(QStringLiteral("application")))
0063         mainWindow->openApplication(QUrl(parser->value(QStringLiteral("application"))));
0064     else if (parser->isSet(QStringLiteral("mime")))
0065         mainWindow->openMimeType(parser->value(QStringLiteral("mime")));
0066     else if (parser->isSet(QStringLiteral("category")))
0067         mainWindow->openCategory(parser->value(QStringLiteral("category")));
0068     else if (parser->isSet(QStringLiteral("mode")))
0069         mainWindow->openMode(parser->value(QStringLiteral("mode")));
0070 
0071     if (parser->isSet(QStringLiteral("search")))
0072         Q_EMIT mainWindow->openSearch(parser->value(QStringLiteral("search")));
0073 
0074     if (parser->isSet(QStringLiteral("local-filename")))
0075         mainWindow->openLocalPackage(QUrl::fromUserInput(parser->value(QStringLiteral("local-filename")), {}, QUrl::AssumeLocalFile));
0076 
0077     const auto positionalArguments = parser->positionalArguments();
0078     for (const QString &arg : positionalArguments) {
0079         const QUrl url = QUrl::fromUserInput(arg, {}, QUrl::AssumeLocalFile);
0080         if (url.isLocalFile())
0081             mainWindow->openLocalPackage(url);
0082         else if (url.scheme() == QLatin1String("apt"))
0083             Q_EMIT mainWindow->openSearch(url.host());
0084         else
0085             mainWindow->openApplication(url);
0086     }
0087 
0088     if (mainWindow->rootObject()->property("pageStack").value<QObject *>()->property("depth").toInt() == 0) {
0089         mainWindow->openMode(QStringLiteral("Browsing"));
0090     }
0091 }
0092 
0093 static void raiseWindow(QWindow *window)
0094 {
0095     KWindowSystem::updateStartupId(window);
0096     KWindowSystem::activateWindow(window);
0097 }
0098 
0099 int main(int argc, char **argv)
0100 {
0101     // needs to be set before we create the QGuiApplication
0102     QCoreApplication::setAttribute(Qt::AA_DisableSessionManager, true);
0103 #if WITH_QTWEBVIEW
0104     QtWebView::initialize();
0105 #endif
0106 
0107     auto format = QSurfaceFormat::defaultFormat();
0108     format.setOption(QSurfaceFormat::ResetNotification);
0109     QSurfaceFormat::setDefaultFormat(format);
0110 
0111     QApplication app(argc, argv);
0112     app.setWindowIcon(QIcon::fromTheme(QStringLiteral("plasmadiscover")));
0113     app.setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
0114 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0115     app.setAttribute(Qt::AA_UseHighDpiPixmaps, true);
0116 #endif
0117     KCrash::initialize();
0118     KLocalizedString::setApplicationDomain("plasma-discover");
0119     KAboutData about(QStringLiteral("discover"),
0120                      i18n("Discover"),
0121                      version,
0122                      i18n("An application explorer"),
0123                      KAboutLicense::GPL,
0124                      i18n("© 2010-2022 Plasma Development Team"));
0125     about.addAuthor(i18n("Aleix Pol Gonzalez"), QString(), QStringLiteral("aleixpol@kde.org"), QStringLiteral("https://proli.net"), QStringLiteral("apol"));
0126     about.addAuthor(i18n("Nate Graham"),
0127                     i18n("Quality Assurance, Design and Usability"),
0128                     QStringLiteral("nate@kde.org"),
0129                     QStringLiteral("https://pointieststick.com/"),
0130                     QStringLiteral("ngraham"));
0131     about.addAuthor(i18n("Dan Leinir Turthra Jensen"),
0132                     i18n("KNewStuff"),
0133                     QStringLiteral("admin@leinir.dk"),
0134                     QStringLiteral("https://leinir.dk/"),
0135                     QStringLiteral("leinir"));
0136     about.setProductName("discover/discover");
0137     about.setProgramLogo(app.windowIcon());
0138 
0139     about.setTranslator(i18nc("NAME OF TRANSLATORS", "Your names"), i18nc("EMAIL OF TRANSLATORS", "Your emails"));
0140 
0141     KAboutData::setApplicationData(about);
0142 
0143     DiscoverObject *mainWindow = nullptr;
0144     {
0145         QScopedPointer<QCommandLineParser> parser(createParser());
0146         parser->process(app);
0147         about.processCommandLine(parser.data());
0148         DiscoverBackendsFactory::processCommandLine(parser.data(), parser->isSet(QStringLiteral("test")));
0149         const bool feedback = parser->isSet(QStringLiteral("feedback"));
0150 
0151         if (parser->isSet(QStringLiteral("listbackends"))) {
0152             QTextStream(stdout) << i18n("Available backends:\n");
0153             DiscoverBackendsFactory f;
0154             const auto backendNames = f.allBackendNames(false, true);
0155             for (const QString &name : backendNames)
0156                 QTextStream(stdout) << " * " << name << '\n';
0157             return 0;
0158         }
0159 
0160         if (parser->isSet(QStringLiteral("test"))) {
0161             QStandardPaths::setTestModeEnabled(true);
0162         }
0163 
0164         KDBusService *service = !feedback ? new KDBusService(KDBusService::Unique, &app) : nullptr;
0165 
0166         {
0167             auto options = parser->optionNames();
0168             options.removeAll(QStringLiteral("backends"));
0169             options.removeAll(QStringLiteral("test"));
0170             QVariantMap initialProperties;
0171             if (!options.isEmpty() || !parser->positionalArguments().isEmpty())
0172                 initialProperties = {{QStringLiteral("currentTopLevel"), QStringLiteral("qrc:/qml/LoadingPage.qml")}};
0173             if (feedback) {
0174                 initialProperties.insert("visible", false);
0175             }
0176             mainWindow = new DiscoverObject(s_decodeCompactMode->value(parser->value(QStringLiteral("compact")), DiscoverObject::Full), initialProperties);
0177         }
0178         if (feedback) {
0179             QTextStream(stdout) << mainWindow->describeSources() << '\n';
0180             delete mainWindow;
0181             return 0;
0182         } else {
0183             auto onActivateRequested = [mainWindow](const QStringList &arguments, const QString & /*workingDirectory*/) {
0184                 mainWindow->restore();
0185                 auto window = qobject_cast<QWindow *>(mainWindow->rootObject());
0186                 if (!window) {
0187                     // Should never happen anyway
0188                     QCoreApplication::instance()->quit();
0189                     return;
0190                 }
0191 
0192                 raiseWindow(window);
0193 
0194                 if (arguments.isEmpty())
0195                     return;
0196                 QScopedPointer<QCommandLineParser> parser(createParser());
0197                 parser->parse(arguments);
0198                 processArgs(parser.data(), mainWindow);
0199             };
0200             QObject::connect(service, &KDBusService::activateRequested, mainWindow, onActivateRequested);
0201         }
0202 
0203         QObject::connect(&app, &QCoreApplication::aboutToQuit, mainWindow, &DiscoverObject::deleteLater);
0204 
0205         processArgs(parser.data(), mainWindow);
0206 
0207         if (parser->isSet(QStringLiteral("listmodes"))) {
0208             QTextStream(stdout) << i18n("Available modes:\n");
0209             const auto modes = mainWindow->modes();
0210             for (const QString &mode : modes)
0211                 QTextStream(stdout) << " * " << mode << '\n';
0212             delete mainWindow;
0213             return 0;
0214         }
0215 
0216         if (parser->isSet(QStringLiteral("test"))) {
0217             const QUrl testFile = QUrl::fromUserInput(parser->value(QStringLiteral("test")), {}, QUrl::AssumeLocalFile);
0218             Q_ASSERT(!testFile.isEmpty() && testFile.isLocalFile());
0219 
0220             mainWindow->loadTest(testFile);
0221         }
0222     }
0223 
0224     return app.exec();
0225 }