chore(cpn): Refactor component updates (#3218)

* Refactoring

* Fix current release id and process dlg events

* enh: Process updates dialog add select/deselect all components

* Change Expression to Pattern
This commit is contained in:
Neil Horne
2023-03-19 16:13:56 +10:00
committed by GitHub
parent fc1d6ba3a0
commit a1178f83a6
47 changed files with 3370 additions and 2813 deletions
+2 -2
View File
@@ -29,7 +29,7 @@
#endif
#include "moduledata.h"
#include "compounditemmodels.h"
#include "updates/updateinterface.h"
#include "updates/updatefactories.h"
#include "updates/updateoptionsdialog.h"
#include <QAbstractItemModel>
@@ -462,7 +462,7 @@ void AppPreferencesDialog::initSettings()
btnComponentOptions[i] = new QPushButton(tr("Options"));
connect(btnComponentOptions[i], &QPushButton::clicked, [=]() {
UpdateOptionsDialog *dlg = new UpdateOptionsDialog(this, factories, i, false);
UpdateOptionsDialog *dlg = new UpdateOptionsDialog(this, factories->instance(i), i, false);
dlg->exec();
dlg->deleteLater();
});
+1 -1
View File
@@ -46,7 +46,7 @@
#include "profilechooser.h"
#include "constants.h"
#include "updates/updates.h"
#include "updates/updateinterface.h"
#include "updates/updatefactories.h"
#include <QtGui>
#include <QFileInfo>
+1 -1
View File
@@ -343,7 +343,7 @@ bool ComponentData::existsOnDisk()
return (m_settings.contains(settingsPath() % "name"));
}
void ComponentData::clearRelease()
void ComponentData::releaseClear()
{
releaseReset();
releaseIdReset();
+1 -1
View File
@@ -472,7 +472,7 @@ class ComponentData: public CompStoreObj
};
Q_ENUM(ReleaseChannel)
void clearRelease();
void releaseClear();
static QStringList releaseChannelsList() { return { tr("Releases"), tr("Pre-release"), tr("Nightly") } ; }
inline ReleaseChannel boundedReleaseChannel() const {
+17 -2
View File
@@ -4,7 +4,14 @@ set(updates_NAMES
updates
updatesdialog
updateoptionsdialog
repomodels
updatestatus
updatenetwork
repodatamodels
repometadata
reporeleases
repoassets
repo
updateparameters
updateinterface
updatefirmware
updatecompanion
@@ -12,6 +19,7 @@ set(updates_NAMES
updatesounds
updatethemes
updatemultiprotocol
updatefactories
chooserdialog
)
@@ -30,8 +38,15 @@ set(updates_QTS
updates.h
updatesdialog.h
updateoptionsdialog.h
repomodels.h
updatestatus.h
updatenetwork.h
repodatamodels.h
reporeleases.h
repoassets.h
repo.h
updateparameters.h
updateinterface.h
updatefactories.h
chooserdialog.h
)
-4
View File
@@ -40,10 +40,6 @@ class ChooserDialog : public QDialog
QItemSelectionModel* selectedItems();
signals:
private slots:
private:
Ui::ChooserDialog *ui;
};
-1
View File
@@ -51,7 +51,6 @@ class DownloadDialog : public QDialog
private:
Ui::DownloadDialog *ui;
QNetworkAccessManager qnam;
QNetworkReply *reply;
QFile *file;
+59
View File
@@ -0,0 +1,59 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program 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.
*/
#include "repo.h"
Repo::Repo(QObject * parent, UpdateStatus * status, UpdateNetwork * network) :
QObject(parent),
m_status(status),
m_network(network),
m_releases(new RepoReleases(this, status, network)),
m_assets(new RepoAssets(this, status, network))
{
connect(m_releases, &RepoReleases::idChanged, [=](const int id) {
m_assets->onReleaseIdChanged(id);
});
}
Repo::~Repo()
{
delete m_assets;
delete m_releases;
}
bool Repo::getJson(const QString filename, QJsonDocument * json)
{
m_network->downloadJson(m_releases->urlContent(filename), json);
if (!m_network->isSuccess()) {
m_status->reportProgress("Unable to download json data", QtDebugMsg);
return false;
}
return true;
}
void Repo::init(const QString & repoPath, const QString & nightly, const int resultsPerPage)
{
m_path = repoPath;
m_releases->init(repoPath, nightly, resultsPerPage);
m_assets->init(repoPath, QString(), resultsPerPage);
}
+54
View File
@@ -0,0 +1,54 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program 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.
*/
#pragma once
#include "updatestatus.h"
#include "updatenetwork.h"
#include "reporeleases.h"
#include "repoassets.h"
#include <QtCore>
constexpr char GH_API_REPOS[] {"https://api.github.com/repos"};
constexpr char GH_API_REPOS_EDGETX[] {"https://api.github.com/repos/EdgeTX"};
class Repo : public QObject
{
Q_OBJECT
public:
explicit Repo(QObject * parent, UpdateStatus * status, UpdateNetwork * network);
virtual ~Repo();
RepoAssets* const assets() const { return m_assets; }
bool getJson(const QString filename, QJsonDocument * json);
void init(const QString & repoPath, const QString & nightly, const int resultsPerPage);
const QString path() const { return m_path; }
RepoReleases* const releases() const { return m_releases; }
private:
UpdateStatus* const m_status;
UpdateNetwork* const m_network;
RepoReleases* const m_releases;
RepoAssets* const m_assets;
QString m_path;
};
+209
View File
@@ -0,0 +1,209 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program 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.
*/
#include "repoassets.h"
/*
AssetsRawItemModel
*/
AssetsRawItemModel::AssetsRawItemModel() :
RepoRawItemModel("Raw Assets")
{
}
void AssetsRawItemModel::parseJsonObject(const QJsonObject & obj)
{
QStandardItem * item = new QStandardItem();
if (!obj.value("name").isUndefined()) {
item->setText(obj.value("name").toString());
}
if (!obj.value("id").isUndefined()) {
item->setData(obj.value("id").toInt(), RIMR_Id);
}
if (!obj.value("content_type").isUndefined()) {
item->setData(obj.value("content_type").toString(), RIMR_Content);
}
if (!obj.value("updated_at").isUndefined()) {
item->setData(QDateTime::fromString(obj.value("updated_at").toString(), Qt::ISODate), RIMR_Date);
}
item->setData(true, RIMR_Available);
item->setData(0, RIMR_Flags);
appendRow(item);
}
/*
AssetsFilteredItemModel
*/
AssetsFilteredItemModel::AssetsFilteredItemModel(AssetsRawItemModel * assetsRawItemModel) :
RepoFilteredItemModel("Filtered Assets")
{
setSourceModel(assetsRawItemModel);
}
const QString AssetsFilteredItemModel::contentType(const int id) const
{
return value(id, RIMR_Content).toString();
}
const QString AssetsFilteredItemModel::copyFilter(const int id) const
{
return value(id, RIMR_CopyFilter).toString();
}
bool AssetsFilteredItemModel::setCopyFilter(const int id, const QString filter)
{
return setValue(id, RIMR_CopyFilter, QVariant(filter));
}
bool AssetsFilteredItemModel::setSubDirectory(const int id, const QString path)
{
return setValue(id, RIMR_SubDirectory, QVariant(path));
}
const QString AssetsFilteredItemModel::subDirectory(const int id) const
{
return value(id, RIMR_SubDirectory).toString();
}
/*
RepoAssets
*/
RepoAssets::RepoAssets(QObject * parent, UpdateStatus * status, UpdateNetwork * network) :
QObject(parent),
RepoMetaData(status, network),
m_rawItemModel(new AssetsRawItemModel()),
m_filteredItemModel(new AssetsFilteredItemModel(m_rawItemModel))
{
setModels(m_rawItemModel, m_filteredItemModel);
}
const QString RepoAssets::contentType() const
{
return m_filteredItemModel->contentType(id());
}
const QString RepoAssets::copyFilter() const
{
return m_filteredItemModel->copyFilter(id());
}
bool RepoAssets::downloadToBuffer(const int id)
{
//progressMessage(tr("Download asset %1").arg(id));
if (id > 0)
setId(id);
network()->downloadToBuffer(UpdateNetwork::DDT_Content, urlAsset(this->id()));
return network()->isSuccess();
}
bool RepoAssets::downloadToFile(const int row, QString & downloadDir)
{
getSetId(row);
QFileInfo f(QString("%1/A%2/%3").arg(downloadDir).arg(id()).arg(name()));
network()->downloadToFile(urlAsset(id()), f.absoluteFilePath());
return network()->isSuccess();
}
void RepoAssets::dumpItemModel(const QString modelName, const QAbstractItemModel * itemModel) const
{
qDebug() << "Contents of model:" << modelName;
if (itemModel) {
for (int i = 0; i < itemModel->rowCount(); ++i) {
qDebug().noquote() << "row:" << i;
qDebug().noquote() << " name:" << itemModel->data(itemModel->index(i, 0), Qt::DisplayRole).toString();
qDebug().noquote() << " id:" << itemModel->data(itemModel->index(i, 0), RepoItemModelBase::RIMR_Id).toInt();
qDebug().noquote() << " date:" << itemModel->data(itemModel->index(i, 0), RepoItemModelBase::RIMR_Date).toDateTime();
qDebug().noquote() << " content:" << itemModel->data(itemModel->index(i, 0), RepoItemModelBase::RIMR_Content).toString();
qDebug().noquote() << " copy filter:" << itemModel->data(itemModel->index(i, 0), RepoItemModelBase::RIMR_CopyFilter).toString();
qDebug().noquote() << " sub dir:" << itemModel->data(itemModel->index(i, 0), RepoItemModelBase::RIMR_SubDirectory).toString();
qDebug().noquote() << " flags:" << itemModel->data(itemModel->index(i, 0), RepoItemModelBase::RIMR_Flags).toInt();
qDebug().noquote() << " avail:" << itemModel->data(itemModel->index(i, 0), RepoItemModelBase::RIMR_Available).toBool();
}
}
}
bool RepoAssets::getJson(QJsonDocument * json)
{
network()->downloadJson(urlAsset(id()), json);
if (!network()->isSuccess()) {
status()->reportProgress("Unable to download json data", QtDebugMsg);
return false;
}
return true;
}
bool RepoAssets::getJson(const QString assetName, QJsonDocument * json)
{
if (!getSetId(assetName))
return false;
return getJson(json);
}
void RepoAssets::init(const QString & repoPath,const QString & nightly, const int resultsPerPage)
{
RepoMetaData::init(repoPath, resultsPerPage);
}
void RepoAssets::onReleaseIdChanged(const int id)
{
m_releaseId = id;
invalidate();
}
bool RepoAssets::retrieveMetaDataAll()
{
return retrieveMetaData(RepoRawItemModel::MDT_Assets, urlAssets(m_releaseId));
}
bool RepoAssets::retrieveMetaDataOne(const int id)
{
setId(id);
return retrieveMetaData(RepoRawItemModel::MDT_Asset, urlAsset(id));
}
bool RepoAssets::setCopyFilter(const QString filter)
{
return m_filteredItemModel->setCopyFilter(id(), filter);
}
bool RepoAssets::setSubDirectory(const QString path)
{
return m_filteredItemModel->setSubDirectory(id(), path);
}
const QString RepoAssets::subDirectory() const
{
return m_filteredItemModel->subDirectory(id());
}
+93
View File
@@ -0,0 +1,93 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program 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.
*/
#pragma once
#include "repodatamodels.h"
#include "repometadata.h"
class AssetsRawItemModel : public RepoRawItemModel
{
Q_OBJECT
public:
explicit AssetsRawItemModel();
virtual ~AssetsRawItemModel() {}
protected:
friend class RepoAssets;
virtual void parseJsonObject(const QJsonObject & obj);
};
class AssetsFilteredItemModel : public RepoFilteredItemModel
{
Q_OBJECT
public:
explicit AssetsFilteredItemModel(AssetsRawItemModel * assetsRawItemModel);
virtual ~AssetsFilteredItemModel() {};
protected:
friend class RepoAssets;
const QString contentType(const int id) const;
const QString copyFilter(const int id) const;
bool setCopyFilter(const int id, const QString filter);
bool setSubDirectory(const int id, const QString path);
const QString subDirectory(const int id) const;
};
class RepoAssets : public QObject, public RepoMetaData
{
Q_OBJECT
public slots:
void onReleaseIdChanged(int id);
public:
explicit RepoAssets(QObject * parent, UpdateStatus * status, UpdateNetwork * network);
virtual ~RepoAssets() {};
virtual void init(const QString & repoPath, const QString & nightly, const int resultsPerPage);
virtual bool retrieveMetaDataAll();
virtual bool retrieveMetaDataOne(const int id);
const QString contentType() const;
const QString copyFilter() const;
bool downloadToBuffer(const int id = 0);
bool downloadToFile(const int row, QString & downloadDir);
bool getJson(QJsonDocument * json);
bool getJson(const QString assetName, QJsonDocument * json);
bool setCopyFilter(const QString filter);
bool setSubDirectory(const QString path);
const QString subDirectory() const;
signals:
void idChanged(int id);
private:
AssetsRawItemModel* const m_rawItemModel;
AssetsFilteredItemModel* const m_filteredItemModel;
int m_releaseId;
virtual void dumpItemModel(const QString modelName, const QAbstractItemModel * itemModel) const;
};
+245
View File
@@ -0,0 +1,245 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program 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.
*/
#include "repodatamodels.h"
#include "constants.h"
/*
RepoRawItemModel
*/
RepoRawItemModel::RepoRawItemModel(QString name) :
QStandardItemModel(),
RepoItemModelBase(name)
{
invalidate();
}
void RepoRawItemModel::invalidate()
{
clear();
setRefreshRequired(true);
}
void RepoRawItemModel::parseAll(QJsonDocument * json)
{
invalidate();
if (json->isArray()) {
const QJsonArray &array = json->array();
foreach (const QJsonValue &v, array)
{
if (v.isObject()) {
const QJsonObject &obj = v.toObject();
parseJsonObject(obj);
}
}
setRefreshRequired(false);
}
}
void RepoRawItemModel::parseMetaData(const int mdt, QJsonDocument * json)
{
switch (mdt) {
case MDT_Asset:
case MDT_Release:
parseOne(json);
break;
case MDT_Assets:
case MDT_Releases:
parseAll(json);
break;
default:
qDebug() << "Unexpected meta data type" << mdt;
break;
}
}
void RepoRawItemModel::parseOne(QJsonDocument * json)
{
invalidate();
if (json->isObject()) {
const QJsonObject &obj = json->object();
parseJsonObject(obj);
setRefreshRequired(false);
}
}
// static
QString RepoRawItemModel::typeToString(const int val)
{
const QStringList strl = { "Asset", "Assets", "Release", "Releases" };
return strl.value(val, CPN_STR_UNKNOWN_ITEM);
}
void RepoRawItemModel::update()
{
for (int i = 0; i < rowCount(); ++i)
setDynamicItemData(item(i));
}
/*
RepoFilteredItemModel
*/
RepoFilteredItemModel::RepoFilteredItemModel(QString name) :
QSortFilterProxyModel(),
RepoItemModelBase(name)
{
m_filterFlags = 0;
m_filterText.setPattern("");
m_filterText.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
setFilterRole(RIMR_Available);
setFilterKeyColumn(0);
setDynamicSortFilter(true);
}
const QString RepoFilteredItemModel::date(const int id) const
{
return value(id, RIMR_Date).toString();
}
bool RepoFilteredItemModel::filterAcceptsRow(int sourceRow, const QModelIndex & sourceParent) const
{
const QModelIndex & srcIdx = sourceModel()->index(sourceRow, 0, sourceParent);
if (!srcIdx.isValid() || !sourceModel()->data(srcIdx, filterRole()).toBool())
return false;
if (!m_filterFlags && m_filterText.pattern().isEmpty())
return true;
if (m_filterFlags) {
bool ok;
const int flags = sourceModel()->data(srcIdx, RIMR_Flags).toInt(&ok);
return (ok && (m_filterFlags & flags));
}
else {
return sourceModel()->data(srcIdx, Qt::DisplayRole).toString().contains(m_filterText);
}
}
const int RepoFilteredItemModel::flags(const int id) const
{
return value(id, RIMR_Flags).toInt();
}
const int RepoFilteredItemModel::id(const int row) const
{
const QModelIndex & idx = index(row, 0);
if (!idx.isValid())
return 0;
bool ok;
const int id = data(idx, RIMR_Id).toInt(&ok);
if (ok)
return id;
return 0;
}
const int RepoFilteredItemModel::id(const QVariant & value, const Qt::MatchFlags flags, const int role) const
{
const QModelIndexList idxlst = match(index(0, 0), role, value, 1, flags);
if (!idxlst.isEmpty()) {
bool ok;
const int id = data(idxlst.at(0), RIMR_Id).toInt(&ok);
if (ok)
return id;
}
return 0;
}
const QStringList RepoFilteredItemModel::list() const
{
QStringList strl;
for (int i = 0; i < rowCount(); i++)
strl << data(index(i, 0)).toString();
return strl;
}
const QString RepoFilteredItemModel::name(const int id) const
{
return value(id, Qt::DisplayRole).toString();
}
void RepoFilteredItemModel::setFilterFlags(const int flags)
{
if (m_filterFlags != flags) {
m_filterFlags = flags;
m_filterText.setPattern("");
invalidateFilter();
}
}
void RepoFilteredItemModel::setFilterPattern(const QString & pattern)
{
if (m_filterText.pattern() != pattern) {
m_filterText.setPattern(pattern);
m_filterFlags = 0;
invalidateFilter();
}
}
bool RepoFilteredItemModel::setFlags(const int id, const int flags)
{
return setValue(id, RIMR_Flags, QVariant(flags));
}
bool RepoFilteredItemModel::setValue(const int id, const int role, const QVariant value)
{
//qDebug() << "id:" << id << "role:" << role << "value:" << value;
const QModelIndexList idxlst = match(index(0, 0), RIMR_Id, QVariant(id), 1, Qt::MatchExactly);
if (!idxlst.isEmpty())
return setData(idxlst.at(0), value, role);
return false;
}
const QVariant RepoFilteredItemModel::value(const int id, const int role) const
{
//qDebug() << "id:" << id << "role:";
const QModelIndexList idxlst = match(index(0, 0), RIMR_Id, QVariant(id), 1, Qt::MatchExactly);
if (!idxlst.isEmpty())
return data(idxlst.at(0), role);
return QVariant();
}
const QMap<int, QVariant> RepoFilteredItemModel::valuesMap(const int id) const
{
const QModelIndexList idxlst = match(index(0, 0), RIMR_Id, QVariant(id), 1, Qt::MatchExactly);
if (!idxlst.isEmpty())
return itemData(idxlst.at(0));
return QMap<int, QVariant>();
}
+125
View File
@@ -0,0 +1,125 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program 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.
*/
#pragma once
#include <QtCore>
#include <QStandardItemModel>
#include <QSortFilterProxyModel>
#include <QStandardItem>
#include <QJsonDocument>
class RepoItemModelBase
{
public:
explicit RepoItemModelBase(QString name) : m_modelName(name) {}
virtual ~RepoItemModelBase() {}
inline const QString modelName() const { return m_modelName; }
enum RepoItemModelRole {
RIMR_Id = Qt::UserRole,
RIMR_Date,
RIMR_SortOrder,
RIMR_Tag,
RIMR_Content,
RIMR_Available,
RIMR_Flags,
RIMR_CopyFilter,
RIMR_SubDirectory
};
private:
QString m_modelName;
};
class RepoRawItemModel : public QStandardItemModel, public RepoItemModelBase
{
Q_OBJECT
public:
explicit RepoRawItemModel(QString name);
virtual ~RepoRawItemModel() {}
enum MetaDataType {
MDT_Asset,
MDT_Assets,
MDT_Release,
MDT_Releases
};
static QString typeToString(const int val);
protected:
friend class RepoMetaData;
friend class RepoAssets;
friend class RepoReleases;
virtual bool isAvailable(QStandardItem * item) { return true; }
virtual void parseJsonObject(const QJsonObject & obj) = 0;
virtual void setDynamicItemData(QStandardItem * item) {}
void invalidate();
const bool isRefreshRequired() const { return m_refreshRequired; }
void parseAll(QJsonDocument * json);
void parseMetaData(const int mdt, QJsonDocument * json);
void parseOne(QJsonDocument * json);
void setRefreshRequired(const bool val) { m_refreshRequired = val; }
void update();
private:
bool m_refreshRequired;
};
class RepoFilteredItemModel: public QSortFilterProxyModel, public RepoItemModelBase
{
Q_OBJECT
public:
explicit RepoFilteredItemModel(QString name);
virtual ~RepoFilteredItemModel() {}
protected:
friend class RepoMetaData;
friend class RepoAssets;
friend class RepoReleases;
virtual bool filterAcceptsRow(int sourceRow, const QModelIndex & sourceParent) const override;
const int count() const { return rowCount(); }
const QString date(const int id) const;
const int flags(const int id) const;
const int id(const int row) const ;
const int id(const QVariant & value, const Qt::MatchFlags flags = Qt::MatchExactly, const int role = Qt::DisplayRole) const;
const bool isEmpty() const { return rowCount() == 0; }
const QStringList list() const;
const QString name(const int id) const;
void setFilterFlags(const int flags);
void setFilterPattern(const QString & pattern);
bool setFlags(const int id, const int flags);
bool setValue(const int id, const int role, const QVariant value);
const QVariant value(const int id, const int role) const;
const QMap<int, QVariant> valuesMap(int id) const;
private:
int m_filterFlags;
QRegularExpression m_filterText;
};
+203
View File
@@ -0,0 +1,203 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program 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.
*/
#include "repometadata.h"
RepoMetaData::RepoMetaData(UpdateStatus * status, UpdateNetwork * network) :
m_status(status),
m_network(network),
m_rawItemModel(nullptr),
m_filteredItemModel(nullptr),
m_id(0)
{
}
const int RepoMetaData::count() const
{
return m_filteredItemModel->count();
}
const QString RepoMetaData::date() const
{
return m_filteredItemModel->date(id());
}
void RepoMetaData::dumpRawItemModel() const
{
dumpItemModel(m_rawItemModel->modelName(), m_rawItemModel);
}
void RepoMetaData::dumpFilteredItemModel() const
{
dumpItemModel(m_filteredItemModel->modelName(), m_filteredItemModel);
}
const int RepoMetaData::flags() const
{
return m_filteredItemModel->flags(id());
}
int RepoMetaData::getSetId()
{
return getSetId(0);
}
int RepoMetaData::getSetId(const int row)
{
return setId(m_filteredItemModel->id(row));
}
int RepoMetaData::getSetId(QVariant value, Qt::MatchFlags flags, int role)
{
return setId(m_filteredItemModel->id(value, flags, role));
}
const int RepoMetaData::id() const
{
return m_id;
}
void RepoMetaData::init(const QString & repoPath, const int resultsPerPage)
{
m_repoPath = repoPath;
m_resultsPerPage = resultsPerPage;
}
void RepoMetaData::invalidate()
{
setId(0);
m_rawItemModel->invalidate();
}
const int RepoMetaData::isEmpty() const
{
return m_filteredItemModel->isEmpty();
}
const bool RepoMetaData::isIdValid() const
{
return m_id > 0;
}
const QStringList RepoMetaData::list() const
{
return m_filteredItemModel->list();
}
const QString RepoMetaData::name() const
{
return m_filteredItemModel->name(id());
}
UpdateNetwork* const RepoMetaData::network() const
{
return m_network;
}
const QString RepoMetaData::repoPath() const
{
return m_repoPath;
}
const int RepoMetaData::resultsPerPage() const
{
return m_resultsPerPage;
}
bool RepoMetaData::retrieveMetaData(const int mdt, const QString url)
{
if (!m_rawItemModel->isRefreshRequired())
return true;
QJsonDocument *json = new QJsonDocument();
network()->downloadMetaData(url, json);
if (!network()->isSuccess()) {
status()->reportProgress("Unable to download release asset meta data", QtDebugMsg);
invalidate();
return false;
}
m_rawItemModel->parseMetaData(mdt, json);
// set current id to first row as most methods assume a current id has been set
getSetId();
delete json;
return true;
}
bool RepoMetaData::setFlags(int flags)
{
return m_filteredItemModel->setFlags(id(), flags);
}
void RepoMetaData::setFilterFlags(int flags)
{
m_filteredItemModel->setFilterFlags(flags);
}
void RepoMetaData::setFilterPattern(const QString & pattern)
{
m_filteredItemModel->setFilterPattern(pattern);
}
int RepoMetaData::setId(const int id)
{
m_id = id;
return m_id;
}
void RepoMetaData::setModels(RepoRawItemModel * repoRawItemModel, RepoFilteredItemModel * repoFilteredItemModel)
{
m_rawItemModel = repoRawItemModel;
m_filteredItemModel = repoFilteredItemModel;
invalidate();
}
UpdateStatus* const RepoMetaData::status() const
{
return m_status;
}
const QString RepoMetaData::urlAsset(const int assetId) const
{
return QString("%1/assets/%2").arg(urlReleases()).arg(assetId);
}
const QString RepoMetaData::urlAssets(const int releaseId) const
{
return QString("%1/%2/assets").arg(urlReleases()).arg(releaseId) % (m_resultsPerPage > -1 ?
QString("\?per_page=%1").arg(m_resultsPerPage) : "");
}
const QString RepoMetaData::urlContent(const QString & filename) const
{
return QString("%1/contents/%2").arg(m_repoPath).arg(filename);
}
const QString RepoMetaData::urlReleases() const
{
return QString("%1/releases").arg(m_repoPath);
}
+83
View File
@@ -0,0 +1,83 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program 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.
*/
#pragma once
#include "updatestatus.h"
#include "updatenetwork.h"
#include "repodatamodels.h"
#include <QtCore>
class QAbstractItemModel;
class RepoMetaData
{
public:
explicit RepoMetaData(UpdateStatus * status, UpdateNetwork * network);
virtual ~RepoMetaData() {}
virtual void init(const QString & repoPath, const QString & nightly, const int resultsPerPage) = 0;
virtual bool retrieveMetaDataAll() = 0;
virtual bool retrieveMetaDataOne(const int id) = 0;
virtual int setId(const int id);
const int count() const;
const QString date() const;
void dumpRawItemModel() const;
void dumpFilteredItemModel() const;
const int flags() const;
int getSetId();
int getSetId(const int row);
int getSetId(QVariant value, Qt::MatchFlags flags = Qt::MatchExactly, int role = Qt::DisplayRole);
const int id() const;
void init(const QString & repoPath, const int resultsPerPage);
void invalidate();
const int isEmpty() const;
const bool isIdValid() const;
const QStringList list() const;
const QString name() const;
UpdateNetwork* const network() const;
const QString repoPath() const;
const int resultsPerPage() const;
bool retrieveMetaData(const int mdt, const QString url);
bool setFlags(int flags);
void setFilterFlags(int flags);
void setFilterPattern(const QString & pattern);
void setModels(RepoRawItemModel * repoRawItemModel, RepoFilteredItemModel * repoFilteredItemModel);
UpdateStatus* const status() const;
const QString urlAsset(const int assetId) const;
const QString urlAssets(const int releaseId) const;
const QString urlContent(const QString & filename) const;
const QString urlReleases() const;
private:
UpdateStatus* const m_status;
UpdateNetwork* const m_network;
RepoRawItemModel* m_rawItemModel;
RepoFilteredItemModel* m_filteredItemModel;
QString m_repoPath;
int m_resultsPerPage;
int m_id;
virtual void dumpItemModel(const QString modelName, const QAbstractItemModel * itemModel) const = 0;
};
-546
View File
@@ -1,546 +0,0 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program 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.
*/
#include "repomodels.h"
#include "appdata.h"
#include "helpers.h"
#include <QStandardItem>
#include <QJsonDocument>
QString metaDataTypeToString(MetaDataType val)
{
switch ((int)val) {
case MDT_Release:
return "Release";
case MDT_Releases:
return "Releases";
case MDT_Asset:
return "Asset";
case MDT_ReleaseAssets:
return "Release Assets";
default:
return CPN_STR_UNKNOWN_ITEM;
}
}
/*
ReleasesItemModel
*/
ReleasesItemModel::ReleasesItemModel() :
UpdatesItemModel("Releases"),
m_settingsIdx(-1),
m_nightlyName(""),
m_releaseChannel(-1),
m_refreshRequired(true)
{
setSortRole(UpdatesItemModel::IMDR_SortOrder);
}
void ReleasesItemModel::setReleaseChannel(const int channel)
{
if (m_releaseChannel != channel) {
m_releaseChannel = channel;
update();
}
}
void ReleasesItemModel::parseMetaData(const int mdt, QJsonDocument * jsonDoc)
{
json = jsonDoc;
switch (mdt) {
case MDT_Release:
parseRelease();
break;
case MDT_Releases:
parseReleases();
break;
}
}
void ReleasesItemModel::parseRelease()
{
clear();
if (json->isObject()) {
const QJsonObject &obj = json->object();
parseJsonObject(obj);
}
}
void ReleasesItemModel::parseReleases()
{
clear();
if (json->isArray()) {
const QJsonArray &array = json->array();
foreach (const QJsonValue &v, array)
{
if (v.isObject()) {
const QJsonObject &obj = v.toObject();
parseJsonObject(obj);
}
}
}
sort(0, Qt::DescendingOrder);
m_refreshRequired = false;
}
void ReleasesItemModel::parseJsonObject(const QJsonObject & obj)
{
QStandardItem * item = new QStandardItem();
QString tag = QString();
bool prerelease = false;
if (!obj.value("name").isUndefined()) {
item->setText(obj.value("name").toString());
}
if (!obj.value("id").isUndefined()) {
item->setData(obj.value("id").toInt(), UpdatesItemModel::IMDR_Id);
}
if (!obj.value("tag_name").isUndefined()) {
tag = obj.value("tag_name").toString();
item->setData(tag, UpdatesItemModel::IMDR_Tag);
}
if (!obj.value("prerelease").isUndefined()) {
prerelease = obj.value("prerelease").toBool();
item->setData(prerelease, UpdatesItemModel::IMDR_Prerelease);
}
if (!obj.value("created_at").isUndefined()) {
item->setData(QDateTime::fromString(obj.value("created_at").toString(), Qt::ISODate), UpdatesItemModel::IMDR_Date);
}
setDynamicItemData(item);
SemanticVersion sv;
if (tag.toLower() == m_nightlyName)
sv.fromString("255.255.255");
else
sv.fromString(tag);
if (sv.isValid())
item->setData(sv.toInt(), UpdatesItemModel::IMDR_SortOrder);
else
item->setData(item->data(IMDR_Date).toInt(), UpdatesItemModel::IMDR_SortOrder);
appendRow(item);
}
void ReleasesItemModel::setDynamicItemData(QStandardItem * item)
{
item->setData(isReleaseAvailable(item), UpdatesItemModel::IMDR_Available);
}
void ReleasesItemModel::update()
{
for (int i = 0; i < rowCount(); ++i)
setDynamicItemData(item(i));
}
bool ReleasesItemModel::isReleaseAvailable(QStandardItem * item)
{
if (item->data(IMDR_Prerelease).toBool()) {
if (m_releaseChannel == ComponentData::RELEASE_CHANNEL_STABLE) {
//qDebug() << "Release channel stable" << tagname << "is pre-release" ;
return false;
}
else if (m_releaseChannel == ComponentData::RELEASE_CHANNEL_PRERELEASE &&
item->data(IMDR_Tag).toString().toLower() == m_nightlyName) {
//qDebug() << "Release channel pre-release" << tagname << "is nightly" ;
return false;
}
}
//qDebug() << "Release available:" << tagname;
return true;
}
/*
AssetsItemModel
*/
AssetsItemModel::AssetsItemModel() :
UpdatesItemModel("Assets")
{
}
void AssetsItemModel::parseMetaData(int mdt, QJsonDocument * jsonDoc)
{
json = jsonDoc;
switch (mdt) {
case MDT_Asset:
parseAsset();
break;
case MDT_ReleaseAssets:
parseReleaseAssets();
break;
}
}
void AssetsItemModel::parseAsset()
{
clear();
if (json->isObject()) {
const QJsonObject &obj = json->object();
parseJsonObject(obj);
}
}
void AssetsItemModel::parseReleaseAssets()
{
clear();
if (json->isArray()) {
const QJsonArray &array = json->array();
foreach (const QJsonValue &v, array)
{
if (v.isObject()) {
const QJsonObject &obj = v.toObject();
parseJsonObject(obj);
}
}
}
sort(0);
}
void AssetsItemModel::parseJsonObject(const QJsonObject & obj)
{
QStandardItem * item = new QStandardItem();
if (!obj.value("name").isUndefined()) {
item->setText(obj.value("name").toString());
}
if (!obj.value("id").isUndefined()) {
item->setData(obj.value("id").toInt(), UpdatesItemModel::IMDR_Id);
}
if (!obj.value("content_type").isUndefined()) {
item->setData(obj.value("content_type").toString(), UpdatesItemModel::IMDR_Content);
}
if (!obj.value("updated_at").isUndefined()) {
item->setData(QDateTime::fromString(obj.value("updated_at").toString(), Qt::ISODate), UpdatesItemModel::IMDR_Date);
}
item->setData(true, UpdatesItemModel::IMDR_Available);
appendRow(item);
}
/*
UpdatesFilteredItemModel
*/
UpdatesFilteredItemModel::UpdatesFilteredItemModel(UpdatesItemModel * sourceModel, QString name) :
QSortFilterProxyModel(nullptr),
filterFlags(0),
m_name(name)
{
filterText.setPattern("");
filterText.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
setFilterRole(UpdatesItemModel::IMDR_Available);
setFilterKeyColumn(0);
setDynamicSortFilter(true);
setSourceModel(sourceModel);
}
void UpdatesFilteredItemModel::setFilterFlags(const int flags)
{
if (filterFlags != flags) {
filterFlags = flags;
filterText.setPattern("");
invalidateFilter();
}
}
void UpdatesFilteredItemModel::setFilterPattern(const QString & pattern)
{
if (filterText.pattern() != pattern) {
filterText.setPattern(pattern);
filterFlags = 0;
invalidateFilter();
}
}
bool UpdatesFilteredItemModel::filterAcceptsRow(int sourceRow, const QModelIndex & sourceParent) const
{
const QModelIndex & srcIdx = sourceModel()->index(sourceRow, 0, sourceParent);
if (!srcIdx.isValid() || !sourceModel()->data(srcIdx, filterRole()).toBool())
return false;
if (!filterFlags && filterText.pattern().isEmpty())
return true;
if (filterFlags) {
bool ok;
const int flags = sourceModel()->data(srcIdx, UpdatesItemModel::IMDR_Flags).toInt(&ok);
return (ok && (!flags || (filterFlags & flags)));
}
else {
return sourceModel()->data(srcIdx, Qt::DisplayRole).toString().contains(filterText);
}
}
void UpdatesFilteredItemModel::dumpContents()
{
qDebug() << "Contents of" << m_name;
for (int i = 0; i < rowCount(); ++i) {
qDebug() << "row:" << i
<< "text:" << data(index(i, 0), Qt::DisplayRole).toString()
<< "id:" << data(index(i, 0), UpdatesItemModel::IMDR_Id).toInt()
<< "date:" << data(index(i, 0), UpdatesItemModel::IMDR_Date).toDateTime()
<< "tag:" << data(index(i, 0), UpdatesItemModel::IMDR_Tag).toString()
<< "avail:" << data(index(i, 0), UpdatesItemModel::IMDR_Available).toBool()
<< "flags:" << data(index(i, 0), UpdatesItemModel::IMDR_Flags).toInt()
<< "content:" << data(index(i, 0), UpdatesItemModel::IMDR_Content).toString()
<< "prerelease:" << data(index(i, 0), UpdatesItemModel::IMDR_Prerelease).toBool();
}
}
QMap<int, QVariant> UpdatesFilteredItemModel::itemMetaData(const int id)
{
const QModelIndexList idxlst = match(index(0, 0), UpdatesItemModel::IMDR_Id, QVariant(id), 1, Qt::MatchExactly);
if (!idxlst.isEmpty())
return itemData(idxlst.at(0));
return QMap<int, QVariant>();
}
QVariant UpdatesFilteredItemModel::metaDataValue(const int id, const int role)
{
//qDebug() << "id:" << id << "role:";
const QModelIndexList idxlst = match(index(0, 0), UpdatesItemModel::IMDR_Id, QVariant(id), 1, Qt::MatchExactly);
if (!idxlst.isEmpty())
return data(idxlst.at(0), role);
return QVariant();
}
bool UpdatesFilteredItemModel::setMetaDataValue(const int id, const int role, const QVariant value)
{
//qDebug() << "id:" << id << "role:" << role << "value:" << value;
const QModelIndexList idxlst = match(index(0, 0), UpdatesItemModel::IMDR_Id, QVariant(id), 1, Qt::MatchExactly);
if (!idxlst.isEmpty())
return setData(idxlst.at(0), value, role);
return false;
}
int UpdatesFilteredItemModel::id(const int row)
{
const QModelIndex & idx = index(row, 0);
if (!idx.isValid())
return 0;
bool ok;
const int id = data(idx, UpdatesItemModel::IMDR_Id).toInt(&ok);
if (ok)
return id;
return 0;
}
int UpdatesFilteredItemModel::id(const QVariant value, const Qt::MatchFlags flags, const int role)
{
const QModelIndexList idxlst = match(index(0, 0), role, value, 1, flags);
if (!idxlst.isEmpty()) {
bool ok;
const int id = data(idxlst.at(0), UpdatesItemModel::IMDR_Id).toInt(&ok);
if (ok)
return id;
}
return 0;
}
QStringList UpdatesFilteredItemModel::list()
{
QStringList strl;
for (int i = 0; i < rowCount(); i++)
strl << data(index(i, 0)).toString();
return strl;
}
/*
ReleasesFilteredItemModel
*/
ReleasesFilteredItemModel::ReleasesFilteredItemModel(UpdatesItemModel * sourceModel) :
UpdatesFilteredItemModel(sourceModel, "Filtered Releases")
{
setSortRole(UpdatesItemModel::IMDR_Date);
}
int ReleasesFilteredItemModel::channelLatestId()
{
return rows() > 0 ? id(0) : 0; // assumes model sorted descending date
}
/*
AssetsFilteredItemModel
*/
AssetsFilteredItemModel::AssetsFilteredItemModel(UpdatesItemModel * sourceModel) :
UpdatesFilteredItemModel(sourceModel, "Filtered Assets")
{
}
bool AssetsFilteredItemModel::setFlags(const int id, const int flags)
{
return setMetaDataValue(id, UpdatesItemModel::IMDR_Flags, QVariant(flags));
}
bool AssetsFilteredItemModel::setSubDirectory(const int id, const QString path)
{
return setMetaDataValue(id, UpdatesItemModel::IMDR_SubDirectory, QVariant(path));
}
bool AssetsFilteredItemModel::setCopyFilter(const int id, const QString filter)
{
return setMetaDataValue(id, UpdatesItemModel::IMDR_CopyFilter, QVariant(filter));
}
/*
RepoMetaData
*/
RepoMetaData::RepoMetaData(QObject * parent) :
QObject(parent)
{
}
/*
ReleasesMetaData
*/
ReleasesMetaData::ReleasesMetaData(QObject * parent) :
RepoMetaData(parent),
m_id(0)
{
itemModel = new ReleasesItemModel();
filteredItemModel = new ReleasesFilteredItemModel(itemModel);
}
void ReleasesMetaData::init(const QString repo, const QString nightly, const int settingsIndex, const int resultsPerPage)
{
m_repo = repo;
m_resultsPerPage = resultsPerPage;
itemModel->setNightlyName(nightly);
itemModel->setSettingsIndex(settingsIndex);
}
bool ReleasesMetaData::refreshRequired()
{
return m_id == 0 ? true : itemModel->refreshRequired();
}
int ReleasesMetaData::getSetId()
{
m_id = filteredItemModel->channelLatestId();
return m_id;
}
int ReleasesMetaData::getSetId(int row)
{
m_id = filteredItemModel->id(row);
return m_id;
}
int ReleasesMetaData::getSetId(QVariant value, Qt::MatchFlags flags, int role)
{
m_id = filteredItemModel->id(value, flags, role);
return m_id;
}
QString ReleasesMetaData::name()
{
if (m_id)
return filteredItemModel->name(m_id);
return "";
}
QString ReleasesMetaData::version()
{
if (m_id)
return filteredItemModel->version(m_id);
return "0";
}
/*
AssetsMetaData
*/
AssetsMetaData::AssetsMetaData(QObject * parent) :
RepoMetaData(parent)
{
itemModel = new AssetsItemModel();
filteredItemModel = new AssetsFilteredItemModel(itemModel);
}
void AssetsMetaData::init(const QString repo, const int resultsPerPage)
{
m_repo = repo;
m_resultsPerPage = resultsPerPage;
}
int AssetsMetaData::getSetId(int row)
{
m_id = filteredItemModel->id(row);
return m_id;
}
int AssetsMetaData::getSetId(QVariant value, Qt::MatchFlags flags, int role)
{
m_id = filteredItemModel->id(value, flags, role);
return m_id;
}
-306
View File
@@ -1,306 +0,0 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program 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.
*/
#pragma once
#include <QtCore>
#include <QStandardItemModel>
#include <QSortFilterProxyModel>
constexpr char GITHUB_API_REPOS[] {"https://api.github.com/repos"};
constexpr char GH_REPOS_EDGETX[] {"https://api.github.com/repos/EdgeTX"};
constexpr char GH_ACCEPT_HEADER_BINARY[] {"application/octet-stream"};
constexpr char GH_ACCEPT_HEADER_METADATA[] {"application/vnd.github.v3+json"};
constexpr char GH_ACCEPT_HEADER_RAW[] {"application/vnd.github.v3.raw"};
enum MetaDataType {
MDT_Release,
MDT_Releases,
MDT_Asset,
MDT_ReleaseAssets
};
QString metaDataTypeToString(MetaDataType val);
class UpdatesItemModel : public QStandardItemModel
{
Q_OBJECT
public:
enum ItemModelDataRoles {
IMDR_Id = Qt::UserRole,
IMDR_Date,
IMDR_SortOrder,
IMDR_Tag,
IMDR_Prerelease,
IMDR_Content,
IMDR_Available,
IMDR_Flags,
IMDR_CopyFilter,
IMDR_SubDirectory
};
Q_ENUM(ItemModelDataRoles)
explicit UpdatesItemModel(QString name = "") :
QStandardItemModel(nullptr),
json(nullptr),
m_name(name) {}
virtual ~UpdatesItemModel() {}
protected:
QJsonDocument * json;
private:
QString m_name;
};
class ReleasesItemModel : public UpdatesItemModel
{
Q_OBJECT
friend class ReleasesMetaData;
public:
explicit ReleasesItemModel();
virtual ~ReleasesItemModel() {}
void setNightlyName(const QString name) { m_nightlyName = name.toLower(); }
void setSettingsIndex(const int index) { m_settingsIdx = index; }
void setReleaseChannel(const int channel);
void parseMetaData(const int mdt, QJsonDocument * json);
int settingsIndex() { return m_settingsIdx; }
bool refreshRequired() { return m_refreshRequired; }
private:
int m_settingsIdx;
QString m_nightlyName;
int m_releaseChannel;
bool m_refreshRequired;
void setDynamicItemData(QStandardItem * item);
void update();
bool isReleaseAvailable(QStandardItem * item);
void parseRelease();
void parseReleases();
void parseJsonObject(const QJsonObject & obj);
};
class AssetsItemModel : public UpdatesItemModel
{
Q_OBJECT
friend class AssetsMetaData;
public:
explicit AssetsItemModel();
virtual ~AssetsItemModel() {}
void parseMetaData(int mdt, QJsonDocument * json);
private:
void parseAsset();
void parseReleaseAssets();
void parseJsonObject(const QJsonObject & obj);
};
class UpdatesFilteredItemModel: public QSortFilterProxyModel
{
Q_OBJECT
public:
explicit UpdatesFilteredItemModel(UpdatesItemModel * sourceModel, QString name = "");
virtual ~UpdatesFilteredItemModel() {};
void dumpContents();
QStringList list();
int id(const int row);
int id(const QVariant value, const Qt::MatchFlags flags = Qt::MatchExactly, const int role = Qt::DisplayRole);
int rows() { return rowCount(); }
QString date(const int id) { return metaDataValue(id, UpdatesItemModel::IMDR_Date).toString(); }
QString name(const int id) { return metaDataValue(id, Qt::DisplayRole).toString(); }
public slots:
void setFilterFlags(const int flags);
void setFilterPattern(const QString & pattern);
signals:
protected:
int filterFlags;
QRegularExpression filterText;
bool filterAcceptsRow(int sourceRow, const QModelIndex & sourceParent) const override;
QMap<int, QVariant> itemMetaData(int id);
QVariant metaDataValue(const int id, const int role);
bool setMetaDataValue(const int id, const int role, const QVariant value);
private:
QString m_name;
};
class ReleasesFilteredItemModel: public UpdatesFilteredItemModel
{
Q_OBJECT
public:
protected:
friend class ReleasesMetaData;
explicit ReleasesFilteredItemModel(UpdatesItemModel * sourceModel);
virtual ~ReleasesFilteredItemModel() {};
int channelLatestId();
bool prerelease(const int id) { return metaDataValue(id, UpdatesItemModel::IMDR_Prerelease).toBool(); }
QString version(const int id) { return metaDataValue(id, UpdatesItemModel::IMDR_Tag).toString(); }
private:
};
class AssetsFilteredItemModel : public UpdatesFilteredItemModel
{
Q_OBJECT
public:
protected:
friend class AssetsMetaData;
explicit AssetsFilteredItemModel(UpdatesItemModel * sourceModel);
virtual ~AssetsFilteredItemModel() {};
bool setFlags(const int id, const int flags);
bool setSubDirectory(const int id, const QString path);
bool setCopyFilter(const int id, const QString filter);
QString filename(int id) { return metaDataValue(id, Qt::DisplayRole).toString(); }
private:
};
class RepoMetaData : public QObject
{
Q_OBJECT
public:
explicit RepoMetaData(QObject * parent);
virtual ~RepoMetaData() {};
const QString urlReleases() { return QString("%1/releases").arg(m_repo); }
const QString urlReleaseLatest() { return QString("%1/latest").arg(urlReleases()); }
const QString urlContent(const QString & path) { return QString("%1/contents/%2").arg(m_repo).arg(path); }
protected:
QString m_repo;
int m_resultsPerPage;
};
class ReleasesMetaData : public RepoMetaData
{
Q_OBJECT
public:
explicit ReleasesMetaData(QObject * parent);
virtual ~ReleasesMetaData() {};
void init(const QString repo, const QString nightly, const int settingsIndex, const int resultsPerPage);
bool refreshRequired();
void setReleaseChannel(const int channel) { itemModel->setReleaseChannel(channel); }
void setId(int id) { m_id = id; }
int id() { return m_id; }
int getSetId();
int getSetId(int row);
int getSetId(QVariant value, Qt::MatchFlags flags = Qt::MatchExactly, int role = Qt::DisplayRole);
void parseMetaData(int mdt, QJsonDocument * jsonDoc) { itemModel->parseMetaData(mdt, jsonDoc); }
int count() { return filteredItemModel->rows(); }
QStringList list() { return filteredItemModel->list(); }
void dumpContentsFiltered() { filteredItemModel->dumpContents(); }
QString date() { return filteredItemModel->date(m_id); }
QString name();
bool prerelease() { return filteredItemModel->prerelease(m_id); }
QString version();
const QString urlRelease() { return QString("%1/%2").arg(urlReleases()).arg(m_id); }
private:
ReleasesItemModel *itemModel;
ReleasesFilteredItemModel *filteredItemModel;
int m_id;
};
class AssetsMetaData : public RepoMetaData
{
Q_OBJECT
public:
explicit AssetsMetaData(QObject * parent);
virtual ~AssetsMetaData() {};
void init(const QString repo, const int resultsPerPage);
void setId(int id) { m_id = id; }
int id() { return m_id; }
int getSetId(int row);
int getSetId(QVariant value, Qt::MatchFlags flags = Qt::MatchExactly, int role = Qt::DisplayRole);
void parseMetaData(int mdt, QJsonDocument * jsonDoc) { itemModel->parseMetaData(mdt, jsonDoc); }
bool setFlags(int flags) { return filteredItemModel->setFlags(m_id, flags); }
void setFilterFlags(int flags) { filteredItemModel->setFilterFlags(flags); }
void setFilterPattern(const QString & pattern) { filteredItemModel->setFilterPattern(pattern); }
bool setSubDirectory(const QString path) { return filteredItemModel->setSubDirectory(m_id, path); }
bool setCopyFilter(const QString filter) { return filteredItemModel->setCopyFilter(m_id, filter); }
int count() { return filteredItemModel->rows(); }
QStringList list() { return filteredItemModel->list(); }
void dumpContentsFiltered() { filteredItemModel->dumpContents(); }
QString date() { return filteredItemModel->date(m_id); }
QString name() { return filteredItemModel->name(m_id); }
QString filename() { return filteredItemModel->metaDataValue(m_id, Qt::DisplayRole).toString(); }
QString contentType() { return filteredItemModel->metaDataValue(m_id, UpdatesItemModel::IMDR_Content).toString(); }
QString subDirectory() { return filteredItemModel->metaDataValue(m_id, UpdatesItemModel::IMDR_SubDirectory).toString(); }
QString copyFilter() { return filteredItemModel->metaDataValue(m_id, UpdatesItemModel::IMDR_CopyFilter).toString(); }
int flags() { return filteredItemModel->metaDataValue(m_id, UpdatesItemModel::IMDR_Flags).toInt(); }
const QString urlReleaseAssets(int releaseId) {
return QString("%1/%2/assets").arg(urlReleases()).arg(releaseId) % (m_resultsPerPage > -1 ?
QString("\?per_page=%1").arg(m_resultsPerPage) : ""); }
const QString urlAsset() { return QString("%1/assets/%2").arg(urlReleases()).arg(m_id); }
private:
AssetsItemModel *itemModel;
AssetsFilteredItemModel *filteredItemModel;
int m_id;
};
+219
View File
@@ -0,0 +1,219 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program 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.
*/
#include "reporeleases.h"
#include "appdata.h"
#include "helpers.h"
/*
ReleasesItemModels
*/
// static
const QString ReleasesItemModels::flagsToString(const int flags)
{
QString str;
for (int i = 0; i < 16; i++) {
int flag = flags & (1 << i);
if (flag) {
if (!str.isEmpty())
str.append(";");
str.append(flagToString(flag));
}
}
return str;
}
// static
const QString ReleasesItemModels::flagToString(const int val)
{
const QStringList strl = { "None", "Stable", "Pre-release", "Nightly" };
return strl.value(val, CPN_STR_UNKNOWN_ITEM);
};
/*
ReleasesRawItemModel
*/
ReleasesRawItemModel::ReleasesRawItemModel() :
RepoRawItemModel("Raw Releases"),
m_nightlyName("")
{
setSortRole(RIMR_SortOrder);
}
void ReleasesRawItemModel::parseJsonObject(const QJsonObject & obj)
{
QStandardItem * item = new QStandardItem();
int flags = RELFLG_Stable;
bool available = true;
if (!obj.value("name").isUndefined()) {
item->setText(obj.value("name").toString());
}
if (!obj.value("id").isUndefined()) {
item->setData(obj.value("id").toInt(), RIMR_Id);
}
if (!obj.value("created_at").isUndefined()) {
item->setData(QDateTime::fromString(obj.value("created_at").toString(), Qt::ISODate), RIMR_Date);
}
if (!obj.value("prerelease").isUndefined()) {
if (obj.value("prerelease").toBool())
flags = RELFLG_PreRelease;
}
if (!obj.value("tag_name").isUndefined()) {
item->setData(obj.value("tag_name").toString(), RIMR_Tag);
if (item->data(RIMR_Tag).toString().toLower() == m_nightlyName)
flags = RELFLG_Nightly;
}
if (!obj.value("draft").isUndefined()) {
if (obj.value("draft").toBool())
available = false;
}
item->setData(available, RIMR_Available);
item->setData(flags, RIMR_Flags);
SemanticVersion sv;
if (item->data(RIMR_Tag).toString().toLower() == m_nightlyName)
sv.fromString("255.255.255");
else
sv.fromString(item->data(RIMR_Tag).toString());
if (sv.isValid())
item->setData(sv.toInt(), RIMR_SortOrder);
else
item->setData(item->data(RIMR_Date).toInt(), RIMR_SortOrder);
appendRow(item);
}
void ReleasesRawItemModel::setNightlyName(const QString name)
{
m_nightlyName = name.toLower();
}
/*
ReleasesFilteredItemModel
*/
ReleasesFilteredItemModel::ReleasesFilteredItemModel(ReleasesRawItemModel * releasesRawItemModel) :
RepoFilteredItemModel("Filtered Releases")
{
setSourceModel(releasesRawItemModel);
setSortRole(RIMR_Date);
}
const int ReleasesFilteredItemModel::channelLatestId() const
{
return rowCount() > 0 ? id(0) : 0;
}
void ReleasesFilteredItemModel::setChannel(const int channel)
{
int flags = 0;
for (int i = 0; i <= channel; i++)
flags |= (1 << (i + 1));
setFilterFlags(flags);
}
const QString ReleasesFilteredItemModel::version(const int id) const
{
return value(id, RIMR_Tag).toString();
}
/*
RepoReleases
*/
RepoReleases::RepoReleases(QObject * parent, UpdateStatus * status, UpdateNetwork * network) :
QObject(parent),
RepoMetaData(status, network),
m_rawItemModel(new ReleasesRawItemModel()),
m_filteredItemModel(new ReleasesFilteredItemModel(m_rawItemModel))
{
setModels(m_rawItemModel, m_filteredItemModel);
}
int RepoReleases::channelLatestId()
{
return m_filteredItemModel->channelLatestId();
}
void RepoReleases::dumpItemModel(const QString modelName, const QAbstractItemModel * itemModel) const
{
qDebug() << "Contents of model:" << modelName;
if (itemModel) {
for (int i = 0; i < itemModel->rowCount(); ++i) {
qDebug().noquote() << "row:" << i;
qDebug().noquote() << " name:" << itemModel->data(itemModel->index(i, 0), Qt::DisplayRole).toString();
qDebug().noquote() << " id:" << itemModel->data(itemModel->index(i, 0), RepoItemModelBase::RIMR_Id).toInt();
qDebug().noquote() << " date:" << itemModel->data(itemModel->index(i, 0), RepoItemModelBase::RIMR_Date).toDateTime();
qDebug().noquote() << " version:" << itemModel->data(itemModel->index(i, 0), RepoItemModelBase::RIMR_Tag).toString();
qDebug().noquote() << " flags:" << ReleasesItemModels::flagsToString(itemModel->data(itemModel->index(i, 0), RepoItemModelBase::RIMR_Flags).toInt());
qDebug().noquote() << " avail:" << itemModel->data(itemModel->index(i, 0), RepoItemModelBase::RIMR_Available).toBool();
}
}
}
void RepoReleases::init(const QString & repoPath,const QString & nightly, const int resultsPerPage)
{
RepoMetaData::init(repoPath, resultsPerPage);
m_rawItemModel->setNightlyName(nightly);
}
bool RepoReleases::retrieveMetaDataAll()
{
return retrieveMetaData(RepoRawItemModel::MDT_Releases, urlReleases());
}
int RepoReleases::setId(const int id)
{
if (this->id() != id) {
RepoMetaData::setId(id);
emit idChanged(this->id());
}
return this->id();
}
void RepoReleases::setChannel(const int channel)
{
m_filteredItemModel->setChannel(channel);
getSetId();
}
const QString RepoReleases::version() const
{
return m_filteredItemModel->version(id());
}
+104
View File
@@ -0,0 +1,104 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program 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.
*/
#pragma once
#include "repodatamodels.h"
#include "repometadata.h"
class ReleasesItemModels
{
public:
explicit ReleasesItemModels() {}
virtual ~ReleasesItemModels() {}
enum ReleaseFlag {
RELFLG_None = 0,
RELFLG_Stable = 1 << 1,
RELFLG_PreRelease = 1 << 2,
RELFLG_Nightly = 1 << 3,
};
static const QString flagsToString(const int flags);
static const QString flagToString(const int flag);
};
class ReleasesRawItemModel : public RepoRawItemModel, public ReleasesItemModels
{
Q_OBJECT
public:
explicit ReleasesRawItemModel();
virtual ~ReleasesRawItemModel() {}
protected:
friend class RepoReleases;
virtual void parseJsonObject(const QJsonObject & obj);
void setNightlyName(const QString name);
private:
QString m_nightlyName;
};
class ReleasesFilteredItemModel: public RepoFilteredItemModel, public ReleasesItemModels
{
Q_OBJECT
public:
explicit ReleasesFilteredItemModel(ReleasesRawItemModel * releasesRawItemModel);
virtual ~ReleasesFilteredItemModel() {}
protected:
friend class RepoReleases;
const int channelLatestId() const;
void setChannel(const int channel);
const QString version(const int id) const;
};
class RepoReleases : public QObject, public RepoMetaData
{
Q_OBJECT
public:
explicit RepoReleases(QObject * parent, UpdateStatus * status, UpdateNetwork * network);
virtual ~RepoReleases() {}
virtual void init(const QString & repoPath, const QString & nightly, const int resultsPerPage);
virtual int setId(const int id) override;
virtual bool retrieveMetaDataAll();
virtual bool retrieveMetaDataOne(const int id) { return false; } // not implemented
int channelLatestId();
void setChannel(const int channel);
const QString version() const;
signals:
void idChanged(int id);
private:
ReleasesRawItemModel* const m_rawItemModel;
ReleasesFilteredItemModel* const m_filteredItemModel;
virtual void dumpItemModel(const QString modelName, const QAbstractItemModel * itemModel) const;
};
+26 -26
View File
@@ -53,14 +53,14 @@
#endif
UpdateCompanion::UpdateCompanion(QWidget * parent) :
UpdateInterface(parent)
UpdateInterface(parent, CID_Companion, QCoreApplication::translate("UpdateCompanion", "Companion"))
{
init(CID_Companion, QCoreApplication::translate("UpdateCompanion", "Companion"), QString(GH_REPOS_EDGETX).append("/edgetx"), "nightly");
init(QString(GH_API_REPOS_EDGETX).append("/edgetx"), "nightly");
}
void UpdateCompanion::initAssetSettings()
void UpdateCompanion::assetSettingsInit()
{
if (!isValidSettingsIndex())
if (!isSettingsIndexValid())
return;
g.component[id()].initAllAssets();
@@ -80,24 +80,24 @@ void UpdateCompanion::initAssetSettings()
bool UpdateCompanion::asyncInstall()
{
#ifdef OS_SUPPORTED_INSTALLER
//reportProgress(tr("Run application installer: %1").arg(g.runAppInstaller() ? tr("true") : tr("false")), QtDebugMsg);
//status()->reportProgress(tr("Run application installer: %1").arg(g.runAppInstaller() ? tr("true") : tr("false")), QtDebugMsg);
if (!g.runAppInstaller())
return true;
progressMessage(tr("Install"));
status()->progressMessage(tr("Install"));
assets->setFilterFlags(UPDFLG_AsyncInstall);
//reportProgress(tr("Asset filter applied: %1 Assets found: %2").arg(updateFlagsToString(UPDFLG_AsyncInstall)).arg(assets->count()), QtDebugMsg);
repo()->assets()->setFilterFlags(UPDFLG_AsyncInstall);
status()->reportProgress(tr("Asset filter applied: %1 - %2 found").arg(updateFlagToString(UPDFLG_AsyncInstall)).arg(repo()->assets()->count()), QtDebugMsg);
if (assets->count() != 1) {
reportProgress(tr("Expected 1 asset for install but none found"), QtCriticalMsg);
if (repo()->assets()->count() != 1) {
status()->reportProgress(tr("Expected 1 asset for install but none found"), QtCriticalMsg);
return false;
}
assets->getSetId(0);
repo()->assets()->getSetId(0);
QString installerPath = decompressDir % QString("/A%1/%2").arg(assets->id()).arg(QFileInfo(assets->filename()).completeBaseName());
QString installerPath = decompressDir() % QString("/A%1/%2").arg(repo()->assets()->id()).arg(QFileInfo(repo()->assets()->name()).completeBaseName());
QDirIterator it(installerPath, { OS_INSTALLER_EXTN }, QDir::Files, QDirIterator::Subdirectories);
@@ -111,14 +111,14 @@ bool UpdateCompanion::asyncInstall()
}
if (!found) {
reportProgress(tr("Installer not found in %1 using filter %2").arg(installerPath).arg(OS_INSTALLER_EXTN), QtCriticalMsg);
status()->reportProgress(tr("Installer not found in %1 using filter %2").arg(installerPath).arg(OS_INSTALLER_EXTN), QtCriticalMsg);
return false;
}
#if defined(__linux__) || defined(__linux)
QFile f(installerPath);
if (!f.setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner)) {
QMessageBox::critical(progress, CPN_STR_APP_NAME, tr("Unable to set file permissions on the AppImage"));
QMessageBox::critical(status()->progress(), CPN_STR_APP_NAME, tr("Unable to set file permissions on the AppImage"));
return false;
}
@@ -131,24 +131,24 @@ bool UpdateCompanion::asyncInstall()
if (dest.exists()) {
if (!dest.remove()) {
QMessageBox::critical(progress, CPN_STR_APP_NAME, tr("Unable to delete file %1").arg(fileName));
QMessageBox::critical(status()->progress(), CPN_STR_APP_NAME, tr("Unable to delete file %1").arg(fileName));
return false;
}
}
if (!QFile::copy(installerPath, fileName)) {
QMessageBox::critical(progress, CPN_STR_APP_NAME, tr("Unable to copy file to %1").arg(fileName));
QMessageBox::critical(status()->progress(), CPN_STR_APP_NAME, tr("Unable to copy file to %1").arg(fileName));
return false;
}
if (QMessageBox::question(progress, CPN_STR_APP_NAME, "Restart Companion?", QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
if (QMessageBox::question(status()->progress(), CPN_STR_APP_NAME, "Restart Companion?", QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
QProcess::startDetached(fileName);
qApp->exit();
}
#else
int ret = QMessageBox::question(progress, CPN_STR_APP_NAME, OS_INSTALL_QUESTION, QMessageBox::Yes | QMessageBox::No);
int ret = QMessageBox::question(status()->progress(), CPN_STR_APP_NAME, OS_INSTALL_QUESTION, QMessageBox::Yes | QMessageBox::No);
if (ret == QMessageBox::Yes) {
if (QDesktopServices::openUrl(QUrl::fromLocalFile(installerPath)))
@@ -160,23 +160,23 @@ bool UpdateCompanion::asyncInstall()
return true;
#else
QMessageBox::warning(progress, CPN_STR_APP_NAME, OS_INSTALL_MSG);
QMessageBox::warning(status()->progress, CPN_STR_APP_NAME, OS_INSTALL_MSG);
return true;
#endif // OS_SUPPORTED_INSTALLER
}
const QString UpdateCompanion::currentRelease()
const QString UpdateCompanion::releaseCurrent()
{
#if defined(VERSION_TAG)
params->currentRelease = QString("EdgeTX \"%1\" %2").arg(CODENAME).arg(VERSION_TAG);
params()->releaseCurrent = QString("EdgeTX \"%1\" %2").arg(CODENAME).arg(VERSION_TAG);
#else
params->currentRelease = QString("EdgeTX v%1.%2.%3-%4").arg(VERSION_MAJOR).arg(VERSION_MINOR).arg(VERSION_REVISION).arg(VERSION_SUFFIX);
params()->releaseCurrent = QString("EdgeTX v%1.%2.%3-%4").arg(VERSION_MAJOR).arg(VERSION_MINOR).arg(VERSION_REVISION).arg(VERSION_SUFFIX);
#endif
return params->currentRelease;
return params()->releaseCurrent;
}
const QString UpdateCompanion::currentVersion()
const QString UpdateCompanion::versionCurrent()
{
#if defined(VERSION_TAG)
return VERSION_TAG;
@@ -185,10 +185,10 @@ const QString UpdateCompanion::currentVersion()
#endif
}
const bool UpdateCompanion::isLatestRelease()
const bool UpdateCompanion::isReleaseLatest()
{
#if defined(VERSION_TAG)
return UpdateInterface::isLatestRelease();
return UpdateInterface::isReleaseLatest();
#else
return true;
#endif
+7 -7
View File
@@ -32,12 +32,12 @@ class UpdateCompanion : public UpdateInterface
explicit UpdateCompanion(QWidget * parent);
virtual ~UpdateCompanion() {}
protected:
virtual void initAssetSettings() override;
virtual bool asyncInstall() override;
virtual const QString currentRelease() override;
virtual const QString currentVersion() override;
virtual const bool isLatestRelease() override;
virtual const QString releaseCurrent() override;
virtual const QString versionCurrent() override;
virtual const bool isReleaseLatest() override;
virtual const bool isUpdateAvailable() override;
protected:
virtual bool asyncInstall() override;
virtual void assetSettingsInit() override;
};
+130
View File
@@ -0,0 +1,130 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program 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.
*/
#include "updatefactories.h"
#include "updatefirmware.h"
#include "updatecompanion.h"
#include "updatesdcard.h"
#include "updatesounds.h"
#include "updatethemes.h"
#include "updatemultiprotocol.h"
UpdateFactories::UpdateFactories(QWidget * parent) :
QWidget(parent)
{
registerUpdateFactories();
}
UpdateFactories::~UpdateFactories()
{
unregisterUpdateFactories();
}
UpdateInterface* const UpdateFactories::instance(const int id)
{
foreach (UpdateFactoryInterface * factory, registeredUpdateFactories) {
if (id == factory->id())
return factory->instance();
}
qDebug() << "Critical error - Interface not found for id:" << id;
return nullptr;
}
const bool UpdateFactories::isUpdateAvailable(QMap<QString, int> & list)
{
bool ret = false;
list.clear();
foreach (UpdateFactoryInterface * factory, registeredUpdateFactories) {
if (factory->instance()->isUpdateable() && factory->instance()->isUpdateAvailable()) {
list.insert(factory->name(), factory->id());
ret = true;
}
}
return ret;
}
void UpdateFactories::registerUpdateFactory(UpdateFactoryInterface * factory)
{
foreach (UpdateFactoryInterface * registeredFactory, registeredUpdateFactories) {
if (registeredFactory->id() == factory->id()) {
qDebug() << "Duplicate factory - id:" << factory->id() << "name:" << factory->name();
return;
}
}
registeredUpdateFactories.append(factory);
qDebug() << "Registered update factory:" << factory->name();
}
void UpdateFactories::registerUpdateFactories()
{
// Note: register in logical updating sequence
// SDCard should be first as it may clean up/erase commmon destination directory
registerUpdateFactory(new UpdateFactory<UpdateSDCard>(this));
registerUpdateFactory(new UpdateFactory<UpdateFirmware>(this));
registerUpdateFactory(new UpdateFactory<UpdateSounds>(this));
registerUpdateFactory(new UpdateFactory<UpdateThemes>(this));
registerUpdateFactory(new UpdateFactory<UpdateMultiProtocol>(this));
// Note: Companion must be last as its install requires the app to be closed and thus would interrupt the update loop
registerUpdateFactory(new UpdateFactory<UpdateCompanion>(this));
}
void UpdateFactories::resetAllEnvironments()
{
foreach (UpdateFactoryInterface * factory, registeredUpdateFactories) {
factory->instance()->resetEnvironment();
}
}
const QMap<QString, int> UpdateFactories::sortedComponentsList(bool updateableOnly)
{
QMap<QString, int> map;
foreach (UpdateFactoryInterface * factory, registeredUpdateFactories) {
if (updateableOnly && !factory->instance()->isUpdateable())
continue;
map.insert(factory->name(), factory->id());
}
return map;
}
bool UpdateFactories::updateAll(ProgressWidget * progress)
{
bool ret = false;
foreach (UpdateFactoryInterface * factory, registeredUpdateFactories) {
ret = factory->instance()->update(progress);
if (!ret)
break;
}
return ret;
}
void UpdateFactories::unregisterUpdateFactories()
{
foreach (UpdateFactoryInterface * factory, registeredUpdateFactories)
delete factory;
}
+77
View File
@@ -0,0 +1,77 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program 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.
*/
#pragma once
#include "updateinterface.h"
#include <QtCore>
class UpdateFactoryInterface
{
public:
explicit UpdateFactoryInterface() {}
virtual ~UpdateFactoryInterface() {}
virtual const int id() = 0;
virtual UpdateInterface* const instance() = 0;
virtual const QString name() = 0;
};
template <class T>
class UpdateFactory : public UpdateFactoryInterface
{
public:
explicit UpdateFactory(QWidget * parent) :
UpdateFactoryInterface(),
m_instance(new T(parent)) {}
virtual ~UpdateFactory() {}
virtual const int id() { return m_instance->id(); }
virtual UpdateInterface* const instance() { return m_instance; }
virtual const QString name() { return m_instance->name(); }
private:
UpdateInterface* const m_instance;
};
class UpdateFactories : public QWidget
{
Q_OBJECT
public:
explicit UpdateFactories(QWidget * parent = nullptr);
virtual ~UpdateFactories();
UpdateInterface* const instance(const int id);
const bool isUpdateAvailable(QMap<QString, int> & names);
void registerUpdateFactories();
void registerUpdateFactory(UpdateFactoryInterface * factory);
void resetAllEnvironments();
const QMap<QString, int> sortedComponentsList(bool updateableOnly = false);
void unregisterUpdateFactories();
bool updateAll(ProgressWidget * progress = nullptr);
private:
QVector<UpdateFactoryInterface *> registeredUpdateFactories;
};
+19 -19
View File
@@ -23,14 +23,14 @@
#include "flashfirmwaredialog.h"
UpdateFirmware::UpdateFirmware(QWidget * parent) :
UpdateInterface(parent)
UpdateInterface(parent, CID_Firmware, tr("Firmware"))
{
init(CID_Firmware, tr("Firmware"), QString(GH_REPOS_EDGETX).append("/edgetx"), "nightly");
init(QString(GH_API_REPOS_EDGETX).append("/edgetx"), "nightly");
}
void UpdateFirmware::initAssetSettings()
void UpdateFirmware::assetSettingsInit()
{
if (!isValidSettingsIndex())
if (!isSettingsIndexValid())
return;
g.component[id()].initAllAssets();
@@ -44,7 +44,7 @@ void UpdateFirmware::initAssetSettings()
cad.filter("edgetx-firmware");
cad.maxExpected(1);
cad.destSubDir("FIRMWARE");
cad.copyFilterType(UpdateParameters::UFT_Expression);
cad.copyFilterType(UpdateParameters::UFT_Pattern);
cad.copyFilter("^%FWFLAVOUR%-.*\\.bin$");
qDebug() << "Asset settings initialised";
@@ -52,32 +52,32 @@ void UpdateFirmware::initAssetSettings()
bool UpdateFirmware::asyncInstall()
{
//reportProgress(tr("Write firmware to radio: %1").arg(g.currentProfile().burnFirmware() ? tr("true") : tr("false")), QtDebugMsg);
//status->reportProgress(tr("Write firmware to radio: %1").arg(g.currentProfile().burnFirmware() ? tr("true") : tr("false")), QtDebugMsg);
if (!g.currentProfile().burnFirmware())
return true;
progressMessage(tr("Install"));
status()->progressMessage(tr("Install"));
assets->setFilterFlags(UPDFLG_AsyncInstall);
//reportProgress(tr("Asset filter applied: %1 Assets found: %2").arg(updateFlagsToString(UPDFLG_AsyncInstall)).arg(assets->count()), QtDebugMsg);
repo()->assets()->setFilterFlags(UPDFLG_AsyncInstall);
//status->reportProgress(tr("Asset filter applied: %1 Assets found: %2").arg(updateFlagsToString(UPDFLG_AsyncInstall)).arg(assets->count()), QtDebugMsg);
if (assets->count() < 1)
if (repo()->assets()->count() < 1)
return true;
if (assets->count() != params->assets.at(0).maxExpected) {
reportProgress(tr("Expected %1 asset for install but %2 found").arg(params->assets.at(0).maxExpected).arg(assets->count()), QtCriticalMsg);
if (repo()->assets()->count() != params()->assets.at(0).maxExpected) {
status()->reportProgress(tr("Expected %1 asset for install but %2 found").arg(params()->assets.at(0).maxExpected).arg(repo()->assets()->count()), QtCriticalMsg);
return false;
}
QString destPath = updateDir;
QString destPath = updateDir();
if (!assets->subDirectory().isEmpty())
destPath.append("/" % assets->subDirectory());
if (!repo()->assets()->subDirectory().isEmpty())
destPath.append("/" % repo()->assets()->subDirectory());
const UpdateParameters::AssetParams &ap = params->assets.at(0);
const UpdateParameters::AssetParams &ap = params()->assets.at(0);
QString pattern(params->buildFilterPattern(ap.copyFilterType, ap.copyFilter));
QString pattern(params()->buildFilterPattern(ap.copyFilterType, ap.copyFilter));
QRegularExpression filter(pattern, QRegularExpression::CaseInsensitiveOption);
QDirIterator it(destPath);
@@ -97,13 +97,13 @@ bool UpdateFirmware::asyncInstall()
}
if (!found) {
reportProgress(tr("Firmware not found in %1 using filter %2").arg(destPath).arg(filter.pattern()), QtCriticalMsg);
status()->reportProgress(tr("Firmware not found in %1 using filter %2").arg(destPath).arg(filter.pattern()), QtCriticalMsg);
return false;
}
g.currentProfile().fwName(destPath);
int ret = QMessageBox::question(progress, CPN_STR_APP_NAME, tr("Write the updated firmware to the radio now ?"), QMessageBox::Yes | QMessageBox::No);
int ret = QMessageBox::question(status()->progress(), CPN_STR_APP_NAME, tr("Write the updated firmware to the radio now ?"), QMessageBox::Yes | QMessageBox::No);
if (ret == QMessageBox::Yes) {
FlashFirmwareDialog *dlg = new FlashFirmwareDialog(this);
dlg->exec();
+1 -2
View File
@@ -33,7 +33,6 @@ class UpdateFirmware : public UpdateInterface
virtual ~UpdateFirmware() {}
protected:
virtual void initAssetSettings() override;
virtual bool asyncInstall() override;
virtual void assetSettingsInit() override;
};
File diff suppressed because it is too large Load Diff
+82 -257
View File
@@ -21,65 +21,15 @@
#pragma once
#include "repomodels.h"
#include "constants.h"
#include "progresswidget.h"
#include "appdata.h"
#include "updatestatus.h"
#include "updatenetwork.h"
#include "repo.h"
#include "updateparameters.h"
#include <QtCore>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
class UpdateParameters : public QObject
{
Q_OBJECT
public:
enum UpdateFilterType {
UFT_None,
UFT_Exact,
UFT_Startswith,
UFT_Endswith,
UFT_Contains,
UFT_Expression,
};
static QStringList updateFilterTypeList();
static QString updateFilterTypeToString(UpdateFilterType uft);
struct AssetParams {
int processes;
int flags;
UpdateFilterType filterType;
QString filter;
int maxExpected;
QString destSubDir;
UpdateFilterType copyFilterType;
QString copyFilter;
};
int flags;
int logLevel;
QString fwFlavour;
QString language;
int releaseChannel;
QString currentRelease;
QString updateRelease;
QString downloadDir;
bool decompressDirUseDwnld;
QString decompressDir;
QString sdDir;
bool updateDirUseSD;
QString updateDir;
QVector<AssetParams> assets;
UpdateParameters(QObject * parent);
virtual ~UpdateParameters() {}
AssetParams & addAsset();
QString buildFilterPattern(const UpdateFilterType filterType, const QString & filter);
};
#include <QWidget>
class UpdateInterface : public QWidget
{
@@ -87,13 +37,19 @@ class UpdateInterface : public QWidget
public:
enum DownloadDataType {
DDT_Binary,
DDT_Content,
DDT_SaveToFile,
DDT_MetaData,
// The CID is used as a key to the application settings therefore
// ids must be explicit so no renumbering if components removed
// new components must be added to the end of the enum
enum ComponentIdentity {
CID_Unknown = -1,
CID_SDCard = 0,
CID_Firmware = 1,
CID_Sounds = 2,
CID_Themes = 3,
CID_MultiProtocol = 4,
CID_Companion = 5,
};
Q_ENUM(DownloadDataType)
Q_ENUM(ComponentIdentity)
enum UpdateFlags {
UPDFLG_None = 0,
@@ -114,212 +70,81 @@ class UpdateInterface : public QWidget
};
Q_ENUM(UpdateFlags)
// The CID is used as a key to the application settings therefore
// ids must be explicit so no renumbering if components removed
// new components must be added to the end of the enum
enum ComponentIdentity {
CID_Unknown = -1,
CID_SDCard = 0,
CID_Firmware = 1,
CID_Sounds = 2,
CID_Themes = 3,
CID_MultiProtocol = 4,
CID_Companion = 5,
};
Q_ENUM(ComponentIdentity)
explicit UpdateInterface(QWidget * parent);
explicit UpdateInterface(QWidget * parent, ComponentIdentity id, QString name);
virtual ~UpdateInterface();
const int id() const { return (int)m_id; }
const QString name() const { return m_name; }
virtual void assetSettingsSave();
virtual const bool isReleaseLatest();
virtual const bool isUpdateAvailable();
virtual const bool isVersionLatest(const QString & current, const QString & latest);
virtual const QString releaseCurrent();
virtual const QString releaseLatest();
virtual const QString releaseUpdate();
virtual bool update(ProgressWidget * progress = nullptr);
virtual const QString versionCurrent();
const int id() const;
const bool isUpdateable() const;
const QString name() const;
UpdateParameters* const params() const;
void releaseClear();
const QStringList releaseList();
void resetEnvironment();
void setReleaseChannel(const int channel);
void setRunUpdate();
protected:
friend class UpdateFactories;
ReleasesMetaData *releases;
AssetsMetaData *assets;
UpdateParameters *params;
ProgressWidget *progress;
QString downloadDir;
QString decompressDir;
QString updateDir;
virtual bool update(ProgressWidget * progress = nullptr);
virtual void initAssetSettings() = 0;
virtual void loadAssetSettings();
virtual void saveAssetSettings();
virtual bool preparation();
virtual bool flagAssets();
virtual bool download();
virtual bool decompress();
virtual bool copyToDestination();
virtual bool copyAsset();
virtual bool housekeeping();
virtual void assetSettingsInit() = 0;
virtual void assetSettingsLoad();
virtual bool asyncInstall();
virtual bool copyAsset();
virtual bool copyToDestination();
virtual bool decompress();
virtual bool download();
virtual bool flagAssets();
virtual bool housekeeping();
virtual bool preparation();
virtual const bool isUpdateAvailable();
virtual const QString currentVersion();
virtual const QString currentRelease();
virtual const QString updateRelease();
virtual const bool isLatestRelease();
virtual const bool isLatestVersion(const QString & current, const QString & latest);
virtual QString latestRelease();
void clearRelease();
const QStringList getReleases();
void init(ComponentIdentity id, QString name, QString repo, QString nightly = "", int resultsPerPage = -1);
void setId(ComponentIdentity id) { m_id = id; }
void setName(QString name) { m_name = name; }
void setReleaseChannel(int channel);
void setReleaseId(QString val);
void setParamFolders();
UpdateParameters * getParams() { return params; }
void resetEnvironment();
void setRunUpdate() { params->flags |= UPDFLG_Update; }
bool isUpdateable();
bool repoReleasesMetaData();
bool repoReleaseAssetsMetaData();
bool downloadReleasesMetaData();
bool downloadReleaseLatestMetaData();
bool downloadReleaseMetaData(const int releaseId);
bool getReleaseJsonAsset(const QString assetName, QJsonDocument * json);
bool downloadReleaseAssetsMetaData(const int releaseId);
bool downloadAssetMetaData(const int assetId);
bool getSetAssets(const UpdateParameters::AssetParams & ap);
bool downloadAsset(int row);
bool downloadFlaggedAssets();
bool decompressAsset(int row);
bool decompressFlaggedAssets();
bool copyFiles();
bool copyFlaggedAssets();
bool copyStructure();
bool copyFiles();
bool saveReleaseSettings();
bool downloadAssetToBuffer(const int assetId);
bool downloadTextFileToBuffer(const QString & path);
void downloadFileToBuffer(const QString & url);
bool convertDownloadToJson(QJsonDocument * json);
bool decompressArchive(const QString & archivePath, const QString & destPath);
QByteArray * getDownloadBuffer() { return buffer; }
bool getRepoJsonFile(const QString filename, QJsonDocument * json);
void reportProgress(const QString & text, const int type = QtInfoMsg);
void progressMessage(const QString & text);
void criticalMsg(const QString & msg);
static QString downloadDataTypeToString(DownloadDataType val);
static QString updateFlagsToString(UpdateFlags val);
void setFlavourLanguage();
bool isValidSettingsIndex() { return m_id > -1 && m_id < MAX_COMPONENTS; }
private slots:
void onDownloadFinished(QNetworkReply * reply, DownloadDataType ddt, int subtype);
private:
QNetworkAccessManager manager;
QNetworkRequest request;
QNetworkReply *reply;
QByteArray *buffer;
QFile *file;
QUrl url;
int m_id;
QString m_name;
bool downloadSuccess;
bool decompressAsset(int row);
const QString decompressDir() const;
bool decompressFlaggedAssets();
const QString downloadDir() const;
bool downloadFlaggedAssets();
void init(QString repo, QString nightly = "", int resultsPerPage = -1);
const bool isSettingsIndexValid() const;
UpdateNetwork* const network() const;
Repo* const repo() const;
bool retrieveAssetsJsonFile(const QString & assetName, QJsonDocument * json);
bool retrieveRepoJsonFile(const QString & filename, QJsonDocument * json);
bool setFilteredAssets(const UpdateParameters::AssetParams & ap);
void setFirmwareFlavour();
void setLanguage();
void setParamFolders();
void setReleaseId(QString name);
UpdateStatus* const status() const;
const QString updateDir() const;
static QStringList versionToStringList(QString version);
static const QString updateFlagsToString(const int flags);
static const QString updateFlagToString(const int flag);
void initAppSettings();
bool setRunFolders();
private:
const ComponentIdentity m_id;
const QString m_name;
UpdateParameters* const m_params;
UpdateStatus* const m_status;
UpdateNetwork* const m_network;
Repo* const m_repo;
QString m_downloadDir;
QString m_decompressDir;
QString m_updateDir;
void appSettingsInit();
bool checkCreateDirectory(const QString & dirSetting, const UpdateFlags flag);
void downloadMetaData(const MetaDataType mdt, const QString & url);
void download(const DownloadDataType type, const int subtype, const QString & urlStr, const char * header, const QString & filePath);
void downloadBinaryToFile(const QString & url, const QString & filename);
void parseMetaData(int mdt);
};
class UpdateFactoryInterface
{
public:
explicit UpdateFactoryInterface() {}
virtual ~UpdateFactoryInterface() {}
virtual UpdateInterface * instance() = 0;
virtual const QString name() = 0;
virtual const int id() = 0;
};
template <class T>
class UpdateFactory : public UpdateFactoryInterface
{
public:
explicit UpdateFactory(QWidget * parent) :
UpdateFactoryInterface(),
m_instance(new T(parent)) {}
virtual ~UpdateFactory() {}
virtual UpdateInterface * instance() { return m_instance; }
virtual const QString name() { return m_instance->name(); }
virtual const int id() { return m_instance->id(); }
private:
UpdateInterface *m_instance;
};
class UpdateFactories : public QWidget
{
Q_OBJECT
public:
explicit UpdateFactories(QWidget * parent = nullptr);
virtual ~UpdateFactories();
const QString name(const int id);
void registerUpdateFactory(UpdateFactoryInterface * factory);
void registerUpdateFactories();
void unregisterUpdateFactories();
void saveAssetSettings(const int id);
UpdateParameters * const getParams(const int id);
void resetEnvironment(const int id);
void resetAllEnvironments();
void setRunUpdate(const int id);
const QMap<QString, int> sortedComponentsList(bool updateableOnly = false);
void clearRelease(const int id);
void setReleaseChannel(const int id, int channel);
void setReleaseId(const int id, QString val);
const QString currentRelease(const int id);
const QString updateRelease(const int id);
const bool isLatestRelease(const int id);
const QString latestRelease(const int id);
const QStringList releases(const int id);
bool getReleaseJsonAsset(const int id, const QString assetName, QJsonDocument * json);
bool getRepoJsonFile(const int id, const QString filename, QJsonDocument * json);
bool update(const int id, ProgressWidget * progress = nullptr);
bool updateAll(ProgressWidget * progress = nullptr);
const bool isUpdateAvailable(QMap<QString, int> & names);
private:
QVector<UpdateFactoryInterface *> registeredUpdateFactories;
UpdateInterface * getInstance(const int id);
bool decompressArchive(const QString & archivePath, const QString & destPath);
bool releaseSettingsSave();
bool setRunFolders();
};
@@ -22,15 +22,15 @@
#include "updatemultiprotocol.h"
UpdateMultiProtocol::UpdateMultiProtocol(QWidget * parent) :
UpdateInterface(parent)
UpdateInterface(parent, CID_MultiProtocol, tr("Multiprotocol"))
{
// GitHub REST API default ResultsPerPage = 30
init(CID_MultiProtocol, tr("Multiprotocol"), QString(GITHUB_API_REPOS).append("/pascallanger/DIY-Multiprotocol-TX-Module"), "", 50);
init(QString(GH_API_REPOS).append("/pascallanger/DIY-Multiprotocol-TX-Module"), "", 50);
}
void UpdateMultiProtocol::initAssetSettings()
void UpdateMultiProtocol::assetSettingsInit()
{
if (!isValidSettingsIndex())
if (!isSettingsIndexValid())
return;
g.component[id()].initAllAssets();
@@ -49,7 +49,7 @@ void UpdateMultiProtocol::initAssetSettings()
cad.desc("binaries");
cad.processes(UPDFLG_Common_Asset &~ UPDFLG_Decompress);
cad.flags(cad.processes() | UPDFLG_CopyFiles);
cad.filterType(UpdateParameters::UFT_Expression);
cad.filterType(UpdateParameters::UFT_Pattern);
cad.filter("^mm-stm-serial-.*\\.bin$");
cad.destSubDir("FIRMWARE");
}
+1 -2
View File
@@ -35,6 +35,5 @@ class UpdateMultiProtocol : public UpdateInterface
virtual ~UpdateMultiProtocol() {}
protected:
virtual void initAssetSettings() override;
virtual void assetSettingsInit() override;
};
+240
View File
@@ -0,0 +1,240 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program 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.
*/
#include "updatenetwork.h"
#include "constants.h"
#include <QMessageBox>
#include <QtNetwork/QNetworkProxyFactory>
constexpr char GH_API_VERSION[] {"2022-11-28"};
constexpr char GH_ACCEPT_HEADER_CONTENT[] {"application/octet-stream"};
constexpr char GH_ACCEPT_HEADER_METADATA[] {"application/vnd.github+json"};
constexpr char GH_ACCEPT_HEADER_RAW[] {"application/vnd.github.raw"};
UpdateNetwork::UpdateNetwork(QObject * parent, UpdateStatus * status) :
QObject(parent),
m_status(status),
m_reply(nullptr),
m_buffer(new QByteArray()),
m_file(nullptr)
{
QNetworkProxyFactory::setUseSystemConfiguration(true);
}
UpdateNetwork::~UpdateNetwork()
{
delete m_buffer;
if (m_reply)
delete m_reply;
if (m_file)
delete m_file;
}
// static
QString UpdateNetwork::downloadDataTypeToString(const DownloadDataType val)
{
switch ((int)val) {
case DDT_Binary:
return "Binary";
case DDT_Content:
return "Content";
case DDT_MetaData:
return "Meta Data";
default:
return CPN_STR_UNKNOWN_ITEM;
}
}
void UpdateNetwork::downloadMetaData(const QString & url, QJsonDocument * json)
{
downloadToBuffer(DDT_MetaData, url);
if (m_success)
convertBufferToJson(json);
}
void UpdateNetwork::downloadJson(const QString & url, QJsonDocument * json)
{
downloadToBuffer(DDT_Content, url);
if (m_success)
convertBufferToJson(json);
}
void UpdateNetwork::downloadToBuffer(const DownloadDataType type, const QString & url)
{
m_success = false;
download(type, url, type == DDT_MetaData ? GH_ACCEPT_HEADER_METADATA : GH_ACCEPT_HEADER_CONTENT, QString());
}
void UpdateNetwork::downloadToFile(const QString & url, const QString & filePath)
{
m_success = false;
QFileInfo fi(filePath);
m_status->progressMessage(tr("Downloading: %1").arg(fi.fileName()));
m_status->reportProgress(tr("Download: %1").arg(fi.fileName()));
if (fi.exists()) {
m_status->reportProgress(tr("File exists: %1").arg(fi.fileName()), QtDebugMsg);
if (m_status) {
int reply = QMessageBox::question(m_status->progress(), CPN_STR_APP_NAME, tr("File %1 exists. Download again?").arg(fi.fileName()),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::No);
if (reply == QMessageBox::Cancel) {
return;
}
else if (reply == QMessageBox::No) {
m_success = true;
return;
}
else {
;
}
}
}
else if (!QDir().mkpath(fi.path())) {
m_status->reportProgress(tr("Failed to create directory %1!").arg(fi.path()), QtCriticalMsg);
return;
}
download(DDT_SaveToFile, url, GH_ACCEPT_HEADER_CONTENT, fi.absoluteFilePath());
}
void UpdateNetwork::download(const DownloadDataType type, const QString & urlStr, const char * header, const QString & filePath)
{
m_status->setValue(0);
m_status->setMaximum(100);
m_buffer->clear();
m_success = false;
if (type == DDT_SaveToFile) {
m_file = new QFile(filePath);
if (!m_file->open(QIODevice::WriteOnly)) {
m_status->criticalMsg(tr("Unable to open the download file %1 for writing. Error: %2").arg(filePath).arg(m_file->errorString()));
return;
}
}
else
m_file = nullptr;
m_url.setUrl(urlStr);
if (!m_url.isValid()) {
m_status->criticalMsg(tr("Invalid URL: %1").arg(urlStr));
return;
}
else
m_status->reportProgress(tr("URL: %1").arg(urlStr), QtDebugMsg);
m_request.setUrl(m_url);
m_request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork);
m_request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
m_request.setRawHeader(QByteArray("X-GitHub-Api-Version"), GH_API_VERSION);
m_request.setRawHeader(QByteArray("Accept"), QByteArray(header));
m_reply = m_manager.get(m_request);
connect(m_reply, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), [=] (QNetworkReply::NetworkError code) {
// leave it to the finished slot to deal with error condition
m_status->criticalMsg(tr("Network error has occurred. Error code: %1").arg(code));
});
connect(m_reply, &QNetworkReply::sslErrors, [=]() {
m_status->reportProgress(tr("Ssl library version: %1").arg(QSslSocket::sslLibraryVersionString()), QtDebugMsg);
});
connect(m_reply, &QNetworkReply::readyRead, [=]() {
if (type == DDT_SaveToFile) {
m_file->write(m_reply->readAll());
}
else {
const QByteArray qba = m_reply->readAll();
m_buffer->append(qba);
}
});
connect(m_reply, &QNetworkReply::finished, [&]() {
onDownloadFinished(m_reply, type);
});
connect(m_reply, &QNetworkReply::downloadProgress, [=](const qint64 bytesRead, const qint64 totalBytes) {
m_status->setMaximum(totalBytes);
m_status->setValue(bytesRead);
});
while (!m_reply->isFinished()) {
qApp->processEvents();
}
return;
}
QByteArray * UpdateNetwork::getDownloadBuffer()
{
return m_buffer;
}
const bool UpdateNetwork::isSuccess()
{
return m_success;
}
void UpdateNetwork::onDownloadFinished(QNetworkReply * reply, DownloadDataType type)
{
m_status->setValue(m_status->maximum());
if (type == DDT_SaveToFile) {
m_file->flush();
m_file->close();
}
if (m_reply->error()) {
m_success = false;
m_status->criticalMsg(tr("Unable to download %1. Error:%2\n%3").arg(downloadDataTypeToString(type)).arg(m_reply->error()).arg(m_reply->errorString()));
if (type == DDT_SaveToFile)
m_file->remove();
}
else
m_success = true;
if (type == DDT_SaveToFile) {
delete m_file;
m_file = nullptr;
}
}
void UpdateNetwork::convertBufferToJson(QJsonDocument * json)
{
m_success = false;
QJsonParseError res;
*json = QJsonDocument::fromJson(*m_buffer, &res);
if (res.error || json->isNull()) {
m_status->criticalMsg(tr("Unable to convert downloaded metadata to json. Error:%1\n%2").arg(res.error).arg(res.errorString()));
return;
}
m_success = true;
}
+72
View File
@@ -0,0 +1,72 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program 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.
*/
#pragma once
#include "updatestatus.h"
#include <QtCore>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
#include <QJsonDocument>
class UpdateNetwork : public QObject
{
Q_OBJECT
public:
enum DownloadDataType {
DDT_Binary,
DDT_Content,
DDT_SaveToFile,
DDT_MetaData,
};
Q_ENUM(DownloadDataType)
explicit UpdateNetwork(QObject * parent, UpdateStatus * status);
virtual ~UpdateNetwork();
void convertBufferToJson(QJsonDocument * json);
void downloadMetaData(const QString & url, QJsonDocument * json);
void downloadJson(const QString & url, QJsonDocument * json);
void downloadToFile(const QString & url, const QString & filePath);
void downloadToBuffer(const DownloadDataType type, const QString & url);
void download(const DownloadDataType type, const QString & urlStr, const char * header, const QString & filePath);
QByteArray * getDownloadBuffer();
const bool isSuccess();
static QString downloadDataTypeToString(const DownloadDataType val);
private slots:
void onDownloadFinished(QNetworkReply * reply, DownloadDataType ddt);
private:
UpdateStatus *m_status;
QNetworkAccessManager m_manager;
QNetworkRequest m_request;
QNetworkReply *m_reply;
QByteArray *m_buffer;
QFile *m_file;
QUrl m_url;
bool m_success;
};
+10 -11
View File
@@ -30,29 +30,28 @@
#include <QVBoxLayout>
#include <QTimer>
UpdateOptionsDialog::UpdateOptionsDialog(QWidget * parent, UpdateFactories * factories, const int idx, const bool isRun) :
UpdateOptionsDialog::UpdateOptionsDialog(QWidget * parent, UpdateInterface * iface, const int idx, const bool isRun) :
QDialog(parent),
ui(new Ui::UpdateOptionsDialog),
factories(factories),
iface(iface),
params(iface->params()),
idx(idx),
isRun(isRun)
{
ui->setupUi(this);
if (!isRun)
factories->resetEnvironment(idx);
iface->resetEnvironment();
params = factories->getParams(idx);
setWindowTitle(tr("%1 %2").arg(iface->name()).arg(tr("Options")));
setWindowTitle(tr("%1 %2").arg(factories->name(idx)).arg(tr("Options")));
ui->txtreleaseCurrent->setText(iface->releaseCurrent());
ui->txtCurrentRelease->setText(factories->currentRelease(idx));
connect(ui->btnClearRelease, &QPushButton::clicked, [=]() {
connect(ui->btnReleaseClear, &QPushButton::clicked, [=]() {
if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear current release information. Are you sure?"),
QMessageBox::Yes |QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) {
factories->clearRelease(idx);
ui->txtCurrentRelease->setText(factories->currentRelease(idx));
iface->releaseClear();
ui->txtreleaseCurrent->setText(iface->releaseCurrent());
emit changed(idx);
}
@@ -251,7 +250,7 @@ UpdateOptionsDialog::UpdateOptionsDialog(QWidget * parent, UpdateFactories * fac
}
if (!isRun)
factories->saveAssetSettings(idx);
iface->assetSettingsSave();
QDialog::accept();
});
+3 -3
View File
@@ -42,7 +42,7 @@ class UpdateOptionsDialog : public QDialog
Q_OBJECT
public:
UpdateOptionsDialog(QWidget * parent, UpdateFactories * factories, const int idx, const bool isRun);
UpdateOptionsDialog(QWidget * parent, UpdateInterface * iface, const int idx, const bool isRun);
virtual ~UpdateOptionsDialog();
signals:
@@ -53,10 +53,10 @@ class UpdateOptionsDialog : public QDialog
private:
Ui::UpdateOptionsDialog *ui;
UpdateFactories *factories;
UpdateInterface *iface;
UpdateParameters *params;
const int idx;
const bool isRun;
UpdateParameters *params;
QVector<QComboBox *> cboAssetFilterTypes;
QVector<QLineEdit *> leAssetFilters;
+3 -3
View File
@@ -23,14 +23,14 @@
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="lblCurrentRelease">
<widget class="QLabel" name="lblreleaseCurrent">
<property name="text">
<string>Current Release:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="txtCurrentRelease">
<widget class="QLabel" name="txtreleaseCurrent">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -43,7 +43,7 @@
</widget>
</item>
<item>
<widget class="QPushButton" name="btnClearRelease">
<widget class="QPushButton" name="btnReleaseClear">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
@@ -0,0 +1,85 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program 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.
*/
#include "updateparameters.h"
#include "constants.h"
UpdateParameters::UpdateParameters(QObject * parent) :
QObject(parent)
{
logLevel = 0;
flags = 0;
decompressDirUseDwnld = true;
updateDirUseSD = true;
}
UpdateParameters::AssetParams & UpdateParameters::addAsset()
{
AssetParams ap;
ap.processes = 0;
ap.flags = 0;
ap.filterType = UFT_None;
ap.maxExpected = 0;
ap.copyFilterType = UFT_None;
assets.append(ap);
return assets[assets.size() - 1];
}
QString UpdateParameters::buildFilterPattern(const UpdateFilterType filterType, const QString & filter)
{
QString pattern(filter);
pattern.replace("%FWFLAVOUR%", fwFlavour);
pattern.replace("%LANGUAGE%", language);
switch ((int)filterType) {
case UFT_Exact:
pattern.prepend("^");
pattern.append("$");
break;
case UFT_Startswith:
pattern.prepend("^");
break;
case UFT_Endswith:
pattern.append("$");
break;
case UFT_Contains:
case UFT_Pattern:
break;
default:
pattern = "";
}
return pattern;
}
// static
QStringList UpdateParameters::updateFilterTypeList()
{
return { tr("None"), tr("Exact"), tr("Startswith"), tr("Endswith"), tr("Contains"), tr("Pattern") };
}
// static
QString UpdateParameters::updateFilterTypeToString(UpdateFilterType uft)
{
return updateFilterTypeList().value(uft, CPN_STR_UNKNOWN_ITEM);
}
+75
View File
@@ -0,0 +1,75 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program 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.
*/
#pragma once
#include <QtCore>
class UpdateParameters : public QObject
{
Q_OBJECT
public:
enum UpdateFilterType {
UFT_None,
UFT_Exact,
UFT_Startswith,
UFT_Endswith,
UFT_Contains,
UFT_Pattern,
};
static QStringList updateFilterTypeList();
static QString updateFilterTypeToString(UpdateFilterType uft);
struct AssetParams {
int processes;
int flags;
UpdateFilterType filterType;
QString filter;
int maxExpected;
QString destSubDir;
UpdateFilterType copyFilterType;
QString copyFilter;
};
int flags;
int logLevel;
QString fwFlavour;
QString language;
int releaseChannel;
QString releaseCurrent;
QString releaseUpdate;
QString downloadDir;
bool decompressDirUseDwnld;
QString decompressDir;
QString sdDir;
bool updateDirUseSD;
QString updateDir;
QVector<AssetParams> assets;
UpdateParameters(QObject * parent);
virtual ~UpdateParameters() {}
AssetParams & addAsset();
QString buildFilterPattern(const UpdateFilterType filterType, const QString & filter);
};
+4 -3
View File
@@ -20,7 +20,7 @@
*/
#include "updates.h"
#include "updateinterface.h"
#include "updatefactories.h"
#include "updatesdialog.h"
#include "progressdialog.h"
#include "progresswidget.h"
@@ -94,8 +94,9 @@ void Updates::autoUpdates(bool interactive)
while (it.hasNext()) {
it.next();
factories->setRunUpdate(it.value());
factories->updateRelease(it.value());
UpdateInterface *iface = factories->instance(it.value());
iface->setRunUpdate();
iface->releaseUpdate();
}
runUpdate();
+26 -21
View File
@@ -24,14 +24,14 @@
#include <QMessageBox>
UpdateSDCard::UpdateSDCard(QWidget * parent) :
UpdateInterface(parent)
UpdateInterface(parent, CID_SDCard, tr("SD Card"))
{
init(CID_SDCard, tr("SD Card"), QString(GH_REPOS_EDGETX).append("/edgetx-sdcard"));
init(QString(GH_API_REPOS_EDGETX).append("/edgetx-sdcard"), "Latest");
}
void UpdateSDCard::initAssetSettings()
void UpdateSDCard::assetSettingsInit()
{
if (!isValidSettingsIndex())
if (!isSettingsIndexValid())
return;
g.component[id()].initAllAssets();
@@ -49,7 +49,22 @@ void UpdateSDCard::initAssetSettings()
bool UpdateSDCard::flagAssets()
{
progressMessage(tr("Flagging assets"));
status()->progressMessage(tr("Flagging assets"));
QJsonDocument *json = new QJsonDocument();
const QString jsonFile("sdcard.json");
if (!retrieveAssetsJsonFile(jsonFile, json)) {
status()->reportProgress(tr("Unable to retrieve asset '%1' from release '%2'").arg(jsonFile).arg(repo()->releases()->name()), QtDebugMsg);
// assume older release where file not an asset
if (!retrieveRepoJsonFile(jsonFile, json)) {
status()->reportProgress(tr("Unable to retrieve file '%1' from repo '%2'").arg(jsonFile).arg(repo()->path()), QtCriticalMsg);
delete json;
return false;
}
}
/*
{
"targets": [
@@ -57,24 +72,14 @@ bool UpdateSDCard::flagAssets()
["FrSky Horus X10", "x10-", "c480x272"],
*/
const QString mappingfile = "sdcard.json";
const UpdateParameters::AssetParams & ap = params()->assets.at(0);
if (!downloadTextFileToBuffer(mappingfile)) {
return false;
}
QJsonDocument *json = new QJsonDocument();
if (!convertDownloadToJson(json)) {
return false;
}
const UpdateParameters::AssetParams & ap = params->assets.at(0);
QRegularExpression filter(params->buildFilterPattern(ap.filterType, ap.filter), QRegularExpression::CaseInsensitiveOption);
QRegularExpression filter(params()->buildFilterPattern(ap.filterType, ap.filter), QRegularExpression::CaseInsensitiveOption);
bool found = false;
QString sdimage;
//qDebug() << *json;
if (json->isObject()) {
const QJsonObject &obj = json->object();
@@ -100,7 +105,7 @@ bool UpdateSDCard::flagAssets()
delete json;
if (!found) {
reportProgress(tr("Radio flavour %1 not listed in %2").arg(params->fwFlavour).arg(mappingfile), QtCriticalMsg);
status()->reportProgress(tr("Radio flavour '%1' not listed in '%2'").arg(params()->fwFlavour).arg(jsonFile), QtCriticalMsg);
return false;
}
@@ -108,7 +113,7 @@ bool UpdateSDCard::flagAssets()
sd.filterType = UpdateParameters::UFT_Startswith;
sd.filter = sdimage;
if (!getSetAssets(sd))
if (!setFilteredAssets(sd))
return false;
return true;
+1 -2
View File
@@ -33,7 +33,6 @@ class UpdateSDCard : public UpdateInterface
virtual ~UpdateSDCard() {}
protected:
virtual void initAssetSettings() override;
virtual void assetSettingsInit() override;
virtual bool flagAssets() override;
};
+28 -13
View File
@@ -139,7 +139,7 @@ UpdatesDialog::UpdatesDialog(QWidget * parent, UpdateFactories * factories) :
QGridLayout *grid = new QGridLayout();
QLabel *h1 = new QLabel();
QCheckBox *h1 = new QCheckBox();
grid->addWidget(h1, row, col++);
QLabel *h2 = new QLabel(tr("Name"));
@@ -166,13 +166,15 @@ UpdatesDialog::UpdatesDialog(QWidget * parent, UpdateFactories * factories) :
const int i = it.value();
const QString name = it.key();
UpdateInterface *iface = factories->instance(i);
row++;
col = 0;
msg->setText(tr("Retrieving latest release information for %1").arg(name));
chkUpdate[i] = new QCheckBox();
chkUpdate[i]->setProperty("index", i);
chkUpdate[i]->setChecked(!factories->isLatestRelease(i));
chkUpdate[i]->setChecked(!iface->isReleaseLatest());
grid->addWidget(chkUpdate[i], row, col++);
grid->setAlignment(chkUpdate[i], Qt::AlignHCenter);
@@ -184,26 +186,31 @@ UpdatesDialog::UpdatesDialog(QWidget * parent, UpdateFactories * factories) :
cboRelChannel[i]->setCurrentIndex(g.component[i].releaseChannel());
grid->addWidget(cboRelChannel[i], row, col++);
lblCurrentRel[i] = new QLabel(factories->currentRelease(i));
lblCurrentRel[i] = new QLabel(iface->releaseCurrent());
grid->addWidget(lblCurrentRel[i], row, col++);
cboUpdateRel[i] = new QComboBox();
cboUpdateRel[i]->addItems(factories->releases(i));
cboUpdateRel[i]->addItems(iface->releaseList());
grid->addWidget(cboUpdateRel[i], row, col++);
connect(cboRelChannel[i], QOverload<int>::of(&QComboBox::currentIndexChanged), [=] (const int index) {
factories->setReleaseChannel(i, index);
iface->setReleaseChannel(index);
chkUpdate[i]->setChecked(!iface->isReleaseLatest());
cboUpdateRel[i]->clear();
cboUpdateRel[i]->addItems(factories->releases(i));
cboUpdateRel[i]->addItems(iface->releaseList());
});
connect(cboUpdateRel[i], QOverload<int>::of(&QComboBox::currentIndexChanged), [=] (const int index) {
chkUpdate[i]->setChecked(cboUpdateRel[i]->currentText() != lblCurrentRel[i]->text());
});
btnOptions[i] = new QPushButton(tr("Options"));
connect(btnOptions[i], &QPushButton::clicked, [=]() {
UpdateOptionsDialog *dlg = new UpdateOptionsDialog(this, factories, i, true);
UpdateOptionsDialog *dlg = new UpdateOptionsDialog(this, iface, i, true);
connect(dlg, &UpdateOptionsDialog::changed, [=](const int i) {
chkUpdate[i]->setChecked(!factories->isLatestRelease(i));
lblCurrentRel[i]->setText(factories->currentRelease(i));
cboUpdateRel[i]->setCurrentText(factories->updateRelease(i));
chkUpdate[i]->setChecked(!iface->isReleaseLatest());
lblCurrentRel[i]->setText(iface->releaseCurrent());
cboUpdateRel[i]->setCurrentText(iface->releaseUpdate());
});
dlg->exec();
dlg->deleteLater();
@@ -214,6 +221,14 @@ UpdatesDialog::UpdatesDialog(QWidget * parent, UpdateFactories * factories) :
ui->grpComponents->setLayout(grid);
connect(h1, &QCheckBox::clicked, [=] (bool checked) {
QMapIterator<QString, int> it(sortedCompList);
while (it.hasNext()) {
it.next();
chkUpdate[it.value()]->setChecked(checked);
}
});
QPushButton *btnSaveAsDefaults = new QPushButton(tr("Save as Defaults"));
ui->buttonBox->addButton(btnSaveAsDefaults, QDialogButtonBox::ActionRole);
connect(btnSaveAsDefaults, &QPushButton::clicked, [=]() {
@@ -272,8 +287,8 @@ void UpdatesDialog::accept()
if (chkUpdate[i]->isChecked()) {
cnt++;
UpdateParameters *params = factories->getParams(i);
params->updateRelease = cboUpdateRel[i]->currentText();
UpdateParameters *params = factories->instance(i)->params();
params->releaseUpdate = cboUpdateRel[i]->currentText();
params->flags |= UpdateInterface::UPDFLG_Update;
params->downloadDir = ui->leDownloadDir->text();
params->decompressDirUseDwnld = ui->chkDecompressDirUseDwnld->isChecked();
@@ -315,6 +330,6 @@ void UpdatesDialog::saveAsDefaults()
it.next();
const int i = it.value();
g.component[i].releaseChannel((ComponentData::ReleaseChannel)cboRelChannel[i]->currentIndex());
factories->saveAssetSettings(i);
factories->instance(i)->assetSettingsSave();
}
}
+1 -3
View File
@@ -21,7 +21,7 @@
#pragma once
#include "updateinterface.h"
#include "updatefactories.h"
#include "appdata.h"
#include <QDialog>
@@ -44,8 +44,6 @@ class UpdatesDialog : public QDialog
UpdatesDialog(QWidget * parent, UpdateFactories * factories);
virtual ~UpdatesDialog();
signals:
private slots:
virtual void accept();
+22 -31
View File
@@ -27,9 +27,9 @@
#include <QItemSelectionModel>
UpdateSounds::UpdateSounds(QWidget * parent) :
UpdateInterface(parent)
UpdateInterface(parent, CID_Sounds, tr("Sounds"))
{
init(CID_Sounds, tr("Sounds"), QString(GH_REPOS_EDGETX).append("/edgetx-sdcard-sounds"));
init(QString(GH_API_REPOS_EDGETX).append("/edgetx-sdcard-sounds"));
langPacks = new QStandardItemModel();
}
@@ -38,9 +38,9 @@ UpdateSounds::~UpdateSounds()
delete langPacks;
}
void UpdateSounds::initAssetSettings()
void UpdateSounds::assetSettingsInit()
{
if (!isValidSettingsIndex())
if (!isSettingsIndexValid())
return;
g.component[id()].initAllAssets();
@@ -57,29 +57,20 @@ void UpdateSounds::initAssetSettings()
bool UpdateSounds::flagAssets()
{
progressMessage(tr("Processing available sounds"));
QString pattern("sounds.json");
assets->setFilterPattern(pattern);
if (assets->count() < 0) {
reportProgress(tr("Asset not found in release '%1' using filter pattern '%2'").arg(releases->name()).arg(pattern), QtCriticalMsg);
return false;
}
else if (assets->count() > 1) {
reportProgress(tr("%1 assets found when %2 expected in release '%3' using filter pattern '%4'")
.arg(assets->count()).arg(1).arg(releases->name()).arg(pattern), QtCriticalMsg);
return false;
}
if (!downloadAssetToBuffer(assets->getSetId(0))) {
return false;
}
status()->progressMessage(tr("Processing available sounds"));
QJsonDocument *json = new QJsonDocument();
if (!convertDownloadToJson(json)) {
return false;
const QString jsonFile("sounds.json");
if (!retrieveAssetsJsonFile(jsonFile, json)) {
status()->reportProgress(tr("Unable to retrieve asset '%1' from release '%2'").arg(jsonFile).arg(repo()->releases()->name()), QtDebugMsg);
// assume older release where file not an asset
if (!retrieveRepoJsonFile(jsonFile, json)) {
status()->reportProgress(tr("Unable to retrieve file '%1' from repo '%2'").arg(jsonFile).arg(repo()->path()), QtCriticalMsg);
delete json;
return false;
}
}
/*
@@ -108,7 +99,7 @@ bool UpdateSounds::flagAssets()
lang = langVariant.split("-").at(0);
}
if (lang == params->language) {
if (lang == params()->language) {
QStandardItem * item = new QStandardItem();
if (!obj.value("language").isUndefined())
@@ -129,7 +120,7 @@ bool UpdateSounds::flagAssets()
delete json;
if (langPacks->rowCount() < 1) {
reportProgress(tr("Language %1 not listed in %2").arg(params->language).arg(pattern), QtCriticalMsg);
status()->reportProgress(tr("Language '%1' not listed in '%2'").arg(params()->language).arg(jsonFile), QtCriticalMsg);
return false;
}
@@ -141,7 +132,7 @@ bool UpdateSounds::flagAssets()
QItemSelectionModel *selItems = dlg->selectedItems();
if (!ok || !selItems->hasSelection()) {
QMessageBox::warning(progress, CPN_STR_APP_NAME, tr("No language packs have been selected. Sounds update will be skipped!"));
QMessageBox::warning(status()->progress(), CPN_STR_APP_NAME, tr("No language packs have been selected. Sounds update will be skipped!"));
dlg->deleteLater();
return true;
}
@@ -165,9 +156,9 @@ bool UpdateSounds::flagAssets()
bool UpdateSounds::flagLanguageAsset(QString lang)
{
progressMessage(tr("Flagging assets"));
status()->progressMessage(tr("Flagging assets"));
params->language = lang;
UpdateParameters::AssetParams ap = params->assets[0];
return getSetAssets(ap);
params()->language = lang;
UpdateParameters::AssetParams ap = params()->assets[0];
return setFilteredAssets(ap);
}
+1 -2
View File
@@ -35,9 +35,8 @@ class UpdateSounds : public UpdateInterface
virtual ~UpdateSounds();
protected:
virtual void initAssetSettings() override;
virtual bool flagAssets() override;
virtual void assetSettingsInit() override;
private:
enum ItemModelDataRoles {
+64
View File
@@ -0,0 +1,64 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program 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.
*/
#include "updatestatus.h"
#include <QMessageBox>
UpdateStatus::UpdateStatus(QObject * parent) :
QObject(parent),
m_progress(nullptr),
m_logLevel(QtInfoMsg)
{
}
void UpdateStatus::reportProgress(QString text, QtMsgType type)
{
if (m_logLevel == QtDebugMsg ||
(m_logLevel == QtInfoMsg && type > QtDebugMsg) ||
(type < QtInfoMsg && type >= m_logLevel)) {
if (m_progress) {
m_progress->addMessage(text, type);
if (m_logLevel == QtDebugMsg)
qDebug() << text;
}
else
qDebug() << text;
}
if (type == QtCriticalMsg || type == QtFatalMsg)
criticalMsg(text);
}
void UpdateStatus::progressMessage(QString text)
{
if (m_progress)
m_progress->setInfo(text);
else
qDebug() << text;
}
void UpdateStatus::criticalMsg(QString msg)
{
QMessageBox::critical(m_progress, tr("Update Interface"), msg);
}
+53
View File
@@ -0,0 +1,53 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program 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.
*/
#pragma once
#include "progresswidget.h"
#include <QtCore>
class UpdateStatus : public QObject
{
Q_OBJECT
public:
UpdateStatus(QObject * parent);
virtual ~UpdateStatus() {}
void setInfo(QString text) { if (m_progress) m_progress->setInfo(text); }
void setLogLevel(int logLevel) { m_logLevel = logLevel; }
void setValue(int val) { if (m_progress) m_progress->setValue(val); }
void setMaximum(int val) { if (m_progress) m_progress->setMaximum(val); }
int maximum() { return m_progress ? m_progress->maximum() : 0; }
void setProgress(ProgressWidget * progress) { m_progress = progress; }
ProgressWidget * progress() { return m_progress; }
void reportProgress(QString text, QtMsgType type = QtInfoMsg);
void progressMessage(QString text);
void criticalMsg(QString msg);
private:
ProgressWidget *m_progress;
int m_logLevel;
};
+4 -4
View File
@@ -22,14 +22,14 @@
#include "updatethemes.h"
UpdateThemes::UpdateThemes(QWidget * parent) :
UpdateInterface(parent)
UpdateInterface(parent, CID_Themes, tr("Themes"))
{
init(CID_Themes, tr("Themes"), QString(GH_REPOS_EDGETX).append("/themes"));
init(QString(GH_API_REPOS_EDGETX).append("/themes"));
}
void UpdateThemes::initAssetSettings()
void UpdateThemes::assetSettingsInit()
{
if (!isValidSettingsIndex())
if (!isSettingsIndexValid())
return;
g.component[id()].initAllAssets();
+1 -2
View File
@@ -33,6 +33,5 @@ class UpdateThemes : public UpdateInterface
virtual ~UpdateThemes() {}
protected:
virtual void initAssetSettings() override;
virtual void assetSettingsInit() override;
};