File indexing completed on 2024-05-12 04:55:01

0001 /**
0002  * \file mainqml.cpp
0003  * Main program.
0004  *
0005  * \b Project: Kid3
0006  * \author Urs Fleisch
0007  * \date 7 Jun 2014
0008  *
0009  * Copyright (C) 2014-2024  Urs Fleisch
0010  *
0011  * This file is part of Kid3.
0012  *
0013  * Kid3 is free software; you can redistribute it and/or modify
0014  * it under the terms of the GNU General Public License as published by
0015  * the Free Software Foundation; either version 2 of the License, or
0016  * (at your option) any later version.
0017  *
0018  * Kid3 is distributed in the hope that it will be useful,
0019  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0020  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0021  * GNU General Public License for more details.
0022  *
0023  * You should have received a copy of the GNU General Public License
0024  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0025  */
0026 
0027 #include <QFile>
0028 #include <QApplication>
0029 #include <QTranslator>
0030 #include <QDir>
0031 #include <QSettings>
0032 #if !defined NDEBUG && !defined QT_QML_DEBUG
0033 #define QT_QML_DEBUG
0034 #endif
0035 #include <QQmlApplicationEngine>
0036 #include <QQuickStyle>
0037 #if QT_VERSION > 0x060200 && defined Q_OS_ANDROID
0038 #include <QtCore/private/qandroidextras_p.h>
0039 #endif
0040 #include <typeinfo>
0041 #include "config.h"
0042 #include "loadtranslation.h"
0043 #include "kid3application.h"
0044 
0045 namespace {
0046 
0047 /**
0048  * QApplication subclass with adapted session management.
0049  */
0050 class Kid3QtApplication : public QApplication {
0051 public:
0052   /**
0053    * Constructor.
0054    * @param argc number of arguments (including command)
0055    * @param argv arguments
0056    */
0057   Kid3QtApplication(int& argc, char** argv) : QApplication(argc, argv) {}
0058 
0059   /**
0060    * Destructor.
0061    */
0062   ~Kid3QtApplication() override = default;
0063 
0064   /**
0065    * Called when session manager wants application to commit all its data.
0066    *
0067    * This method is reimplemented to avoid closing all top level widgets and
0068    * make restoring with the KDE window manager working.
0069    *
0070    * @param manager session manager
0071    */
0072   virtual void commitData(QSessionManager& manager) {
0073     emit commitDataRequest(manager);
0074   }
0075 
0076   /**
0077    * Send event to receiver.
0078    * @param receiver receiver
0079    * @param event event
0080    * @return return value from receiver's event handler.
0081    */
0082   bool notify(QObject* receiver, QEvent* event) override;
0083 };
0084 
0085 /**
0086  * Send event to receiver.
0087  * @param receiver receiver
0088  * @param event event
0089  * @return return value from receiver's event handler.
0090  */
0091 bool Kid3QtApplication::notify(QObject* receiver, QEvent* event)
0092 {
0093   try {
0094     return QApplication::notify(receiver, event);
0095   } catch (std::exception& ex) {
0096     qWarning("Exception %s (%s) was caught", typeid(ex).name(), ex.what());
0097   }
0098   return false;
0099 }
0100 
0101 }
0102 
0103 /**
0104  * Main program.
0105  *
0106  * @param argc number of arguments including command name
0107  * @param argv arguments, argv[0] is command name
0108  *
0109  * @return exit code of application.
0110  */
0111 
0112 int main(int argc, char* argv[])
0113 {
0114 #ifdef HAVE_QMLDIR_IN_QRC
0115   Q_INIT_RESOURCE(qmlapp);
0116 #endif
0117 #ifdef HAVE_TRANSLATIONSDIR_IN_QRC
0118   Q_INIT_RESOURCE(translations);
0119 #endif
0120 
0121 #if QT_VERSION > 0x060200 && defined Q_OS_ANDROID
0122   const QString storagePermission =
0123       QLatin1String("android.permission.WRITE_EXTERNAL_STORAGE");
0124   auto permissionResult =
0125       QtAndroidPrivate::checkPermission(storagePermission).result();
0126   if (permissionResult != QtAndroidPrivate::Authorized) {
0127     permissionResult =
0128         QtAndroidPrivate::requestPermission(storagePermission).result();
0129   }
0130 #endif
0131 
0132   QCoreApplication::setApplicationName(QLatin1String("Kid3"));
0133 #if QT_VERSION < 0x060000
0134   QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
0135 #endif
0136 
0137   // The QtQuickStyle setting has to be read bypassing the regular
0138   // configuration object because the style environment variable
0139   // must be set before the QGuiApplication is created.
0140   auto style = QSettings(QSettings::UserScope, QLatin1String("Kid3"),
0141                          QLatin1String("Kid3"))
0142       .value(QLatin1String("MainWindow/QtQuickStyle")).toByteArray();
0143   auto configuredLanguage = QSettings(
0144         QSettings::UserScope, QLatin1String("Kid3"), QLatin1String("Kid3"))
0145       .value(QLatin1String("MainWindow/Language")).toString();
0146   if (style.isEmpty()) {
0147 #ifdef Q_OS_ANDROID
0148     style = "Material/Light";
0149 #else
0150     style = "Default";
0151 #endif
0152   }
0153   auto styleTheme = style.split('/');
0154   style = styleTheme.at(0);
0155   if (!style.isEmpty()) {
0156     qputenv("QT_QUICK_CONTROLS_STYLE", style);
0157   }
0158   auto theme = styleTheme.size() > 1 ? styleTheme.at(1) : "";
0159   if (!theme.isEmpty() && style == "Material") {
0160     qputenv("QT_QUICK_CONTROLS_MATERIAL_THEME", theme);
0161   }
0162 
0163   Kid3QtApplication app(argc, argv);
0164   Utils::loadTranslation(configuredLanguage);
0165 #ifdef Q_OS_MAC
0166   QDir dir(QCoreApplication::applicationDirPath());
0167   dir.cdUp();
0168   dir.cd(QLatin1String("PlugIns"));
0169   QCoreApplication::setLibraryPaths(QStringList(dir.absolutePath()));
0170 #endif
0171 
0172   QStringList qmlDirs;
0173 #if !defined NDEBUG && defined CFG_QMLSRCDIR
0174   qmlDirs.append(QLatin1String(CFG_QMLSRCDIR));
0175 #endif
0176 #ifdef CFG_QMLDIR
0177   qmlDirs.append(QLatin1String(CFG_QMLDIR));
0178 #endif
0179   QString mainQmlPath;
0180   const auto constQmlDirs = qmlDirs;
0181   for (const QString& qmlDir : constQmlDirs) {
0182     QString qmlPath(qmlDir);
0183     Utils::prependApplicationDirPathIfRelative(qmlPath);
0184     qmlPath += QDir::separator();
0185     qmlPath += QLatin1String("app");
0186     qmlPath += QDir::separator();
0187     qmlPath += QLatin1String("Main.qml");
0188     if (QFile::exists(qmlPath)) {
0189       mainQmlPath = qmlPath;
0190       break;
0191     }
0192   }
0193   if (mainQmlPath.isEmpty()) {
0194     qWarning("Could not find app/Main.qml in the following paths:\n%s",
0195              qPrintable(qmlDirs.join(QLatin1String("\n"))));
0196     return 1;
0197   }
0198 
0199   QQmlApplicationEngine engine;
0200 #ifdef HAVE_QMLDIR_IN_QRC
0201   engine.addImportPath(QLatin1String(CFG_QMLDIR "imports"));
0202   QDir pluginsDir;
0203   if (Kid3Application::findPluginsDirectory(pluginsDir) &&
0204       pluginsDir.cd(QLatin1String("imports/Kid3"))) {
0205     engine.addPluginPath(pluginsDir.absolutePath());
0206   }
0207   engine.load(QUrl(QLatin1String("qrc:///app/Main.qml")));
0208 #else
0209   QDir pluginsDir;
0210   if (Kid3Application::findPluginsDirectory(pluginsDir) &&
0211       pluginsDir.cd(QLatin1String("imports"))) {
0212     engine.addImportPath(pluginsDir.absolutePath());
0213   }
0214   engine.load(QUrl::fromLocalFile(mainQmlPath));
0215 #endif
0216   return QApplication::exec();
0217 }