From 0ab6aefe1139b25a672778bf111097f0c9c5c36e Mon Sep 17 00:00:00 2001
From: gera
Date: Thu, 10 Jun 2021 22:43:08 +0300
Subject: [PATCH] Merge with master and resolve CI configure script conflict.
---
CMakeLists.txt | 8 ++
scripts/ci/configure.sh | 1 +
src/AudacityApp.cpp | 5 +
src/AudacityApp.h | 5 +
src/CMakeLists.txt | 16 ++++
src/update/UpdateDataParser.cpp | 152 +++++++++++++++++++++++++++++++
src/update/UpdateDataParser.h | 66 ++++++++++++++
src/update/UpdateManager.cpp | 150 ++++++++++++++++++++++++++++++
src/update/UpdateManager.h | 57 ++++++++++++
src/update/UpdatePopupDialog.cpp | 136 +++++++++++++++++++++++++++
src/update/UpdatePopupDialog.h | 46 ++++++++++
src/update/VersionId.cpp | 85 +++++++++++++++++
src/update/VersionId.h | 49 ++++++++++
src/update/VersionPatch.h | 23 +++++
14 files changed, 799 insertions(+)
create mode 100644 src/update/UpdateDataParser.cpp
create mode 100644 src/update/UpdateDataParser.h
create mode 100644 src/update/UpdateManager.cpp
create mode 100644 src/update/UpdateManager.h
create mode 100644 src/update/UpdatePopupDialog.cpp
create mode 100644 src/update/UpdatePopupDialog.h
create mode 100644 src/update/VersionId.cpp
create mode 100644 src/update/VersionId.h
create mode 100644 src/update/VersionPatch.h
diff --git a/CMakeLists.txt b/CMakeLists.txt
index dc960f3bd..d305c064f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -191,6 +191,14 @@ cmake_dependent_option(
Off
)
+cmake_dependent_option(
+ ${_OPT}has_updates_check
+ "Has a builtin support for checking of updates"
+ On
+ "${_OPT}has_networking"
+ Off
+)
+
# Determine 32-bit or 64-bit target
if( CMAKE_C_COMPILER_ID MATCHES "MSVC" AND CMAKE_VS_PLATFORM_NAME MATCHES "Win64|x64" )
set( IS_64BIT ON )
diff --git a/scripts/ci/configure.sh b/scripts/ci/configure.sh
index 77ab945aa..fb0fd3724 100755
--- a/scripts/ci/configure.sh
+++ b/scripts/ci/configure.sh
@@ -12,6 +12,7 @@ cmake_args=(
-G "${AUDACITY_CMAKE_GENERATOR}"
-D audacity_use_pch=no
-D audacity_has_networking=yes
+ -D audacity_has_updates_check=yes
-D CMAKE_BUILD_TYPE="${AUDACITY_BUILD_TYPE}"
-D CMAKE_INSTALL_PREFIX="${AUDACITY_INSTALL_PREFIX}"
)
diff --git a/src/AudacityApp.cpp b/src/AudacityApp.cpp
index 14f2e5e3c..e30069e60 100644
--- a/src/AudacityApp.cpp
+++ b/src/AudacityApp.cpp
@@ -111,6 +111,7 @@ It handles initialization and termination by subclassing wxApp.
#include "tracks/ui/Scrubbing.h"
#include "widgets/FileConfig.h"
#include "widgets/FileHistory.h"
+#include "update/UpdateManager.h"
#ifdef HAS_NETWORKING
#include "NetworkManager.h"
@@ -1489,6 +1490,10 @@ bool AudacityApp::InitPart2()
SplashDialog::DoHelpWelcome(*project);
}
+#if defined(HAVE_UPDATES_CHECK)
+ mUpdateManager = std::make_unique(*project);
+#endif
+
#ifdef USE_FFMPEG
FFmpegStartup();
#endif
diff --git a/src/AudacityApp.h b/src/AudacityApp.h
index a5c0eaaed..aeb556e69 100644
--- a/src/AudacityApp.h
+++ b/src/AudacityApp.h
@@ -33,6 +33,7 @@ class Importer;
class CommandHandler;
class AppCommandEvent;
class AudacityProject;
+class UpdateManager;
class AudacityApp final : public wxApp {
public:
@@ -113,6 +114,10 @@ class AudacityApp final : public wxApp {
std::unique_ptr mIPCServ;
#endif
+#if defined(HAVE_UPDATES_CHECK)
+ std::unique_ptr mUpdateManager;
+#endif
+
public:
DECLARE_EVENT_TABLE()
};
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index eea73d7d5..541683cf9 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -982,6 +982,19 @@ list( APPEND SOURCES
xml/XMLWriter.h
xml/audacityproject.dtd
+ # Update version
+ $<$:
+ update/VersionId.h
+ update/VersionId.cpp
+ update/VersionPatch.h
+ update/UpdateDataParser.h
+ update/UpdateDataParser.cpp
+ update/UpdateManager.h
+ update/UpdateManager.cpp
+ update/UpdatePopupDialog.h
+ update/UpdatePopupDialog.cpp
+ >
+
Experimental.cmake
)
@@ -1038,6 +1051,9 @@ list( APPEND DEFINES
__STDC_CONSTANT_MACROS
STRICT
>
+ $<$:
+ HAVE_UPDATES_CHECK
+ >
)
# If we have cmake 3.16 or higher, we can use precompiled headers, but
diff --git a/src/update/UpdateDataParser.cpp b/src/update/UpdateDataParser.cpp
new file mode 100644
index 000000000..c5bb923ea
--- /dev/null
+++ b/src/update/UpdateDataParser.cpp
@@ -0,0 +1,152 @@
+/*!********************************************************************
+ Audacity: A Digital Audio Editor
+
+ @file UpdateDataParser.cpp
+ @brief Declare a class that parse update server data format.
+
+ Anton Gerasimov
+ **********************************************************************/
+
+#include "UpdateDataParser.h"
+
+#include "xml/XMLFileReader.h"
+
+UpdateDataParser::UpdateDataParser()
+{}
+
+UpdateDataParser::~UpdateDataParser()
+{}
+
+bool UpdateDataParser::Parse(const VersionPatch::UpdateDataFormat& updateData, VersionPatch* versionPatch)
+{
+ XMLFileReader xmlReader;
+
+ mVersionPatch = versionPatch;
+ auto ok = xmlReader.ParseString(this, updateData);
+ mVersionPatch = nullptr;
+
+ return ok;
+}
+
+wxArrayString UpdateDataParser::splitChangelogSentences(const wxString& changelogContent)
+{
+ wxArrayString changelogSentenceList;
+
+ size_t pos = 0;
+ std::string s(changelogContent.ToStdString());
+ std::string token;
+ const std::string delimiter(". ");
+
+ while ((pos = s.find(delimiter)) != std::string::npos)
+ {
+ token = s.substr(0, pos + 1);
+ changelogSentenceList.Add(token);
+
+ s.erase(0, pos + delimiter.length());
+ }
+ changelogSentenceList.Add(s);
+
+ return changelogSentenceList;
+}
+
+bool UpdateDataParser::HandleXMLTag(const wxChar* tag, const wxChar** attrs)
+{
+ if (wxStrcmp(tag, mXmlTagNames[XmlParsedTags::kDescriptionTag]) == 0)
+ {
+ mXmlParsingState = XmlParsedTags::kDescriptionTag;
+ return true;
+ }
+
+ if (wxStrcmp(tag, mXmlTagNames[XmlParsedTags::kWindowsTag]) == 0)
+ {
+ if (wxPlatformInfo::Get().GetOperatingSystemId() & wxOS_WINDOWS)
+ mXmlParsingState = XmlParsedTags::kOsTag;
+ return true;
+ }
+
+ if (wxStrcmp(tag, mXmlTagNames[XmlParsedTags::kMacosTag]) == 0)
+ {
+ if (wxPlatformInfo::Get().GetOperatingSystemId() & wxOS_MAC)
+ mXmlParsingState = XmlParsedTags::kOsTag;
+ return true;
+ }
+
+ if (wxStrcmp(tag, mXmlTagNames[XmlParsedTags::kLinuxTag]) == 0)
+ {
+ if (wxPlatformInfo::Get().GetOperatingSystemId() & wxOS_UNIX_LINUX)
+ mXmlParsingState = XmlParsedTags::kOsTag;
+ return true;
+ }
+
+ if (wxStrcmp(tag, mXmlTagNames[XmlParsedTags::kVersionTag]) == 0)
+ {
+ if (mXmlParsingState == XmlParsedTags::kOsTag)
+ mXmlParsingState = XmlParsedTags::kVersionTag;
+ return true;
+ }
+
+ if (wxStrcmp(tag, mXmlTagNames[XmlParsedTags::kLinkTag]) == 0)
+ {
+ if (mXmlParsingState == XmlParsedTags::kOsTag)
+ mXmlParsingState = XmlParsedTags::kLinkTag;
+ return true;
+ }
+
+ for (auto& xmlTag : mXmlTagNames)
+ {
+ if (wxStrcmp(tag, xmlTag.second) == 0)
+ return true;
+ }
+
+ return false;
+}
+
+void UpdateDataParser::HandleXMLEndTag(const wxChar* tag)
+{
+ if (mXmlParsingState == XmlParsedTags::kDescriptionTag ||
+ mXmlParsingState == XmlParsedTags::kLinkTag)
+ mXmlParsingState = XmlParsedTags::kNotUsedTag;
+
+ if (mXmlParsingState == XmlParsedTags::kVersionTag)
+ mXmlParsingState = XmlParsedTags::kOsTag;
+}
+
+void UpdateDataParser::HandleXMLContent(const wxString& content)
+{
+ if (mVersionPatch == nullptr)
+ return;
+
+ wxString trimedContent(content);
+
+ switch (mXmlParsingState)
+ {
+ case XmlParsedTags::kDescriptionTag:
+ trimedContent.Trim(true).Trim(false);
+ mVersionPatch->changelog = splitChangelogSentences(trimedContent);
+ break;
+
+ case XmlParsedTags::kVersionTag:
+ trimedContent.Trim(true).Trim(false);
+ mVersionPatch->version = VersionId::ParseFromString(trimedContent);
+ break;
+
+ case XmlParsedTags::kLinkTag:
+ trimedContent.Trim(true).Trim(false);
+ mVersionPatch->download = trimedContent;
+ break;
+
+ default:
+ break;
+ }
+}
+
+XMLTagHandler* UpdateDataParser::HandleXMLChild(const wxChar* tag)
+{
+ for (auto& xmlTag : mXmlTagNames)
+ {
+ if (wxStrcmp(tag, xmlTag.second) == 0)
+ return this;
+ }
+
+ return NULL;
+}
diff --git a/src/update/UpdateDataParser.h b/src/update/UpdateDataParser.h
new file mode 100644
index 000000000..abb8b526b
--- /dev/null
+++ b/src/update/UpdateDataParser.h
@@ -0,0 +1,66 @@
+/*!********************************************************************
+ Audacity: A Digital Audio Editor
+
+ @file UpdateDataParser.h
+ @brief Declare a class that parse update server data format.
+
+ Anton Gerasimov
+ **********************************************************************/
+#pragma once
+
+#include "VersionPatch.h"
+
+#include "xml/XMLTagHandler.h"
+
+#include
+#include
");
+
+ informationStr << wxT("");
+ informationStr << XO("Read more on GitHub");
+ informationStr << wxT("
");
+
+ informationStr << wxT("");
+
+ HtmlWindow* html = safenew LinkingHtmlWindow (parent, -1,
+ wxDefaultPosition,
+ wxSize (500, -1),
+ wxHW_SCROLLBAR_AUTO | wxSUNKEN_BORDER);
+
+ html->SetBorders (20);
+ html->SetPage (o.GetString());
+
+ wxHtmlContainerCell* cell = html->GetInternalRepresentation();
+
+ cell->Layout (500);
+
+ const wxSize size = wxSize (500, cell->GetHeight());
+
+ html->SetMinSize (size);
+ html->SetMaxSize (size);
+ html->SetSize (size);
+
+ return html;
+}
diff --git a/src/update/UpdatePopupDialog.h b/src/update/UpdatePopupDialog.h
new file mode 100644
index 000000000..815f2e8bc
--- /dev/null
+++ b/src/update/UpdatePopupDialog.h
@@ -0,0 +1,46 @@
+/*!********************************************************************
+ Audacity: A Digital Audio Editor
+
+ @file UpdatePopupDialog.h
+ @brief Define a dialog for notifying users about new version available.
+
+ Anton Gerasimov
+ **********************************************************************/
+
+#pragma once
+
+#include "widgets/wxPanelWrapper.h"
+#include "wx/string.h"
+
+#include "Project.h"
+
+#include "update/UpdateManager.h"
+
+class HtmlWindow;
+class wxWindow;
+class AudacityProject;
+class UpdateManager;
+
+/// Show dialog window with update information for the user.
+/**
+ Support user action behaviors as skip, download a new version,
+ and a checkbox that does not allow tracking version again.
+*/
+class UpdatePopupDialog final : public wxDialogWrapper
+{
+ DECLARE_DYNAMIC_CLASS (AboutDialog)
+public:
+ explicit UpdatePopupDialog (wxWindow* parent, UpdateManager *updateManager);
+ virtual ~UpdatePopupDialog();
+
+ void OnUpdate (wxCommandEvent& event);
+ void OnSkip (wxCommandEvent& event);
+ void OnDontShow (wxCommandEvent& event);
+
+ DECLARE_EVENT_TABLE()
+
+private:
+ HtmlWindow* AddHtmlContent (wxWindow* parent);
+
+ UpdateManager* const mUpdateManager;
+};
diff --git a/src/update/VersionId.cpp b/src/update/VersionId.cpp
new file mode 100644
index 000000000..a01c83803
--- /dev/null
+++ b/src/update/VersionId.cpp
@@ -0,0 +1,85 @@
+/*!********************************************************************
+ Audacity: A Digital Audio Editor
+
+ @file VersionId.h
+ @brief Declare a class with version number manipulation.
+
+ Anton Gerasimov
+ **********************************************************************/
+#include "VersionId.h"
+
+VersionId::VersionId(int version, int release, int revision)
+ : mVersion(version),
+ mRelease(release),
+ mRevision(revision)
+{}
+
+wxString VersionId::MakeString(int version, int release, int revision)
+{
+ return std::to_string(version)
+ + "." + std::to_string(release)
+ + "." + std::to_string(revision);
+}
+
+VersionId VersionId::ParseFromString(wxString& versionString)
+{
+ auto versionStringParts = wxSplit(versionString, '.');
+
+ // If we have corrupted version string,
+ // then return the zero version number, that not allow us to update.
+ if (versionStringParts.size() != 3)
+ return VersionId{};
+
+ for (auto& v : versionStringParts)
+ {
+ if (v.empty() || !v.IsNumber())
+ return VersionId{};
+ }
+
+ return VersionId(
+ std::stoi(versionStringParts[0].ToStdString()),
+ std::stoi(versionStringParts[1].ToStdString()),
+ std::stoi(versionStringParts[2].ToStdString())
+ );
+}
+
+wxString VersionId::getString() const
+{
+ return MakeString(mVersion, mRelease, mRevision);
+}
+
+bool VersionId::operator== (const VersionId& other)
+{
+ return mVersion == other.mVersion &&
+ mRelease == other.mRelease &&
+ mRevision == other.mRevision;
+}
+
+bool VersionId::operator!= (const VersionId& other)
+{
+ return !(*this == other);
+}
+
+bool VersionId::operator< (const VersionId& other)
+{
+ if (mVersion < other.mVersion)
+ return true;
+
+ if (mRelease < other.mRelease &&
+ mVersion == other.mVersion)
+ return true;
+
+ if (mRevision < other.mRevision &&
+ mVersion == other.mVersion &&
+ mRelease == other.mRelease)
+ return true;
+
+ return false;
+}
+
+bool VersionId::operator> (const VersionId& other)
+{
+ if (*this == other) return false;
+
+ return !(*this < other);
+}
diff --git a/src/update/VersionId.h b/src/update/VersionId.h
new file mode 100644
index 000000000..d85d19aaf
--- /dev/null
+++ b/src/update/VersionId.h
@@ -0,0 +1,49 @@
+/*!********************************************************************
+ Audacity: A Digital Audio Editor
+
+ @file VersionId.h
+ @brief Declare a class with version number manipulation.
+
+ Anton Gerasimov
+ **********************************************************************/
+#pragma once
+
+#include
+
+ /// A class, that support base manipulation with version number.
+ /**
+ By default initialized by zero version number (Version = 0, Release = 0, Revision = 0),
+ that not allow us to update.
+ */
+class VersionId final
+{
+public:
+ /// Creates an zero version object.
+ VersionId() = default;
+ VersionId(int version, int release, int revision);
+
+ /// Creates version string like "1.2.3" by parameters.
+ static wxString MakeString(int version, int release, int revision);
+ /// Parse and return version object from version string like "1.2.3".
+ static VersionId ParseFromString(wxString& versionString);
+
+ /// Make string with version by MakeString() from instance values.
+ wxString getString() const;
+
+ bool operator== (const VersionId& other);
+ bool operator!= (const VersionId& other);
+
+ bool operator< (const VersionId& other);
+ bool operator> (const VersionId& other);
+
+private:
+ int mVersion{ 0 };
+ int mRelease{ 0 };
+ int mRevision{ 0 };
+};
+
+/// Return version (VersionId) object with current Audacity build version.
+static inline VersionId CurrentBuildVersion()
+{
+ return VersionId{ AUDACITY_VERSION, AUDACITY_RELEASE, AUDACITY_REVISION };
+}
diff --git a/src/update/VersionPatch.h b/src/update/VersionPatch.h
new file mode 100644
index 000000000..b33515755
--- /dev/null
+++ b/src/update/VersionPatch.h
@@ -0,0 +1,23 @@
+/*!********************************************************************
+ Audacity: A Digital Audio Editor
+
+ @file VersionPatch.h
+ @brief Declare a structure that describes patch fields.
+
+ Anton Gerasimov
+ **********************************************************************/
+#pragma once
+
+#include "VersionId.h"
+
+/// A structure that describes patch fields.
+struct VersionPatch final
+{
+ using UpdateDataFormat = std::string;
+
+ VersionPatch() = default;
+
+ VersionId version;
+ wxArrayString changelog;
+ wxString download;
+};