diff --git a/CMakeLists.txt b/CMakeLists.txt
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -77,6 +77,7 @@ qt5_wrap_ui(UI_FILES
src/binarystreamreadersettings.ui
src/asciireadersettings.ui
src/framedreadersettings.ui
+ src/updatecheckdialog.ui
)
if (WIN32)
@@ -126,6 +127,9 @@ add_executable(${PROGRAM_NAME} WIN32
src/framedreadersettings.cpp
src/plotmanager.cpp
src/numberformat.cpp
+ src/updatechecker.cpp
+ src/versionnumber.cpp
+ src/updatecheckdialog.cpp
misc/windows_icon.rc
${UI_FILES}
${RES_FILES}
@@ -136,7 +140,7 @@ target_link_libraries(${PROGRAM_NAME}
${QWT_LIBRARY}
${QTCOLORWIDGETS_LIBRARIES}
)
-qt5_use_modules(${PROGRAM_NAME} Widgets SerialPort)
+qt5_use_modules(${PROGRAM_NAME} Widgets SerialPort Network)
if (BUILD_QWT)
add_dependencies(${PROGRAM_NAME} QWT)
@@ -169,6 +173,9 @@ set(VERSION_REVISION "0")
include(GetVersion)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DVERSION_STRING=\\\"${VERSION_STRING}\\\" ")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DVERSION_MAJOR=${VERSION_MAJOR} ")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DVERSION_MINOR=${VERSION_MINOR} ")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DVERSION_PATCH=${VERSION_PATCH} ")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DVERSION_REVISION=\\\"${VERSION_REVISION}\\\" ")
# add make run target
diff --git a/serialplot.pro b/serialplot.pro
--- a/serialplot.pro
+++ b/serialplot.pro
@@ -68,7 +68,9 @@ SOURCES += \
src/framedreader.cpp \
src/plotmanager.cpp \
src/numberformat.cpp \
- src/recordpanel.cpp
+ src/recordpanel.cpp \
+ src/updatechecker.cpp \
+ src/updatecheckdialog.cpp
HEADERS += \
src/mainwindow.h \
@@ -108,7 +110,9 @@ HEADERS += \
src/plotmanager.h \
src/setting_defines.h \
src/numberformat.h \
- src/recordpanel.h
+ src/recordpanel.h \
+ src/updatechecker.h \
+ src/updatecheckdialog.h
FORMS += \
src/mainwindow.ui \
@@ -124,7 +128,8 @@ FORMS += \
src/framedreadersettings.ui \
src/binarystreamreadersettings.ui \
src/asciireadersettings.ui \
- src/recordpanel.ui
+ src/recordpanel.ui \
+ src/updatecheckdialog.ui
INCLUDEPATH += qmake/ src/
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -63,7 +63,8 @@ MainWindow::MainWindow(QWidget *parent)
snapshotMan(this, &channelMan),
commandPanel(&serialPort),
dataFormatPanel(&serialPort, &channelMan, &recorder),
- recordPanel(&recorder, &channelMan)
+ recordPanel(&recorder, &channelMan),
+ updateCheckDialog(this)
{
ui->setupUi(this);
@@ -112,6 +113,9 @@ MainWindow::MainWindow(QWidget *parent)
QObject::connect(ui->actionHelpAbout, &QAction::triggered,
&aboutDialog, &QWidget::show);
+ QObject::connect(ui->actionCheckUpdate, &QAction::triggered,
+ &updateCheckDialog, &QWidget::show);
+
QObject::connect(ui->actionReportBug, &QAction::triggered,
[](){QDesktopServices::openUrl(QUrl(BUG_REPORT_URL));});
@@ -546,6 +550,7 @@ void MainWindow::saveAllSettings(QSettin
plotMan->saveSettings(settings);
commandPanel.saveSettings(settings);
recordPanel.saveSettings(settings);
+ updateCheckDialog.saveSettings(settings);
}
void MainWindow::loadAllSettings(QSettings* settings)
@@ -558,6 +563,7 @@ void MainWindow::loadAllSettings(QSettin
plotMan->loadSettings(settings);
commandPanel.loadSettings(settings);
recordPanel.loadSettings(settings);
+ updateCheckDialog.loadSettings(settings);
}
void MainWindow::saveMWSettings(QSettings* settings)
diff --git a/src/mainwindow.h b/src/mainwindow.h
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -45,6 +45,7 @@
#include "snapshotmanager.h"
#include "plotmanager.h"
#include "datarecorder.h"
+#include "updatecheckdialog.h"
namespace Ui {
class MainWindow;
@@ -85,6 +86,7 @@ private:
DataFormatPanel dataFormatPanel;
RecordPanel recordPanel;
PlotControlPanel plotControlPanel;
+ UpdateCheckDialog updateCheckDialog;
bool isDemoRunning();
/// Stores settings for all modules
diff --git a/src/mainwindow.ui b/src/mainwindow.ui
--- a/src/mainwindow.ui
+++ b/src/mainwindow.ui
@@ -90,7 +90,7 @@
0
0
653
- 27
+ 25
diff --git a/src/setting_defines.h b/src/setting_defines.h
--- a/src/setting_defines.h
+++ b/src/setting_defines.h
@@ -30,6 +30,7 @@ const char SettingGroup_Channels[] = "Ch
const char SettingGroup_Plot[] = "Plot";
const char SettingGroup_Commands[] = "Commands";
const char SettingGroup_Record[] = "Record";
+const char SettingGroup_UpdateCheck[] = "UpdateCheck";
// mainwindow setting keys
const char SG_MainWindow_Size[] = "size";
@@ -103,4 +104,8 @@ const char SG_Record_Header[]
const char SG_Record_Separator[] = "separator";
const char SG_Record_DisableBuffering[] = "disableBuffering";
+// update check settings keys
+const char SG_UpdateCheck_Periodic[] = "periodicCheck";
+const char SG_UpdateCheck_LastCheck[] = "lastCheck";
+
#endif // SETTING_DEFINES_H
diff --git a/src/updatecheckdialog.cpp b/src/updatecheckdialog.cpp
new file mode 100644
--- /dev/null
+++ b/src/updatecheckdialog.cpp
@@ -0,0 +1,105 @@
+/*
+ Copyright © 2017 Hasan Yavuz Özderya
+
+ This file is part of serialplot.
+
+ serialplot 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.
+
+ serialplot 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 serialplot. If not, see .
+*/
+
+#include "setting_defines.h"
+#include "updatecheckdialog.h"
+#include "ui_updatecheckdialog.h"
+
+UpdateCheckDialog::UpdateCheckDialog(QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::UpdateCheckDialog)
+{
+ ui->setupUi(this);
+
+ // by default start from yesterday, so that we check at first run
+ lastCheck = QDate::currentDate().addDays(-1);
+
+ connect(&updateChecker, &UpdateChecker::checkFailed,
+ [this](QString errorMessage)
+ {
+ lastCheck = QDate::currentDate();
+ ui->label->setText(QString("Update check failed.\n") + errorMessage);
+ });
+
+ connect(&updateChecker, &UpdateChecker::checkFinished,
+ [this](bool found, QString newVersion, QString downloadUrl)
+ {
+ QString text;
+ if (!found)
+ {
+ text = "There is no update yet.";
+ }
+ else
+ {
+ show();
+#ifdef UPDATE_TYPE_PKGMAN
+ text = QString("There is a new version: %1. "
+ "Use your package manager to update"
+ " or click to download.")\
+ .arg(newVersion).arg(downloadUrl);
+#else
+ text = QString("Found update to version %1. Click to download.")\
+ .arg(newVersion).arg(downloadUrl);
+#endif
+ }
+
+ lastCheck = QDate::currentDate();
+ ui->label->setText(text);
+ });
+}
+
+UpdateCheckDialog::~UpdateCheckDialog()
+{
+ delete ui;
+}
+
+void UpdateCheckDialog::showEvent(QShowEvent *event)
+{
+ updateChecker.checkUpdate();
+ ui->label->setText("Checking update...");
+}
+
+void UpdateCheckDialog::closeEvent(QShowEvent *event)
+{
+ if (updateChecker.isChecking()) updateChecker.cancelCheck();
+}
+
+void UpdateCheckDialog::saveSettings(QSettings* settings)
+{
+ settings->beginGroup(SettingGroup_UpdateCheck);
+ settings->setValue(SG_UpdateCheck_Periodic, ui->cbPeriodic->isChecked());
+ settings->setValue(SG_UpdateCheck_LastCheck, lastCheck.toString(Qt::ISODate));
+ settings->endGroup();
+}
+
+void UpdateCheckDialog::loadSettings(QSettings* settings)
+{
+ settings->beginGroup(SettingGroup_UpdateCheck);
+ ui->cbPeriodic->setChecked(settings->value(SG_UpdateCheck_Periodic,
+ ui->cbPeriodic->isChecked()).toBool());
+ auto lastCheckS = settings->value(SG_UpdateCheck_LastCheck, lastCheck.toString(Qt::ISODate)).toString();
+ lastCheck = QDate::fromString(lastCheckS, Qt::ISODate);
+ settings->endGroup();
+
+ // start the periodic update if required
+ if (ui->cbPeriodic->isChecked() && lastCheck < QDate::currentDate())
+ {
+ updateChecker.checkUpdate();
+ }
+}
diff --git a/src/updatecheckdialog.h b/src/updatecheckdialog.h
new file mode 100644
--- /dev/null
+++ b/src/updatecheckdialog.h
@@ -0,0 +1,54 @@
+/*
+ Copyright © 2017 Hasan Yavuz Özderya
+
+ This file is part of serialplot.
+
+ serialplot 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.
+
+ serialplot 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 serialplot. If not, see .
+*/
+
+#ifndef UPDATECHECKDIALOG_H
+#define UPDATECHECKDIALOG_H
+
+#include
+#include
+#include
+#include "updatechecker.h"
+
+namespace Ui {
+class UpdateCheckDialog;
+}
+
+class UpdateCheckDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit UpdateCheckDialog(QWidget *parent = 0);
+ ~UpdateCheckDialog();
+
+ /// Stores update settings into a `QSettings`.
+ void saveSettings(QSettings* settings);
+ /// Loads update settings from a `QSettings`.
+ void loadSettings(QSettings* settings);
+
+private:
+ Ui::UpdateCheckDialog *ui;
+ UpdateChecker updateChecker;
+ QDate lastCheck;
+
+ void showEvent(QShowEvent *event);
+ void closeEvent(QShowEvent *event);
+};
+
+#endif // UPDATECHECKDIALOG_H
diff --git a/src/updatecheckdialog.ui b/src/updatecheckdialog.ui
new file mode 100644
--- /dev/null
+++ b/src/updatecheckdialog.ui
@@ -0,0 +1,71 @@
+
+
+ UpdateCheckDialog
+
+
+
+ 0
+ 0
+ 400
+ 148
+
+
+
+ Check Update
+
+
+ -
+
+
+ Checking update...
+
+
+ true
+
+
+
+ -
+
+
+ Updates will be checked only once a day at first start of the application
+
+
+ Check updates periodically
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Close
+
+
+
+
+
+
+
+
+ buttonBox
+ clicked(QAbstractButton*)
+ UpdateCheckDialog
+ close()
+
+
+ 199
+ 125
+
+
+ 199
+ 73
+
+
+
+
+
diff --git a/src/updatechecker.cpp b/src/updatechecker.cpp
new file mode 100644
--- /dev/null
+++ b/src/updatechecker.cpp
@@ -0,0 +1,222 @@
+/*
+ Copyright © 2017 Hasan Yavuz Özderya
+
+ This file is part of serialplot.
+
+ serialplot 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.
+
+ serialplot 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 serialplot. If not, see .
+*/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "updatechecker.h"
+
+// This link returns the list of downloads in JSON format. Note that we only use
+// the first page because results are sorted new to old.
+const char BB_DOWNLOADS_URL[] = "https://api.bitbucket.org/2.0/repositories/hyozd/serialplot/downloads?fields=values.name,values.links.self.href";
+
+UpdateChecker::UpdateChecker(QObject *parent) :
+ QObject(parent), nam(this)
+{
+ activeReply = NULL;
+
+ connect(&nam, &QNetworkAccessManager::finished,
+ this, &UpdateChecker::onReqFinished);
+}
+
+bool UpdateChecker::isChecking() const
+{
+ return activeReply != NULL && !activeReply->isFinished();
+}
+
+void UpdateChecker::checkUpdate()
+{
+ if (isChecking()) return;
+
+ auto req = QNetworkRequest(QUrl(BB_DOWNLOADS_URL));
+ activeReply = nam.get(req);
+}
+
+void UpdateChecker::cancelCheck()
+{
+ if (activeReply != NULL) activeReply->abort();
+}
+
+void UpdateChecker::onReqFinished(QNetworkReply* reply)
+{
+ if (reply->error() != QNetworkReply::NoError)
+ {
+ emit checkFailed(QString("Network error: ") + reply->errorString());
+ }
+ else
+ {
+ QJsonParseError error;
+ auto data = QJsonDocument::fromJson(reply->readAll(), &error);
+ if (error.error != QJsonParseError::NoError)
+ {
+ emit checkFailed(QString("JSon parsing error: ") + error.errorString());
+ }
+ else
+ {
+ QList files;
+ if (!parseData(data, files))
+ {
+ // TODO: emit detailed data contents for logging
+ emit checkFailed("Data parsing error.");
+ }
+ else
+ {
+ FileInfo updateFile;
+ if (findUpdate(files, updateFile))
+ {
+ emit checkFinished(
+ true, updateFile.version.toString(), updateFile.link);
+ }
+ else
+ {
+ emit checkFinished(false, "", "");
+ }
+ }
+ }
+ }
+ reply->deleteLater();
+ activeReply = NULL;
+}
+
+bool UpdateChecker::parseData(const QJsonDocument& data, QList& files) const
+{
+ /* Data is expected to be in this form:
+
+ {
+ "values": [
+ {
+ "name": "serialplot-0.9.1-x86_64.AppImage",
+ "links": {
+ "self": {
+ "href": "https://api.bitbucket.org/2.0/repositories/hyOzd/serialplot/downloads/serialplot-0.9.1-x86_64.AppImage"
+ }
+ }
+ }, ... ]
+ }
+ */
+
+ if (!data.isObject()) return false;
+
+ auto values = data.object()["values"];
+ if (values == QJsonValue::Undefined || !values.isArray()) return false;
+
+ for (auto value : values.toArray())
+ {
+ if (!value.isObject()) return false;
+
+ auto name = value.toObject().value("name");
+ if (name.isUndefined() || !name.isString())
+ return false;
+
+ auto links = value.toObject().value("links");
+ if (links.isUndefined() || !links.isObject())
+ return false;
+
+ auto self = links.toObject().value("self");
+ if (self.isUndefined() || !self.isObject())
+ return false;
+
+ auto href = self.toObject().value("href");
+ if (href.isUndefined() || !href.isString())
+ return false;
+
+ FileInfo finfo;
+ finfo.name = name.toString();
+ finfo.link = href.toString();
+ finfo.hasVersion = VersionNumber::extract(name.toString(), finfo.version);
+
+ if (finfo.name.contains("amd64") ||
+ finfo.name.contains("x86_64") ||
+ finfo.name.contains("win64"))
+ {
+ finfo.arch = FileArch::amd64;
+ }
+ else if (finfo.name.contains("win32") ||
+ finfo.name.contains("i386"))
+ {
+ finfo.arch = FileArch::i386;
+ }
+ else
+ {
+ finfo.arch = FileArch::unknown;
+ }
+
+ files += finfo;
+ }
+
+ return true;
+}
+
+bool UpdateChecker::findUpdate(const QList& files, FileInfo& foundFile) const
+{
+ QList fflist;
+
+ // filter the file list according to extension and version number
+ for (int i = 0; i < files.length(); i++)
+ {
+ // file type to look
+#if defined(Q_OS_WIN)
+ const char ext[] = ".exe";
+#else // of course linux
+ const char ext[] = ".appimage";
+#endif
+
+ // file architecture to look
+#if defined(Q_PROCESSOR_X86_64)
+ const FileArch arch = FileArch::amd64;
+#elif defined(Q_PROCESSOR_X86_32)
+ const FileArch arch = FileArch::i386;
+#elif defined(Q_PROCESSOR_ARM)
+ const FileArch arch = FileArch::arm;
+#else
+ #error Unknown architecture for update file detection.
+#endif
+
+ // filter the file list
+ auto file = files[i];
+ if (file.name.contains(ext, Qt::CaseInsensitive) &&
+ file.arch == arch &&
+ file.hasVersion && file.version > CurrentVersion)
+ {
+ fflist += file;
+ }
+ }
+
+ // sort and find most up to date file
+ if (!fflist.empty())
+ {
+ std::sort(fflist.begin(), fflist.end(),
+ [](const FileInfo& a, const FileInfo& b)
+ {
+ return a.version > b.version;
+ });
+
+ foundFile = fflist[0];
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
diff --git a/src/updatechecker.h b/src/updatechecker.h
new file mode 100644
--- /dev/null
+++ b/src/updatechecker.h
@@ -0,0 +1,77 @@
+/*
+ Copyright © 2017 Hasan Yavuz Özderya
+
+ This file is part of serialplot.
+
+ serialplot 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.
+
+ serialplot 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 serialplot. If not, see .
+*/
+
+#ifndef UPDATECHECKER_H
+#define UPDATECHECKER_H
+
+#include
+#include
+#include
+#include
+
+#include "versionnumber.h"
+
+class UpdateChecker : public QObject
+{
+ Q_OBJECT
+public:
+ explicit UpdateChecker(QObject *parent = 0);
+
+ bool isChecking() const;
+
+signals:
+ void checkFinished(bool found, QString newVersion, QString downloadUrl);
+ void checkFailed(QString errorMessage);
+
+public slots:
+ void checkUpdate();
+ void cancelCheck();
+
+private:
+ enum class FileArch
+ {
+ unknown,
+ i386,
+ amd64,
+ arm
+ };
+
+ struct FileInfo
+ {
+ QString name;
+ QString link;
+ bool hasVersion;
+ VersionNumber version;
+ FileArch arch;
+ };
+
+ QNetworkAccessManager nam;
+ QNetworkReply* activeReply;
+
+ /// Parses json and creates a list of files
+ bool parseData(const QJsonDocument& data, QList& files) const;
+ /// Finds the update file in the file list. Returns `-1` if no new version
+ /// is found.
+ bool findUpdate(const QList& files, FileInfo& foundFile) const;
+
+private slots:
+ void onReqFinished(QNetworkReply* reply);
+};
+
+#endif // UPDATECHECKER_H
diff --git a/src/versionnumber.cpp b/src/versionnumber.cpp
new file mode 100644
--- /dev/null
+++ b/src/versionnumber.cpp
@@ -0,0 +1,103 @@
+/*
+ Copyright © 2017 Hasan Yavuz Özderya
+
+ This file is part of serialplot.
+
+ serialplot 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.
+
+ serialplot 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 serialplot. If not, see .
+*/
+
+#include
+
+#include "versionnumber.h"
+
+VersionNumber::VersionNumber(unsigned mj, unsigned mn, unsigned pt)
+{
+ major = mj;
+ minor = mn;
+ patch = pt;
+}
+
+QString VersionNumber::toString() const
+{
+ return QString("%1.%2.%3").arg(major).arg(minor).arg(patch);
+}
+
+bool VersionNumber::extract(const QString& str, VersionNumber& number)
+{
+ QRegularExpression regexp("(?:[-_vV \\t]|^)(?\\d+)"
+ "(?:\\.(?\\d+))(?:\\.(?\\d+))?[-_ \\t]?");
+ auto match = regexp.match(str, 0, QRegularExpression::PartialPreferCompleteMatch);
+
+ if (!(match.hasMatch() || match.hasPartialMatch())) return false;
+
+ number.major = match.captured("major").toUInt();
+
+ auto zeroIfNull = [](QString str) -> unsigned
+ {
+ if (str.isNull()) return 0;
+ return str.toUInt();
+ };
+
+ number.minor = zeroIfNull(match.captured("minor"));
+ number.patch = zeroIfNull(match.captured("patch"));
+
+ return true;
+}
+
+bool operator==(const VersionNumber& lhs, const VersionNumber& rhs)
+{
+ return lhs.major == rhs.major &&
+ lhs.minor == rhs.minor &&
+ lhs.patch == rhs.patch;
+}
+
+bool operator<(const VersionNumber& lhs, const VersionNumber& rhs)
+{
+ if (lhs.major < rhs.major)
+ {
+ return true;
+ }
+ else if (lhs.major == rhs.major)
+ {
+ if (lhs.minor < rhs.minor)
+ {
+ return true;
+ }
+ else if (lhs.minor == rhs.minor)
+ {
+ if (lhs.patch < rhs.patch) return true;
+ }
+ }
+ return false;
+}
+
+bool operator>(const VersionNumber& lhs, const VersionNumber& rhs)
+{
+ if (lhs.major > rhs.major)
+ {
+ return true;
+ }
+ else if (lhs.major == rhs.major)
+ {
+ if (lhs.minor > rhs.minor)
+ {
+ return true;
+ }
+ else if (lhs.minor == rhs.minor)
+ {
+ if (lhs.patch > rhs.patch) return true;
+ }
+ }
+ return false;
+}
diff --git a/src/versionnumber.h b/src/versionnumber.h
new file mode 100644
--- /dev/null
+++ b/src/versionnumber.h
@@ -0,0 +1,46 @@
+/*
+ Copyright © 2017 Hasan Yavuz Özderya
+
+ This file is part of serialplot.
+
+ serialplot 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.
+
+ serialplot 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 serialplot. If not, see .
+*/
+
+#ifndef VERSIONNUMBER_H
+#define VERSIONNUMBER_H
+
+#include
+
+struct VersionNumber
+{
+ unsigned major = 0;
+ unsigned minor = 0;
+ unsigned patch = 0;
+
+ VersionNumber(unsigned mj=0, unsigned mn=0, unsigned pt=0);
+
+ /// Convert version number to string.
+ QString toString() const;
+
+ /// Extracts the version number from given string.
+ static bool extract(const QString& str, VersionNumber& number);
+};
+
+bool operator==(const VersionNumber& lhs, const VersionNumber& rhs);
+bool operator<(const VersionNumber& lhs, const VersionNumber& rhs);
+bool operator>(const VersionNumber& lhs, const VersionNumber& rhs);
+
+const VersionNumber CurrentVersion(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
+
+#endif // VERSIONNUMBER_H