diff options
author | saturneric <[email protected]> | 2024-07-12 18:39:17 +0000 |
---|---|---|
committer | saturneric <[email protected]> | 2024-07-12 18:39:17 +0000 |
commit | 96e4c85edc09e106631860f823a42440d013e455 (patch) | |
tree | fd69604b607da4f7d9a9d51503ef5fc64ed75f89 /src/m_ver_check | |
parent | feat: initialize and move in two modules (diff) | |
download | Modules-96e4c85edc09e106631860f823a42440d013e455.tar.gz Modules-96e4c85edc09e106631860f823a42440d013e455.zip |
feat: try using ui and i18n apis from sdk
Diffstat (limited to 'src/m_ver_check')
-rw-r--r-- | src/m_ver_check/CMakeLists.txt | 68 | ||||
-rw-r--r-- | src/m_ver_check/QtLoggerFmt.h | 68 | ||||
-rw-r--r-- | src/m_ver_check/SoftwareVersion.cpp | 100 | ||||
-rw-r--r-- | src/m_ver_check/SoftwareVersion.h | 83 | ||||
-rw-r--r-- | src/m_ver_check/UpdateTab.cpp | 171 | ||||
-rw-r--r-- | src/m_ver_check/UpdateTab.h | 74 | ||||
-rw-r--r-- | src/m_ver_check/VersionCheckTask.cpp | 221 | ||||
-rw-r--r-- | src/m_ver_check/VersionCheckTask.h | 95 | ||||
-rw-r--r-- | src/m_ver_check/VersionCheckingModule.cpp | 115 | ||||
-rw-r--r-- | src/m_ver_check/VersionCheckingModule.h | 56 | ||||
-rw-r--r-- | src/m_ver_check/ts/ModuleVersionChecking.de_DE.ts | 68 | ||||
-rw-r--r-- | src/m_ver_check/ts/ModuleVersionChecking.en_US.ts | 68 | ||||
-rw-r--r-- | src/m_ver_check/ts/ModuleVersionChecking.fr_FR.ts | 68 | ||||
-rw-r--r-- | src/m_ver_check/ts/ModuleVersionChecking.zh_CN.ts | 68 |
14 files changed, 1323 insertions, 0 deletions
diff --git a/src/m_ver_check/CMakeLists.txt b/src/m_ver_check/CMakeLists.txt new file mode 100644 index 0000000..58edcd7 --- /dev/null +++ b/src/m_ver_check/CMakeLists.txt @@ -0,0 +1,68 @@ +# Copyright (C) 2021 Saturneric <[email protected]> +# +# This file is part of GpgFrontend. +# +# GpgFrontend is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# GpgFrontend is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>. +# +# The initial version of the source code is inherited from +# the gpg4usb project, which is under GPL-3.0-or-later. +# +# All the source code of GpgFrontend was modified and released by +# Saturneric <[email protected]> starting on May 12, 2021. +# +# SPDX-License-Identifier: GPL-3.0-or-later + +# com.bktus.gpgfrontend.module.integrated.version_checking + +aux_source_directory(. MODULE_SOURCE_FILES) + +# define libgpgfrontend_module +add_library(mod_ver_check SHARED ${MODULE_SOURCE_FILES}) + +target_include_directories(mod_ver_check PRIVATE + ${CMAKE_SOURCE_DIR}/third_party/spdlog/include) + +# install dir +install(TARGETS mod_ver_check + LIBRARY DESTINATION "${CMAKE_INSTALL_PREFIX}/modules") + +# link sdk +target_link_libraries(mod_ver_check PRIVATE + gpgfrontend_module_sdk) + +if(GPGFRONTEND_QT5_BUILD) + # link Qt + target_link_libraries(mod_ver_check PUBLIC Qt5::Core Qt5::Network Qt5::Widgets) +else() + # link Qt + target_link_libraries(mod_ver_check PUBLIC Qt6::Core Qt6::Network Qt6::Widgets) +endif() + +# using std c++ 17 +target_compile_features(mod_ver_check PRIVATE cxx_std_17) + +# ui +set(CMAKE_AUTOUIC_SEARCH_PATHS ${CMAKE_AUTOUIC_SEARCH_PATHS} ${CMAKE_CURRENT_SOURCE_DIR}/ui) + +# i18n +set(LOCALE_TS_PATH ${CMAKE_CURRENT_SOURCE_DIR}/ts) +set(TS_FILES "${LOCALE_TS_PATH}/ModuleVersionChecking.en_US.ts" + "${LOCALE_TS_PATH}/ModuleVersionChecking.de_DE.ts" + "${LOCALE_TS_PATH}/ModuleVersionChecking.fr_FR.ts" + "${LOCALE_TS_PATH}/ModuleVersionChecking.zh_CN.ts") +qt_add_translations(mod_ver_check + RESOURCE_PREFIX "/i18n" + TS_FILES ${TS_FILES} + SOURCES ${MODULE_SOURCE_FILES} + INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR})
\ No newline at end of file diff --git a/src/m_ver_check/QtLoggerFmt.h b/src/m_ver_check/QtLoggerFmt.h new file mode 100644 index 0000000..2399746 --- /dev/null +++ b/src/m_ver_check/QtLoggerFmt.h @@ -0,0 +1,68 @@ +/** + * Copyright (C) 2021 Saturneric <[email protected]> + * + * This file is part of GpgFrontend. + * + * GpgFrontend is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GpgFrontend is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>. + * + * The initial version of the source code is inherited from + * the gpg4usb project, which is under GPL-3.0-or-later. + * + * All the source code of GpgFrontend was modified and released by + * Saturneric <[email protected]> starting on May 12, 2021. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#pragma once + +#include <spdlog/spdlog.h> + +#include <QString> + +template <> +struct fmt::formatter<QString> { + // Parses format specifications. + constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + + // Formats the QString qstr and writes it to the output. + template <typename FormatContext> + auto format(const QString& qstr, FormatContext& ctx) const + -> decltype(ctx.out()) { + // Convert QString to UTF-8 QString (to handle Unicode characters + // correctly) + QByteArray utf8_array = qstr.toUtf8(); + return fmt::format_to(ctx.out(), "{}", utf8_array.constData()); + } +}; + +template <> +struct fmt::formatter<QByteArray> { + // Parses format specifications. + constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + + // Formats the QString qstr and writes it to the output. + template <typename FormatContext> + auto format(const QByteArray& qarray, FormatContext& ctx) const + -> decltype(ctx.out()) { + // Convert QString to UTF-8 QString (to handle Unicode characters + // correctly) + return fmt::format_to(ctx.out(), "{}", qarray.constData()); + } +}; diff --git a/src/m_ver_check/SoftwareVersion.cpp b/src/m_ver_check/SoftwareVersion.cpp new file mode 100644 index 0000000..cd86419 --- /dev/null +++ b/src/m_ver_check/SoftwareVersion.cpp @@ -0,0 +1,100 @@ +/** + * Copyright (C) 2021 Saturneric <[email protected]> + * + * This file is part of GpgFrontend. + * + * GpgFrontend is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GpgFrontend is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>. + * + * The initial version of the source code is inherited from + * the gpg4usb project, which is under GPL-3.0-or-later. + * + * All the source code of GpgFrontend was modified and released by + * Saturneric <[email protected]> starting on May 12, 2021. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#include "SoftwareVersion.h" + +#include <GFSDKBasic.h> +#include <GFSDKExtra.h> +#include <GFSDKLog.h> +#include <spdlog/spdlog.h> + +#include <QString> + +template <> +struct fmt::formatter<QString> { + // Parses format specifications. + constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + + // Formats the QString qstr and writes it to the output. + template <typename FormatContext> + auto format(const QString& qstr, FormatContext& ctx) const + -> decltype(ctx.out()) { + // Convert QString to UTF-8 QString (to handle Unicode characters + // correctly) + QByteArray utf8_array = qstr.toUtf8(); + return fmt::format_to(ctx.out(), "{}", utf8_array.constData()); + } +}; + +template <> +struct fmt::formatter<QByteArray> { + // Parses format specifications. + constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + + // Formats the QString qstr and writes it to the output. + template <typename FormatContext> + auto format(const QByteArray& qarray, FormatContext& ctx) const + -> decltype(ctx.out()) { + // Convert QString to UTF-8 QString (to handle Unicode characters + // correctly) + return fmt::format_to(ctx.out(), "{}", qarray.constData()); + } +}; + +auto SoftwareVersion::NeedUpgrade() const -> bool { + GFModuleLogDebug( + fmt::format( + "compair version current {} latest {}, result {}", current_version, + latest_version, + GFCompareSoftwareVersion(GFModuleStrDup(current_version.toUtf8()), + GFModuleStrDup(latest_version.toUtf8()))) + .c_str()); + + GFModuleLogDebug(fmt::format("load done: {}, pre-release: {}, draft: {}", + loading_done, + latest_prerelease_version_from_remote, + latest_draft_from_remote) + .c_str()); + return loading_done && !latest_prerelease_version_from_remote && + !latest_draft_from_remote && + GFCompareSoftwareVersion(GFModuleStrDup(current_version.toUtf8()), + GFModuleStrDup(latest_version.toUtf8())) < 0; +} + +auto SoftwareVersion::VersionWithdrawn() const -> bool { + return loading_done && !current_version_publish_in_remote && + current_version_is_a_prerelease && !current_version_is_drafted; +} + +auto SoftwareVersion::CurrentVersionReleased() const -> bool { + return loading_done && current_version_publish_in_remote; +}
\ No newline at end of file diff --git a/src/m_ver_check/SoftwareVersion.h b/src/m_ver_check/SoftwareVersion.h new file mode 100644 index 0000000..6b05d41 --- /dev/null +++ b/src/m_ver_check/SoftwareVersion.h @@ -0,0 +1,83 @@ +/** + * Copyright (C) 2021 Saturneric <[email protected]> + * + * This file is part of GpgFrontend. + * + * GpgFrontend is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GpgFrontend is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>. + * + * The initial version of the source code is inherited from + * the gpg4usb project, which is under GPL-3.0-or-later. + * + * All the source code of GpgFrontend was modified and released by + * Saturneric <[email protected]> starting on May 12, 2021. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#pragma once + +#include <QString> + +/** + * @brief + * + */ +struct SoftwareVersion { + QString latest_version; ///< + QString current_version; ///< + bool latest_prerelease_version_from_remote = false; ///< + bool latest_draft_from_remote = false; ///< + bool current_version_is_a_prerelease = false; ///< + bool current_version_is_drafted = false; ///< + bool loading_done = false; ///< + bool current_version_publish_in_remote = false; ///< + QString publish_date; ///< + QString release_note; ///< + + /** + * @brief + * + * @return true + * @return false + */ + [[nodiscard]] auto InfoValid() const -> bool { return loading_done; } + + /** + * @brief + * + * @return true + * @return false + */ + [[nodiscard]] auto NeedUpgrade() const -> bool; + + /** + * @brief + * + * @return true + * @return false + */ + [[nodiscard]] auto VersionWithdrawn() const -> bool; + + /** + * @brief + * + * @return true + * @return false + */ + [[nodiscard]] auto CurrentVersionReleased() const -> bool; + + private: + static auto version_compare(const QString& a, const QString& b) -> int; +}; diff --git a/src/m_ver_check/UpdateTab.cpp b/src/m_ver_check/UpdateTab.cpp new file mode 100644 index 0000000..ea38780 --- /dev/null +++ b/src/m_ver_check/UpdateTab.cpp @@ -0,0 +1,171 @@ +/** + * Copyright (C) 2021 Saturneric <[email protected]> + * + * This file is part of GpgFrontend. + * + * GpgFrontend is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GpgFrontend is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>. + * + * The initial version of the source code is inherited from + * the gpg4usb project, which is under GPL-3.0-or-later. + * + * All the source code of GpgFrontend was modified and released by + * Saturneric <[email protected]> starting on May 12, 2021. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#include "UpdateTab.h" + +#include "GFSDKBasic.h" +#include "GFSDKLog.h" +#include "GFSDKModule.h" +#include "VersionCheckTask.h" +#include "VersionCheckingModule.h" + +UpdateTab::UpdateTab(QWidget* parent) : QWidget(parent) { + auto* layout = new QGridLayout(); + + current_version_ = GFProjectVersion(); + + auto* tips_label = new QLabel(); + tips_label->setText( + "<center>" + + tr("It is recommended that you always check the version " + "of GpgFrontend and upgrade to the latest version.") + + "</center><center>" + + tr("New versions not only represent new features, but " + "also often represent functional and security fixes.") + + "</center>"); + tips_label->setWordWrap(true); + + current_version_label_ = new QLabel(); + current_version_label_->setText("<center>" + tr("Current Version") + + tr(": ") + "<b>" + current_version_ + + "</b></center>"); + current_version_label_->setWordWrap(true); + + latest_version_label_ = new QLabel(); + latest_version_label_->setWordWrap(true); + + upgrade_label_ = new QLabel(); + upgrade_label_->setWordWrap(true); + upgrade_label_->setOpenExternalLinks(true); + upgrade_label_->setHidden(true); + + pb_ = new QProgressBar(); + pb_->setRange(0, 0); + pb_->setTextVisible(false); + + layout->addWidget(tips_label, 1, 0, 1, -1); + layout->addWidget(current_version_label_, 2, 0, 1, -1); + layout->addWidget(latest_version_label_, 3, 0, 1, -1); + layout->addWidget(upgrade_label_, 4, 0, 1, -1); + layout->addWidget(pb_, 5, 0, 1, -1); + layout->addItem( + new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Fixed), 2, 1, + 1, 1); + + setLayout(layout); +} + +void UpdateTab::showEvent(QShowEvent* event) { + QWidget::showEvent(event); + GFModuleLogDebug("loading version loading info from rt"); + + auto is_loading_done = GFModuleRetrieveRTValueOrDefaultBool( + GFGetModuleID(), GFModuleStrDup("version.loading_done"), 0); + + if (is_loading_done == 0) { + auto* task = new VersionCheckTask(); + QObject::connect( + task, &VersionCheckTask::SignalUpgradeVersion, QThread::currentThread(), + [this](const SoftwareVersion&) { slot_show_version_status(); }); + QObject::connect(task, &VersionCheckTask::SignalUpgradeVersion, task, + &QObject::deleteLater); + task->Run(); + + } else { + slot_show_version_status(); + } +} + +void UpdateTab::slot_show_version_status() { + GFModuleLogDebug("loading version info from rt"); + this->pb_->setHidden(true); + + auto is_loading_done = GFModuleRetrieveRTValueOrDefaultBool( + GFGetModuleID(), GFModuleStrDup("version.loading_done"), 0); + + if (is_loading_done == 0) { + GFModuleLogDebug("version info loading haven't been done yet."); + return; + } + + auto is_need_upgrade = GFModuleRetrieveRTValueOrDefaultBool( + GFGetModuleID(), GFModuleStrDup("version.need_upgrade"), 0); + + auto is_current_a_withdrawn_version = GFModuleRetrieveRTValueOrDefaultBool( + GFGetModuleID(), GFModuleStrDup("version.current_a_withdrawn_version"), + 0); + + auto is_current_version_released = GFModuleRetrieveRTValueOrDefaultBool( + GFGetModuleID(), GFModuleStrDup("version.current_version_released"), 0); + + QString const latest_version = GFModuleRetrieveRTValueOrDefault( + GFGetModuleID(), GFModuleStrDup("version.latest_version"), + GFModuleStrDup("")); + + latest_version_label_->setText("<center><b>" + + tr("Latest Version From Github") + ": " + + latest_version + "</b></center>"); + + if (is_need_upgrade != 0) { + upgrade_label_->setText( + "<center>" + + tr("The current version is less than the latest version on " + "github.") + + "</center><center>" + tr("Please click") + + " <a " + "href=\"https://www.gpgfrontend.bktus.com/#/downloads\">" + + tr("Here") + "</a> " + tr("to download the latest stable version.") + + "</center>"); + upgrade_label_->show(); + } else if (is_current_a_withdrawn_version != 0) { + upgrade_label_->setText( + "<center>" + + tr("This version has serious problems and has been withdrawn. " + "Please stop using it immediately.") + + "</center><center>" + tr("Please click") + + " <a " + "href=\"https://github.com/saturneric/GpgFrontend/releases\">" + + tr("Here") + "</a> " + tr("to download the latest stable version.") + + "</center>"); + upgrade_label_->show(); + } else if (is_current_version_released == 0) { + upgrade_label_->setText( + "<center>" + + tr("This version has not been released yet, it may be a beta " + "version. If you are not a tester and care about version " + "stability, please do not use this version.") + + "</center><center>" + tr("Please click") + + " <a " + "href=\"https://www.gpgfrontend.bktus.com/#/downloads\">" + + tr("Here") + "</a> " + tr("to download the latest stable version.") + + "</center>"); + upgrade_label_->show(); + } +} + +auto UpdateTabFactory(const char* id) -> void* { return new UpdateTab(); }
\ No newline at end of file diff --git a/src/m_ver_check/UpdateTab.h b/src/m_ver_check/UpdateTab.h new file mode 100644 index 0000000..fd48f05 --- /dev/null +++ b/src/m_ver_check/UpdateTab.h @@ -0,0 +1,74 @@ +/** + * Copyright (C) 2021 Saturneric <[email protected]> + * + * This file is part of GpgFrontend. + * + * GpgFrontend is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GpgFrontend is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>. + * + * The initial version of the source code is inherited from + * the gpg4usb project, which is under GPL-3.0-or-later. + * + * All the source code of GpgFrontend was modified and released by + * Saturneric <[email protected]> starting on May 12, 2021. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#pragma once + +#include <QtWidgets/QtWidgets> + +/** + * @brief Class containing the main tab of about dialog + * + */ +class UpdateTab : public QWidget { + Q_OBJECT + + QLabel* current_version_label_; ///< + QLabel* latest_version_label_; ///< + QLabel* upgrade_label_; ///< + QProgressBar* pb_; ///< + QString current_version_; ///< + + public: + /** + * @brief Construct a new Update Tab object + * + * @param parent + */ + explicit UpdateTab(QWidget* parent = nullptr); + + protected: + void showEvent(QShowEvent* event) override; + + private slots: + /** + * @brief + * + * @param version + */ + void slot_show_version_status(); + + signals: + /** + * @brief + * + * @param data + */ + void SignalReplyFromUpdateServer(QByteArray data); +}; + +auto UpdateTabFactory(const char* id) -> void*;
\ No newline at end of file diff --git a/src/m_ver_check/VersionCheckTask.cpp b/src/m_ver_check/VersionCheckTask.cpp new file mode 100644 index 0000000..3455ddc --- /dev/null +++ b/src/m_ver_check/VersionCheckTask.cpp @@ -0,0 +1,221 @@ +/** + * Copyright (C) 2021 Saturneric <[email protected]> + * + * This file is part of GpgFrontend. + * + * GpgFrontend is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GpgFrontend is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>. + * + * The initial version of the source code is inherited from + * the gpg4usb project, which is under GPL-3.0-or-later. + * + * All the source code of GpgFrontend was modified and released by + * Saturneric <[email protected]> starting on May 12, 2021. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#include "VersionCheckTask.h" + +#include <GFSDKBasic.h> +#include <GFSDKExtra.h> +#include <GFSDKLog.h> + +#include <QMetaType> +#include <QtNetwork> + +// spdlog +#include <spdlog/spdlog.h> + +#include "LogFormatter.h" +#include "SoftwareVersion.h" +#include "VersionCheckingModule.h" + +VersionCheckTask::VersionCheckTask() + : network_manager_(new QNetworkAccessManager(this)), + current_version_(GFProjectVersion()) { + qRegisterMetaType<SoftwareVersion>("SoftwareVersion"); + version_.current_version = current_version_; +} + +auto VersionCheckTask::Run() -> int { + GFModuleLogDebug( + fmt::format("current project version: {}", current_version_).c_str()); + QString latest_version_url = + "https://api.github.com/repos/saturneric/gpgfrontend/releases/latest"; + + QNetworkRequest latest_request(latest_version_url); + latest_request.setHeader(QNetworkRequest::UserAgentHeader, + GFHttpRequestUserAgent()); + + latest_reply_ = network_manager_->get(latest_request); + connect(latest_reply_, &QNetworkReply::finished, this, + &VersionCheckTask::slot_parse_latest_version_info); + return 0; +} + +void VersionCheckTask::slot_parse_latest_version_info() { + if (latest_reply_ == nullptr) { + version_.latest_version = current_version_; + version_.loading_done = false; + } else if (latest_reply_->error() != QNetworkReply::NoError) { + GFModuleLogError(fmt::format("latest version request error: ", + latest_reply_->errorString()) + .c_str()); + version_.latest_version = current_version_; + } else { + latest_reply_bytes_ = latest_reply_->readAll(); + auto latest_reply_json = QJsonDocument::fromJson(latest_reply_bytes_); + + if (latest_reply_json.isObject()) { + QString latest_version = latest_reply_json["tag_name"].toString(); + + QRegularExpression re(R"(^[vV](\d+\.)?(\d+\.)?(\*|\d+))"); + auto version_match = re.match(latest_version); + if (version_match.hasMatch()) { + latest_version = version_match.captured(0); + GFModuleLogInfo(fmt::format("latest released version from github: {}", + latest_version) + .c_str()); + } else { + latest_version = current_version_; + GFModuleLogWarn( + fmt::format("latest version unknown, set to current version: {}", + current_version_) + .c_str()); + } + + bool prerelease = latest_reply_json["prerelease"].toBool(); + bool draft = latest_reply_json["draft"].toBool(); + auto publish_date = latest_reply_json["published_at"].toString(); + auto release_note = latest_reply_json["body"].toString(); + version_.latest_version = latest_version; + version_.latest_prerelease_version_from_remote = prerelease; + version_.latest_draft_from_remote = draft; + version_.publish_date = publish_date; + version_.release_note = release_note; + } else { + GFModuleLogWarn(fmt::format("cannot parse data got from github: {}", + latest_reply_bytes_) + .c_str()); + } + } + + if (latest_reply_ != nullptr) { + latest_reply_->deleteLater(); + } + + try { + QString current_version_url = + "https://api.github.com/repos/saturneric/gpgfrontend/releases/tags/" + + current_version_; + GFModuleLogDebug( + fmt::format("current version info query url: {}", current_version_url) + .c_str()); + + QNetworkRequest current_request(current_version_url); + current_request.setHeader(QNetworkRequest::UserAgentHeader, + GFHttpRequestUserAgent()); + + current_reply_ = network_manager_->get(current_request); + + connect(current_reply_, &QNetworkReply::finished, this, + &VersionCheckTask::slot_parse_current_version_info); + } catch (...) { + GFModuleLogError("current version request create error"); + } +} + +void VersionCheckTask::slot_parse_current_version_info() { + if (current_reply_ == nullptr) { + // loading done + version_.loading_done = false; + + } else if (current_reply_->error() != QNetworkReply::NoError) { + GFModuleLogError(fmt::format("current version request network error: {}", + current_reply_->errorString()) + .c_str()); + + // loading done + version_.loading_done = true; + version_.current_version_publish_in_remote = false; + } else { + version_.current_version_publish_in_remote = true; + current_reply_bytes_ = current_reply_->readAll(); + auto current_reply_json = QJsonDocument::fromJson(current_reply_bytes_); + + if (current_reply_json.isObject()) { + bool current_prerelease = current_reply_json["prerelease"].toBool(); + bool current_draft = current_reply_json["draft"].toBool(); + version_.latest_prerelease_version_from_remote = current_prerelease; + version_.latest_draft_from_remote = current_draft; + // loading done + version_.loading_done = true; + } else { + GFModuleLogWarn(fmt::format("cannot parse data got from github: {}", + current_reply_bytes_) + .c_str()); + } + } + + GFModuleLogDebug(fmt::format("current version parse done: {}", + version_.current_version_publish_in_remote) + .c_str()); + + if (current_reply_ != nullptr) current_reply_->deleteLater(); + + slot_fill_grt_with_version_info(version_); + emit SignalUpgradeVersion(version_); +} + +void VersionCheckTask::slot_fill_grt_with_version_info( + const SoftwareVersion &version) { + GFModuleLogDebug("filling software information info in rt..."); + + GFModuleUpsertRTValue(GFGetModuleID(), + GFModuleStrDup("version.current_version"), + GFModuleStrDup(version.current_version.toUtf8())); + GFModuleUpsertRTValue(GFGetModuleID(), + GFModuleStrDup("version.latest_version"), + GFModuleStrDup(version.latest_version.toUtf8())); + GFModuleUpsertRTValueBool( + GFGetModuleID(), GFModuleStrDup("version.current_version_is_drafted"), + version.current_version_is_drafted ? 1 : 0); + GFModuleUpsertRTValueBool( + GFGetModuleID(), + GFModuleStrDup("version.current_version_is_a_prerelease"), + version.current_version_is_a_prerelease ? 1 : 0); + GFModuleUpsertRTValueBool( + GFGetModuleID(), + GFModuleStrDup("version.current_version_publish_in_remote"), + version.current_version_publish_in_remote ? 1 : 0); + GFModuleUpsertRTValueBool( + GFGetModuleID(), + GFModuleStrDup("version.latest_prerelease_version_from_remote"), + version.latest_prerelease_version_from_remote ? 1 : 0); + GFModuleUpsertRTValueBool(GFGetModuleID(), + GFModuleStrDup("version.need_upgrade"), + version.NeedUpgrade() ? 1 : 0); + GFModuleUpsertRTValueBool(GFGetModuleID(), + GFModuleStrDup("version.current_version_released"), + version.CurrentVersionReleased() ? 1 : 0); + GFModuleUpsertRTValueBool( + GFGetModuleID(), GFModuleStrDup("version.current_a_withdrawn_version"), + version.VersionWithdrawn() ? 1 : 0); + GFModuleUpsertRTValueBool(GFGetModuleID(), + GFModuleStrDup("version.loading_done"), + version.loading_done ? 1 : 0); + + GFModuleLogDebug("software information filled in rt"); +} diff --git a/src/m_ver_check/VersionCheckTask.h b/src/m_ver_check/VersionCheckTask.h new file mode 100644 index 0000000..b915be6 --- /dev/null +++ b/src/m_ver_check/VersionCheckTask.h @@ -0,0 +1,95 @@ +/** + * Copyright (C) 2021 Saturneric <[email protected]> + * + * This file is part of GpgFrontend. + * + * GpgFrontend is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GpgFrontend is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>. + * + * The initial version of the source code is inherited from + * the gpg4usb project, which is under GPL-3.0-or-later. + * + * All the source code of GpgFrontend was modified and released by + * Saturneric <[email protected]> starting on May 12, 2021. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#pragma once + +#include <QObject> + +#include "SoftwareVersion.h" + +class QNetworkReply; +class QNetworkAccessManager; + +/** + * @brief + * + */ +class VersionCheckTask : public QObject { + Q_OBJECT + public: + /** + * @brief Construct a new Version Check Thread object + * + */ + VersionCheckTask(); + + /** + * @brief + * + * @return int + */ + auto Run() -> int; + + signals: + + /** + * @brief + * + * @param version + */ + void SignalUpgradeVersion(SoftwareVersion version); + + private slots: + + /** + * @brief + * + */ + void slot_parse_latest_version_info(); + + /** + * @brief + * + */ + void slot_parse_current_version_info(); + + /** + * @brief + * + */ + void slot_fill_grt_with_version_info(const SoftwareVersion&); + + private: + QByteArray latest_reply_bytes_; ///< + QByteArray current_reply_bytes_; ///< + QNetworkReply* latest_reply_ = nullptr; ///< latest version info reply + QNetworkReply* current_reply_ = nullptr; ///< current version info reply + QNetworkAccessManager* network_manager_; ///< + QString current_version_; + SoftwareVersion version_; +}; diff --git a/src/m_ver_check/VersionCheckingModule.cpp b/src/m_ver_check/VersionCheckingModule.cpp new file mode 100644 index 0000000..0b57a25 --- /dev/null +++ b/src/m_ver_check/VersionCheckingModule.cpp @@ -0,0 +1,115 @@ +/** + * Copyright (C) 2021 Saturneric <[email protected]> + * + * This file is part of GpgFrontend. + * + * GpgFrontend is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GpgFrontend is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>. + * + * The initial version of the source code is inherited from + * the gpg4usb project, which is under GPL-3.0-or-later. + * + * All the source code of GpgFrontend was modified and released by + * Saturneric <[email protected]> starting on May 12, 2021. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#include "VersionCheckingModule.h" + +#include <GFSDKBasic.h> +#include <GFSDKBuildInfo.h> +#include <GFSDKExtra.h> +#include <GFSDKLog.h> +#include <GFSDKUI.h> +#include <spdlog/spdlog.h> + +#include <QMetaType> +#include <QtNetwork> + +#include "GFModuleCommonUtils.hpp" +#include "SoftwareVersion.h" +#include "UpdateTab.h" +#include "VersionCheckTask.h" + +auto GFGetModuleGFSDKVersion() -> const char* { + return DUP(GF_SDK_VERSION_STR); +} + +auto GFGetModuleQtEnvVersion() -> const char* { return DUP(QT_VERSION_STR); } + +auto GFGetModuleID() -> const char* { + return DUP("com.bktus.gpgfrontend.module.version_checking"); +} + +auto GFGetModuleVersion() -> const char* { return DUP("1.0.0"); } + +auto GFGetModuleMetaData() -> GFModuleMetaData* { + return QMapToGFModuleMetaDataList( + {{"Name", "VersionChecking"}, + {"Description", "Try checking GpgFrontend version."}, + {"Author", "Saturneric"}}); +} + +auto GFRegisterModule() -> int { + MLogInfo("version checking module registering"); + return 0; +} + +auto GFActiveModule() -> int { + MLogInfo("version checking module activating"); + + GFModuleListenEvent(GFGetModuleID(), DUP("APPLICATION_LOADED")); + GFModuleListenEvent(GFGetModuleID(), DUP("CHECK_APPLICATION_VERSION")); + + GFUIMountEntry(DUP("AboutDialogTabs"), + QMapToMetaDataArray({{"TabTitle", "Update"}}), 1, + UpdateTabFactory); + + // load translations + QFile f( + QString(":/i18n/ModuleVersionChecking.%1.qm").arg(GFAppActiveLocale())); + if (f.exists() && f.open(QIODevice::ReadOnly)) { + auto f_n = f.fileName().toUtf8(); + MLogInfoS("version checking module loading, locale: %s, path: %s", + GFAppActiveLocale(), f_n.data()); + auto b = f.readAll(); + GFAppRegisterTranslator(AllocBufferAndCopy(b), b.size()); + } + return 0; +} + +auto GFExecuteModule(GFModuleEvent* event) -> int { + MLogInfoS("version checking module executing, event id: %s", event->id); + + auto* task = new VersionCheckTask(); + QObject::connect(task, &VersionCheckTask::SignalUpgradeVersion, + QThread::currentThread(), [event](const SoftwareVersion&) { + char** event_argv = static_cast<char**>( + GFAllocateMemory(sizeof(char**) * 1)); + event_argv[0] = DUP("0"); + + GFModuleTriggerModuleEventCallback(event, GFGetModuleID(), + 1, event_argv); + }); + QObject::connect(task, &VersionCheckTask::SignalUpgradeVersion, task, + &QObject::deleteLater); + task->Run(); + + return 0; +} + +auto GFDeactiveModule() -> int { return 0; } + +auto GFUnregisterModule() -> int { return 0; }
\ No newline at end of file diff --git a/src/m_ver_check/VersionCheckingModule.h b/src/m_ver_check/VersionCheckingModule.h new file mode 100644 index 0000000..35ee4ac --- /dev/null +++ b/src/m_ver_check/VersionCheckingModule.h @@ -0,0 +1,56 @@ +/** + * Copyright (C) 2021 Saturneric <[email protected]> + * + * This file is part of GpgFrontend. + * + * GpgFrontend is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GpgFrontend is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>. + * + * The initial version of the source code is inherited from + * the gpg4usb project, which is under GPL-3.0-or-later. + * + * All the source code of GpgFrontend was modified and released by + * Saturneric <[email protected]> starting on May 12, 2021. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#pragma once + +#include <GFSDKModule.h> + +#include "GFModuleExport.h" + +extern "C" { + +auto GF_MODULE_EXPORT GFGetModuleGFSDKVersion() -> const char *; + +auto GF_MODULE_EXPORT GFGetModuleQtEnvVersion() -> const char *; + +auto GF_MODULE_EXPORT GFGetModuleID() -> const char *; + +auto GF_MODULE_EXPORT GFGetModuleVersion() -> const char *; + +auto GF_MODULE_EXPORT GFGetModuleMetaData() -> GFModuleMetaData *; + +auto GF_MODULE_EXPORT GFRegisterModule() -> int; + +auto GF_MODULE_EXPORT GFActiveModule() -> int; + +auto GF_MODULE_EXPORT GFExecuteModule(GFModuleEvent *) -> int; + +auto GF_MODULE_EXPORT GFDeactiveModule() -> int; + +auto GF_MODULE_EXPORT GFUnregisterModule() -> int; +}; diff --git a/src/m_ver_check/ts/ModuleVersionChecking.de_DE.ts b/src/m_ver_check/ts/ModuleVersionChecking.de_DE.ts new file mode 100644 index 0000000..ad113a5 --- /dev/null +++ b/src/m_ver_check/ts/ModuleVersionChecking.de_DE.ts @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="de_DE"> +<context> + <name>UpdateTab</name> + <message> + <location filename="../UpdateTab.cpp" line="45"/> + <source>It is recommended that you always check the version of GpgFrontend and upgrade to the latest version.</source> + <translation type="unfinished">Es wird empfohlen, immer die Version von GpgFrontend zu überprüfen und auf die neueste Version zu aktualisieren.</translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="48"/> + <source>New versions not only represent new features, but also often represent functional and security fixes.</source> + <translation type="unfinished">Neue Versionen beinhalten nicht nur neue Funktionen, sondern stellen oft auch Funktions- und Sicherheitskorrekturen dar.</translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="54"/> + <source>Current Version</source> + <translation type="unfinished">Aktuelle Version</translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="55"/> + <source>: </source> + <translation type="unfinished">: </translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="131"/> + <source>Latest Version From Github</source> + <translation type="unfinished">Neueste Version von Github</translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="137"/> + <source>The current version is less than the latest version on github.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="139"/> + <location filename="../UpdateTab.cpp" line="150"/> + <location filename="../UpdateTab.cpp" line="162"/> + <source>Please click</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="142"/> + <location filename="../UpdateTab.cpp" line="153"/> + <location filename="../UpdateTab.cpp" line="165"/> + <source>Here</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="142"/> + <location filename="../UpdateTab.cpp" line="153"/> + <location filename="../UpdateTab.cpp" line="165"/> + <source>to download the latest stable version.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="148"/> + <source>This version has serious problems and has been withdrawn. Please stop using it immediately.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="159"/> + <source>This version has not been released yet, it may be a beta version. If you are not a tester and care about version stability, please do not use this version.</source> + <translation type="unfinished"></translation> + </message> +</context> +</TS> diff --git a/src/m_ver_check/ts/ModuleVersionChecking.en_US.ts b/src/m_ver_check/ts/ModuleVersionChecking.en_US.ts new file mode 100644 index 0000000..6e4afc5 --- /dev/null +++ b/src/m_ver_check/ts/ModuleVersionChecking.en_US.ts @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1"> +<context> + <name>UpdateTab</name> + <message> + <location filename="../UpdateTab.cpp" line="45"/> + <source>It is recommended that you always check the version of GpgFrontend and upgrade to the latest version.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="48"/> + <source>New versions not only represent new features, but also often represent functional and security fixes.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="54"/> + <source>Current Version</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="55"/> + <source>: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="131"/> + <source>Latest Version From Github</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="137"/> + <source>The current version is less than the latest version on github.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="139"/> + <location filename="../UpdateTab.cpp" line="150"/> + <location filename="../UpdateTab.cpp" line="162"/> + <source>Please click</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="142"/> + <location filename="../UpdateTab.cpp" line="153"/> + <location filename="../UpdateTab.cpp" line="165"/> + <source>Here</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="142"/> + <location filename="../UpdateTab.cpp" line="153"/> + <location filename="../UpdateTab.cpp" line="165"/> + <source>to download the latest stable version.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="148"/> + <source>This version has serious problems and has been withdrawn. Please stop using it immediately.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="159"/> + <source>This version has not been released yet, it may be a beta version. If you are not a tester and care about version stability, please do not use this version.</source> + <translation type="unfinished"></translation> + </message> +</context> +</TS> diff --git a/src/m_ver_check/ts/ModuleVersionChecking.fr_FR.ts b/src/m_ver_check/ts/ModuleVersionChecking.fr_FR.ts new file mode 100644 index 0000000..6e4afc5 --- /dev/null +++ b/src/m_ver_check/ts/ModuleVersionChecking.fr_FR.ts @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1"> +<context> + <name>UpdateTab</name> + <message> + <location filename="../UpdateTab.cpp" line="45"/> + <source>It is recommended that you always check the version of GpgFrontend and upgrade to the latest version.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="48"/> + <source>New versions not only represent new features, but also often represent functional and security fixes.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="54"/> + <source>Current Version</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="55"/> + <source>: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="131"/> + <source>Latest Version From Github</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="137"/> + <source>The current version is less than the latest version on github.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="139"/> + <location filename="../UpdateTab.cpp" line="150"/> + <location filename="../UpdateTab.cpp" line="162"/> + <source>Please click</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="142"/> + <location filename="../UpdateTab.cpp" line="153"/> + <location filename="../UpdateTab.cpp" line="165"/> + <source>Here</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="142"/> + <location filename="../UpdateTab.cpp" line="153"/> + <location filename="../UpdateTab.cpp" line="165"/> + <source>to download the latest stable version.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="148"/> + <source>This version has serious problems and has been withdrawn. Please stop using it immediately.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="159"/> + <source>This version has not been released yet, it may be a beta version. If you are not a tester and care about version stability, please do not use this version.</source> + <translation type="unfinished"></translation> + </message> +</context> +</TS> diff --git a/src/m_ver_check/ts/ModuleVersionChecking.zh_CN.ts b/src/m_ver_check/ts/ModuleVersionChecking.zh_CN.ts new file mode 100644 index 0000000..6e4afc5 --- /dev/null +++ b/src/m_ver_check/ts/ModuleVersionChecking.zh_CN.ts @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1"> +<context> + <name>UpdateTab</name> + <message> + <location filename="../UpdateTab.cpp" line="45"/> + <source>It is recommended that you always check the version of GpgFrontend and upgrade to the latest version.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="48"/> + <source>New versions not only represent new features, but also often represent functional and security fixes.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="54"/> + <source>Current Version</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="55"/> + <source>: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="131"/> + <source>Latest Version From Github</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="137"/> + <source>The current version is less than the latest version on github.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="139"/> + <location filename="../UpdateTab.cpp" line="150"/> + <location filename="../UpdateTab.cpp" line="162"/> + <source>Please click</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="142"/> + <location filename="../UpdateTab.cpp" line="153"/> + <location filename="../UpdateTab.cpp" line="165"/> + <source>Here</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="142"/> + <location filename="../UpdateTab.cpp" line="153"/> + <location filename="../UpdateTab.cpp" line="165"/> + <source>to download the latest stable version.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="148"/> + <source>This version has serious problems and has been withdrawn. Please stop using it immediately.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../UpdateTab.cpp" line="159"/> + <source>This version has not been released yet, it may be a beta version. If you are not a tester and care about version stability, please do not use this version.</source> + <translation type="unfinished"></translation> + </message> +</context> +</TS> |