diff --git a/src/blackcore/application.cpp b/src/blackcore/application.cpp index 05bd95f64..07fe3e141 100644 --- a/src/blackcore/application.cpp +++ b/src/blackcore/application.cpp @@ -102,7 +102,7 @@ namespace BlackCore Q_ASSERT_X(!sApp, Q_FUNC_INFO, "already initialized"); Q_ASSERT_X(QCoreApplication::instance(), Q_FUNC_INFO, "no application object"); - // init skiped when called from CGuiApplication + // init skipped when called from CGuiApplication if (init) { this->init(true); @@ -133,8 +133,6 @@ namespace BlackCore // // Translations - const QFile file(":blackmisc/translations/blackmisc_i18n_de.qm"); - CLogMessage(this).debug() << (file.exists() ? "Found translations in resources" : "No translations in resources"); QTranslator translator; if (translator.load("blackmisc_i18n_de", ":blackmisc/translations/")) { CLogMessage(this).debug() << "Translator loaded"; } QCoreApplication::instance()->installTranslator(&translator); @@ -315,7 +313,7 @@ namespace BlackCore // parse if needed, parsing contains its own error handling if (!this->m_parsed) { - const bool s = this->parse(); + const bool s = this->parseAndStartupCheck(); if (!s) { return false; } } @@ -985,15 +983,24 @@ namespace BlackCore return this->m_parser.value(option).trimmed(); } - bool CApplication::parse() + bool CApplication::parseAndStartupCheck() { - if (this->m_parsed) { return m_parsed; } + if (this->m_parsed) { return m_parsed; } // already done + + // checks if (CBuildConfig::isLifetimeExpired()) { this->cmdLineErrorMessage("Program exired " + CBuildConfig::getEol().toString()); return false; } + const QStringList verifyErrors = CDirectoryUtils::verifyRuntimeDirectoriesAndFiles(); + if (!verifyErrors.isEmpty()) + { + this->cmdLineErrorMessage("Missing runtime directories/files: " + verifyErrors.join(", ")); + return false; + } + if (this->m_singleApplication && this->m_alreadyRunning) { this->cmdLineErrorMessage("Program must only run once"); @@ -1036,7 +1043,7 @@ namespace BlackCore bool CApplication::cmdLineErrorMessage(const QString &errorMessage, bool retry) const { - Q_UNUSED(retry); // onyl works with UI version + Q_UNUSED(retry); // only works with UI version fputs(qPrintable(errorMessage), stderr); fputs("\n\n", stderr); fputs(qPrintable(this->m_parser.helpText()), stderr); @@ -1045,7 +1052,7 @@ namespace BlackCore bool CApplication::cmdLineErrorMessage(const CStatusMessageList &msgs, bool retry) const { - Q_UNUSED(retry); // onyl works with UI version + Q_UNUSED(retry); // only works with UI version if (msgs.isEmpty()) { return false; } if (!msgs.hasErrorMessages()) { return false; } CApplication::cmdLineErrorMessage( diff --git a/src/blackcore/application.h b/src/blackcore/application.h index a578d218c..6ccd305d6 100644 --- a/src/blackcore/application.h +++ b/src/blackcore/application.h @@ -290,7 +290,7 @@ namespace BlackCore //! \note in some cases (error, version, help) application is terminated during this step //! \sa parsingHookIn //! \return true means to continue, false to stop - bool parse(); + bool parseAndStartupCheck(); //! @} //! Display error message diff --git a/src/blackgui/guiapplication.cpp b/src/blackgui/guiapplication.cpp index 36255709d..fe06888bd 100644 --- a/src/blackgui/guiapplication.cpp +++ b/src/blackgui/guiapplication.cpp @@ -263,9 +263,23 @@ namespace BlackGui bool CGuiApplication::cmdLineErrorMessage(const QString &errorMessage, bool retry) const { const QString helpText(beautifyHelpMessage(this->m_parser.helpText())); + constexpr int MaxLength = 60; + + QString htmlMsg; + if (errorMessage.length() > MaxLength) + { + htmlMsg = "

" + errorMessage.left(MaxLength) + "..." + "

" + + "Details: " + errorMessage + "

"; + } + else + { + htmlMsg = "

" + errorMessage + "

"; + } + htmlMsg += helpText + ""; + const int r = QMessageBox::warning(nullptr, QGuiApplication::applicationDisplayName(), - "

" + errorMessage + "


" + helpText + "", QMessageBox::Abort, retry ? QMessageBox::Retry : QMessageBox::NoButton); + htmlMsg, QMessageBox::Abort, retry ? QMessageBox::Retry : QMessageBox::NoButton); return (r == QMessageBox::Retry); } @@ -279,7 +293,7 @@ namespace BlackGui const QString msgsHtml = msgs.toHtml(msgs.size() > 1 ? propertiesMulti : propertiesSingle); const int r = QMessageBox::critical(nullptr, QGuiApplication::applicationDisplayName(), - "" + msgsHtml + "

" + helpText + "", QMessageBox::Abort, retry ? QMessageBox::Retry : QMessageBox::NoButton); + "" + msgsHtml + "

" + helpText + "", QMessageBox::Abort, retry ? QMessageBox::Retry : QMessageBox::NoButton); return (r == QMessageBox::Retry); } diff --git a/src/blackmisc/directoryutils.cpp b/src/blackmisc/directoryutils.cpp index 1b8482abc..9879e5be9 100644 --- a/src/blackmisc/directoryutils.cpp +++ b/src/blackmisc/directoryutils.cpp @@ -327,6 +327,34 @@ namespace BlackMisc return dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks); } + QStringList CDirectoryUtils::verifyRuntimeDirectoriesAndFiles() + { + QStringList failed; + QDir d(CDirectoryUtils::binDirectory()); + if (!d.isReadable()) { failed.append(d.absolutePath()); } + + d = QDir(CDirectoryUtils::imagesDirectory()); + if (!d.isReadable()) { failed.append(d.absolutePath()); } + + d = QDir(CDirectoryUtils::stylesheetsDirectory()); + if (!d.isReadable()) { failed.append(d.absolutePath()); } + + d = QDir(CDirectoryUtils::applicationDataDirectory()); + if (!d.isReadable()) { failed.append(d.absolutePath()); } + + // check if the executables are avialable + QString fn = CDirectoryUtils::executableFilePath(CBuildConfig::swiftCoreExecutableName()); + if (!QFile::exists(fn)) { failed.append(fn); } + + fn = CDirectoryUtils::executableFilePath(CBuildConfig::swiftDataExecutableName()); + if (!QFile::exists(fn)) { failed.append(fn); } + + fn = CDirectoryUtils::executableFilePath(CBuildConfig::swiftGuiExecutableName()); + if (!QFile::exists(fn)) { failed.append(fn); } + + return failed; + } + QSet CDirectoryUtils::fileNamesToQSet(const QFileInfoList &fileInfoList) { QSet sl; diff --git a/src/blackmisc/directoryutils.h b/src/blackmisc/directoryutils.h index 701b5bf33..1526a6e85 100644 --- a/src/blackmisc/directoryutils.h +++ b/src/blackmisc/directoryutils.h @@ -100,7 +100,7 @@ namespace BlackMisc //! \remark In BlackMisc so it can also be used from BlackMisc classes static const QString &logDirectory(); - //! Directory for log files + //! Directory for crashpad files static const QString &crashpadDirectory(); //! Virtually the inverse operation of CDirectoryUtils::normalizedApplicationDirectory @@ -109,6 +109,9 @@ namespace BlackMisc //! All sub directories of given dir static QStringList getSubDirectories(const QString &rootDir); + //! Check if the (most important) runtime directories are available + static QStringList verifyRuntimeDirectoriesAndFiles(); + //! Result of directory comparison struct DirComparison { diff --git a/src/swiftcore/main.cpp b/src/swiftcore/main.cpp index da59c9d1f..26cb5ac47 100644 --- a/src/swiftcore/main.cpp +++ b/src/swiftcore/main.cpp @@ -35,7 +35,7 @@ int main(int argc, char *argv[]) a.addVatlibOptions(); a.addParserOption({{"r", "start"}, QCoreApplication::translate("main", "Start the server.")}); a.addParserOption({{"c", "coreaudio"}, QCoreApplication::translate("main", "Audio in core.")}); - if (!a.parse()) { return EXIT_FAILURE; } + if (!a.parseAndStartupCheck()) { return EXIT_FAILURE; } const QString dBusAdress(a.getCmdDBusAddressValue()); a.useContexts(a.isParserOptionSet("coreaudio") ? diff --git a/src/swiftdata/main.cpp b/src/swiftdata/main.cpp index 7a0e2a0bc..8c6f92543 100644 --- a/src/swiftdata/main.cpp +++ b/src/swiftdata/main.cpp @@ -30,7 +30,7 @@ int main(int argc, char *argv[]) CGuiApplication a(CApplicationInfo::swiftMappingTool(), CApplicationInfo::MappingTool, CIcons::swiftDatabase48()); a.setSignalStartupAutomatically(false); // application will signal startup on its own a.splashScreen(CIcons::swiftDatabase256()); - if (!a.parse()) { return EXIT_FAILURE; } + if (!a.parseAndStartupCheck()) { return EXIT_FAILURE; } a.useWebDataServices(BlackCore::CWebReaderFlags::AllSwiftDbReaders, CDatabaseReaderConfigList::forMappingTool()); if (!a.start()) { diff --git a/src/swiftguistandard/main.cpp b/src/swiftguistandard/main.cpp index 6066a09bc..7b379cae9 100644 --- a/src/swiftguistandard/main.cpp +++ b/src/swiftguistandard/main.cpp @@ -29,7 +29,7 @@ int main(int argc, char *argv[]) CSwiftGuiStdApplication a; a.setSignalStartupAutomatically(false); // application will signal startup on its own a.splashScreen(CIcons::swift256()); - if (!a.parse()) { return EXIT_FAILURE; } + if (!a.parseAndStartupCheck()) { return EXIT_FAILURE; } if (!a.hasSetupReader() || !a.start()) { a.gracefulShutdown(); diff --git a/src/swiftlauncher/main.cpp b/src/swiftlauncher/main.cpp index d79ef561c..3d0630f45 100644 --- a/src/swiftlauncher/main.cpp +++ b/src/swiftlauncher/main.cpp @@ -35,7 +35,7 @@ int main(int argc, char *argv[]) Q_UNUSED(qa); CGuiApplication a(CApplicationInfo::swiftLauncher(), CApplicationInfo::Laucher, CIcons::swiftLauncher1024()); a.addParserOption({{"i", "installer"}, QCoreApplication::translate("main", "Installer setup.") }); - if (!a.parse()) { return EXIT_FAILURE; } + if (!a.parseAndStartupCheck()) { return EXIT_FAILURE; } a.useWebDataServices(BlackCore::CWebReaderFlags::AllSwiftDbReaders, CDatabaseReaderConfigList::forLauncher()); if (!a.start()) {