diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index e9afdfe1a4..76b880f7f1 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -150,7 +150,7 @@ jobs: uses: johnwbyrd/update-release@v1.0.0 with: token: ${{ secrets.GITHUB_TOKEN }} - files: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-linux-x64 + files: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-linux-x64.AppImage release: Continuous Build tag: continuous-build @@ -159,7 +159,7 @@ jobs: uses: ncipollo/release-action@v1.11.0 with: token: ${{ secrets.GITHUB_TOKEN }} - artifacts: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-linux-x64 + artifacts: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-linux-x64.AppImage commit: master tag: v${{env.VNOTE_VER}} allowUpdates: true diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index 3e590f3b37..8c798a27b5 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -112,7 +112,7 @@ jobs: - name: Archive Artifacts uses: actions/upload-artifact@v2 with: - name: VNote-${{env.VNOTE_VER}}-win64${{matrix.config.suffix}} + name: VNote-${{env.VNOTE_VER}}-win64${{matrix.config.suffix}}.zip path: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-win64${{matrix.config.suffix}} - name: Archive Installer @@ -135,7 +135,7 @@ jobs: with: token: ${{ secrets.GITHUB_TOKEN }} # glob not supported - files: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-win64${{matrix.config.suffix}} + files: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-win64${{matrix.config.suffix}}.zip release: Continuous Build tag: continuous-build diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4f61822361..1ea5eeee00 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,6 +32,8 @@ if((QT_DEFAULT_MAJOR_VERSION GREATER 5)) qt_add_lupdate(vnote TS_FILES ${VX_TS_FILES}) endif() # Generate .qm files from .ts files (lrelease) +set_source_files_properties(${VX_TS_FILES} PROPERTIES + OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/translations") qt_add_translation(VX_QM_FILES ${VX_TS_FILES}) add_custom_target(lrelease DEPENDS ${VX_QM_FILES}) diff --git a/src/commandlineoptions.cpp b/src/commandlineoptions.cpp index f7958e4852..aa03f8b0e5 100644 --- a/src/commandlineoptions.cpp +++ b/src/commandlineoptions.cpp @@ -22,6 +22,9 @@ CommandLineOptions::ParseResult CommandLineOptions::parse(const QStringList &p_a const QCommandLineOption verboseOpt("verbose", MainWindow::tr("Print more logs.")); parser.addOption(verboseOpt); + const QCommandLineOption logStderrOpt("log-stderr", MainWindow::tr("Log to stderr.")); + parser.addOption(logStderrOpt); + // WebEngine options. // No need to handle them. Just add them to the parser to avoid parse error. { @@ -63,5 +66,9 @@ CommandLineOptions::ParseResult CommandLineOptions::parse(const QStringList &p_a m_verbose = true; } + if (parser.isSet(logStderrOpt)) { + m_logToStderr = true; + } + return ParseResult::Ok; } diff --git a/src/commandlineoptions.h b/src/commandlineoptions.h index f8b22b80d7..88d6f340ef 100644 --- a/src/commandlineoptions.h +++ b/src/commandlineoptions.h @@ -25,6 +25,8 @@ class CommandLineOptions QStringList m_pathsToOpen; bool m_verbose = false; + + bool m_logToStderr = false; }; #endif // COMMANDLINEOPTIONS_H diff --git a/src/core/configmgr.cpp b/src/core/configmgr.cpp index 4f2f81bcf2..047f7ffe98 100644 --- a/src/core/configmgr.cpp +++ b/src/core/configmgr.cpp @@ -191,8 +191,7 @@ bool ConfigMgr::checkAppConfig() // Load extra data. splash->showMessage("Loading extra resource data"); - const QString extraRcc(PathUtils::concatenateFilePath(QCoreApplication::applicationDirPath(), - QStringLiteral("vnote_extra.rcc"))); + const QString extraRcc("app:vnote_extra.rcc"); bool ret = QResource::registerResource(extraRcc); if (!ret) { Exception::throwOne(Exception::Type::FailToReadFile, @@ -571,6 +570,26 @@ QString ConfigMgr::getApplicationVersion() return appVersion; } +void ConfigMgr::initAppPrefixPath() +{ + // Support QFile("app:abc.txt"). + QStringList potential_dirs; + auto app_dir_path = QCoreApplication::applicationDirPath(); + qInfo() << "executable dir: " << app_dir_path; + potential_dirs << app_dir_path; + +#if defined(Q_OS_LINUX) + QDir localBinDir(app_dir_path); + if (localBinDir.exists("../local/bin/vnote")) { + auto app_dir_path2 = localBinDir.cleanPath(localBinDir.filePath("../local/bin")); + qInfo() << "executable dir: " << app_dir_path2; + potential_dirs << app_dir_path2; + } +#endif + + QDir::setSearchPaths("app", potential_dirs); +} + QJsonValue ConfigMgr::parseAndReadConfig(const QString &p_exp) const { if (p_exp.startsWith(QStringLiteral("main."))) { diff --git a/src/core/configmgr.h b/src/core/configmgr.h index c397dd933b..0642baebc8 100644 --- a/src/core/configmgr.h +++ b/src/core/configmgr.h @@ -120,6 +120,8 @@ namespace vnotex static QString getApplicationVersion(); + static void initAppPrefixPath(); + static void initForUnitTest(); static const QString c_orgName; diff --git a/src/core/logger.cpp b/src/core/logger.cpp index 6742c78887..5b9cfcd7ae 100644 --- a/src/core/logger.cpp +++ b/src/core/logger.cpp @@ -8,19 +8,27 @@ using namespace vnotex; QFile Logger::s_file; -bool Logger::s_debugLog = false; +bool Logger::s_verbose = false; -void Logger::init(bool p_debugLog) +bool Logger::s_logToStderr = false; + +void Logger::init(bool p_verbose, bool p_logToStderr) { - s_debugLog = p_debugLog; + s_verbose = p_verbose; + s_logToStderr = p_logToStderr; #if defined(QT_NO_DEBUG) - s_file.setFileName(ConfigMgr::getInst().getLogFile()); - if (s_file.size() >= 5 * 1024 * 1024) { - s_file.open(QIODevice::WriteOnly | QIODevice::Text); - } else { - s_file.open(QIODevice::Append | QIODevice::Text); + if (!s_logToStderr) { + s_file.setFileName(ConfigMgr::getInst().getLogFile()); + if (s_file.size() >= 5 * 1024 * 1024) { + s_file.open(QIODevice::WriteOnly | QIODevice::Text); + } else { + s_file.open(QIODevice::Append | QIODevice::Text); + } } +#else + // Always log to stderr in debug. + s_logToStderr = true; #endif qInstallMessageHandler(Logger::log); @@ -44,7 +52,7 @@ static QString getFileName(const char *p_file) void Logger::log(QtMsgType p_type, const QMessageLogContext &p_context, const QString &p_msg) { #if defined(QT_NO_DEBUG) - if (!s_debugLog && p_type == QtDebugMsg) { + if (!s_verbose && p_type == QtDebugMsg) { return; } #endif @@ -76,43 +84,43 @@ void Logger::log(QtMsgType p_type, const QMessageLogContext &p_context, const QS QString fileName = getFileName(p_context.file); -#if defined(QT_NO_DEBUG) - QTextStream stream(&s_file); - stream << header << (QString("(%1:%2) ").arg(fileName).arg(p_context.line)) - << localMsg << "\n"; + if (!s_logToStderr) { + QTextStream stream(&s_file); + stream << header << (QString("(%1:%2) ").arg(fileName).arg(p_context.line)) + << localMsg << "\n"; - if (p_type == QtFatalMsg) { - s_file.close(); - abort(); - } -#else - std::string fileStr = fileName.toStdString(); - const char *file = fileStr.c_str(); - - switch (p_type) { - case QtDebugMsg: - fprintf(stderr, "%s(%s:%u) %s\n", - header.toStdString().c_str(), file, p_context.line, localMsg.constData()); - break; - case QtInfoMsg: - fprintf(stderr, "%s(%s:%u) %s\n", - header.toStdString().c_str(), file, p_context.line, localMsg.constData()); - break; - case QtWarningMsg: - fprintf(stderr, "%s(%s:%u) %s\n", - header.toStdString().c_str(), file, p_context.line, localMsg.constData()); - break; - case QtCriticalMsg: - fprintf(stderr, "%s(%s:%u) %s\n", - header.toStdString().c_str(), file, p_context.line, localMsg.constData()); - break; - case QtFatalMsg: - fprintf(stderr, "%s(%s:%u) %s\n", - header.toStdString().c_str(), file, p_context.line, localMsg.constData()); - abort(); - break; + if (p_type == QtFatalMsg) { + s_file.close(); + abort(); + } + } else { + std::string fileStr = fileName.toStdString(); + const char *file = fileStr.c_str(); + + switch (p_type) { + case QtDebugMsg: + fprintf(stderr, "%s(%s:%u) %s\n", + header.toStdString().c_str(), file, p_context.line, localMsg.constData()); + break; + case QtInfoMsg: + fprintf(stderr, "%s(%s:%u) %s\n", + header.toStdString().c_str(), file, p_context.line, localMsg.constData()); + break; + case QtWarningMsg: + fprintf(stderr, "%s(%s:%u) %s\n", + header.toStdString().c_str(), file, p_context.line, localMsg.constData()); + break; + case QtCriticalMsg: + fprintf(stderr, "%s(%s:%u) %s\n", + header.toStdString().c_str(), file, p_context.line, localMsg.constData()); + break; + case QtFatalMsg: + fprintf(stderr, "%s(%s:%u) %s\n", + header.toStdString().c_str(), file, p_context.line, localMsg.constData()); + abort(); + break; + } + + fflush(stderr); } - - fflush(stderr); -#endif } diff --git a/src/core/logger.h b/src/core/logger.h index 3b12d1004e..e19b421381 100644 --- a/src/core/logger.h +++ b/src/core/logger.h @@ -13,14 +13,16 @@ namespace vnotex public: Logger() = delete; - static void init(bool p_debugLog); + static void init(bool p_verbose, bool p_logToStderr); private: static void log(QtMsgType p_type, const QMessageLogContext &p_context, const QString &p_msg); static QFile s_file; - static bool s_debugLog; + static bool s_verbose; + + static bool s_logToStderr; }; } diff --git a/src/main.cpp b/src/main.cpp index 23f180d77d..44076294c8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -40,10 +41,12 @@ int main(int argc, char *argv[]) QTextCodec::setCodecForLocale(codec); } +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); // This only takes effect on Win, X11 and Android. QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); +#endif // Set OpenGL option on Windows. // Set environment QT_OPENGL to "angle/desktop/software". @@ -77,6 +80,8 @@ int main(int argc, char *argv[]) Application app(argc, argv); + ConfigMgr::initAppPrefixPath(); + QAccessible::installFactory(&FakeAccessible::accessibleFactory); { @@ -135,7 +140,7 @@ int main(int argc, char *argv[]) } // Init logger after app info is set. - Logger::init(cmdOptions.m_verbose); + Logger::init(cmdOptions.m_verbose, cmdOptions.m_logToStderr); qInfo() << QString("%1 (v%2) started at %3 (%4)").arg(ConfigMgr::c_appName, app.applicationVersion(), @@ -204,42 +209,47 @@ void loadTranslators(QApplication &p_app) QLocale locale; qInfo() << "locale:" << locale.name(); - const QString envTranslationFolder(QStringLiteral("translations")); + const auto translationsPath = QDir("app:translations").absolutePath(); + qInfo() << "translations dir: " << translationsPath; + if (translationsPath.isEmpty()) { + qWarning() << "failed to locate translations directory"; + return; + } // For QTextEdit/QTextBrowser and other basic widgets. QScopedPointer qtbaseTranslator(new QTranslator(&p_app)); - if (qtbaseTranslator->load(locale, "qtbase", "_", envTranslationFolder)) { + if (qtbaseTranslator->load(locale, "qtbase", "_", translationsPath)) { p_app.installTranslator(qtbaseTranslator.take()); } // qt_zh_CN.ts does not cover the real QDialogButtonBox which uses QPlatformTheme. QScopedPointer dialogButtonBoxTranslator(new QTranslator(&p_app)); - if (dialogButtonBoxTranslator->load(locale, "qdialogbuttonbox", "_", envTranslationFolder)) { + if (dialogButtonBoxTranslator->load(locale, "qdialogbuttonbox", "_", translationsPath)) { p_app.installTranslator(dialogButtonBoxTranslator.take()); } QScopedPointer webengineTranslator(new QTranslator(&p_app)); - if (webengineTranslator->load(locale, "qwebengine", "_", envTranslationFolder)) { + if (webengineTranslator->load(locale, "qwebengine", "_", translationsPath)) { p_app.installTranslator(webengineTranslator.take()); } QScopedPointer qtTranslator(new QTranslator(&p_app)); - if (qtTranslator->load(locale, "qtv", "_", envTranslationFolder)) { + if (qtTranslator->load(locale, "qtv", "_", translationsPath)) { p_app.installTranslator(qtTranslator.take()); } QScopedPointer qtEnvTranslator(new QTranslator(&p_app)); - if (qtEnvTranslator->load(locale, "qt", "_", envTranslationFolder)) { + if (qtEnvTranslator->load(locale, "qt", "_", translationsPath)) { p_app.installTranslator(qtEnvTranslator.take()); } QScopedPointer vnoteTranslator(new QTranslator(&p_app)); - if (vnoteTranslator->load(locale, "vnote", "_", envTranslationFolder)) { + if (vnoteTranslator->load(locale, "vnote", "_", translationsPath)) { p_app.installTranslator(vnoteTranslator.take()); } QScopedPointer vtexteditTranslator(new QTranslator(&p_app)); - if (vtexteditTranslator->load(locale, "vtextedit", "_", envTranslationFolder)) { + if (vtexteditTranslator->load(locale, "vtextedit", "_", translationsPath)) { p_app.installTranslator(vtexteditTranslator.take()); } }