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 + +/// A class that parse update server data format. +class UpdateDataParser final : public XMLTagHandler +{ +public: + UpdateDataParser(); + ~UpdateDataParser(); + + /// + /// Parsing from update data format to VersionPatch fields. + /// + /// Input data. + /// Parsed output data. + /// True if success. + bool Parse(const VersionPatch::UpdateDataFormat& updateData, VersionPatch* versionPatch); + +private: + enum class XmlParsedTags : int { + kNotUsedTag, + kUpdateTag, + kDescriptionTag, + kOsTag, + kWindowsTag, + kMacosTag, + kLinuxTag, + kVersionTag, + kLinkTag + }; + XmlParsedTags mXmlParsingState{ XmlParsedTags::kNotUsedTag }; + + std::map mXmlTagNames{ + { XmlParsedTags::kUpdateTag, wxT("Updates") }, + { XmlParsedTags::kDescriptionTag, wxT("Description") }, + { XmlParsedTags::kOsTag, wxT("OS") }, + { XmlParsedTags::kWindowsTag, wxT("Windows") }, + { XmlParsedTags::kMacosTag, wxT("Macos") }, + { XmlParsedTags::kLinuxTag, wxT("Linux") }, + { XmlParsedTags::kVersionTag, wxT("Version") }, + { XmlParsedTags::kLinkTag, wxT("Link") }, + }; + + bool HandleXMLTag(const wxChar* tag, const wxChar** attrs) override; + void HandleXMLEndTag(const wxChar* tag) override; + void HandleXMLContent(const wxString& content) override; + XMLTagHandler* HandleXMLChild(const wxChar* tag) override; + + wxArrayString splitChangelogSentences(const wxString& changelogContent); + + VersionPatch* mVersionPatch{ nullptr }; +}; diff --git a/src/update/UpdateManager.cpp b/src/update/UpdateManager.cpp new file mode 100644 index 000000000..cba3d503d --- /dev/null +++ b/src/update/UpdateManager.cpp @@ -0,0 +1,150 @@ +/*!******************************************************************** + Audacity: A Digital Audio Editor + + @file UpdateManager.cpp + @brief Declare a class that managing of updates. + + Anton Gerasimov + **********************************************************************/ + +#include "UpdateManager.h" +#include "UpdatePopupDialog.h" + +#include "NetworkManager.h" +#include "IResponse.h" +#include "Request.h" + +#include "widgets/AudacityMessageBox.h" + +#include +#include + +static const char* prefsUpdatePopupDialogShown = "/Update/UpdatePopupDialogShown"; +static const char* prefsUpdateScheduledTime = "/Update/UpdateScheduledTime"; + +enum { ID_TIMER = wxID_HIGHEST + 1 }; + +BEGIN_EVENT_TABLE(UpdateManager, wxEvtHandler) + EVT_TIMER(ID_TIMER, UpdateManager::OnTimer) +END_EVENT_TABLE() + +UpdateManager::UpdateManager(AudacityProject& project) + : mTrackingInterval( + std::chrono::milliseconds(std::chrono::hours(12)).count()) +{ + mParent = (wxWindow*)(&GetProjectFrame(project)); + wxASSERT(mParent); + + mTimer.SetOwner(this, ID_TIMER); + mTimer.StartOnce(); +} + +UpdateManager::~UpdateManager() +{ + mTimer.Stop(); +} + +void UpdateManager::enableTracking(bool enable) +{ + gPrefs->Write(prefsUpdatePopupDialogShown, enable); + gPrefs->Flush(); +} + +bool UpdateManager::isTrackingEnabled() +{ + return gPrefs->ReadBool(prefsUpdatePopupDialogShown, true); +} + +VersionPatch UpdateManager::getVersionPatch() const +{ + return mVersionPatch; +} + +void UpdateManager::getUpdates() +{ + audacity::network_manager::Request request("https://updates.audacityteam.org/feed/latest.xml"); + auto response = audacity::network_manager::NetworkManager::GetInstance().doGet(request); + + response->setRequestFinishedCallback([response, this](audacity::network_manager::IResponse*) { + + if (response->getError() != audacity::network_manager::NetworkError::NoError) + { + AudacityMessageBox( + XO("Unable to connect to Audacity update server."), + XO("Error checking for update"), + wxOK | wxCENTRE, + NULL); + + return; + } + + if (!mUpdateDataParser.Parse(response->readAll(), &mVersionPatch)) + { + AudacityMessageBox( + XO("Update data was corrupted."), + XO("Error checking for update"), + wxOK | wxCENTRE, + NULL); + + return; + } + + if (mVersionPatch.version > CurrentBuildVersion()) + { + mParent->CallAfter([this] { + UpdatePopupDialog dlg(mParent, this); + const int code = dlg.ShowModal(); + + if (code == wxID_YES) + { + if (!wxLaunchDefaultBrowser(mVersionPatch.download)) + { + AudacityMessageBox( + XO("Can't open the Audacity download link."), + XO("Error downloading update"), + wxOK | wxCENTRE, + NULL); + + return; + } + } + }); + } + }); +} + +void UpdateManager::OnTimer(wxTimerEvent& WXUNUSED(event)) +{ + if (isTrackingEnabled() && isTimeToUpdate()) + getUpdates(); + + mTimer.StartOnce(mTrackingInterval); +} + +bool UpdateManager::isTimeToUpdate() +{ + long long nextTrackingTime = std::stoll( + gPrefs->Read(prefsUpdateScheduledTime, "0").ToStdString()); + + // Get current time in milliseconds + auto now_ms = std::chrono::time_point_cast( + std::chrono::system_clock::now()); + + auto currentTimeInMillisec = std::chrono::duration_cast( + now_ms.time_since_epoch()).count(); + + // If next update time 0 or less then current time -> show update dialog, + // else this condition allow us to avoid from duplicating update notifications. + if (nextTrackingTime < currentTimeInMillisec) + { + nextTrackingTime = currentTimeInMillisec + mTrackingInterval; + + gPrefs->Write(prefsUpdateScheduledTime, + wxString(std::to_string(nextTrackingTime))); + gPrefs->Flush(); + + return true; + } + + return false; +} diff --git a/src/update/UpdateManager.h b/src/update/UpdateManager.h new file mode 100644 index 000000000..8b9514cda --- /dev/null +++ b/src/update/UpdateManager.h @@ -0,0 +1,57 @@ +/*!******************************************************************** + Audacity: A Digital Audio Editor + + @file UpdateManager.h + @brief Declare a class that managing of updates. + + Anton Gerasimov + **********************************************************************/ +#pragma once + +#include "VersionId.h" +#include "VersionPatch.h" +#include "UpdateDataParser.h" + +#include "Project.h" +#include "Prefs.h" + +#include +#include +#include + +/// A class that managing of updates. +/** + Opt-in request and show update dialog by the scheduled time. + Have a built-in check that allows avoiding multiplying update notifications + when multiple Audacity windows are shown. +*/ +class UpdateManager final : public wxEvtHandler +{ +public: + UpdateManager(AudacityProject& project); + ~UpdateManager(); + + void getUpdates(); + + void enableTracking(bool enable); + bool isTrackingEnabled(); + + VersionPatch getVersionPatch() const; + +private: + UpdateDataParser mUpdateDataParser; + VersionPatch mVersionPatch; + + wxWindow* mParent; + + wxTimer mTimer; + const int mTrackingInterval; + + void OnTimer(wxTimerEvent& event); + + /// Scheduling update time for avoiding multiplying update notifications. + bool isTimeToUpdate(); + +public: + DECLARE_EVENT_TABLE() +}; diff --git a/src/update/UpdatePopupDialog.cpp b/src/update/UpdatePopupDialog.cpp new file mode 100644 index 000000000..fe9ad3a2d --- /dev/null +++ b/src/update/UpdatePopupDialog.cpp @@ -0,0 +1,136 @@ +/*!******************************************************************** + Audacity: A Digital Audio Editor + + @file UpdatePopupDialog.cpp + @brief Define a dialog for notifying users about new version available. + + Anton Gerasimov + **********************************************************************/ + +#include "update/UpdatePopupDialog.h" + +#include "ShuttleGui.h" +#include "widgets/HelpSystem.h" + +#include +#include +#include + +enum { DontShowID = wxID_HIGHEST + 1 }; + +BEGIN_EVENT_TABLE (UpdatePopupDialog, wxDialogWrapper) + EVT_BUTTON (wxID_YES, UpdatePopupDialog::OnUpdate) + EVT_BUTTON (wxID_NO, UpdatePopupDialog::OnSkip) + EVT_CHECKBOX (DontShowID, UpdatePopupDialog::OnDontShow) +END_EVENT_TABLE() + +IMPLEMENT_CLASS (UpdatePopupDialog, wxDialogWrapper) + +UpdatePopupDialog::UpdatePopupDialog (wxWindow* parent, UpdateManager* updateManager) + : wxDialogWrapper (parent, -1, XO ("Update Audacity"), + wxDefaultPosition, wxDefaultSize, + wxCAPTION), + mUpdateManager (updateManager) +{ + wxASSERT(mUpdateManager); + + ShuttleGui S (this, eIsCreating); + S.SetBorder (5); + S.StartVerticalLay (wxEXPAND, 1); + { + S.AddWindow (AddHtmlContent (S.GetParent())); + + S.StartHorizontalLay (wxEXPAND, 0); + { + S.SetBorder (5); + + S.Id (DontShowID).AddCheckBox ( + XO ("Don't show this again at start up"), !mUpdateManager->isTrackingEnabled()); + + S.Prop(1).AddSpace(1, 0, 1); + + S.Id (wxID_NO).AddButton (XO ("Skip")); + S.Id (wxID_YES).AddButton (XO ("Install update")); + + S.SetBorder (5); + } + S.EndHorizontalLay(); + } + S.EndVerticalLay(); + + Layout(); + Fit(); + Center(); +} + +UpdatePopupDialog::~UpdatePopupDialog() +{ + ; +} + +void UpdatePopupDialog::OnUpdate (wxCommandEvent&) +{ + EndModal (wxID_YES); +} + +void UpdatePopupDialog::OnSkip (wxCommandEvent&) +{ + EndModal (wxID_NO); +} + +void UpdatePopupDialog::OnDontShow (wxCommandEvent& event) +{ + mUpdateManager->enableTracking(!event.IsChecked()); +} + +HtmlWindow* UpdatePopupDialog::AddHtmlContent (wxWindow* parent) +{ + wxStringOutputStream o; + wxTextOutputStream informationStr (o); + + static const auto title = XO("Audacity %s is available!") + .Format(mUpdateManager->getVersionPatch().version.getString()); + + informationStr + << wxT("

") + << title.Translation() + << wxT("

") + << XO("Changelog") + << wxT("

"); + + informationStr << wxT("

    "); + for (auto& logLine : mUpdateManager->getVersionPatch().changelog) + { + informationStr << wxT("
  • "); + // We won't to translate downloaded text. + informationStr << logLine; + informationStr << wxT("
  • "); + } + informationStr << wxT("

"); + + 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; +};