File indexing completed on 2024-05-05 05:29:12

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 QCommandLineParser *createParser()
0030 {
0031     // clang-format off
0032     QCommandLineParser *parser = new QCommandLineParser;
0033     parser->addOption(QCommandLineOption(QStringLiteral("application"), i18n("Directly open the specified application by its appstream:// URI."), QStringLiteral("name")));
0034     parser->addOption(QCommandLineOption(QStringLiteral("mime"), i18n("Open with a search for programs that can deal with the given mimetype."), QStringLiteral("name")));
0035     parser->addOption(QCommandLineOption(QStringLiteral("category"), i18n("Display a list of entries with a category."), QStringLiteral("name")));
0036     parser->addOption(QCommandLineOption(QStringLiteral("mode"), i18n("Open Discover in a said mode. Modes correspond to the toolbar buttons."), QStringLiteral("name")));
0037     parser->addOption(QCommandLineOption(QStringLiteral("listmodes"), i18n("List all the available modes.")));
0038     parser->addOption(QCommandLineOption(QStringLiteral("local-filename"), i18n("Local package file to install"), QStringLiteral("package")));
0039     parser->addOption(QCommandLineOption(QStringLiteral("listbackends"), i18n("List all the available backends.")));
0040     parser->addOption(QCommandLineOption(QStringLiteral("search"), i18n("Search string."), QStringLiteral("text")));
0041     parser->addOption(QCommandLineOption(QStringLiteral("feedback"), i18n("Lists the available options for user feedback")));
0042     parser->addOption(QCommandLineOption(QStringLiteral("test"), QStringLiteral("Test file"), QStringLiteral("file.qml")));
0043     parser->addPositionalArgument(QStringLiteral("urls"), i18n("Supports appstream: url scheme"));
0044     // clang-format on
0045     DiscoverBackendsFactory::setupCommandLine(parser);
0046     KAboutData::applicationData().setupCommandLine(parser);
0047     return parser;
0048 }
0049 
0050 void processArgs(QCommandLineParser *parser, DiscoverObject *discoverObject)
0051 {
0052     if (parser->isSet(QStringLiteral("application"))) {
0053         discoverObject->openApplication(QUrl(parser->value(QStringLiteral("application"))));
0054     } else if (parser->isSet(QStringLiteral("mime"))) {
0055         discoverObject->openMimeType(parser->value(QStringLiteral("mime")));
0056     } else if (parser->isSet(QStringLiteral("category"))) {
0057         discoverObject->openCategory(parser->value(QStringLiteral("category")));
0058     } else if (parser->isSet(QStringLiteral("mode"))) {
0059         discoverObject->openMode(parser->value(QStringLiteral("mode")));
0060     }
0061 
0062     if (parser->isSet(QStringLiteral("search"))) {
0063         Q_EMIT discoverObject->openSearch(parser->value(QStringLiteral("search")));
0064     }
0065 
0066     if (parser->isSet(QStringLiteral("local-filename"))) {
0067         discoverObject->openLocalPackage(QUrl::fromUserInput(parser->value(QStringLiteral("local-filename")), {}, QUrl::AssumeLocalFile));
0068     }
0069 
0070     const auto positionalArguments = parser->positionalArguments();
0071     for (const QString &arg : positionalArguments) {
0072         const QUrl url = QUrl::fromUserInput(arg, {}, QUrl::AssumeLocalFile);
0073         if (url.isLocalFile()) {
0074             discoverObject->openLocalPackage(url);
0075         } else if (url.scheme() == QLatin1String("apt")) {
0076             Q_EMIT discoverObject->openSearch(url.host());
0077         } else {
0078             discoverObject->openApplication(url);
0079         }
0080     }
0081 
0082     if (auto window = discoverObject->mainWindow()) {
0083         if (window->property("pageStack").value<QObject *>()->property("depth").toInt() == 0) {
0084             discoverObject->openMode(QStringLiteral("Browsing"));
0085         }
0086     }
0087 }
0088 
0089 static void raiseWindow(QWindow *window)
0090 {
0091     KWindowSystem::updateStartupId(window);
0092     KWindowSystem::activateWindow(window);
0093 }
0094 
0095 int main(int argc, char **argv)
0096 {
0097     // needs to be set before we create the QGuiApplication
0098     QCoreApplication::setAttribute(Qt::AA_DisableSessionManager, true);
0099 #if WITH_QTWEBVIEW
0100     { // as required by a QtWebEngine warning
0101         QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true);
0102         QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi);
0103     }
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     KCrash::initialize();
0115     KLocalizedString::setApplicationDomain("plasma-discover");
0116     KAboutData about(QStringLiteral("discover"),
0117                      i18n("Discover"),
0118                      version,
0119                      i18n("An application explorer"),
0120                      KAboutLicense::GPL,
0121                      i18n("© 2010-2024 Plasma Development Team"));
0122     about.addAuthor(i18n("Aleix Pol Gonzalez"), QString(), QStringLiteral("aleixpol@kde.org"), QStringLiteral("https://proli.net"), QStringLiteral("apol"));
0123     about.addAuthor(i18n("Nate Graham"),
0124                     i18n("Quality Assurance, Design and Usability"),
0125                     QStringLiteral("nate@kde.org"),
0126                     QStringLiteral("https://pointieststick.com/"),
0127                     QStringLiteral("ngraham"));
0128     about.addAuthor(i18n("Dan Leinir Turthra Jensen"),
0129                     i18n("KNewStuff"),
0130                     QStringLiteral("admin@leinir.dk"),
0131                     QStringLiteral("https://leinir.dk/"),
0132                     QStringLiteral("leinir"));
0133     about.setProductName("discover/discover");
0134     about.setProgramLogo(app.windowIcon());
0135 
0136     about.setTranslator(i18nc("NAME OF TRANSLATORS", "Your names"), i18nc("EMAIL OF TRANSLATORS", "Your emails"));
0137 
0138     KAboutData::setApplicationData(about);
0139 
0140     DiscoverObject *discoverObject = nullptr;
0141     {
0142         QScopedPointer<QCommandLineParser> parser(createParser());
0143         parser->process(app);
0144         about.processCommandLine(parser.data());
0145         DiscoverBackendsFactory::processCommandLine(parser.data(), parser->isSet(QStringLiteral("test")));
0146         const bool feedback = parser->isSet(QStringLiteral("feedback"));
0147 
0148         if (parser->isSet(QStringLiteral("listbackends"))) {
0149             QTextStream(stdout) << i18n("Available backends:\n");
0150             DiscoverBackendsFactory f;
0151             const auto backendNames = f.allBackendNames(false, true);
0152             for (const QString &name : backendNames)
0153                 QTextStream(stdout) << " * " << name << '\n';
0154             return 0;
0155         }
0156 
0157         if (parser->isSet(QStringLiteral("test"))) {
0158             QStandardPaths::setTestModeEnabled(true);
0159         }
0160 
0161         KDBusService *service = !feedback ? new KDBusService(KDBusService::Unique, &app) : nullptr;
0162 
0163         {
0164             auto options = parser->optionNames();
0165             options.removeAll(QStringLiteral("backends"));
0166             options.removeAll(QStringLiteral("test"));
0167             QVariantMap initialProperties;
0168             if (!options.isEmpty() || !parser->positionalArguments().isEmpty())
0169                 initialProperties = {{QStringLiteral("currentTopLevel"), QStringLiteral(DISCOVER_BASE_URL "/LoadingPage.qml")}};
0170             if (feedback) {
0171                 initialProperties.insert(QStringLiteral("visible"), false);
0172             }
0173             discoverObject = new DiscoverObject(initialProperties);
0174         }
0175         if (feedback) {
0176             QTextStream(stdout) << discoverObject->describeSources() << '\n';
0177             delete discoverObject;
0178             return 0;
0179         } else {
0180             QObject::connect(service,
0181                              &KDBusService::activateRequested,
0182                              discoverObject,
0183                              [discoverObject](const QStringList &arguments, const QString & /*workingDirectory*/) {
0184                                  discoverObject->restore();
0185                                  if (auto window = discoverObject->mainWindow()) {
0186                                      raiseWindow(window);
0187                                      if (arguments.isEmpty()) {
0188                                          return;
0189                                      }
0190                                      QScopedPointer<QCommandLineParser> parser(createParser());
0191                                      parser->parse(arguments);
0192                                      processArgs(parser.data(), discoverObject);
0193                                  }
0194                              });
0195         }
0196 
0197         QObject::connect(&app, &QCoreApplication::aboutToQuit, discoverObject, &DiscoverObject::deleteLater);
0198 
0199         processArgs(parser.data(), discoverObject);
0200 
0201         if (parser->isSet(QStringLiteral("listmodes"))) {
0202             QTextStream(stdout) << i18n("Available modes:\n");
0203             const auto modes = discoverObject->modes();
0204             for (const QString &mode : modes)
0205                 QTextStream(stdout) << " * " << mode << '\n';
0206             delete discoverObject;
0207             return 0;
0208         }
0209 
0210         if (parser->isSet(QStringLiteral("test"))) {
0211             const QUrl testFile = QUrl::fromUserInput(parser->value(QStringLiteral("test")), {}, QUrl::AssumeLocalFile);
0212             Q_ASSERT(!testFile.isEmpty() && testFile.isLocalFile());
0213 
0214             discoverObject->loadTest(testFile);
0215         }
0216     }
0217 
0218     return app.exec();
0219 }