Refactor firmware and companion updates plus add extra supporting components

This commit is contained in:
elecpower
2022-09-06 09:29:41 +10:00
committed by Peter Feerick
parent b4e1ceb4f8
commit bbfaf86bc8
70 changed files with 5526 additions and 1310 deletions
+2 -16
View File
@@ -25,11 +25,6 @@ set(COMPANION_OSX_APP_BUNDLE_NAME "EdgeTX Companion ${VERSION_MAJOR}.${VERSION_M
# the binary (default homebrew path)
set(DFU_UTIL_PATH "/usr/local/bin/dfu-util")
option(ALLOW_NIGHTLY_BUILDS "Allow nightly builds download / update") # Disabled by default
if(ALLOW_NIGHTLY_BUILDS)
add_definitions(-DALLOW_NIGHTLY_BUILDS)
endif(ALLOW_NIGHTLY_BUILDS)
option(DEBUG_STORAGE_IMPORT "Turn on debug output for storage import") # Disabled by default
if(DEBUG_STORAGE_IMPORT)
add_definitions(-DDEBUG_STORAGE_IMPORT)
@@ -198,6 +193,7 @@ add_subdirectory(storage)
add_subdirectory(thirdparty/qcustomplot)
add_subdirectory(thirdparty/maxlibqt/src/widgets)
add_subdirectory(thirdparty/miniz)
add_subdirectory(updates)
set(YAML_CPP_BUILD_TESTS OFF CACHE BOOL "Enable testing" FORCE)
set(YAML_CPP_BUILD_TOOLS OFF CACHE BOOL "Enable parse tools" FORCE)
@@ -213,12 +209,9 @@ set(companion_SRCS
modelslist.cpp
labels.cpp
apppreferencesdialog.cpp
fwpreferencesdialog.cpp
burnconfigdialog.cpp
multimodelprinter.cpp
comparedialog.cpp
releasenotesdialog.cpp
releasenotesfirmwaredialog.cpp
customizesplashdialog.cpp
radiointerface.cpp
progresswidget.cpp
@@ -231,7 +224,6 @@ set(companion_SRCS
printdialog.cpp
modelprinter.cpp
logsdialog.cpp
downloaddialog.cpp
splashlibrarydialog.cpp
mainwindow.cpp
companion.cpp
@@ -245,13 +237,10 @@ set(companion_SRCS
set(companion_MOC_HDRS
apppreferencesdialog.h
fwpreferencesdialog.h
burnconfigdialog.h
comparedialog.h
printdialog.h
logsdialog.h
releasenotesdialog.h
releasenotesfirmwaredialog.h
customizesplashdialog.h
splashlibrarydialog.h
splashlabel.h
@@ -262,7 +251,6 @@ set(companion_MOC_HDRS
process_sync.h
flashfirmwaredialog.h
flasheepromdialog.h
downloaddialog.h
mdichild.h
mainwindow.h
radionotfound.h
@@ -282,9 +270,7 @@ set(companion_UIS
comparedialog.ui
logsdialog.ui
apppreferencesdialog.ui
fwpreferencesdialog.ui
burnconfigdialog.ui
downloaddialog.ui
printdialog.ui
htmldialog.ui
customizesplashdialog.ui
@@ -306,7 +292,7 @@ qt5_wrap_ui(companion_SRCS ${companion_UIS})
qt5_wrap_cpp(companion_SRCS ${companion_MOC_HDRS})
add_executable(${COMPANION_NAME} MACOSX_BUNDLE ${WIN_EXECUTABLE_TYPE} ${companion_SRCS} ${icon_RC})
target_link_libraries(${COMPANION_NAME} PRIVATE generaledit modeledit qcustomplot miniz shared ${CPN_COMMON_LIB})
target_link_libraries(${COMPANION_NAME} PRIVATE generaledit modeledit qcustomplot miniz shared updates ${CPN_COMMON_LIB})
PrintTargetReport("${COMPANION_NAME}")
+154 -23
View File
@@ -21,7 +21,6 @@
#include "apppreferencesdialog.h"
#include "ui_apppreferencesdialog.h"
#include "mainwindow.h"
#include "appdata.h"
#include "helpers.h"
#include "storage.h"
#if defined(JOYSTICKS)
@@ -30,14 +29,16 @@
#endif
#include "moduledata.h"
#include "compounditemmodels.h"
#include "updates/updateinterface.h"
#include <QAbstractItemModel>
AppPreferencesDialog::AppPreferencesDialog(QWidget * parent) :
AppPreferencesDialog::AppPreferencesDialog(QWidget * parent, UpdateFactories * factories) :
QDialog(parent),
ui(new Ui::AppPreferencesDialog),
updateLock(false),
mainWinHasDirtyChild(false)
mainWinHasDirtyChild(false),
factories(factories)
{
ui->setupUi(this);
setWindowIcon(CompanionIcon("apppreferences.png"));
@@ -73,9 +74,6 @@ void AppPreferencesDialog::accept()
{
Profile & profile = g.currentProfile();
g.autoCheckApp(ui->autoCheckCompanion->isChecked());
g.OpenTxBranch(AppData::DownloadBranchType(ui->OpenTxBranch->currentIndex()));
g.autoCheckFw(ui->autoCheckFirmware->isChecked());
g.showSplash(ui->showSplash->isChecked());
g.promptProfile(ui->chkPromptProfile->isChecked());
g.simuSW(ui->simuSW->isChecked());
@@ -101,6 +99,28 @@ void AppPreferencesDialog::accept()
g.jsSupport(false);
g.jsCtrl(0);
}
// Updates tab
g.updateCheckFreq(AppData::UpdateCheckFreq(ui->cboUpdateCheckFreq->currentIndex()));
g.downloadDir(ui->leDownloadDir->text());
g.decompressUseDwnld(ui->chkDecompressUseDwnld->isChecked());
g.decompressDir(ui->leDecompressDir->text());
g.updateUseSD(ui->chkUpdateUseSD->isChecked());
g.updateDir(ui->leUpdateDir->text());
QMapIterator<QString, int> it(factories->sortedComponentsList());
while (it.hasNext()) {
it.next();
int i = it.value();
g.component[i].checkForUpdate(chkCheckForUpdate[i]->isChecked());
g.component[i].releaseChannel((ComponentData::ReleaseChannel)cboReleaseChannel[i]->currentIndex());
}
profile.defaultInternalModule(ui->defaultInternalModuleCB->currentData().toInt());
profile.channelOrder(ui->channelorderCB->currentIndex());
profile.defaultMode(ui->stickmodeCB->currentIndex());
@@ -169,11 +189,6 @@ void AppPreferencesDialog::initSettings()
{
const Profile & profile = g.currentProfile();
// Disable these for now, until online elements are implemented
ui->autoCheckFirmware->setDisabled(true);
ui->autoCheckCompanion->setDisabled(true);
ui->OpenTxBranch->setDisabled(true);
ui->snapshotClipboardCKB->setChecked(g.snapToClpbrd());
ui->burnFirmware->setChecked(profile.burnFirmware());
ui->snapshotPath->setText(g.snapshotDir());
@@ -183,18 +198,6 @@ void AppPreferencesDialog::initSettings()
ui->snapshotPathButton->setDisabled(true);
}
#if !defined(ALLOW_NIGHTLY_BUILDS)
// remove nightly version option entirely unless it's the current one (because user might have nightly version also installed)
if (g.OpenTxBranch() != AppData::BRANCH_NIGHTLY_UNSTABLE)
ui->OpenTxBranch->removeItem(2);
// if it's already selected, add a warning message
else
ui->updatesLayout->addWidget(new QLabel("<font color='red'>" % tr("Note: Nightly builds are not available in this version, Release/RC update channel will be used.") % "</font>", this));
#endif
ui->OpenTxBranch->setCurrentIndex(g.OpenTxBranch());
ui->autoCheckCompanion->setChecked(g.autoCheckApp());
ui->autoCheckFirmware->setChecked(g.autoCheckFw());
ui->showSplash->setChecked(g.showSplash());
ui->chkPromptProfile->setChecked(g.promptProfile());
ui->historySize->setValue(g.historySize());
@@ -303,6 +306,134 @@ void AppPreferencesDialog::initSettings()
}
populateFirmwareOptions(getBaseFirmware());
// Updates tab
ui->cboUpdateCheckFreq->addItems(AppData::updateCheckFreqsList());
connect(ui->btnResetUpdatesToDefaults, &QPushButton::clicked, [=]() {
if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Reset all update settings to defaults. Are you sure?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) {
for (int i = 0; i < MAX_COMPONENTS; i++) {
if (g.component[i].existsOnDisk())
g.getComponent(i).resetAll();
}
g.resetUpdatesSettings();
QMessageBox::warning(this, CPN_STR_APP_NAME,
tr("Update settings have been reset. You must restart Companion to avoid unexpected behaviour!"));
}
});
connect(ui->chkDecompressUseDwnld, &QCheckBox::toggled, [=](const bool checked) {
if (!checked) {
ui->leDecompressDir->setText(g.decompressDir());
ui->leDecompressDir->setEnabled(true);
ui->btnDecompressSelect->setEnabled(true);
}
else {
ui->leDecompressDir->setText(g.downloadDir());
ui->leDecompressDir->setEnabled(false);
ui->btnDecompressSelect->setEnabled(false);
}
});
connect(ui->chkUpdateUseSD, &QCheckBox::toggled, [=](const bool checked) {
if (!checked) {
ui->leUpdateDir->setText(g.updateDir());
ui->leUpdateDir->setEnabled(true);
ui->btnUpdateSelect->setEnabled(true);
}
else {
ui->leUpdateDir->setText(g.currentProfile().sdPath());
ui->leUpdateDir->setEnabled(false);
ui->btnUpdateSelect->setEnabled(false);
}
});
connect(ui->btnDownloadSelect, &QPushButton::clicked, [=]() {
QString dirPath = QFileDialog::getExistingDirectory(this,tr("Select your download folder"), g.downloadDir());
if (!dirPath.isEmpty()) {
ui->leDownloadDir->setText(dirPath);
}
});
connect(ui->btnDecompressSelect, &QPushButton::clicked, [=]() {
QString dirPath = QFileDialog::getExistingDirectory(this,tr("Select your decompress folder"), g.decompressDir());
if (!dirPath.isEmpty()) {
ui->leDecompressDir->setText(dirPath);
}
});
connect(ui->btnUpdateSelect, &QPushButton::clicked, [=]() {
QString dirPath = QFileDialog::getExistingDirectory(this,tr("Select your update destination folder"), g.updateDir());
if (!dirPath.isEmpty()) {
ui->leUpdateDir->setText(dirPath);
}
});
int row = 0;
int col = 0;
QGridLayout *grid = new QGridLayout();
col++; // leave col 0 blank
QLabel *h1 = new QLabel(tr("Check"));
grid->addWidget(h1, row, col++);
QLabel *h2 = new QLabel(tr("Release channel"));
grid->addWidget(h2, row, col++);
QSpacerItem * spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum );
grid->addItem(spacer, row, col++);
QMapIterator<QString, int> it(factories->sortedComponentsList());
while (it.hasNext()) {
it.next();
int i = it.value();
row++;
col = 0;
lblName[i] = new QLabel();
grid->addWidget(lblName[i], row, col++);
chkCheckForUpdate[i] = new QCheckBox();
grid->addWidget(chkCheckForUpdate[i], row, col++);
grid->setAlignment(chkCheckForUpdate[i], Qt::AlignHCenter);
cboReleaseChannel[i] = new QComboBox();
cboReleaseChannel[i]->addItems(ComponentData::releaseChannelsList());
grid->addWidget(cboReleaseChannel[i], row, col++);
}
ui->grpComponents->setLayout(grid);
loadUpdatesTab();
}
void AppPreferencesDialog::loadUpdatesTab()
{
ui->cboUpdateCheckFreq->setCurrentIndex(g.updateCheckFreq());
ui->leDownloadDir->setText(g.downloadDir());
// trigger toggled signal by changing design value and then setting to saved value
ui->chkDecompressUseDwnld->setChecked(!ui->chkDecompressUseDwnld->isChecked());
ui->chkDecompressUseDwnld->setChecked(g.decompressUseDwnld());
// trigger toggled signal by changing design value and then setting to saved value
ui->chkUpdateUseSD->setChecked(!ui->chkUpdateUseSD->isChecked());
ui->chkUpdateUseSD->setChecked(g.updateUseSD());
QMapIterator<QString, int> it(factories->sortedComponentsList());
while (it.hasNext()) {
it.next();
int i = it.value();
lblName[i]->setText(it.key());
chkCheckForUpdate[i]->setChecked(g.component[i].checkForUpdate());
cboReleaseChannel[i]->setCurrentIndex(g.component[i].releaseChannel());
}
}
void AppPreferencesDialog::on_libraryPathButton_clicked()
+15 -6
View File
@@ -18,25 +18,28 @@
* GNU General Public License for more details.
*/
#ifndef _APPPREFERENCESDIALOG_H_
#define _APPPREFERENCESDIALOG_H_
#pragma once
#include "eeprominterface.h"
#include "appdata.h"
#include <QDialog>
#include <QCheckBox>
#include "eeprominterface.h"
#include <QComboBox>
namespace Ui {
class AppPreferencesDialog;
}
class Joystick;
class UpdateFactories;
class AppPreferencesDialog : public QDialog
{
Q_OBJECT
public:
explicit AppPreferencesDialog(QWidget * parent = 0);
explicit AppPreferencesDialog(QWidget * parent, UpdateFactories * factories);
~AppPreferencesDialog();
Joystick * joystick;
@@ -84,6 +87,12 @@ class AppPreferencesDialog : public QDialog
QMap<QString, QCheckBox *> optionsCheckBoxes;
bool updateLock;
bool mainWinHasDirtyChild;
};
#endif // _APPPREFERENCESDIALOG_H_
UpdateFactories *factories;
QLabel *lblName[MAX_COMPONENTS];
QCheckBox *chkCheckForUpdate[MAX_COMPONENTS];
QComboBox *cboReleaseChannel[MAX_COMPONENTS];
void loadUpdatesTab();
};
+349 -253
View File
@@ -36,7 +36,7 @@
</property>
</widget>
</item>
<item row="1" column="0">
<item row="0" column="0">
<widget class="QTabWidget" name="tabWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
@@ -45,7 +45,7 @@
</sizepolicy>
</property>
<property name="currentIndex">
<number>1</number>
<number>3</number>
</property>
<widget class="QWidget" name="profileTab">
<attribute name="title">
@@ -732,7 +732,7 @@ Mode 4:
<property name="spacing">
<number>6</number>
</property>
<item row="15" column="1">
<item row="14" column="1">
<widget class="QCheckBox" name="backupEnable">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
@@ -745,8 +745,8 @@ Mode 4:
</property>
</widget>
</item>
<item row="22" column="0">
<widget class="QLabel" name="ge_label">
<item row="6" column="0" rowspan="3">
<widget class="QLabel" name="label_16">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
@@ -754,36 +754,89 @@ Mode 4:
</sizepolicy>
</property>
<property name="text">
<string>Google Earth Executable</string>
<string>Startup Settings</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QComboBox" name="OpenTxBranch">
<item row="22" column="0" colspan="2">
<widget class="Line" name="line_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>Releases (stable)</string>
</property>
</item>
<item>
<property name="text">
<string>Release candidates (testing)</string>
</property>
</item>
<item>
<property name="text">
<string>Nightly builds (unstable)</string>
</property>
</item>
<property name="text">
<string>Remember</string>
</property>
</widget>
</item>
<item row="18" column="1">
<item row="23" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QCheckBox" name="opt_appDebugLog">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Keep a log of all debugging messages generated by the desktop Companion/Simulator applications. An EdgeTX developer may request this to help diagnose an issue.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Application (Companion/Simulator)</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="opt_fwTraceLog">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Keep a log of all messages generated by the radio firmware when running in Simulator. This is the same information one would also see in the Simulator &lt;span style=&quot; font-style:italic;&quot;&gt;Debug Output&lt;/span&gt; window.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Radio Firmware (in Simulator)</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="15" column="0" rowspan="2" colspan="2">
<widget class="Line" name="line_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="23" column="0">
<widget class="QLabel" name="label_12">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Debug Output Logging</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QCheckBox" name="chkPromptProfile">
<property name="text">
<string>Prompt for radio profile</string>
</property>
</widget>
</item>
<item row="11" column="0" rowspan="2" colspan="2">
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="17" column="1">
<widget class="QComboBox" name="splashincludeCB">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
@@ -803,20 +856,86 @@ Mode 4:
</item>
</widget>
</item>
<item row="12" column="0" rowspan="2" colspan="2">
<widget class="Line" name="line_2">
<item row="19" column="0" rowspan="2" colspan="2">
<widget class="Line" name="line_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="16" column="0" rowspan="2" colspan="2">
<widget class="Line" name="line_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<item row="6" column="1">
<widget class="QCheckBox" name="showSplash">
<property name="text">
<string>Show splash screen</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QLabel" name="label_8">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Action on New Model</string>
</property>
</widget>
</item>
<item row="18" column="0">
<widget class="QLabel" name="label_9">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>User Splash Screens</string>
</property>
</widget>
</item>
<item row="21" column="0">
<widget class="QLabel" name="ge_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Google Earth Executable</string>
</property>
</widget>
</item>
<item row="21" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<widget class="QLineEdit" name="ge_lineedit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="ge_pathButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Select Executable</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_10" stretch="0,1">
<property name="spacing">
@@ -853,41 +972,50 @@ Mode 4:
</item>
</layout>
</item>
<item row="6" column="1">
<widget class="QCheckBox" name="showSplash">
<property name="text">
<string>Show splash screen</string>
</property>
<property name="checked">
<bool>true</bool>
<item row="4" column="0" rowspan="2" colspan="2">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="22" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<widget class="QLineEdit" name="ge_lineedit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="ge_pathButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Select Executable</string>
</property>
</widget>
</item>
</layout>
<item row="17" column="0">
<widget class="QLabel" name="label_10">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Splash Screen Library</string>
</property>
</widget>
</item>
<item row="14" column="1">
<item row="24" column="0">
<widget class="QLabel" name="lbl_appLogsDir">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Output Logs Folder</string>
</property>
</widget>
</item>
<item row="10" column="1">
<widget class="QCheckBox" name="opt_removeBlankSlots">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This option maintains the behaviour from older OpenTx versions where empty model slots are preserved when a model is deleted or moved. &lt;/p&gt;&lt;p&gt;When this option is de-selected, the other models may be re-arranged to fill the gap left by the removed model.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Remove empty model slots when deleting models (only applies for radios w/out categories)</string>
</property>
</widget>
</item>
<item row="13" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QLineEdit" name="backupPath">
@@ -911,93 +1039,10 @@ Mode 4:
</item>
</layout>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Remember</string>
</property>
</widget>
</item>
<item row="25" column="0">
<widget class="QLabel" name="lbl_appLogsDir">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Output Logs Folder</string>
</property>
</widget>
</item>
<item row="6" column="0" rowspan="3">
<widget class="QLabel" name="label_16">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Startup Settings</string>
</property>
</widget>
</item>
<item row="11" column="1">
<widget class="QCheckBox" name="opt_removeBlankSlots">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This option maintains the behaviour from older OpenTx versions where empty model slots are preserved when a model is deleted or moved. &lt;/p&gt;&lt;p&gt;When this option is de-selected, the other models may be re-arranged to fill the gap left by the removed model.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Remove empty model slots when deleting models (only applies for radios w/out categories)</string>
</property>
</widget>
</item>
<item row="19" column="0">
<widget class="QLabel" name="label_9">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>User Splash Screens</string>
</property>
</widget>
<item row="9" column="1">
<widget class="QComboBox" name="cboNewModelAction"/>
</item>
<item row="24" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QCheckBox" name="opt_appDebugLog">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Keep a log of all debugging messages generated by the desktop Companion/Simulator applications. An EdgeTX developer may request this to help diagnose an issue.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Application (Companion/Simulator)</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="opt_fwTraceLog">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Keep a log of all messages generated by the radio firmware when running in Simulator. This is the same information one would also see in the Simulator &lt;span style=&quot; font-style:italic;&quot;&gt;Debug Output&lt;/span&gt; window.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Radio Firmware (in Simulator)</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="25" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLineEdit" name="appLogsDir">
@@ -1018,20 +1063,7 @@ Mode 4:
</item>
</layout>
</item>
<item row="10" column="0">
<widget class="QLabel" name="label_8">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Action on New Model</string>
</property>
</widget>
</item>
<item row="19" column="1">
<item row="18" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLineEdit" name="libraryPath">
@@ -1058,54 +1090,7 @@ Mode 4:
</item>
</layout>
</item>
<item row="18" column="0">
<widget class="QLabel" name="label_10">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Splash Screen Library</string>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Release channel</string>
</property>
</widget>
</item>
<item row="4" column="0" rowspan="2" colspan="2">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="24" column="0">
<widget class="QLabel" name="label_12">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Debug Output Logging</string>
</property>
</widget>
</item>
<item row="23" column="0" colspan="2">
<widget class="Line" name="line_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="14" column="0">
<item row="13" column="0">
<widget class="QLabel" name="label_17">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
@@ -1118,47 +1103,6 @@ Mode 4:
</property>
</widget>
</item>
<item row="8" column="1">
<layout class="QVBoxLayout" name="updatesLayout">
<item>
<widget class="QCheckBox" name="autoCheckFirmware">
<property name="text">
<string>Automatic check for firmware updates</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="autoCheckCompanion">
<property name="text">
<string>Automatic check for Companion updates</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="20" column="0" rowspan="2" colspan="2">
<widget class="Line" name="line_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QCheckBox" name="chkPromptProfile">
<property name="text">
<string>Prompt for radio profile</string>
</property>
</widget>
</item>
<item row="10" column="1">
<widget class="QComboBox" name="cboNewModelAction"/>
</item>
</layout>
</item>
<item>
@@ -1425,6 +1369,161 @@ Mode 4:
</item>
</layout>
</widget>
<widget class="QWidget" name="updateTab">
<attribute name="title">
<string>Update Settings</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="lblCheckFreq">
<property name="text">
<string>Check frequency</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cboUpdateCheckFreq"/>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnResetUpdatesToDefaults">
<property name="text">
<string>Reset to Defaults</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="grpFolders">
<property name="title">
<string>Folders</string>
</property>
<layout class="QGridLayout" name="gridLayout_6">
<item row="0" column="0">
<widget class="QLabel" name="lblDownloadDir">
<property name="text">
<string>Download</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="leDownloadDir"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="lblUpdateDir">
<property name="text">
<string>Update</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="chkDecompressUseDwnld">
<property name="text">
<string>Create sub-folders in Download folder</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="leDecompressDir">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lblDecompressDir">
<property name="text">
<string>Decompress</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="chkUpdateUseSD">
<property name="text">
<string>Use Radio Profile SD Structure</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="leUpdateDir">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QPushButton" name="btnUpdateSelect">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Select Folder</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="btnDecompressSelect">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Select Folder</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="btnDownloadSelect">
<property name="text">
<string>Select Folder</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="grpComponents">
<property name="title">
<string>Components</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>671</width>
<height>240</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
@@ -1488,7 +1587,4 @@ Mode 4:
</hints>
</connection>
</connections>
<buttongroups>
<buttongroup name="btnGrp_newModelActions"/>
</buttongroups>
</ui>
+20 -20
View File
@@ -405,7 +405,7 @@
<file>themes/monochrome/16/compare.png</file>
<file>themes/monochrome/16/saveas.png</file>
<file>themes/monochrome/16/apppreferences.png</file>
<file>themes/monochrome/16/fwpreferences.png</file>
<file>themes/monochrome/16/download.png</file>
<file>themes/monochrome/16/movedown.png</file>
<file>themes/monochrome/16/currentmodel.png</file>
<file>themes/monochrome/16/read_flash.png</file>
@@ -449,7 +449,7 @@
<file>themes/monochrome/24/compare.png</file>
<file>themes/monochrome/24/saveas.png</file>
<file>themes/monochrome/24/apppreferences.png</file>
<file>themes/monochrome/24/fwpreferences.png</file>
<file>themes/monochrome/24/download.png</file>
<file>themes/monochrome/24/movedown.png</file>
<file>themes/monochrome/24/currentmodel.png</file>
<file>themes/monochrome/24/read_flash.png</file>
@@ -493,7 +493,7 @@
<file>themes/monochrome/32/compare.png</file>
<file>themes/monochrome/32/saveas.png</file>
<file>themes/monochrome/32/apppreferences.png</file>
<file>themes/monochrome/32/fwpreferences.png</file>
<file>themes/monochrome/32/download.png</file>
<file>themes/monochrome/32/movedown.png</file>
<file>themes/monochrome/32/currentmodel.png</file>
<file>themes/monochrome/32/read_flash.png</file>
@@ -537,7 +537,7 @@
<file>themes/monochrome/48/compare.png</file>
<file>themes/monochrome/48/saveas.png</file>
<file>themes/monochrome/48/apppreferences.png</file>
<file>themes/monochrome/48/fwpreferences.png</file>
<file>themes/monochrome/48/download.png</file>
<file>themes/monochrome/48/movedown.png</file>
<file>themes/monochrome/48/currentmodel.png</file>
<file>themes/monochrome/48/read_flash.png</file>
@@ -581,7 +581,7 @@
<file>themes/classic/16/compare.png</file>
<file>themes/classic/16/saveas.png</file>
<file>themes/classic/16/apppreferences.png</file>
<file>themes/classic/16/fwpreferences.png</file>
<file>themes/classic/16/download.png</file>
<file>themes/classic/16/movedown.png</file>
<file>themes/classic/16/currentmodel.png</file>
<file>themes/classic/16/read_flash.png</file>
@@ -625,7 +625,7 @@
<file>themes/classic/24/compare.png</file>
<file>themes/classic/24/saveas.png</file>
<file>themes/classic/24/apppreferences.png</file>
<file>themes/classic/24/fwpreferences.png</file>
<file>themes/classic/24/download.png</file>
<file>themes/classic/24/movedown.png</file>
<file>themes/classic/24/currentmodel.png</file>
<file>themes/classic/24/read_flash.png</file>
@@ -669,7 +669,7 @@
<file>themes/classic/32/compare.png</file>
<file>themes/classic/32/saveas.png</file>
<file>themes/classic/32/apppreferences.png</file>
<file>themes/classic/32/fwpreferences.png</file>
<file>themes/classic/32/download.png</file>
<file>themes/classic/32/movedown.png</file>
<file>themes/classic/32/currentmodel.png</file>
<file>themes/classic/32/read_flash.png</file>
@@ -713,7 +713,7 @@
<file>themes/classic/48/compare.png</file>
<file>themes/classic/48/saveas.png</file>
<file>themes/classic/48/apppreferences.png</file>
<file>themes/classic/48/fwpreferences.png</file>
<file>themes/classic/48/download.png</file>
<file>themes/classic/48/movedown.png</file>
<file>themes/classic/48/currentmodel.png</file>
<file>themes/classic/48/read_flash.png</file>
@@ -757,7 +757,7 @@
<file>themes/monoblue/16/compare.png</file>
<file>themes/monoblue/16/saveas.png</file>
<file>themes/monoblue/16/apppreferences.png</file>
<file>themes/monoblue/16/fwpreferences.png</file>
<file>themes/monoblue/16/download.png</file>
<file>themes/monoblue/16/movedown.png</file>
<file>themes/monoblue/16/currentmodel.png</file>
<file>themes/monoblue/16/read_flash.png</file>
@@ -801,7 +801,7 @@
<file>themes/monoblue/24/compare.png</file>
<file>themes/monoblue/24/saveas.png</file>
<file>themes/monoblue/24/apppreferences.png</file>
<file>themes/monoblue/24/fwpreferences.png</file>
<file>themes/monoblue/24/download.png</file>
<file>themes/monoblue/24/movedown.png</file>
<file>themes/monoblue/24/currentmodel.png</file>
<file>themes/monoblue/24/read_flash.png</file>
@@ -845,7 +845,7 @@
<file>themes/monoblue/32/compare.png</file>
<file>themes/monoblue/32/saveas.png</file>
<file>themes/monoblue/32/apppreferences.png</file>
<file>themes/monoblue/32/fwpreferences.png</file>
<file>themes/monoblue/32/download.png</file>
<file>themes/monoblue/32/movedown.png</file>
<file>themes/monoblue/32/currentmodel.png</file>
<file>themes/monoblue/32/read_flash.png</file>
@@ -889,7 +889,7 @@
<file>themes/monoblue/48/compare.png</file>
<file>themes/monoblue/48/saveas.png</file>
<file>themes/monoblue/48/apppreferences.png</file>
<file>themes/monoblue/48/fwpreferences.png</file>
<file>themes/monoblue/48/download.png</file>
<file>themes/monoblue/48/movedown.png</file>
<file>themes/monoblue/48/currentmodel.png</file>
<file>themes/monoblue/48/read_flash.png</file>
@@ -933,7 +933,7 @@
<file>themes/monowhite/16/compare.png</file>
<file>themes/monowhite/16/saveas.png</file>
<file>themes/monowhite/16/apppreferences.png</file>
<file>themes/monowhite/16/fwpreferences.png</file>
<file>themes/monowhite/16/download.png</file>
<file>themes/monowhite/16/movedown.png</file>
<file>themes/monowhite/16/currentmodel.png</file>
<file>themes/monowhite/16/read_flash.png</file>
@@ -977,7 +977,7 @@
<file>themes/monowhite/24/compare.png</file>
<file>themes/monowhite/24/saveas.png</file>
<file>themes/monowhite/24/apppreferences.png</file>
<file>themes/monowhite/24/fwpreferences.png</file>
<file>themes/monowhite/24/download.png</file>
<file>themes/monowhite/24/movedown.png</file>
<file>themes/monowhite/24/currentmodel.png</file>
<file>themes/monowhite/24/read_flash.png</file>
@@ -1021,7 +1021,7 @@
<file>themes/monowhite/32/compare.png</file>
<file>themes/monowhite/32/saveas.png</file>
<file>themes/monowhite/32/apppreferences.png</file>
<file>themes/monowhite/32/fwpreferences.png</file>
<file>themes/monowhite/32/download.png</file>
<file>themes/monowhite/32/movedown.png</file>
<file>themes/monowhite/32/currentmodel.png</file>
<file>themes/monowhite/32/read_flash.png</file>
@@ -1065,7 +1065,7 @@
<file>themes/monowhite/48/compare.png</file>
<file>themes/monowhite/48/saveas.png</file>
<file>themes/monowhite/48/apppreferences.png</file>
<file>themes/monowhite/48/fwpreferences.png</file>
<file>themes/monowhite/48/download.png</file>
<file>themes/monowhite/48/movedown.png</file>
<file>themes/monowhite/48/currentmodel.png</file>
<file>themes/monowhite/48/read_flash.png</file>
@@ -1109,7 +1109,7 @@
<file>themes/yerico/16/compare.png</file>
<file>themes/yerico/16/saveas.png</file>
<file>themes/yerico/16/apppreferences.png</file>
<file>themes/yerico/16/fwpreferences.png</file>
<file>themes/yerico/16/download.png</file>
<file>themes/yerico/16/movedown.png</file>
<file>themes/yerico/16/currentmodel.png</file>
<file>themes/yerico/16/read_flash.png</file>
@@ -1153,7 +1153,7 @@
<file>themes/yerico/24/compare.png</file>
<file>themes/yerico/24/saveas.png</file>
<file>themes/yerico/24/apppreferences.png</file>
<file>themes/yerico/24/fwpreferences.png</file>
<file>themes/yerico/24/download.png</file>
<file>themes/yerico/24/movedown.png</file>
<file>themes/yerico/24/currentmodel.png</file>
<file>themes/yerico/24/read_flash.png</file>
@@ -1197,7 +1197,7 @@
<file>themes/yerico/32/compare.png</file>
<file>themes/yerico/32/saveas.png</file>
<file>themes/yerico/32/apppreferences.png</file>
<file>themes/yerico/32/fwpreferences.png</file>
<file>themes/yerico/32/download.png</file>
<file>themes/yerico/32/movedown.png</file>
<file>themes/yerico/32/currentmodel.png</file>
<file>themes/yerico/32/read_flash.png</file>
@@ -1241,7 +1241,7 @@
<file>themes/yerico/48/compare.png</file>
<file>themes/yerico/48/saveas.png</file>
<file>themes/yerico/48/apppreferences.png</file>
<file>themes/yerico/48/fwpreferences.png</file>
<file>themes/yerico/48/download.png</file>
<file>themes/yerico/48/movedown.png</file>
<file>themes/yerico/48/currentmodel.png</file>
<file>themes/yerico/48/read_flash.png</file>
+1 -4
View File
@@ -18,8 +18,7 @@
* GNU General Public License for more details.
*/
#ifndef _CONSTANTS_H_
#define _CONSTANTS_H_
#pragma once
#include <QCoreApplication>
@@ -84,5 +83,3 @@
#define CPN_STR_SW_INDICATOR_REV QCoreApplication::translate("RawSwitch", "!") // Switch reversed logic (NOT) indicator.
#define EDGETX_HOME_PAGE_URL "https://edgetx.org"
#endif // _CONSTANTS_H_
@@ -1219,27 +1219,11 @@ unsigned long OpenTxEepromInterface::loadBackup(RadioData &radioData, const uint
return errors.to_ulong();
}
QString OpenTxFirmware::getFirmwareBaseUrl()
{
return g.openTxCurrentDownloadBranchUrl() % QStringLiteral("firmware/");
}
QString OpenTxFirmware::getFirmwareUrl()
{
return getFirmwareBaseUrl() % QString("getfw.php?fw=%1.bin").arg(QString(QUrl::toPercentEncoding(id)));
}
QString OpenTxFirmware::getReleaseNotesUrl()
{
return QString("%1/downloads").arg(EDGETX_HOME_PAGE_URL);
}
QString OpenTxFirmware::getStampUrl()
{
return getFirmwareBaseUrl() % QStringLiteral("stamp-opentx.txt");
}
// Firmware registrations
// NOTE: "recognized" build options are defined in /radio/util/fwoptions.py
@@ -111,11 +111,11 @@ class OpenTxFirmware: public Firmware
virtual Firmware * getFirmwareVariant(const QString & id);
virtual QString getStampUrl();
virtual QString getStampUrl() { return ""; } // depreciated
virtual QString getReleaseNotesUrl();
virtual QString getFirmwareUrl();
virtual QString getFirmwareUrl() { return ""; } // depreciated;
virtual int getCapability(Capability);
@@ -129,7 +129,7 @@ class OpenTxFirmware: public Firmware
protected:
QString getFirmwareBaseUrl();
QString getFirmwareBaseUrl() { return ""; } // depreciated;
};
-78
View File
@@ -1,78 +0,0 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* 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 "fwpreferencesdialog.h"
#include "ui_fwpreferencesdialog.h"
#include "mainwindow.h"
#include "helpers.h"
#include "appdata.h"
FirmwarePreferencesDialog::FirmwarePreferencesDialog(QWidget * parent) :
QDialog(parent),
ui(new Ui::FirmwarePreferencesDialog)
{
ui->setupUi(this);
setWindowIcon(CompanionIcon("fwpreferences.png"));
initSettings();
MainWindow * mw = qobject_cast<MainWindow *>(this->parent());
if (mw)
connect(mw, &MainWindow::firmwareDownloadCompleted, this, &FirmwarePreferencesDialog::initSettings);
}
FirmwarePreferencesDialog::~FirmwarePreferencesDialog()
{
delete ui;
}
void FirmwarePreferencesDialog::initSettings()
{
ui->fwTypeLbl->setText(g.currentProfile().fwType());
int version = g.fwRev.get(g.currentProfile().fwType());
if (version > 0)
ui->lastRevisionLbl->setText(index2version(version));
else
ui->lastRevisionLbl->setText(tr("Unknown"));
}
void FirmwarePreferencesDialog::on_checkFWUpdates_clicked()
{
MainWindow * mw = qobject_cast<MainWindow *>(this->parent());
if (mw)
mw->checkForFirmwareUpdate();
}
void FirmwarePreferencesDialog::on_fw_dnld_clicked()
{
MainWindow * mw = qobject_cast<MainWindow *>(this->parent());
if (mw)
mw->dowloadLastFirmwareUpdate();
}
void FirmwarePreferencesDialog::on_sd_dnld_clicked()
{
QString url = g.openTxCurrentDownloadBranchUrl() % QStringLiteral("sdcard/");
QString fwType = g.profile[g.id()].fwType();
QStringList list = fwType.split("-");
QString firmware = QString("%1-%2").arg(list[0]).arg(list[1]);
if (g.boundedOpenTxBranch() != AppData::BRANCH_NIGHTLY_UNSTABLE) {
url.append(QString("%1/").arg(firmware));
}
QDesktopServices::openUrl(url);
}
-180
View File
@@ -1,180 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FirmwarePreferencesDialog</class>
<widget class="QDialog" name="FirmwarePreferencesDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>416</width>
<height>154</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Downloads</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<property name="verticalSpacing">
<number>2</number>
</property>
<item row="23" column="3">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QPushButton" name="checkFWUpdates">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Check for updates</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<family>Sans Serif</family>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Firmware</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="5" column="3">
<widget class="QPushButton" name="fw_dnld">
<property name="text">
<string>Download firmware</string>
</property>
</widget>
</item>
<item row="22" column="0" colspan="4">
<widget class="Line" name="line_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="6" column="3">
<widget class="QPushButton" name="sd_dnld">
<property name="text">
<string>Download SD contents</string>
</property>
</widget>
</item>
<item row="3" column="1" colspan="2">
<widget class="QLabel" name="lastRevisionLbl">
<property name="text">
<string notr="true">-</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Latest Download</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="3">
<widget class="QLabel" name="fwTypeLbl">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">FwType</string>
</property>
</widget>
</item>
<item row="6" column="2">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<tabstops>
<tabstop>checkFWUpdates</tabstop>
<tabstop>fw_dnld</tabstop>
<tabstop>sd_dnld</tabstop>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources>
<include location="companion.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>FirmwarePreferencesDialog</receiver>
<slot>close()</slot>
<hints>
<hint type="sourcelabel">
<x>345</x>
<y>133</y>
</hint>
<hint type="destinationlabel">
<x>207</x>
<y>76</y>
</hint>
</hints>
</connection>
</connections>
</ui>
Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 482 B

+39 -442
View File
@@ -24,13 +24,9 @@
#include "comparedialog.h"
#include "logsdialog.h"
#include "apppreferencesdialog.h"
#include "fwpreferencesdialog.h"
#include "firmwareinterface.h"
#include "downloaddialog.h"
#include "printdialog.h"
#include "version.h"
#include "releasenotesdialog.h"
#include "releasenotesfirmwaredialog.h"
#include "customizesplashdialog.h"
#include "flasheepromdialog.h"
#include "flashfirmwaredialog.h"
@@ -49,51 +45,22 @@
#include "dialogs/filesyncdialog.h"
#include "profilechooser.h"
#include "constants.h"
#include "updates/updates.h"
#include "updates/updateinterface.h"
#include <QtGui>
#include <QFileInfo>
#include <QDesktopServices>
#include <QMessageBox>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkProxyFactory>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>
// update check flags
#define CHECK_COMPANION 1
#define CHECK_FIRMWARE 2
#define INTERACTIVE_DOWNLOAD 4
#define AUTOMATIC_DOWNLOAD 8
#define EDGETX_DOWNLOADS_PAGE_URL QStringLiteral("https://edgetx.org/downloads")
#define DONATE_STR QStringLiteral("https://opencollective.com/edgetx/donate")
#ifdef Q_OS_MACOS
#define COMPANION_STAMP QStringLiteral("companion-macosx.stamp")
#define COMPANION_INSTALLER QStringLiteral("macosx/opentx-companion-%1.dmg")
#define COMPANION_FILEMASK QT_TRANSLATE_NOOP("MainWindow", "Diskimage (*.dmg)")
#define COMPANION_INSTALL_QUESTION QT_TRANSLATE_NOOP("MainWindow", "Would you like to open the disk image to install the new version?")
#elif defined(Q_OS_WIN)
#define COMPANION_STAMP QStringLiteral("companion-windows.stamp")
#define COMPANION_INSTALLER QStringLiteral("windows/companion-windows-%1.exe")
#define COMPANION_FILEMASK QT_TRANSLATE_NOOP("MainWindow", "Executable (*.exe)")
#define COMPANION_INSTALL_QUESTION QT_TRANSLATE_NOOP("MainWindow", "Would you like to launch the installer?")
#else
#define COMPANION_STAMP QStringLiteral("companion-linux.stamp")
#define COMPANION_INSTALLER "" // no automated updates for linux
#define COMPANION_FILEMASK QStringLiteral("*.*")
#define COMPANION_INSTALL_QUESTION QT_TRANSLATE_NOOP("MainWindow", "Would you like to launch the installer?")
#endif
MainWindow::MainWindow():
downloadDialog_forWait(nullptr),
checkForUpdatesState(0),
networkManager(nullptr),
updateFactories(nullptr),
windowsListActions(new QActionGroup(this))
{
// setUnifiedTitleAndToolBarOnMac(true);
this->setWindowIcon(QIcon(":/icon.png"));
QNetworkProxyFactory::setUseSystemConfiguration(true);
setAcceptDrops(true);
mdiArea = new QMdiArea(this);
@@ -179,8 +146,10 @@ MainWindow::MainWindow():
QTimer::singleShot(0, this, SLOT(autoClose()));
}
updateFactories = new UpdateFactories(this);
if (checkProfileRadioExists(g.sessionId()))
QTimer::singleShot(updateDelay, this, SLOT(doAutoUpdates()));
QTimer::singleShot(updateDelay, this, &MainWindow::autoUpdates);
else
g.warningId(g.warningId() | AppMessages::MSG_NO_RADIO_TYPE);
}
@@ -191,6 +160,11 @@ MainWindow::~MainWindow()
delete windowsListActions;
windowsListActions = nullptr;
}
if (updateFactories) {
delete updateFactories;
updateFactories = nullptr;
}
}
void MainWindow::initWindowOptions()
@@ -221,388 +195,6 @@ void MainWindow::displayWarnings()
displayWarnings(); // in case more warnings need showing
}
void MainWindow::doAutoUpdates()
{
if (g.autoCheckApp())
checkForUpdatesState |= CHECK_COMPANION;
if (g.autoCheckFw())
checkForUpdatesState |= CHECK_FIRMWARE;
checkForUpdates();
}
void MainWindow::doUpdates()
{
checkForUpdatesState = CHECK_COMPANION | CHECK_FIRMWARE | INTERACTIVE_DOWNLOAD;
checkForUpdates();
}
void MainWindow::checkForFirmwareUpdate()
{
checkForUpdatesState = CHECK_FIRMWARE | INTERACTIVE_DOWNLOAD;
checkForUpdates();
}
void MainWindow::dowloadLastFirmwareUpdate()
{
checkForUpdatesState = CHECK_FIRMWARE | AUTOMATIC_DOWNLOAD | INTERACTIVE_DOWNLOAD;
checkForUpdates();
}
QString MainWindow::getCompanionUpdateBaseUrl() const
{
return g.openTxCurrentDownloadBranchUrl() % QStringLiteral("companion/");
}
void MainWindow::checkForUpdates()
{
if (!(checkForUpdatesState & (CHECK_COMPANION | CHECK_FIRMWARE))) {
if (networkManager) {
networkManager->deleteLater();
networkManager = nullptr;
}
checkForUpdatesState = 0;
return;
}
QMessageBox::information(this, CPN_STR_APP_NAME, tr("Updates via Companion currently unavailable. Please go to the EdgeTX <a href='%1'>website</a> for installation instructions. Update the application settings to disable this message.").arg("https://github.com/EdgeTX/edgetx.github.io/wiki/EdgeTX-Installation-Guide"));
return;
if (networkManager)
disconnect(networkManager, 0, this, 0);
else
networkManager = new QNetworkAccessManager(this);
QUrl url;
if (checkForUpdatesState & CHECK_COMPANION) {
checkForUpdatesState -= CHECK_COMPANION;
if (checkForUpdatesState & INTERACTIVE_DOWNLOAD)
openUpdatesWaitDialog();
url.setUrl(getCompanionUpdateBaseUrl() % COMPANION_STAMP);
connect(networkManager, &QNetworkAccessManager::finished, this, &MainWindow::checkForCompanionUpdateFinished);
qDebug() << "Checking for Companion update " << url.url();
}
else if (checkForUpdatesState & CHECK_FIRMWARE) {
checkForUpdatesState -= CHECK_FIRMWARE;
const QString stamp = getCurrentFirmware()->getStampUrl();
if (!stamp.isEmpty()) {
if (checkForUpdatesState & INTERACTIVE_DOWNLOAD)
openUpdatesWaitDialog();
url.setUrl(stamp);
connect(networkManager, &QNetworkAccessManager::finished, this, &MainWindow::checkForFirmwareUpdateFinished);
qDebug() << "Checking for firmware update " << url.url();
}
}
if (!url.isValid()) {
checkForUpdates();
return;
}
QNetworkRequest request(url);
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork);
QNetworkReply * repl = networkManager->get(request);
if (downloadDialog_forWait)
connect(downloadDialog_forWait, &DownloadDialog::rejected, repl, &QNetworkReply::abort);
}
void MainWindow::onUpdatesError(const QString &err)
{
QMessageBox::warning(this, CPN_STR_APP_NAME, err);
checkForUpdates();
}
void MainWindow::openUpdatesWaitDialog()
{
if (!downloadDialog_forWait) {
downloadDialog_forWait = new DownloadDialog(nullptr, tr("Checking for updates"));
downloadDialog_forWait->show();
}
}
void MainWindow::closeUpdatesWaitDialog()
{
if (downloadDialog_forWait) {
downloadDialog_forWait->close();
delete downloadDialog_forWait;
downloadDialog_forWait = nullptr;
}
}
QString MainWindow::seekCodeString(const QByteArray & qba, const QString & label) const
{
int posLabel = qba.indexOf(label);
if (posLabel < 0)
return QString();
int start = qba.indexOf("\"", posLabel + label.length());
if (start < 0)
return QString();
int end = qba.indexOf("\"", start + 1);
if (end < 0)
return QString();
return qba.mid(start + 1, end - start - 1);
}
void MainWindow::checkForCompanionUpdateFinished(QNetworkReply * reply)
{
QByteArray qba = reply->readAll();
reply->deleteLater();
closeUpdatesWaitDialog();
QString version = seekCodeString(qba, "VERSION");
const QString errorString = seekCodeString(qba, "ERROR");
if (errorString == "NO_RC")
return onUpdatesError(tr("No Companion release candidates are currently being served for this version, please switch release channel"));
else if (errorString == "NO_NIGHTLY")
return onUpdatesError(tr("No nightly Companion builds are currently being served for this version, please switch release channel"));
else if (errorString == "NO_RELEASE")
return onUpdatesError(tr("No Companion release builds are currently being served for this version, please switch release channel"));
if (version.isNull())
return onUpdatesError(tr("Companion update check failed, new version information not found."));
int webVersion = version2index(version);
int ownVersion = version2index(VERSION);
if (ownVersion < webVersion) {
#if defined WIN32 || defined __APPLE__
int ret = QMessageBox::question(this, CPN_STR_APP_NAME, tr("A new version of Companion is available (version %1)<br>"
"Would you like to download it?").arg(version) ,
QMessageBox::Yes | QMessageBox::No);
if (ret == QMessageBox::Yes) {
QDir dir(g.updatesDir());
QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"), dir.absoluteFilePath(QString(COMPANION_INSTALLER).arg(version)), tr(COMPANION_FILEMASK));
if (!fileName.isEmpty()) {
g.updatesDir(QFileInfo(fileName).dir().absolutePath());
DownloadDialog * dd = new DownloadDialog(this, getCompanionUpdateBaseUrl() % QString(COMPANION_INSTALLER).arg(version), fileName);
installer_fileName = fileName;
connect(dd, SIGNAL(accepted()), this, SLOT(updateDownloaded()));
dd->exec();
}
}
#else
QMessageBox::warning(this, tr("New release available"), tr("A new release of Companion is available, please check the <a href='%1'>EdgeTX website!</a>").arg(EDGETX_DOWNLOADS_PAGE_URL));
#endif
}
else {
if (checkForUpdatesState == INTERACTIVE_DOWNLOAD) {
QMessageBox::information(this, CPN_STR_APP_NAME, tr("No updates available at this time."));
}
}
checkForUpdates();
}
void MainWindow::updateDownloaded()
{
int ret = QMessageBox::question(this, CPN_STR_APP_NAME, tr(COMPANION_INSTALL_QUESTION), QMessageBox::Yes | QMessageBox::No);
if (ret == QMessageBox::Yes) {
if (QDesktopServices::openUrl(QUrl::fromLocalFile(installer_fileName)))
QApplication::exit();
}
}
void MainWindow::firmwareDownloadAccepted()
{
QString errormsg;
QFile file(g.profile[g.id()].fwName());
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { //reading HEX TEXT file
QMessageBox::critical(this, CPN_STR_TTL_ERROR,
tr("Error opening file %1:\n%2.")
.arg(g.profile[g.id()].fwName())
.arg(file.errorString()));
return;
}
file.reset();
QTextStream inputStream(&file);
QString hline = inputStream.readLine();
if (hline.startsWith("ERROR:")) {
int errnum = hline.mid(6).toInt();
switch(errnum) {
case 1:
errormsg = tr("Not enough flash available on this board for all the selected options");
break;
case 2:
errormsg = tr("Compilation server temporary failure, try later");
break;
case 3:
errormsg = tr("Compilation server too busy, try later");
break;
case 4:
errormsg = tr("Compilation error");
break;
case 5:
errormsg = tr("Invalid firmware");
break;
case 6:
errormsg = tr("Invalid board");
break;
case 7:
errormsg = tr("Invalid language");
break;
default:
errormsg = tr("Unknown server failure, try later");
break;
}
file.close();
file.remove();
QMessageBox::critical(this, CPN_STR_TTL_ERROR, errormsg);
return;
}
file.close();
g.fwRev.set(Firmware::getCurrentVariant()->getId(), version2index(firmwareVersionString));
if (g.profile[g.id()].burnFirmware()) {
int ret = QMessageBox::question(this, CPN_STR_APP_NAME, tr("Do you want to write the firmware to the radio now ?"), QMessageBox::Yes | QMessageBox::No);
if (ret == QMessageBox::Yes) {
writeFlash(g.profile[g.id()].fwName());
}
}
emit firmwareDownloadCompleted();
}
void MainWindow::checkForFirmwareUpdateFinished(QNetworkReply * reply)
{
bool download = false;
bool ignore = false;
const QByteArray qba = reply->readAll();
reply->deleteLater();
closeUpdatesWaitDialog();
const QString versionString = seekCodeString(qba, "VERSION");
const QString dateString = seekCodeString(qba, "DATE");
const QString errorString = seekCodeString(qba, "ERROR");
const QString blockedRadios = seekCodeString(qba, "BLOCK");
long version;
if (errorString == "NO_RC")
return onUpdatesError(tr("No firmware release candidates are currently being served for this version, please switch release channel"));
else if (errorString == "NO_NIGHTLY")
return onUpdatesError(tr("No firmware nightly builds are currently being served for this version, please switch release channel"));
else if (errorString == "NO_RELEASE")
return onUpdatesError(tr("No firmware release builds are currently being served for this version, please switch release channel"));
else if (errorString == "MOVE_TO_RC") {
QMessageBox msgbox;
msgbox.setIcon(QMessageBox::Question);
msgbox.setText(tr("Release candidate builds are now available for this version, would you like to switch to using them?"));
msgbox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
msgbox.setDefaultButton(QMessageBox::Yes);
if(msgbox.exec() == QMessageBox::Yes) {
g.OpenTxBranch(AppData::DownloadBranchType(AppData::BRANCH_RC_TESTING));
return onUpdatesError(tr("Channel changed to RC, please restart the download process"));
}
}
else if (errorString == "MOVE_TO_RELEASE") {
QMessageBox msgbox;
msgbox.setIcon(QMessageBox::Question);
msgbox.setText(tr("Official release builds are now available for this version, would you like to switch to using them?"));
msgbox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
msgbox.setDefaultButton(QMessageBox::Yes);
if(msgbox.exec() == QMessageBox::Yes) {
g.OpenTxBranch(AppData::DownloadBranchType(AppData::BRANCH_RELEASE_STABLE));
return onUpdatesError(tr("Channel changed to Release, please restart the download process"));
}
}
QString variant = Firmware::getCurrentVariant()->getId();
QStringList splitId = variant.split("-");
if (blockedRadios.contains(splitId.value(1)))
return onUpdatesError(tr("This radio (%1) is not currently available in this firmware release channel").arg(getCurrentFirmware()->getName()));
if (versionString.isNull() || dateString.isNull() || (version = version2index(versionString)) <= 0)
return onUpdatesError(tr("Firmware update check failed, new version information not found or invalid."));
QString fullVersionString = QString("%1 (%2)").arg(versionString).arg(dateString);
if (checkForUpdatesState & AUTOMATIC_DOWNLOAD) {
checkForUpdatesState -= AUTOMATIC_DOWNLOAD;
download = true;
}
else {
int currentVersion = g.fwRev.get(Firmware::getCurrentVariant()->getId());
QString currentVersionString = index2version(currentVersion);
QString msgText;
if (currentVersion == 0)
msgText = tr("Firmware %1 does not seem to have ever been downloaded.\nVersion %2 is available.\nDo you want to download it now?\n\nWe recommend you view the release notes using the button below to learn about any changes that may be important to you.")
.arg(Firmware::getCurrentVariant()->getId()).arg(fullVersionString);
else if (version > currentVersion)
msgText = tr("A new version of %1 firmware is available:\n - current is %2\n - newer is %3\n\nDo you want to download it now?\n\nWe recommend you view the release notes using the button below to learn about any changes that may be important to you.")
.arg(Firmware::getCurrentVariant()->getId()).arg(currentVersionString).arg(fullVersionString);
else if (checkForUpdatesState == INTERACTIVE_DOWNLOAD)
QMessageBox::information(this, CPN_STR_APP_NAME, tr("No updates available at this time."));
if (currentVersion == 0 || version > currentVersion) {
QMessageBox msgBox;
msgBox.setWindowTitle(CPN_STR_APP_NAME);
QSpacerItem * horizontalSpacer = new QSpacerItem(500, 0, QSizePolicy::Minimum, QSizePolicy::Expanding);
QGridLayout * layout = (QGridLayout*)msgBox.layout();
layout->addItem(horizontalSpacer, layout->rowCount(), 0, 1, layout->columnCount());
QString rn = getCurrentFirmware()->getReleaseNotesUrl();
QAbstractButton *rnButton = nullptr;
msgBox.setText(msgText);
QAbstractButton *YesButton = msgBox.addButton(tr("Yes"), QMessageBox::YesRole);
msgBox.addButton(tr("No"), QMessageBox::NoRole);
if (!rn.isEmpty()) {
rnButton = msgBox.addButton(tr("Release Notes"), QMessageBox::ActionRole);
}
msgBox.setIcon(QMessageBox::Question);
msgBox.resize(0, 0);
msgBox.exec();
if (msgBox.clickedButton() == rnButton) {
QDesktopServices::openUrl(QUrl(rn));
int ret2 = QMessageBox::question(this, CPN_STR_APP_NAME, tr("Do you want to download version %1 now ?").arg(fullVersionString), QMessageBox::Yes | QMessageBox::No);
if (ret2 == QMessageBox::Yes)
download = true;
else
ignore = true;
}
else if (msgBox.clickedButton() == YesButton )
download = true;
else
ignore = true;
}
}
if (ignore) {
int res = QMessageBox::question(this, CPN_STR_APP_NAME, tr("Ignore this version %1?").arg(fullVersionString), QMessageBox::Yes | QMessageBox::No);
if (res==QMessageBox::Yes) {
g.fwRev.set(Firmware::getCurrentVariant()->getId(), version);
}
}
else if (download == true) {
firmwareVersionString = versionString;
startFirmwareDownload();
}
checkForUpdates();
}
void MainWindow::startFirmwareDownload()
{
QString url = Firmware::getCurrentVariant()->getFirmwareUrl();
qDebug() << "Downloading firmware" << url;
QString ext = url.mid(url.lastIndexOf("."));
QString defaultFilename = g.flashDir() + "/" + Firmware::getCurrentVariant()->getId();
if (g.profile[g.id()].renameFwFiles()) {
defaultFilename += "-" + firmwareVersionString;
}
defaultFilename += ext;
QString filename = QFileDialog::getSaveFileName(this, tr("Save As"), defaultFilename);
if (!filename.isEmpty()) {
g.profile[g.id()].fwName(filename);
g.flashDir(QFileInfo(filename).dir().absolutePath());
DownloadDialog * dd = new DownloadDialog(this, url, filename);
connect(dd, SIGNAL(accepted()), this, SLOT(firmwareDownloadAccepted()));
dd->exec();
}
}
void MainWindow::closeEvent(QCloseEvent *event)
{
g.mainWinGeo(saveGeometry());
@@ -643,6 +235,20 @@ void MainWindow::onLanguageChanged(QAction * act)
setLanguage(lang);
}
void MainWindow::autoUpdates()
{
Updates *upd = new Updates(this, updateFactories);
upd->autoUpdates();
delete upd;
}
void MainWindow::manualUpdates()
{
Updates *upd = new Updates(this, updateFactories);
upd->manualUpdates();
delete upd;
}
void MainWindow::setTheme(int index)
{
g.theme(index);
@@ -825,7 +431,7 @@ void MainWindow::loadProfile()
void MainWindow::appPrefs()
{
AppPreferencesDialog * dialog = new AppPreferencesDialog(this);
AppPreferencesDialog * dialog = new AppPreferencesDialog(this, updateFactories);
dialog->setMainWinHasDirtyChild(anyChildrenDirty());
connect(dialog, &AppPreferencesDialog::firmwareProfileAboutToChange, this, &MainWindow::saveAll);
connect(dialog, &AppPreferencesDialog::firmwareProfileChanged, this, &MainWindow::onCurrentProfileChanged);
@@ -833,13 +439,6 @@ void MainWindow::appPrefs()
dialog->deleteLater();
}
void MainWindow::fwPrefs()
{
FirmwarePreferencesDialog * dialog = new FirmwarePreferencesDialog(this);
dialog->exec();
dialog->deleteLater();
}
void MainWindow::sdsync()
{
// remember user-selectable options for duration of session TODO: save to settings
@@ -886,7 +485,7 @@ void MainWindow::sdsync()
void MainWindow::changelog()
{
QString link = "https://edgetx.org";
QString link = "https://github.com/EdgeTX/edgetx/releases";
QDesktopServices::openUrl(QUrl(link));
}
@@ -1191,14 +790,14 @@ void MainWindow::retranslateUi(bool showMsg)
trAct(saveAsAct, tr("Save As..."), tr("Save Models and Settings file"));
trAct(closeAct, tr("Close"), tr("Close Models and Settings file"));
trAct(exitAct, tr("Exit"), tr("Exit the application"));
trAct(aboutAct, tr("About..."), tr("Show the application's About box"));
trAct(aboutAct, tr("About Companion..."), tr("Show the application's About box"));
trAct(recentFilesAct, tr("Recent Files"), tr("List of recently used files"));
trAct(profilesMenuAct, tr("Radio Profiles"), tr("Create or Select Radio Profiles"));
trAct(logsAct, tr("View Log File..."), tr("Open and view log file"));
trAct(appPrefsAct, tr("Settings..."), tr("Edit Settings"));
trAct(fwPrefsAct, tr("Download..."), tr("Download firmware and voice files"));
trAct(checkForUpdatesAct, tr("Check for Updates..."), tr("Check EdgeTX and Companion updates"));
trAct(updatesAct, tr("Updates..."), tr("Update EdgeTX and supporting resources"));
trAct(checkForUpdatesAct, tr("Check for updates..."), tr("Check for updates to EdgeTX and supporting resources"));
trAct(changelogAct, tr("Release notes..."), tr("Show release notes"));
trAct(compareAct, tr("Compare Models..."), tr("Compare models"));
trAct(editSplashAct, tr("Edit Radio Splash Image..."), tr("Edit the splash image of your Radio"));
@@ -1256,7 +855,7 @@ void MainWindow::createActions()
logsAct = addAct("logs.png", SLOT(logFile()), tr("Ctrl+Alt+L"));
appPrefsAct = addAct("apppreferences.png", SLOT(appPrefs()), QKeySequence::Preferences);
fwPrefsAct = addAct("fwpreferences.png", SLOT(fwPrefs()), tr("Ctrl+Alt+D"));
updatesAct = addAct("download.png", SLOT(manualUpdates()), tr("Ctrl+Alt+D"));
compareAct = addAct("compare.png", SLOT(compare()), tr("Ctrl+Alt+R"));
sdsyncAct = addAct("sdsync.png", SLOT(sdsync()));
@@ -1283,7 +882,7 @@ void MainWindow::createActions()
actCascadeWindows = addAct("", SLOT(cascadeSubWindows()), 0, mdiArea);
actCloseAllWindows = addAct("", SLOT(closeAllSubWindows()), 0, mdiArea);
checkForUpdatesAct = addAct("update.png", SLOT(doUpdates()));
checkForUpdatesAct = addAct("update.png", SLOT(autoUpdates()));
aboutAct = addAct("information.png", SLOT(about()));
openDocURLAct = addAct("changelog.png", SLOT(openDocURL()));
changelogAct = addAct("changelog.png", SLOT(changelog()));
@@ -1301,7 +900,6 @@ void MainWindow::createActions()
actTabbedWindows->setCheckable(true);
compareAct->setEnabled(false);
fwPrefsAct->setEnabled(false);
}
void MainWindow::createMenus()
@@ -1315,7 +913,7 @@ void MainWindow::createMenus()
fileMenu->addAction(recentFilesAct);
fileMenu->addSeparator();
fileMenu->addAction(logsAct);
fileMenu->addAction(fwPrefsAct);
fileMenu->addAction(updatesAct);
fileMenu->addAction(compareAct);
fileMenu->addAction(sdsyncAct);
fileMenu->addSeparator();
@@ -1374,14 +972,13 @@ void MainWindow::createMenus()
windowMenu->addSeparator();
helpMenu = menuBar()->addMenu("");
helpMenu->addSeparator();
helpMenu->addAction(checkForUpdatesAct);
helpMenu->addSeparator();
helpMenu->addAction(aboutAct);
helpMenu->addAction(openDocURLAct);
helpMenu->addSeparator();
//helpMenu->addSeparator();
//helpMenu->addAction(openDocURLAct);
//helpMenu->addSeparator();
helpMenu->addAction(changelogAct);
helpMenu->addSeparator();
//helpMenu->addSeparator();
helpMenu->addAction(aboutAct);
recentFilesMenu = new QMenu(this);
recentFilesMenu->setToolTipsVisible(true);
@@ -1419,7 +1016,7 @@ void MainWindow::createToolBars()
fileToolBar->addAction(closeAct);
fileToolBar->addSeparator();
fileToolBar->addAction(logsAct);
fileToolBar->addAction(fwPrefsAct);
fileToolBar->addAction(updatesAct);
fileToolBar->addSeparator();
fileToolBar->addAction(appPrefsAct);
fileToolBar->addAction(profilesMenuAct);
+7 -32
View File
@@ -18,17 +18,17 @@
* GNU General Public License for more details.
*/
#ifndef _MAINWINDOW_H_
#define _MAINWINDOW_H_
#pragma once
#include <QtWidgets>
#include <QDateTime>
#include "downloaddialog.h"
#include "eeprominterface.h"
#define SPLASH_TIME 5
class MdiChild;
class UpdateFactories;
QT_BEGIN_NAMESPACE
class QAction;
class QMenu;
@@ -47,18 +47,11 @@ class MainWindow : public QMainWindow
~MainWindow();
signals:
void firmwareDownloadCompleted();
void firmwareChanged();
void startSync();
void onInternalModuleChanged();
protected:
QString getCompanionUpdateBaseUrl() const;
QString seekCodeString(const QByteArray & qba, const QString & label) const;
protected slots:
void dowloadLastFirmwareUpdate();
void startFirmwareDownload();
virtual void closeEvent(QCloseEvent *event);
virtual void changeEvent(QEvent *e);
virtual void dragEnterEvent(QDragEnterEvent *event);
@@ -83,17 +76,7 @@ class MainWindow : public QMainWindow
void onSubwindowModified();
void onCurrentProfileChanged();
void checkForUpdates();
void checkForFirmwareUpdate();
void checkForCompanionUpdateFinished(QNetworkReply * reply);
void checkForFirmwareUpdateFinished(QNetworkReply * reply);
void displayWarnings();
void doAutoUpdates();
void doUpdates();
void updateDownloaded();
void firmwareDownloadAccepted();
void newFile();
void openFile();
void save();
@@ -118,7 +101,6 @@ class MainWindow : public QMainWindow
void about();
void compare();
void appPrefs();
void fwPrefs();
void updateMenus();
void createProfile();
void copyProfile();
@@ -128,10 +110,9 @@ class MainWindow : public QMainWindow
void importSettings();
void autoClose();
void chooseProfile();
void manualUpdates();
void autoUpdates();
void openUpdatesWaitDialog();
void closeUpdatesWaitDialog();
void onUpdatesError(const QString & err);
void openFile(const QString & fileName, bool updateLastUsedDir = false);
private:
@@ -165,11 +146,7 @@ class MainWindow : public QMainWindow
QMdiArea *mdiArea;
QString installer_fileName;
DownloadDialog * downloadDialog_forWait;
unsigned int checkForUpdatesState;
QString firmwareVersionString;
QNetworkAccessManager *networkManager;
UpdateFactories *updateFactories;
QVector<QAction *> recentFileActs;
QVector<QAction *> profileActs;
@@ -198,7 +175,7 @@ class MainWindow : public QMainWindow
QAction *recentFilesAct;
QAction *exitAct;
QAction *appPrefsAct;
QAction *fwPrefsAct;
QAction *updatesAct;
QAction *checkForUpdatesAct;
QAction *sdsyncAct;
QAction *changelogAct;
@@ -226,5 +203,3 @@ class MainWindow : public QMainWindow
QAction *actCascadeWindows;
QAction *actCloseAllWindows;
};
#endif // _MAINWINDOW_H_
+1 -8
View File
@@ -18,15 +18,10 @@
* GNU General Public License for more details.
*/
#ifndef _PROGRESSWIDGET_H_
#define _PROGRESSWIDGET_H_
#pragma once
#include <QWidget>
#if (QT_VERSION < QT_VERSION_CHECK(5, 5, 0))
#define QtInfoMsg QtMsgType(4)
#endif
namespace Ui {
class ProgressWidget;
}
@@ -71,5 +66,3 @@ class ProgressWidget : public QWidget
bool m_forceOpen;
bool m_hasDetails;
};
#endif // _PROGRESSWIDGET_H_
+10
View File
@@ -49,6 +49,11 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string notr="true"/>
</property>
@@ -86,6 +91,11 @@
<height>0</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="value">
<number>0</number>
</property>
@@ -1,52 +0,0 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* 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 "releasenotesfirmwaredialog.h"
#include "ui_htmldialog.h"
#include "helpers.h"
ReleaseNotesFirmwareDialog::ReleaseNotesFirmwareDialog(QWidget * parent, const QString & rnurl):
QDialog(parent),
ui(new Ui::HtmlDialog)
{
ui->setupUi(this);
setWindowTitle(tr("OpenTX Release Notes"));
setWindowIcon(CompanionIcon("changelog.png"));
manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(replyFinished(QNetworkReply *)));
QUrl url(rnurl);
QNetworkRequest request(url);
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork);
manager->get(request);
}
ReleaseNotesFirmwareDialog::~ReleaseNotesFirmwareDialog()
{
delete ui;
}
void ReleaseNotesFirmwareDialog::replyFinished(QNetworkReply * reply)
{
ui->textEditor->setHtml(reply->readAll());
ui->textEditor->setOpenExternalLinks(true);
reply->deleteLater();
}
+1
View File
@@ -16,6 +16,7 @@ set(storage_NAMES
otx
yaml
crc
minizinterface
)
set(storage_SRCS
+81 -1
View File
@@ -289,6 +289,42 @@ void Profile::resetFwVariables()
}
// ** ComponentData class********************
ComponentData::ComponentData() : CompStoreObj(), index(-1)
{
qRegisterMetaTypeStreamOperators<ComponentData::ReleaseChannel>("ComponentData::ReleaseChannel");
CompStoreObj::addObjectMapping(propertyGroup(), this);
}
// The default copy operator can not be used since the index variable would be destroyed
ComponentData & ComponentData::operator= (const ComponentData & rhs)
{
for (int i = metaObject()->propertyOffset(), e = metaObject()->propertyCount(); i < e; ++i) {
const QMetaProperty & prop = metaObject()->property(i);
if (!prop.isValid() || !prop.isWritable()) {
qWarning() << "Could not copy property" << QString(prop.name()) << "isValid:" << prop.isValid() << "isWritable:" << prop.isWritable();
continue;
}
prop.write(this, prop.read(&rhs));
}
return *this;
}
bool ComponentData::existsOnDisk()
{
return (m_settings.contains(settingsPath() % "name"));
}
void ComponentData::clearRelease()
{
release(CompStoreObj::propertyDefaultValue(this, "release").toString());
id(CompStoreObj::propertyDefaultValue(this, "id").toInt());
prerelease(CompStoreObj::propertyDefaultValue(this, "prerelease").toBool());
date(CompStoreObj::propertyDefaultValue(this, "date").toString());
version(CompStoreObj::propertyDefaultValue(this, "version").toString());
}
// ** AppData class********************
AppData::AppData() :
@@ -297,8 +333,8 @@ AppData::AppData() :
{
QMetaType::registerComparators<SimulatorOptions>();
qRegisterMetaTypeStreamOperators<SimulatorOptions>("SimulatorOptions");
qRegisterMetaTypeStreamOperators<AppData::DownloadBranchType>("AppData::DownloadBranchType");
qRegisterMetaTypeStreamOperators<AppData::NewModelAction>("AppData::NewModelAction");
qRegisterMetaTypeStreamOperators<AppData::UpdateCheckFreq>("AppData::UpdateCheckFreq");
CompStoreObj::addObjectMapping(propertyGroup(), this);
@@ -315,6 +351,10 @@ AppData::AppData() :
// Configure the joysticks
for (int i=0; i<MAX_JOYSTICKS; i++)
joystick[i].setIndex(i);
// Configure the updates
for (int i=0; i<MAX_COMPONENTS; i++)
component[i].setIndex(i);
}
static QString fmtHex(quint32 num)
@@ -346,6 +386,9 @@ void AppData::initAll()
// Initialize the joysticks
for (int i=0; i<MAX_JOYSTICKS; i++)
joystick[i].init();
// Initialize the updatess
for (int i=0; i<MAX_COMPONENTS; i++)
component[i].init();
}
void AppData::resetAllSettings()
@@ -356,6 +399,8 @@ void AppData::resetAllSettings()
profile[i].resetAll();
for (int i=0; i<MAX_JOYSTICKS; i++)
joystick[i].resetAll();
for (int i=0; i<MAX_COMPONENTS; i++)
component[i].resetAll();
firstUse = true;
}
@@ -366,6 +411,8 @@ void AppData::storeAllSettings()
profile[i].storeAll();
for (int i=0; i<MAX_JOYSTICKS; i++)
joystick[i].storeAll();
for (int i=0; i<MAX_COMPONENTS; i++)
component[i].storeAll();
}
bool AppData::hasCurrentSettings() const
@@ -406,6 +453,15 @@ QMap<int, QString> AppData::getActiveProfiles() const
return active;
}
int AppData::getComponentIndex(QString name) const
{
for (int i=0; i<MAX_COMPONENTS; i++) {
if (g.component[i].existsOnDisk() && g.component[i].name() == name)
return i;
}
return -1;
}
void AppData::convertSettings(QSettings & settings)
{
quint32 savedVer = settings.value(SETTINGS_VERSION_KEY, 0).toUInt();
@@ -580,3 +636,27 @@ bool AppData::exportSettingsToFile(const QString & expFile, QString & resultMsg)
resultMsg.append(tr("for unknown reasons."));
return false;
}
ComponentData & AppData::getComponent(int index)
{
if (index > -1 && index < MAX_COMPONENTS)
return component[index];
return component[0];
}
const ComponentData & AppData::getComponent(int index) const
{
if (index > -1 && index < MAX_COMPONENTS)
return component[index];
return component[0];
}
void AppData::resetUpdatesSettings()
{
g.updateCheckFreqReset();
g.downloadDirReset();
g.decompressDirReset();
g.decompressUseDwnldReset();
g.updateDirReset();
g.updateUseSDReset();
}
+103 -62
View File
@@ -25,8 +25,7 @@
@{
*/
#ifndef _APPDATA_H_
#define _APPDATA_H_
#pragma once
#include "constants.h"
#include "macros.h"
@@ -59,14 +58,9 @@
#define CPN_SETTINGS_INI_FILE QString(PRODUCT % " " % QCoreApplication::translate("Companion", "settings") % " %1.ini")
#define CPN_SETTINGS_INI_PATH QString(CPN_SETTINGS_BACKUP_DIR % "/" % CPN_SETTINGS_INI_FILE)
#define CPN_URL_DOWNLOAD "https://downloads.edgetx.org/"
#define CPN_URL_DOWNLOAD_CUR_VERS CPN_URL_DOWNLOAD QT_STRINGIFY(VERSION_MAJOR) "." QT_STRINGIFY(VERSION_MINOR) "/"
#define CPN_URL_DOWNLOAD_CUR_REL CPN_URL_DOWNLOAD_CUR_VERS "release/"
#define CPN_URL_DOWNLOAD_CUR_RC CPN_URL_DOWNLOAD_CUR_VERS "rc/"
#define CPN_URL_DOWNLOAD_CUR_UNST CPN_URL_DOWNLOAD_CUR_VERS "nightlies/"
#define MAX_PROFILES 20
#define MAX_JOYSTICKS 8
#define MAX_COMPONENTS 10
// It important that these function names are consistent everywhere.
#define PROP_FSIG_INIT_IMPL _init()
@@ -427,6 +421,51 @@ class Profile: public CompStoreObj
}
};
//! \brief ComponentData class stores properties related to each updateable component.
class ComponentData: public CompStoreObj
{
Q_OBJECT
public:
ComponentData & operator=(const ComponentData & rhs);
enum ReleaseChannel {
RELEASE_CHANNEL_STABLE,
RELEASE_CHANNEL_PRERELEASE,
RELEASE_CHANNEL_NIGHTLY
};
Q_ENUM(ReleaseChannel)
void clearRelease();
static QStringList releaseChannelsList() { return { tr("Releases"), tr("Pre-release"), tr("Nightly") } ; }
inline ReleaseChannel boundedReleaseChannel() const {
return qBound(RELEASE_CHANNEL_STABLE, releaseChannel(), RELEASE_CHANNEL_NIGHTLY);
}
public slots:
bool existsOnDisk();
protected:
explicit ComponentData();
void setIndex(int idx) { index = idx; }
inline QString propertyGroup() const override { return QStringLiteral("Components"); }
inline QString settingsPath() const override { return QString("%1/component%2/").arg(propertyGroup()).arg(index); }
friend class AppData;
private:
PROPERTYSTR (name)
PROPERTY (bool, checkForUpdate, false)
PROPERTY (ReleaseChannel, releaseChannel, RELEASE_CHANNEL_STABLE)
PROPERTYSTRD( release, "unknown")
PROPERTY (int, id, 0)
PROPERTY (bool, prerelease, false)
PROPERTYSTRD( version, "0")
PROPERTYSTRD( date, "")
int index;
CREATE_ENUM_FRIEND_STREAM_OPS(ComponentData::ReleaseChannel)
};
/*!
\brief The AppData class acts as the main interface for all application settings.
@@ -441,13 +480,6 @@ class AppData: public CompStoreObj
{
Q_OBJECT
public:
enum DownloadBranchType {
BRANCH_RELEASE_STABLE,
BRANCH_RC_TESTING,
BRANCH_NIGHTLY_UNSTABLE
};
Q_ENUM(DownloadBranchType)
enum NewModelAction {
MODEL_ACT_NONE,
MODEL_ACT_WIZARD,
@@ -457,13 +489,24 @@ class AppData: public CompStoreObj
};
Q_ENUM(NewModelAction)
enum UpdateCheckFreq {
UPDATE_CHECK_MANUAL,
UPDATE_CHECK_STARTUP,
UPDATE_CHECK_DAILY,
UPDATE_CHECK_WEEKLY,
UPDATE_CHECK_MONTHLY
};
Q_ENUM(UpdateCheckFreq)
static QStringList newModelActionsList() { return { tr("None"), tr("Wizard"), tr("Editor"), tr("Template"), tr("Prompt") } ; }
static QStringList updateCheckFreqsList() { return { tr("Manually"), tr("Startup"), tr("Daily"), tr("Weekly"), tr("Monthly") } ; }
explicit AppData();
void init() override;
void initAll();
void resetAllSettings();
void storeAllSettings();
void resetUpdatesSettings();
inline bool isFirstUse() const { return firstUse; }
inline QString previousVersion() const { return upgradeFromVersion; }
@@ -487,6 +530,13 @@ class AppData: public CompStoreObj
//! List of all active profiles mapped by index.
QMap<int, QString> getActiveProfiles() const;
//! Get a modifiable (non-const) index to ComponentData for \a name. Returns -1 if \a name not found.
int getComponentIndex(QString name) const;
//! Get a modifiable (non-const) reference to the ComponentData at \a index. Returns component[0] if \a index is invalid.
ComponentData & getComponent(int index);
//! Get a non-modifiable (const) reference to the ComponentData at \a index. Returns component[0] if \a index is invalid.
const ComponentData & getComponent(int index) const;
//! Find the set of settings from the last previous version installed, if any. Used at virgin startup to offer import option.
bool findPreviousVersionSettings(QString * version) const;
//! Converts any old/refactored settings to new ones and removes anything stale if \a removeUnused is true. This is only important when
@@ -500,25 +550,10 @@ class AppData: public CompStoreObj
bool exportSettings(QSettings * toSettings, bool clearDestination = true);
bool exportSettingsToFile(const QString & expFile, QString & resultMsg);
inline DownloadBranchType boundedOpenTxBranch() const {
#if defined(ALLOW_NIGHTLY_BUILDS)
return qBound(BRANCH_RELEASE_STABLE, OpenTxBranch(), BRANCH_NIGHTLY_UNSTABLE);
#else
return qBound(BRANCH_RELEASE_STABLE, OpenTxBranch(), BRANCH_RC_TESTING);
#endif
}
inline QString openTxCurrentDownloadBranchUrl() const { return openTxDownloadBranchUrl(boundedOpenTxBranch()); }
static const QString openTxDownloadBranchUrl(DownloadBranchType type)
{
static const QStringList urlList({ CPN_URL_DOWNLOAD_CUR_REL, CPN_URL_DOWNLOAD_CUR_RC, CPN_URL_DOWNLOAD_CUR_UNST });
return urlList.value(type, CPN_URL_DOWNLOAD_CUR_VERS);
}
Profile profile[MAX_PROFILES];
JStickData joystick[MAX_JOYSTICKS];
FwRevision fwRev;
ComponentData component[MAX_COMPONENTS];
signals:
void currentProfileChanged();
@@ -541,47 +576,56 @@ class AppData: public CompStoreObj
PROPERTYQBA (mdiWinState)
PROPERTYQBA (compareWinGeo)
PROPERTYSTR3(armMcu, "arm_mcu", QStringLiteral("at91sam3s4-9x"))
PROPERTYSTR3(armMcu, "arm_mcu", QStringLiteral("at91sam3s4-9x"))
PROPERTYSTR2(avrArguments, "avr_arguments")
PROPERTYSTR2(avrPort, "avr_port")
PROPERTYSTR2(avrdudeLocation, "avrdudeLocation")
PROPERTYSTR3(dfuArguments, "dfu_arguments", QStringLiteral("-a 0"))
PROPERTYSTR3(dfuArguments, "dfu_arguments", QStringLiteral("-a 0"))
PROPERTYSTR2(dfuLocation, "dfu_location")
PROPERTYSTR2(sambaLocation, "samba_location")
PROPERTYSTR3(sambaPort, "samba_port", QStringLiteral("\\USBserial\\COM23"))
PROPERTYSTR2(backupDir, "backupPath")
PROPERTYSTR2(eepromDir, "lastDir")
PROPERTYSTR2(flashDir, "lastFlashDir")
PROPERTYSTR2(imagesDir, "lastImagesDir")
PROPERTYSTR2(logDir, "lastLogDir")
PROPERTYSTR3(sambaPort, "samba_port", QStringLiteral("\\USBserial\\COM23"))
PROPERTYSTR2(libDir, "libraryPath")
PROPERTYSTR2(snapshotDir, "snapshotpath")
PROPERTYSTR2(updatesDir, "lastUpdatesDir")
PROPERTYSTR3(backupDir, "backupPath", CPN_DOCUMENTS_LOCATION % "/backups")
PROPERTYSTR3(eepromDir, "lastDir", CPN_DOCUMENTS_LOCATION % "/eeproms")
PROPERTYSTR3(flashDir, "lastFlashDir", CPN_DOCUMENTS_LOCATION % "/flash")
PROPERTYSTR3(imagesDir, "lastImagesDir", CPN_DOCUMENTS_LOCATION % "/images")
PROPERTYSTR3(logDir, "lastLogDir", CPN_DOCUMENTS_LOCATION % "/debuglogs")
PROPERTYSTRD(appLogsDir, CPN_DOCUMENTS_LOCATION % "/debuglogs")
PROPERTYSTR3(snapshotDir, "snapshotpath", CPN_DOCUMENTS_LOCATION % "/snapshots")
PROPERTYSTRD(templatesDir, CPN_DOCUMENTS_LOCATION % "/templates")
PROPERTYSTRD(downloadDir, CPN_DOCUMENTS_LOCATION % "/downloads")
PROPERTYSTRD(decompressDir, CPN_DOCUMENTS_LOCATION % "/decompress")
PROPERTY (bool, decompressUseDwnld, true)
PROPERTYSTRD(updateDir, CPN_DOCUMENTS_LOCATION % "/updates")
PROPERTY (bool, updateUseSD, true)
PROPERTY (int, updateLogLevel, 0)
PROPERTYSTR (locale)
PROPERTYSTR (gePath)
PROPERTYSTRD(mcu, QStringLiteral("m64"))
PROPERTYSTRD(programmer, QStringLiteral("usbasp"))
PROPERTYSTRD(appLogsDir, CPN_DOCUMENTS_LOCATION % "/DebugLogs")
PROPERTY(DownloadBranchType, OpenTxBranch, BRANCH_RELEASE_STABLE)
PROPERTY(NewModelAction, newModelAction, MODEL_ACT_WIZARD)
PROPERTY(NewModelAction, newModelAction, MODEL_ACT_WIZARD)
PROPERTY4(int, embedSplashes, "embedded_splashes", 0)
PROPERTY4(int, fwServerFails, "fwserver", 0)
PROPERTY4(int, iconSize, "icon_size", 2)
PROPERTY4(int, jsCtrl, "js_ctrl", 0)
PROPERTY4(int, historySize, "history_size", 10)
PROPERTY (int, generalEditTab, 0)
PROPERTY (int, theme, 1)
PROPERTY (int, warningId, 0)
PROPERTY4(int, embedSplashes, "embedded_splashes", 0)
PROPERTY4(int, fwServerFails, "fwserver", 0)
PROPERTY4(int, iconSize, "icon_size", 2)
PROPERTY4(int, jsCtrl, "js_ctrl", 0)
PROPERTY4(int, historySize, "history_size", 10)
PROPERTY (int, generalEditTab, 0)
PROPERTY (int, theme, 1)
PROPERTY (int, warningId, 0)
PROPERTY4(bool, jsSupport, "js_support", false)
PROPERTY4(bool, showSplash, "show_splash", true)
PROPERTY4(bool, snapToClpbrd, "snapshot_to_clipboard", false)
PROPERTY4(bool, autoCheckApp, "startup_check_companion", false)
PROPERTY4(bool, autoCheckFw, "startup_check_fw", false)
PROPERTY4(bool, promptProfile, "startup_prompt_profile", false)
PROPERTY4(bool, jsSupport, "js_support", false)
PROPERTY4(bool, showSplash, "show_splash", true)
PROPERTY4(bool, snapToClpbrd, "snapshot_to_clipboard", false)
PROPERTY(UpdateCheckFreq, updateCheckFreq, UPDATE_CHECK_MANUAL)
PROPERTYSTR(lastUpdateCheck)
PROPERTY4(bool, promptProfile, "startup_prompt_profile", false)
PROPERTY(bool, enableBackup, false)
PROPERTY(bool, backupOnFlash, true)
@@ -605,11 +649,8 @@ class AppData: public CompStoreObj
bool firstUse;
QString upgradeFromVersion;
CREATE_ENUM_FRIEND_STREAM_OPS(AppData::DownloadBranchType)
CREATE_ENUM_FRIEND_STREAM_OPS(AppData::NewModelAction)
CREATE_ENUM_FRIEND_STREAM_OPS(AppData::UpdateCheckFreq)
};
extern AppData g;
//! @}
#endif // _APPDATA_H_
+292
View File
@@ -0,0 +1,292 @@
/*
* 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 "minizinterface.h"
#include "progresswidget.h"
#include <QDir>
#include <QDirIterator>
#include <QFileInfo>
#include <QDebug>
#include <QWidget>
MinizInterface::MinizInterface(ProgressWidget * progress, const ProgressCalcMethod progressMethod, const int & logLevel) :
progress(progress),
progressMethod(progressMethod),
progressValue(0),
logLevel(logLevel),
path(""),
archiveFile("")
{
reportProgress(tr("Progress calculation method: file %1").arg(progressMethod == PCM_COUNT ? tr("count") : tr("size")), QtDebugMsg);
}
MinizInterface::~MinizInterface()
{
}
bool MinizInterface::zipPathToFile(const QString & sourcePath, const QString & archiveFile, bool append)
{
if (progress)
progress->setInfo(tr("Compressing %1 to %2 with append %3").arg(sourcePath, archiveFile, append ? "true" : "false"));
else
qDebug() << QString("Compressing %1 to %2 with append %3").arg(sourcePath, archiveFile, append ? "true" : "false");
reportProgress(tr("Miniz version: %1").arg(MZ_VERSION), QtDebugMsg);
progressValue = 0;
path = sourcePath;
this->archiveFile = archiveFile;
mz_zip_zero_struct(&zip_archive);
QFileInfo afi(archiveFile);
if (afi.exists() && !append)
reportProgress(tr("Existing archive will be overwritten"), QtCriticalMsg);
if (afi.exists() && append) {
if (!mz_zip_reader_init_file(&zip_archive, qPrintable(archiveFile), 0)) {
reportProgress(tr("Unable to open existing archive"), QtFatalMsg);
return false;
}
else
reportProgress(tr("Existing archive opened"), QtDebugMsg);
if (!mz_zip_writer_init_from_reader(&zip_archive, qPrintable(archiveFile))) {
reportProgress(tr("Unable to write to existing archive"), QtFatalMsg);
return false;
}
else
reportProgress(tr("Existing archive initialised"), QtDebugMsg);
}
else {
if (!mz_zip_writer_init_file(&zip_archive, qPrintable(archiveFile), 0)) {
reportProgress(tr("Failure to initialise archive"), QtFatalMsg);
return false;
}
else
reportProgress(tr("Archive initialised"), QtDebugMsg);
}
QFileInfo pfi(path);
int fileCount = 0;
if (pfi.isDir()) {
if (progressMethod == PCM_COUNT) {
reportProgress(tr("Calculating number of items to archive"), QtDebugMsg);
QDirIterator itcnt(path, QDirIterator::Subdirectories);
while (itcnt.hasNext()) {
fileCount++;
}
}
if (progress) {
if (progressMethod == PCM_COUNT)
progress->setMaximum(fileCount);
else
progress->setMaximum(pfi.size());
}
fileCount = 0;
QDirIterator it(path, QDirIterator::Subdirectories);
while (it.hasNext()) {
it.next();
if (!addFileToArchive(it.filePath()))
return false;
}
}
else {
fileCount = 1;
if (progress) {
if (progressMethod == PCM_COUNT)
progress->setMaximum(fileCount);
else
progress->setMaximum(pfi.size());
}
if (!addFileToArchive(path))
return false;
}
if (!mz_zip_writer_finalize_archive(&zip_archive)) {
mz_zip_writer_end(&zip_archive);
reportProgress(tr("Failure to finalise archive"), QtFatalMsg);
return false;
}
mz_zip_writer_end(&zip_archive);
if (progress)
progress->setValue(progress->maximum());
reportProgress(tr("Compress complete"));
return true;
}
bool MinizInterface::addFileToArchive(const QString & path)
{
if (!mz_zip_writer_add_file(&zip_archive, qPrintable(archiveFile), qPrintable(path), NULL, 0, MZ_DEFAULT_COMPRESSION)) {
mz_zip_writer_end(&zip_archive);
reportProgress(tr("Failure to add %1").arg(path), QtFatalMsg);
return false;
}
if (progressMethod == PCM_COUNT)
++progressValue;
else
progressValue += QFileInfo(path).size();
if (progress)
progress->setValue(progressValue);
reportProgress(tr("Added file: %1").arg(path), QtDebugMsg);
return true;
}
bool MinizInterface::unzipArchiveToPath(const QString & archiveFile, const QString & destinationPath)
{
if (progress)
progress->setInfo(tr("Decompressing %1 to %2").arg(archiveFile, destinationPath));
else
qDebug() << QString("Decompressing %1 to %2").arg(archiveFile, destinationPath);
reportProgress(tr("Miniz version: %1").arg(MZ_VERSION), QtDebugMsg);
progressValue = 0;
path = destinationPath;
this->archiveFile = archiveFile;
mz_zip_zero_struct(&zip_archive);
if (!mz_zip_reader_init_file(&zip_archive, qPrintable(archiveFile), 0)) {
reportProgress(tr("File does not appear to be a compressed archive"), QtFatalMsg);
return false;
}
int fileCount = (int)mz_zip_reader_get_num_files(&zip_archive);
if (fileCount == 0) {
mz_zip_reader_end(&zip_archive);
reportProgress(tr("Compressed archive does not contain any files"), QtFatalMsg);
return false;
}
if (progress) {
if (progressMethod == PCM_COUNT)
progress->setMaximum(fileCount);
else
progress->setMaximum(QFileInfo(archiveFile).size());
}
mz_zip_archive_file_stat file_stat;
if (!mz_zip_reader_file_stat(&zip_archive, 0, &file_stat)) {
mz_zip_reader_end(&zip_archive);
reportProgress(tr("File status error"), QtFatalMsg);
return false;
}
for (int i = 0; i < fileCount; i++) {
if (!mz_zip_reader_file_stat(&zip_archive, i, &file_stat)) {
mz_zip_reader_end(&zip_archive);
reportProgress(tr("Unable to obtain file status for index: %1").arg(i), QtFatalMsg);
return false;
}
QString destPath = path % "/" % QString(file_stat.m_filename);
if (mz_zip_reader_is_file_a_directory(&zip_archive, i)) {
if (!createDirectory(destPath)) {
mz_zip_reader_end(&zip_archive);
return false;
}
}
else {
QFileInfo dfi(destPath);
if (!createDirectory(dfi.absolutePath())) {
mz_zip_reader_end(&zip_archive);
return false;
}
if (!mz_zip_reader_extract_to_file(&zip_archive, i, qPrintable(destPath), 0)) {
mz_zip_reader_end(&zip_archive);
reportProgress(tr("Failed to extract %1 to %2").arg(dfi.fileName(), dfi.absolutePath()), QtFatalMsg);
return false;
}
else {
if (dfi.size() != (qint64)file_stat.m_uncomp_size) {
reportProgress(tr("File %1 extracted size %2 does not match original %3").arg(file_stat.m_filename).arg(dfi.size()).arg((qint64)file_stat.m_uncomp_size), QtFatalMsg);
// return false; disable until testing complete as there maybe some adjustment factor or even OS strangeness
}
}
if (progressMethod == PCM_COUNT)
++progressValue;
else
progressValue += file_stat.m_comp_size;
if (progress)
progress->setValue(progressValue);
reportProgress(tr("Extracted file: %1").arg(file_stat.m_filename), QtDebugMsg);
}
}
mz_zip_reader_end(&zip_archive);
if (progress)
progress->setValue(progress->maximum());
reportProgress(tr("Decompress complete"));
return true;
}
bool MinizInterface::createDirectory(const QString & path)
{
if (!QDir(path).exists()) {
if (!QDir().mkpath(path)) {
reportProgress(tr("Failed to create directory: %1").arg(path), QtFatalMsg);
return false;
}
else
reportProgress(tr("Created directory: %1").arg(path));
}
return true;
}
void MinizInterface::reportProgress(const QString & text, const int & type, bool richText)
{
if (type >= logLevel) {
if (progress)
progress->addMessage(text, type);
else
qDebug() << text;
}
}
+60
View File
@@ -0,0 +1,60 @@
/*
* 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 "miniz.h"
#include <QtCore>
#include <QString>
class ProgressWidget;
class MinizInterface
{
Q_DECLARE_TR_FUNCTIONS(MinizInterface)
public:
enum ProgressCalcMethod {
PCM_COUNT,
PCM_SIZE
};
MinizInterface(ProgressWidget * progress = nullptr, const ProgressCalcMethod progressMethod = PCM_COUNT, const int & logLevel = QtWarningMsg);
~MinizInterface();
bool zipPathToFile(const QString & sourcePath, const QString & archiveFile, bool append = true);
bool unzipArchiveToPath(const QString & archiveFile, const QString & destinationPath);
private:
ProgressWidget *progress;
ProgressCalcMethod progressMethod;
qint64 progressValue;
int logLevel;
QString path;
QString archiveFile;
mz_zip_archive zip_archive;
bool addFileToArchive(const QString & path);
bool createDirectory(const QString & path);
void reportProgress(const QString & text, const int & type = QtInfoMsg, bool richText = false);
};

Before

Width:  |  Height:  |  Size: 1020 B

After

Width:  |  Height:  |  Size: 1020 B

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Before

Width:  |  Height:  |  Size: 454 B

After

Width:  |  Height:  |  Size: 454 B

Before

Width:  |  Height:  |  Size: 500 B

After

Width:  |  Height:  |  Size: 500 B

Before

Width:  |  Height:  |  Size: 653 B

After

Width:  |  Height:  |  Size: 653 B

Before

Width:  |  Height:  |  Size: 705 B

After

Width:  |  Height:  |  Size: 705 B

Before

Width:  |  Height:  |  Size: 245 B

After

Width:  |  Height:  |  Size: 245 B

Before

Width:  |  Height:  |  Size: 286 B

After

Width:  |  Height:  |  Size: 286 B

Before

Width:  |  Height:  |  Size: 341 B

After

Width:  |  Height:  |  Size: 341 B

Before

Width:  |  Height:  |  Size: 460 B

After

Width:  |  Height:  |  Size: 460 B

Before

Width:  |  Height:  |  Size: 245 B

After

Width:  |  Height:  |  Size: 245 B

Before

Width:  |  Height:  |  Size: 499 B

After

Width:  |  Height:  |  Size: 499 B

Before

Width:  |  Height:  |  Size: 675 B

After

Width:  |  Height:  |  Size: 675 B

Before

Width:  |  Height:  |  Size: 724 B

After

Width:  |  Height:  |  Size: 724 B

Before

Width:  |  Height:  |  Size: 873 B

After

Width:  |  Height:  |  Size: 873 B

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

+39
View File
@@ -0,0 +1,39 @@
include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
set(updates_NAMES
updates
updatesdialog
updateoptionsdialog
repomodels
updateinterface
updatefirmware
updatecompanion
updatesdcard
updatesounds
updatethemes
updatemultiprotocol
)
foreach(name ${updates_NAMES})
set(updates_SRCS ${updates_SRCS} ${name}.cpp)
set(updates_HDRS ${updates_HDRS} ${name}.h)
endforeach()
set(updates_UIS
updatesdialog.ui
updateoptionsdialog.ui
)
set(updates_QTS
updates.h
updatesdialog.h
updateoptionsdialog.h
repomodels.h
updateinterface.h
)
qt5_wrap_ui(updates_SRCS ${updates_UIS})
qt5_wrap_cpp(updates_SRCS ${updates_QTS})
add_library(updates ${updates_SRCS} ${updates_HDRS})
target_link_libraries(updates PRIVATE ${CPN_COMMON_LIB})
@@ -1,7 +1,8 @@
/*
* Copyright (C) OpenTX
* 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
@@ -25,7 +26,7 @@
#include <QTime>
DownloadDialog::DownloadDialog(QWidget *parent, QString src, QString tgt):
DownloadDialog::DownloadDialog(QWidget *parent, QString src, QString tgt, QString contentType, QString title):
QDialog(parent),
ui(new Ui::DownloadDialog),
reply(nullptr),
@@ -33,25 +34,33 @@ DownloadDialog::DownloadDialog(QWidget *parent, QString src, QString tgt):
aborted(false)
{
ui->setupUi(this);
setWindowIcon(CompanionIcon("fwpreferences.png"));
ui->progressBar->setValue(1);
setWindowIcon(CompanionIcon("download.png"));
ui->progressBar->setValue(0);
ui->progressBar->setMinimum(0);
ui->progressBar->setMaximum(0);
ui->progressBar->setMaximum(100);
if (tgt.isEmpty()) {
setWindowTitle(src);
return; // just show wait dialog.
}
setWindowTitle(windowTitle() % title);
file = new QFile(tgt);
if (!file->open(QIODevice::WriteOnly)) {
QMessageBox::critical(this, CPN_STR_APP_NAME,
tr("Unable to save the file %1: %2.")
.arg(tgt).arg(file->errorString()));
QMessageBox::critical(this, CPN_STR_APP_NAME, tr("Unable to open the download file %1 for writing.\nError: %2").arg(tgt).arg(file->errorString()));
QTimer::singleShot(0, this, SLOT(fileError()));
}
else {
reply = qnam.get(QNetworkRequest(QUrl(src)));
url.setUrl(src);
qDebug() << "url:" << url.url();
request.setUrl(url);
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork);
request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
request.setRawHeader(QByteArray("Accept"), contentType.toUtf8());
reply = qnam.get(request);
connect(reply, SIGNAL(finished()), this, SLOT(httpFinished()));
connect(reply, SIGNAL(readyRead()), this, SLOT(httpReadyRead()));
connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(updateDataReadProgress(qint64, qint64)));
@@ -81,6 +90,7 @@ void DownloadDialog::httpFinished()
file->close();
const bool ok = !(reply->error() || aborted);
if (!ok) {
file->remove();
if (!aborted)
@@ -117,15 +127,3 @@ void DownloadDialog::fileError()
file = nullptr;
reject();
}
#if 0
void DownloadDialog::closeEvent( QCloseEvent * event)
{
// Delay closing 2 seconds to avoid unpleasant flashing download dialogs
QTime closeTime= QTime::currentTime().addSecs(2);
while( QTime::currentTime() < closeTime )
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
event->accept();
}
#endif
@@ -1,7 +1,8 @@
/*
* Copyright (C) OpenTX
* 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
@@ -18,8 +19,7 @@
* GNU General Public License for more details.
*/
#ifndef _DOWNLOADDIALOG_H_
#define _DOWNLOADDIALOG_H_
#pragma once
#include <QtWidgets>
#include <QtNetwork/QNetworkAccessManager>
@@ -36,34 +36,26 @@ class DownloadDialog : public QDialog
Q_OBJECT
public:
explicit DownloadDialog(QWidget *parent = 0, QString src = "", QString tgt = "");
explicit DownloadDialog(QWidget *parent = 0, QString src = "", QString tgt = "", QString contentType = "", QString title = "");
~DownloadDialog();
public slots:
virtual void reject() override;
private slots:
void fileError();
void httpFinished();
void httpReadyRead();
void updateDataReadProgress(qint64 bytesRead, qint64 totalBytes);
private:
#if 0
void closeEvent(QCloseEvent * event);
#endif
Ui::DownloadDialog *ui;
QNetworkAccessManager qnam;
QNetworkReply *reply;
QFile *file;
bool aborted;
QNetworkRequest request;
QUrl url;
};
#endif // _DOWNLOADDIALOG_H_
+456
View File
@@ -0,0 +1,456 @@
/*
* 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 <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)
{
setSortRole(UpdatesItemModel::IMDR_Date);
}
bool ReleasesItemModel::releaseChannelChanged()
{
if (m_settingsIdx > -1 && m_releaseChannel != (int)g.component[m_settingsIdx].releaseChannel())
return true;
else
return false;
}
void ReleasesItemModel::parseMetaData(const int mdt, QJsonDocument * jsonDoc)
{
json = jsonDoc;
if (m_settingsIdx > -1)
m_releaseChannel = g.component[m_settingsIdx].releaseChannel();
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);
}
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);
}
item->setData(isReleaseAvailable(tag, prerelease), UpdatesItemModel::IMDR_Available);
appendRow(item);
}
bool ReleasesItemModel::isReleaseAvailable(const QString tagname, const bool prerelease)
{
if (prerelease) {
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 && tagname.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));
}
/*
ReleasesMetaData
*/
ReleasesMetaData::ReleasesMetaData(QWidget * parent) :
QWidget(parent),
m_repo(""),
m_id(0)
{
itemModel = new ReleasesItemModel();
filteredItemModel = new ReleasesFilteredItemModel(itemModel);
}
bool ReleasesMetaData::refreshRequired()
{
if (m_id != 0)
return itemModel->releaseChannelChanged();
return true;
}
/*
AssetsMetaData
*/
AssetsMetaData::AssetsMetaData(QWidget * parent) :
QWidget(parent)
{
itemModel = new AssetsItemModel();
filteredItemModel = new AssetsFilteredItemModel(itemModel);
}
+300
View File
@@ -0,0 +1,300 @@
/*
* 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 <QWidget>
#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_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 parseMetaData(const int mdt, QJsonDocument * json);
int settingsIndex() { return m_settingsIdx; }
bool releaseChannelChanged();
private:
int m_settingsIdx;
QString m_nightlyName;
int m_releaseChannel;
bool isReleaseAvailable(const QString tagname, const bool prerelease);
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 ReleasesMetaData : public QWidget
{
Q_OBJECT
public:
explicit ReleasesMetaData(QWidget * parent);
virtual ~ReleasesMetaData() {};
bool refreshRequired();
void setRepo(QString repo) { m_repo = repo; }
void setNightlyName(QString name) { itemModel->setNightlyName(name); }
void setSettingsIndex(int index) { itemModel->setSettingsIndex(index); }
void setId(int id) { m_id = id; }
int id() { return m_id; }
int getSetId() { m_id = filteredItemModel->channelLatestId(); return m_id; }
int getSetId(int row) { m_id = filteredItemModel->id(row); return m_id; }
int getSetId(QVariant value, Qt::MatchFlags flags = Qt::MatchExactly, int role = Qt::DisplayRole)
{ m_id = filteredItemModel->id(value, flags, role); return m_id; }
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() { return filteredItemModel->name(m_id); }
bool prerelease() { return filteredItemModel->prerelease(m_id); }
QString version() { return filteredItemModel->version(m_id); }
QString urlReleases() { return m_repo % "/releases"; }
QString urlReleaseLatest() { return urlReleases() % "/latest"; }
QString urlRelease() { return urlReleases() % QString("/%1").arg(m_id); }
private:
ReleasesItemModel *itemModel;
ReleasesFilteredItemModel *filteredItemModel;
QString m_repo;
int m_id;
};
class AssetsMetaData : public QWidget
{
Q_OBJECT
public:
explicit AssetsMetaData(QWidget * parent);
virtual ~AssetsMetaData() {};
void setRepo(QString repo) { m_repo = repo; }
void setId(int id) { m_id = id; }
int id() { return m_id; }
int getSetId(int row) { m_id = filteredItemModel->id(row); return m_id; }
int getSetId(QVariant value, Qt::MatchFlags flags = Qt::MatchExactly, int role = Qt::DisplayRole)
{ m_id = filteredItemModel->id(value, flags, role); return m_id; }
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(); }
QString urlReleaseAssets(int releaseId, int max)
{ return urlReleases() % QString("/%1/assets").arg(releaseId) % (max > -1 ? QString("\?per_page=%1").arg(max) : ""); }
QString urlAsset() { return urlReleases() % QString("/assets/%1").arg(m_id); }
QString urlContent(QString path) { return m_repo % QString("/contents/%1").arg(path); }
private:
AssetsItemModel *itemModel;
AssetsFilteredItemModel *filteredItemModel;
QString m_repo;
int m_id;
QString urlReleases() { return m_repo % "/releases"; }
};
+143
View File
@@ -0,0 +1,143 @@
/*
* 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 "updatecompanion.h"
#include "constants.h"
#include "version.h"
#include <QMessageBox>
#include <QDesktopServices>
#include <QDirIterator>
#ifdef Q_OS_MACOS
#define
#define OS_FILEPATTERN "edgetx-cpn-osx"
#define OS_INSTALLER_EXTN "*.dmg"
#define OS_INSTALL_QUESTION tr("Would you like to open the disk image to install the new version?")
#elif defined(Q_OS_WIN64)
#define OS_FILEPATTERN "edgetx-cpn-win64"
#define OS_INSTALLER_EXTN "*.exe"
#define OS_INSTALL_QUESTION tr("Would you like to launch the installer?")
#elif defined(Q_OS_WIN32)
#define OS_FILEPATTERN "edgetx-cpn-win32"
#define OS_INSTALLER_EXTN "*.exe"
#define OS_INSTALL_QUESTION tr("Would you like to launch the installer?")
#else
#define OS_FILEPATTERN "edgetx-cpn-linux"
#define OS_INSTALLER_EXTN "*.AppImage"
#define OS_INSTALL_QUESTION tr("Would you like to open the app image to install the new version?")
#endif
UpdateCompanion::UpdateCompanion(QWidget * parent) :
UpdateInterface(parent)
{
setName("Companion");
setRepo(QString(GH_REPOS_EDGETX).append("/edgetx"));
setReleasesNightlyName("nightly");
dfltParams->data.flags = (UPDFLG_Common | UPDFLG_Locked | UPDFLG_AsyncInstall) & ~UPDFLG_CopyDest;
UpdateParameters::AssetParams &ap = dfltParams->addAsset();
ap.filterType = UpdateParameters::UFT_Startswith;
ap.filter = QString("%1").arg(QString(OS_FILEPATTERN));
ap.maxExpected = 1;
ap.flags = dfltParams->data.flags;
}
bool UpdateCompanion::asyncInstall()
{
progressMessage(tr("Async install"));
assets->setFilterFlags(UPDFLG_AsyncInstall);
reportProgress(tr("Asset filter applied: %1 Assets found: %2").arg(updateFlagsToString(UPDFLG_AsyncInstall)).arg(assets->count()), QtDebugMsg);
if (assets->count() != 1) {
reportProgress(tr("Expected 1 asset for install but none found"), QtCriticalMsg);
return false;
}
assets->getSetId(0);
QString installerPath = decompressDir % QString("/A%1/%2").arg(assets->id()).arg(QFileInfo(assets->filename()).completeBaseName());
QDirIterator it(installerPath, { OS_INSTALLER_EXTN }, QDir::Files, QDirIterator::Subdirectories);
bool found = false;
while (it.hasNext())
{
installerPath = it.next();
found = true;
break;
}
if (!found) {
reportProgress(tr("Installer not found in %1 using filter %2").arg(installerPath).arg(OS_INSTALLER_EXTN), QtCriticalMsg);
return false;
}
int ret = QMessageBox::question(parentWidget(), CPN_STR_APP_NAME, OS_INSTALL_QUESTION, QMessageBox::Yes | QMessageBox::No);
if (ret == QMessageBox::Yes) {
if (QDesktopServices::openUrl(QUrl::fromLocalFile(installerPath)))
qApp->exit();
}
return true;
}
const QString UpdateCompanion::currentRelease()
{
#if defined(VERSION_TAG)
return UpdateInterface::latestRelease();
#else
dfltParams->data.currentRelease = QString("EdgeTX v%1.%2 (Custom)").arg(VERSION_MAJOR).arg(VERSION_MINOR);
runParams->data.currentRelease = dfltParams->data.currentRelease;
return dfltParams->data.currentRelease;
#endif
}
const QString UpdateCompanion::currentVersion()
{
#if defined(VERSION_TAG)
return UpdateInterface::latestRelease();
#else
return QString(QString("v%1.%2").arg(VERSION_MAJOR).arg(VERSION_MINOR));
#endif
}
const bool UpdateCompanion::isLatestRelease()
{
#if defined(VERSION_TAG)
return UpdateInterface::isLatestRelease();
#else
return true;
#endif
}
const bool UpdateCompanion::isUpdateAvailable()
{
#if defined(VERSION_TAG)
return UpdateInterface::isUpdateAvailable();
#else
return false;
#endif
}
@@ -1,7 +1,8 @@
/*
* Copyright (C) OpenTX
* 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
@@ -18,29 +19,23 @@
* GNU General Public License for more details.
*/
#include "releasenotesdialog.h"
#include "ui_htmldialog.h"
#include <QFile>
#pragma once
ReleaseNotesDialog::ReleaseNotesDialog(QWidget * parent) :
QDialog(parent),
ui(new Ui::HtmlDialog)
#include "updateinterface.h"
class UpdateCompanion : public UpdateInterface
{
ui->setupUi(this);
Q_DECLARE_TR_FUNCTIONS(UpdateCompanion)
setWindowTitle(tr("Companion Release Notes"));
setWindowIcon(CompanionIcon("changelog.png"));
public:
QFile file(":/releasenotes.txt");
if (file.open( QIODevice::ReadOnly | QIODevice::Text ) ) {
ui->textEditor->setHtml(file.readAll());
ui->textEditor->setOpenExternalLinks(true);
}
ui->textEditor->scroll(0, 0);
ui->textEditor->setOpenExternalLinks(true);
}
explicit UpdateCompanion(QWidget * parent);
virtual ~UpdateCompanion() {}
ReleaseNotesDialog::~ReleaseNotesDialog()
{
delete ui;
}
protected:
virtual bool asyncInstall() override;
virtual const QString currentRelease();
virtual const QString currentVersion();
virtual const bool isLatestRelease();
virtual const bool isUpdateAvailable();
};
+99
View File
@@ -0,0 +1,99 @@
/*
* 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 "updatefirmware.h"
#include "appdata.h"
#include "flashfirmwaredialog.h"
UpdateFirmware::UpdateFirmware(QWidget * parent) :
UpdateInterface(parent)
{
setName("Firmware");
setRepo(QString(GH_REPOS_EDGETX).append("/edgetx"));
setReleasesNightlyName("nightly");
dfltParams->data.flags = UPDFLG_Common | UPDFLG_Locked | UPDFLG_AsyncInstall;
UpdateParameters::AssetParams &ap = dfltParams->addAsset();
ap.filterType = UpdateParameters::UFT_Startswith;
ap.filter = "edgetx-firmware";
ap.maxExpected = 1;
ap.flags = dfltParams->data.flags | UPDFLG_CopyFiles;
ap.destSubDir = "FIRMWARE";
ap.copyFilterType = UpdateParameters::UFT_Expression;
ap.copyFilter = QString("^%1-.*\\.bin$").arg(fwFlavour);
}
bool UpdateFirmware::asyncInstall()
{
progressMessage(tr("Async install"));
assets->setFilterFlags(UPDFLG_AsyncInstall);
reportProgress(tr("Asset filter applied: %1 Assets found: %2").arg(updateFlagsToString(UPDFLG_AsyncInstall)).arg(assets->count()), QtDebugMsg);
if (assets->count() != 1) {
reportProgress(tr("Expected 1 asset for install but none found"), QtCriticalMsg);
return false;
}
QString destPath = updateDir;
if (!assets->subDirectory().isEmpty())
destPath.append("/" % assets->subDirectory());
QRegularExpression filter(runParams->data.assets.at(0).copyFilter, QRegularExpression::CaseInsensitiveOption);
QDirIterator it(destPath);
bool found = false;
while (it.hasNext())
{
QFileInfo file(it.next());
if ((!filter.pattern().isEmpty()) && (!file.fileName().contains(filter)))
continue;
destPath.append("/" % file.fileName());
found = true;
break;
}
if (!found) {
reportProgress(tr("Firmware not found in %1 using filter %2").arg(destPath).arg(filter.pattern()), QtCriticalMsg);
return false;
}
g.currentProfile().fwName(destPath);
reportProgress(tr("Write firmware to radio option setting: %1").arg(g.currentProfile().burnFirmware() ? tr("true") : tr("false")), QtDebugMsg);
if (g.currentProfile().burnFirmware()) {
int ret = QMessageBox::question(this, CPN_STR_APP_NAME, tr("Do you want to write the firmware to the radio now ?"), QMessageBox::Yes | QMessageBox::No);
if (ret == QMessageBox::Yes) {
FlashFirmwareDialog *dlg = new FlashFirmwareDialog(this);
dlg->exec();
dlg->deleteLater();
}
}
return true;
}
+37
View File
@@ -0,0 +1,37 @@
/*
* 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"
class UpdateFirmware : public UpdateInterface
{
Q_DECLARE_TR_FUNCTIONS(UpdateFirmware)
public:
explicit UpdateFirmware(QWidget * parent);
virtual ~UpdateFirmware() {}
protected:
virtual bool asyncInstall() override;
};
File diff suppressed because it is too large Load Diff
+299
View File
@@ -0,0 +1,299 @@
/*
* 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 "repomodels.h"
#include "constants.h"
#include "progresswidget.h"
#include <QtCore>
#include <QWidget>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
class UpdateParameters : public QWidget
{
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 flags;
UpdateFilterType filterType;
QString filter;
int maxExpected;
QString destSubDir;
UpdateFilterType copyFilterType;
QString copyFilter;
};
struct GeneralParams{
int flags;
QString currentRelease;
QString updateRelease;
QString downloadDir;
bool decompressUseDwnld;
QString decompressDir;
bool updateUseSD;
QString updateDir;
QVector<AssetParams> assets;
};
GeneralParams data;
UpdateParameters(QWidget * parent);
virtual ~UpdateParameters() {}
AssetParams & addAsset();
static QString buildFilterPattern(const UpdateFilterType filterType, const QString & filter);
UpdateParameters& operator=(const UpdateParameters& source);
};
class UpdateInterface : public QWidget
{
Q_OBJECT
public:
enum DownloadDataType {
DDT_Binary,
DDT_Content,
DDT_SaveToFile,
DDT_MetaData,
};
Q_ENUM(DownloadDataType)
enum UpdateFlags {
UPDFLG_None = 0,
UPDFLG_Update = 1 << 1,
UPDFLG_Locked = 1 << 2,
UPDFLG_Preparation = 1 << 3,
UPDFLG_Download = 1 << 4,
UPDFLG_DelDownload = 1 << 5,
UPDFLG_Decompress = 1 << 6,
UPDFLG_DelDecompress = 1 << 7,
UPDFLG_CopyDest = 1 << 8,
UPDFLG_CopyFiles = 1 << 9,
UPDFLG_CopyStructure = 1 << 10,
UPDFLG_Housekeeping = 1 << 11,
UPDFLG_AsyncInstall = 1 << 12,
UPDFLG_Common = UPDFLG_Preparation | UPDFLG_Download | UPDFLG_Decompress | UPDFLG_CopyDest | UPDFLG_Housekeeping,
};
Q_ENUM(UpdateFlags)
explicit UpdateInterface(QWidget * parent);
virtual ~UpdateInterface();
const QString getName() const { return name; }
protected:
friend class UpdateFactories;
ReleasesMetaData *releases;
AssetsMetaData *assets;
UpdateParameters *dfltParams;
UpdateParameters *runParams;
ProgressWidget *progress;
QString name;
int settingsIdx;
int resultsPerPage;
QString downloadDir;
QString decompressDir;
QString updateDir;
QString fwFlavour;
QString language;
int logLevel;
virtual bool update();
virtual bool preparation();
virtual bool flagAssets();
virtual bool download();
virtual bool decompress();
virtual bool copyToDestination();
virtual bool copyAsset();
virtual bool housekeeping();
virtual bool asyncInstall();
void setName(QString name);
void setRepo(QString repo) { releases->setRepo(repo); assets->setRepo(repo); }
void setResultsPerPage(int cnt) { resultsPerPage = cnt; }
void setReleasesNightlyName(QString name) { releases->setNightlyName(name); }
void initParamFolders(UpdateParameters * params);
const UpdateParameters * const getDefaultParams() { return dfltParams; }
UpdateParameters * getRunParams() { return runParams; }
void resetRunParams();
void setRunUpdate() { runParams->data.flags |= UPDFLG_Update; }
const QStringList getReleases();
virtual const bool isUpdateAvailable();
virtual bool autoUpdate(ProgressWidget * progress = nullptr);
virtual bool manualUpdate(ProgressWidget * progress = nullptr);
virtual const QString currentVersion();
void clearRelease();
virtual const QString currentRelease();
virtual const QString updateRelease();
virtual const bool isLatestRelease();
virtual const bool isLatestVersion(const QString & current, const QString & latest);
virtual const QString latestRelease();
void getRadioProfileSettings();
bool repoReleasesMetaData();
bool repoReleaseAssetsMetaData();
bool downloadReleasesMetaData();
bool downloadReleaseLatestMetaData();
bool downloadReleaseMetaData(const int releaseId);
bool downloadReleaseAssetsMetaData(const int releaseId);
bool downloadAssetMetaData(const int assetId);
bool getSetAssets(const int flags, const UpdateParameters::UpdateFilterType assetFilterType, const QString & assetsFilter,
const int maxAssetsExpected, const QString & subDirectory,
const UpdateParameters::UpdateFilterType copyFilterType, const QString & copyFilter);
bool downloadAsset(int row);
bool downloadFlaggedAssets();
bool decompressAsset(int row);
bool decompressFlaggedAssets();
bool copyFlaggedAssets();
bool copyStructure();
bool copyFiles();
bool downloadTextFileToBuffer(const QString & path);
void downloadFileToBuffer(const QString & url);
QByteArray * getDownloadBuffer() { return buffer; }
bool convertDownloadToJson(QJsonDocument * json);
bool decompressArchive(const QString & archivePath, const QString & destPath);
bool saveReleaseSettings();
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);
private slots:
void onDownloadFinished(QNetworkReply * reply, DownloadDataType ddt, int subtype);
private:
QNetworkAccessManager manager;
QNetworkRequest request;
QNetworkReply *reply;
QByteArray *buffer;
QFile *file;
QUrl url;
bool downloadSuccess;
static QString semanticVersion(QString version);
int getSettingsIdx();
void setSettingsIdx();
bool setRunFolders();
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 QString name() = 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 QString name() { return m_instance->getName(); }
private:
UpdateInterface *m_instance;
};
class UpdateFactories : public QWidget
{
Q_OBJECT
public:
explicit UpdateFactories(QWidget * parent = nullptr);
virtual ~UpdateFactories();
void registerUpdateFactory(UpdateFactoryInterface * factory);
void registerUpdateFactories();
void unregisterUpdateFactories();
const UpdateParameters * const getDefaultParams(const QString & name);
UpdateParameters * const getRunParams(const QString & name);
void resetRunParams(const QString & name);
void resetAllRunParams();
void setRunUpdate(const QString & name);
const QMap<QString, int> sortedComponentsList();
void clearRelease(const QString & name);
const QString currentRelease(const QString & name);
const QString updateRelease(const QString & name);
const bool isLatestRelease(const QString & name);
const QString latestRelease(const QString & name);
const QStringList releases(const QString & name);
bool autoUpdate(ProgressWidget * progress = nullptr);
bool manualUpdate(ProgressWidget * progress = nullptr);
const bool updatesAvailable();
private:
QVector<UpdateFactoryInterface *> registeredUpdateFactories;
};
@@ -0,0 +1,46 @@
/*
* 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 "updatemultiprotocol.h"
UpdateMultiProtocol::UpdateMultiProtocol(QWidget * parent) :
UpdateInterface(parent)
{
setName("Multiprotocol");
setRepo(QString(GITHUB_API_REPOS).append("/pascallanger/DIY-Multiprotocol-TX-Module"));
setResultsPerPage(50); // GitHub REST API default 30
{
UpdateParameters::AssetParams &ap = dfltParams->addAsset();
ap.filterType = UpdateParameters::UFT_Startswith;
ap.filter = "MultiLuaScripts";
ap.maxExpected = 1;
ap.flags = dfltParams->data.flags | UPDFLG_Locked | UPDFLG_CopyStructure;
}
{
UpdateParameters::AssetParams &ap = dfltParams->addAsset();
ap.filterType = UpdateParameters::UFT_Expression;
ap.filter = "^mm-stm-serial-.*\\.bin$";
ap.flags = (dfltParams->data.flags | UPDFLG_CopyFiles) &~ (UPDFLG_Decompress | UPDFLG_DelDecompress);
ap.destSubDir = "FIRMWARE";
}
}
@@ -0,0 +1,37 @@
/*
* 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
#pragma once
#include "updateinterface.h"
class UpdateMultiProtocol : public UpdateInterface
{
Q_DECLARE_TR_FUNCTIONS(UpdateMultiProtocol)
public:
explicit UpdateMultiProtocol(QWidget * parent);
virtual ~UpdateMultiProtocol() {}
};
@@ -0,0 +1,259 @@
/*
* 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 "updateoptionsdialog.h"
#include "ui_updateoptionsdialog.h"
#include "appdata.h"
#include <QAbstractButton>
#include <QStandardItemModel>
#include <QMessageBox>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QTimer>
UpdateOptionsDialog::UpdateOptionsDialog(QWidget * parent, UpdateFactories * factories, const int idx) :
QDialog(parent),
ui(new Ui::UpdateOptionsDialog),
factories(factories),
idx(idx),
name(g.component[idx].name()),
dfltParams(factories->getDefaultParams(name)),
runParams(factories->getRunParams(name))
{
ui->setupUi(this);
setWindowTitle(tr("%1 %2").arg(name).arg(tr("Options")));
ui->leCurrent->setText(dfltParams->data.currentRelease);
connect(ui->btnForget, &QPushButton::clicked, [=]() {
if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear current release information.\nWarning: There is no undo! Are you sure?"),
QMessageBox::Yes |QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) {
factories->clearRelease(name);
ui->leCurrent->setText(dfltParams->data.currentRelease);
emit changed(idx);
}
});
ui->cboReleases->addItems(factories->releases(name));
int i = -1;
QVBoxLayout *grpAssetsLayout = new QVBoxLayout();
foreach (const UpdateParameters::AssetParams &ap, dfltParams->data.assets) {
int flags = ap.flags;
const bool locked = flags & UpdateInterface::UPDFLG_Locked;
i++;
QHBoxLayout *layout1 = new QHBoxLayout();
if (i > 0) {
QFrame *line = new QFrame(this);
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
line->setLineWidth(1);
line->setMidLineWidth(0);
grpAssetsLayout->addWidget(line);
}
QLabel *aflbl = new QLabel(tr("Filter"));
layout1->addWidget(aflbl);
QComboBox *cboAssetFilterType = new QComboBox();
cboAssetFilterType->setEnabled(!locked);
cboAssetFilterType->addItems(UpdateParameters::updateFilterTypeList());
cboAssetFilterTypes << cboAssetFilterType;
layout1->addWidget(cboAssetFilterType);
QLineEdit *leAssetFilter = new QLineEdit();
leAssetFilter->setEnabled(!locked);
leAssetFilters << leAssetFilter;
layout1->addWidget(leAssetFilter);
connect(leAssetFilter, &QLineEdit::editingFinished, [=]() {
QRegularExpression re(leAssetFilter->text());
leAssetFilter->setStyleSheet((re.isValid() ? "QLineEdit { color : black; }" : "QLineEdit { color : red; }"));
});
connect(cboAssetFilterType, QOverload<int>::of(&QComboBox::currentIndexChanged), [=](const int index) {
if (index == UpdateParameters::UFT_None) {
leAssetFilter->setEnabled(false);
leAssetFilter->setText("");
}
else
leAssetFilter->setEnabled(!locked);
});
QSpacerItem *hsp1 = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum );
layout1->addItem(hsp1);
grpAssetsLayout->addLayout(layout1);
QHBoxLayout *layout2 = new QHBoxLayout();
QCheckBox *chkDownload = new QCheckBox(tr("Download"));
chkDownloads << chkDownload;
layout2->addWidget(chkDownload);
QCheckBox *chkDecompress = new QCheckBox(tr("Decompress"));
chkDecompress->setEnabled(flags & UpdateInterface::UPDFLG_Decompress);
chkDecompresses << chkDecompress;
layout2->addWidget(chkDecompress);
QCheckBox *chkInstall = new QCheckBox(tr("Install"));
chkInstall->setEnabled(flags & UpdateInterface::UPDFLG_AsyncInstall);
chkInstalls << chkInstall;
layout2->addWidget(chkInstall);
QSpacerItem *hsp2 = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum );
layout2->addItem(hsp2);
grpAssetsLayout->addLayout(layout2);
QGridLayout *layout3 = new QGridLayout();
QCheckBox *chkCopy = new QCheckBox(tr("Copy"));
chkCopy->setEnabled(flags & UpdateInterface::UPDFLG_CopyDest);
chkCopies << chkCopy;
layout3->addWidget(chkCopy, 0, 0);
QLabel *lblCopyFilter = new QLabel(tr("Filter"));
lblCopyFilter->setEnabled(flags & UpdateInterface::UPDFLG_CopyDest);
lblCopyFilters << lblCopyFilter;
layout3->addWidget(lblCopyFilter, 0, 1);
QComboBox *cboCopyFilterType = new QComboBox();
cboCopyFilterType->setEnabled(!locked);
cboCopyFilterType->addItems(UpdateParameters::updateFilterTypeList());
cboCopyFilterTypes << cboCopyFilterType;
layout3->addWidget(cboCopyFilterType, 0, 2);
QLineEdit *leCopyFilter = new QLineEdit();
leCopyFilter->setEnabled(flags & UpdateInterface::UPDFLG_CopyDest && (!locked));
leCopyFilters << leCopyFilter;
layout3->addWidget(leCopyFilter, 0, 3);
connect(leCopyFilter, &QLineEdit::editingFinished, [=]() {
QRegularExpression re(leCopyFilter->text());
leCopyFilter->setStyleSheet((re.isValid() ? "QLineEdit { color : black; }" : "QLineEdit { color : red; }"));
});
connect(cboCopyFilterType, QOverload<int>::of(&QComboBox::currentIndexChanged), [=](const int index) {
if (index == UpdateParameters::UFT_None) {
leCopyFilter->setEnabled(false);
leCopyFilter->setText("");
}
else
leCopyFilter->setEnabled(!locked);
});
QLabel *lblSubFolder = new QLabel(tr("Sub folder"));
lblSubFolder->setEnabled(flags & UpdateInterface::UPDFLG_CopyDest);
lblSubFolders << lblSubFolder;
layout3->addWidget(lblSubFolder, 1, 1);
QLineEdit *leSubFolder = new QLineEdit();
leSubFolder->setEnabled(flags & UpdateInterface::UPDFLG_CopyDest && (!locked));
leSubFolders << leSubFolder;
layout3->addWidget(leSubFolder, 1, 2, 1, 2);
grpAssetsLayout->addLayout(layout3);
}
ui->grpAssets->setLayout(grpAssetsLayout);
connect(ui->buttonBox, &QDialogButtonBox::accepted, [=]() {
QDialog::accept();
});
connect(ui->buttonBox, &QDialogButtonBox::clicked, [=](QAbstractButton * button) {
if (ui->buttonBox->standardButton(button) == QDialogButtonBox::RestoreDefaults) {
factories->resetRunParams(name);
update();
}
else if (ui->buttonBox->standardButton(button) == QDialogButtonBox::Discard) {
QDialog::reject();
}
else if (ui->buttonBox->standardButton(button) == QDialogButtonBox::Save) {
runParams->data.updateRelease = ui->cboReleases->currentText();
for (int i = 0; i < runParams->data.assets.size(); i++) {
UpdateParameters::AssetParams &ap = runParams->data.assets[i];
if (!(ap.flags & UpdateInterface::UPDFLG_Locked)) {
ap.filterType = (UpdateParameters::UpdateFilterType)cboAssetFilterTypes.at(i)->currentIndex();
ap.filter = leAssetFilters.at(i)->text();
ap.copyFilterType = (UpdateParameters::UpdateFilterType)cboCopyFilterTypes.at(i)->currentIndex();
ap.copyFilter = leCopyFilters.at(i)->text();
ap.destSubDir = leSubFolders.at(i)->text();
}
chkDownloads.at(i)->isChecked() ? ap.flags |= UpdateInterface::UPDFLG_Download : ap.flags &= ~UpdateInterface::UPDFLG_Download;
chkDecompresses.at(i)->isChecked() ? ap.flags |= UpdateInterface::UPDFLG_Decompress :ap.flags &= ~UpdateInterface::UPDFLG_Decompress;
chkInstalls.at(i)->isChecked() ? ap.flags |= UpdateInterface::UPDFLG_AsyncInstall : ap.flags &= ~UpdateInterface::UPDFLG_AsyncInstall;
chkCopies.at(i)->isChecked() ? ap.flags |= UpdateInterface::UPDFLG_CopyDest : ap.flags &= ~UpdateInterface::UPDFLG_CopyDest;
ui->chkDelDownload->isChecked() ? ap.flags |= UpdateInterface::UPDFLG_DelDownload : ap.flags &= ~UpdateInterface::UPDFLG_DelDownload;
ui->chkDelDecompress->isChecked() ? ap.flags |= UpdateInterface::UPDFLG_DelDecompress : ap.flags &= ~UpdateInterface::UPDFLG_DelDecompress;
}
emit changed(idx);
}
});
update();
QTimer::singleShot(50, this, [=]() {
adjustSize();
});
}
UpdateOptionsDialog::~UpdateOptionsDialog()
{
delete ui;
}
void UpdateOptionsDialog::update()
{
ui->cboReleases->setCurrentText(runParams->data.updateRelease);
int i = 0;
foreach (const UpdateParameters::AssetParams &ap, runParams->data.assets) {
cboAssetFilterTypes.at(i)->setCurrentIndex((int)ap.filterType);
leAssetFilters.at(i)->setText(ap.filter);
chkDownloads.at(i)->setChecked(ap.flags & UpdateInterface::UPDFLG_Download);
chkDecompresses.at(i)->setChecked(ap.flags & UpdateInterface::UPDFLG_Decompress);
chkInstalls.at(i)->setChecked(ap.flags & UpdateInterface::UPDFLG_AsyncInstall);
chkCopies.at(i)->setChecked(ap.flags & UpdateInterface::UPDFLG_CopyDest);
cboCopyFilterTypes.at(i)->setCurrentIndex((int)ap.copyFilterType);
leCopyFilters.at(i)->setText(ap.copyFilter);
leSubFolders.at(i)->setText(ap.destSubDir);
ui->chkDelDownload->setChecked(ap.flags & UpdateInterface::UPDFLG_DelDownload);
ui->chkDelDecompress->setChecked(ap.flags & UpdateInterface::UPDFLG_DelDecompress);
i++;
}
}
@@ -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 "updateinterface.h"
#include <QDialog>
#include <QLabel>
#include <QLineEdit>
#include <QCheckBox>
#include <QComboBox>
#include <QVector>
namespace Ui {
class UpdateOptionsDialog;
}
class QAbstractButton;
class UpdateOptionsDialog : public QDialog
{
Q_OBJECT
public:
UpdateOptionsDialog(QWidget * parent, UpdateFactories * factories, const int idx);
virtual ~UpdateOptionsDialog();
signals:
void changed(int index);
private slots:
void update();
private:
Ui::UpdateOptionsDialog *ui;
const UpdateFactories *factories;
const int idx;
const QString name;
const UpdateParameters *dfltParams;
UpdateParameters *runParams;
QVector<QComboBox *> cboAssetFilterTypes;
QVector<QLineEdit *> leAssetFilters;
QVector<QCheckBox *> chkDownloads;
QVector<QCheckBox *> chkDecompresses;
QVector<QCheckBox *> chkInstalls;
QVector<QCheckBox *> chkCopies;
QVector<QLabel *> lblCopyFilters;
QVector<QComboBox *> cboCopyFilterTypes;
QVector<QLineEdit *> leCopyFilters;
QVector<QLabel *> lblSubFolders;
QVector<QLineEdit *> leSubFolders;
};
@@ -0,0 +1,171 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>UpdateOptionsDialog</class>
<widget class="QDialog" name="UpdateOptionsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>677</width>
<height>393</height>
</rect>
</property>
<property name="windowTitle">
<string>Advanced Options</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="grpRelease">
<property name="title">
<string>Release</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="2">
<widget class="QPushButton" name="btnForget">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Clear the last updated release information</string>
</property>
<property name="text">
<string>Forget</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="cboReleases"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="lblCurrentRelease">
<property name="text">
<string>Current</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lblUpdateToRelease">
<property name="text">
<string>Update to</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="leCurrent">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="grpAssets">
<property name="title">
<string>Assets</string>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="grpGeneral">
<property name="title">
<string>General</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QCheckBox" name="chkDelDownload">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Delete downloads</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="chkDelDecompress">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Delete decompressions</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>286</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Discard|QDialogButtonBox::RestoreDefaults|QDialogButtonBox::Save</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+94
View File
@@ -0,0 +1,94 @@
/*
* 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 "updates.h"
#include "updateinterface.h"
#include "updatesdialog.h"
#include "progressdialog.h"
#include "progresswidget.h"
#include "appdata.h"
#include "helpers.h"
#include <QMessageBox>
Updates::Updates(QWidget * parent, UpdateFactories * updateFactories) :
QWidget(parent),
factories(updateFactories)
{
}
Updates::~Updates()
{
}
void Updates::autoUpdates()
{
if (g.updateCheckFreq() == AppData::UPDATE_CHECK_MANUAL)
return;
if (g.lastUpdateCheck().trimmed().isEmpty())
g.lastUpdateCheck(QDateTime::currentDateTime().addDays(-60).toString(Qt::ISODate));
QDateTime dt = QDateTime::fromString(g.lastUpdateCheck(), Qt::ISODate);
if (g.updateCheckFreq() == AppData::UPDATE_CHECK_DAILY)
dt = dt.addDays(1);
else if (g.updateCheckFreq() == AppData::UPDATE_CHECK_WEEKLY)
dt = dt.addDays(7);
else if (g.updateCheckFreq() == AppData::UPDATE_CHECK_MONTHLY)
dt = dt.addDays(30);
if (dt > QDateTime::currentDateTime().toLocalTime()) {
qDebug() << "Update next due:" << dt.toString(Qt::ISODate);
return;
}
g.lastUpdateCheck(QDateTime::currentDateTime().toString(Qt::ISODate));
if (!factories->updatesAvailable())
return;
if (QMessageBox::question(parentWidget(), CPN_STR_APP_NAME % ": " % tr("Checking for Updates"),
tr("One or more updates are available. Update now?"),
(QMessageBox::Yes | QMessageBox::No), QMessageBox::No) == QMessageBox::Yes) {
factories->resetAllRunParams();
factories->autoUpdate();
}
}
void Updates::manualUpdates()
{
factories->resetAllRunParams();
UpdatesDialog *dlg = new UpdatesDialog(this, factories);
if (dlg->exec()) {
ProgressDialog progressDialog(this, tr("Update Components"), CompanionIcon("fuses.png"), true);
progressDialog.progress()->lock(true);
progressDialog.progress()->setInfo(tr("Starting..."));
bool ok = factories->manualUpdate(progressDialog.progress());
progressDialog.progress()->lock(false);
progressDialog.progress()->setInfo(tr("Finished %1").arg(ok ? tr("successfully") : tr("with errors")));
progressDialog.exec();
}
delete dlg;
}
@@ -1,7 +1,8 @@
/*
* Copyright (C) OpenTX
* 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
@@ -18,26 +19,24 @@
* GNU General Public License for more details.
*/
#ifndef _RELEASENOTESDIALOG_H_
#define _RELEASENOTESDIALOG_H_
#pragma once
#include <QDialog>
#include "helpers.h"
#include <QtCore>
#include <QWidget>
namespace Ui {
class HtmlDialog;
}
class UpdateFactories;
class ReleaseNotesDialog : public QDialog
class Updates : public QWidget
{
Q_OBJECT
public:
explicit ReleaseNotesDialog(QWidget *parent = 0);
~ReleaseNotesDialog();
explicit Updates(QWidget * parent, UpdateFactories * updateFactories);
virtual ~Updates();
void autoUpdates();
void manualUpdates();
private:
Ui::HtmlDialog * ui;
UpdateFactories *factories;
};
#endif // _RELEASENOTESDIALOG_H_
+102
View File
@@ -0,0 +1,102 @@
/*
* 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 "updatesdcard.h"
#include <QMessageBox>
UpdateSDCard::UpdateSDCard(QWidget * parent) :
UpdateInterface(parent)
{
setName("SD Card");
setRepo(QString(GH_REPOS_EDGETX).append("/edgetx-sdcard"));
dfltParams->data.flags = UPDFLG_Common | UPDFLG_Locked;
UpdateParameters::AssetParams &ap = dfltParams->addAsset();
ap.filterType = UpdateParameters::UFT_Startswith;
ap.filter = QString("%1-").arg(fwFlavour);
ap.maxExpected = 1;
ap.flags = dfltParams->data.flags | UPDFLG_CopyStructure;
}
bool UpdateSDCard::flagAssets()
{
progressMessage(tr("Flagging assets"));
/*
{
"targets": [
["Flysky NV14", "nv14-", "nv14"],
["FrSky Horus X10", "x10-", "horus"],
*/
const QString mappingfile = "sdcard.json";
if (!downloadTextFileToBuffer(mappingfile)) {
return false;
}
QJsonDocument *json = new QJsonDocument();
if (!convertDownloadToJson(json)) {
return false;
}
UpdateParameters::AssetParams ap = runParams->data.assets.at(0);
QRegularExpression filter(UpdateParameters::buildFilterPattern(ap.filterType, ap.filter), QRegularExpression::CaseInsensitiveOption);
bool found = false;
QString sdimage;
if (json->isObject()) {
const QJsonObject &obj = json->object();
if (obj.value("targets").isArray()) {
const QJsonArray &t = obj.value("targets").toArray();
foreach (const QJsonValue &v, t) {
if (v.isArray()) {
const QJsonArray &i = v.toArray();
if (i.size() > 2) {
if (i.at(1).toString().contains(filter)) {
sdimage = i.at(2).toString();
found = true;
break;
}
}
}
}
}
}
if (!found) {
reportProgress(tr("Radio flavour %1 not listed in %2").arg(fwFlavour).arg(mappingfile), QtCriticalMsg);
return false;
}
delete json;
if (!getSetAssets(ap.flags, UpdateParameters::UFT_Startswith, sdimage, ap.maxExpected, ap.destSubDir, ap.copyFilterType, ap.copyFilter))
return false;
return true;
}
+37
View File
@@ -0,0 +1,37 @@
/*
* 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"
class UpdateSDCard : public UpdateInterface
{
Q_DECLARE_TR_FUNCTIONS(UpdateSDCard)
public:
explicit UpdateSDCard(QWidget * parent);
virtual ~UpdateSDCard() {}
protected:
virtual bool flagAssets() override;
};
+218
View File
@@ -0,0 +1,218 @@
/*
* 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 "updatesdialog.h"
#include "ui_updatesdialog.h"
#include "updateoptionsdialog.h"
#include <QStandardItemModel>
#include <QMap>
#include <QFileDialog>
#include <QTimer>
UpdatesDialog::UpdatesDialog(QWidget * parent, UpdateFactories * factories) :
QDialog(parent),
ui(new Ui::UpdatesDialog),
factories(factories)
{
// downloading the release meta data takes a few seconds for each component so display progress
QDialog *status = new QDialog(parent);
status->setWindowTitle(tr("Progress"));
QVBoxLayout *layout = new QVBoxLayout(status);
QLabel *msg = new QLabel(status);
msg->setFixedWidth(400);
msg->setContentsMargins(50, 50, 50, 50);
layout->addWidget(msg);
status->show();
ui->setupUi(this);
ui->leDownloadDir->setText(g.downloadDir());
connect(ui->chkDecompressUseDwnld, &QCheckBox::stateChanged, [=](const int checked) {
if (!checked) {
ui->leDecompressDir->setText(g.decompressDir());
ui->leDecompressDir->setEnabled(true);
ui->btnDecompressSelect->setEnabled(true);
}
else {
ui->leDecompressDir->setText(g.downloadDir());
ui->leDecompressDir->setEnabled(false);
ui->btnDecompressSelect->setEnabled(false);
}
});
ui->chkDecompressUseDwnld->setChecked(!g.decompressUseDwnld());
ui->chkDecompressUseDwnld->setChecked(g.decompressUseDwnld());
connect(ui->chkUpdateUseSD, &QCheckBox::stateChanged, [=](const int checked) {
if (!checked) {
ui->leUpdateDir->setText(g.updateDir());
ui->leUpdateDir->setEnabled(true);
ui->btnUpdateSelect->setEnabled(true);
}
else {
ui->leUpdateDir->setText(g.currentProfile().sdPath());
ui->leUpdateDir->setEnabled(false);
ui->btnUpdateSelect->setEnabled(false);
}
});
ui->chkUpdateUseSD->setChecked(!g.updateUseSD());
ui->chkUpdateUseSD->setChecked(g.updateUseSD());
connect(ui->btnDownloadSelect, &QPushButton::clicked, [=]() {
QString dirPath = QFileDialog::getExistingDirectory(this,tr("Select your download folder"), g.downloadDir());
if (!dirPath.isEmpty()) {
ui->leDownloadDir->setText(dirPath);
}
});
connect(ui->btnDecompressSelect, &QPushButton::clicked, [=]() {
QString dirPath = QFileDialog::getExistingDirectory(this,tr("Select your decompress folder"), g.decompressDir());
if (!dirPath.isEmpty()) {
ui->leDecompressDir->setText(dirPath);
}
});
connect(ui->btnUpdateSelect, &QPushButton::clicked, [=]() {
QString dirPath = QFileDialog::getExistingDirectory(this,tr("Select your update destination folder"), g.updateDir());
if (!dirPath.isEmpty()) {
ui->leUpdateDir->setText(dirPath);
}
});
int row = 0;
int col = 0;
QGridLayout *grid = new QGridLayout();
QLabel *h1 = new QLabel();
grid->addWidget(h1, row, col++);
QLabel *h2 = new QLabel(tr("Name"));
grid->addWidget(h2, row, col++);
QLabel *h3 = new QLabel(tr("Channel"));
grid->addWidget(h3, row, col++);
QLabel *h4 = new QLabel(tr("Current Release"));
grid->addWidget(h4, row, col++);
QLabel *h5 = new QLabel(tr("Update Release"));
grid->addWidget(h5, row, col++);
col++; // options button
QSpacerItem * spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum );
grid->addItem(spacer, row, col++);
QMapIterator<QString, int> it(factories->sortedComponentsList());
while (it.hasNext()) {
it.next();
int i = it.value();
const QString name = it.key();
if (g.component[i].checkForUpdate()) {
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(name));
grid->addWidget(chkUpdate[i], row, col++);
grid->setAlignment(chkUpdate[i], Qt::AlignHCenter);
QLabel *lblName = new QLabel(name);
grid->addWidget(lblName, row, col++);
QLabel *lblRC = new QLabel(ComponentData::releaseChannelsList().at(g.component[i].releaseChannel()));
grid->addWidget(lblRC, row, col++);
lblCurrentRel[i] = new QLabel(factories->currentRelease(name));
grid->addWidget(lblCurrentRel[i], row, col++);
lblUpdateRel[i] = new QLabel(factories->updateRelease(name));
grid->addWidget(lblUpdateRel[i], row, col++);
btnOptions[i] = new QPushButton(tr("Options"));
connect(btnOptions[i], &QPushButton::clicked, [=]() {
UpdateOptionsDialog *dlg = new UpdateOptionsDialog(this, factories, i);
connect(dlg, &UpdateOptionsDialog::changed, [=](const int i) {
QString name = g.component[i].name();
lblCurrentRel[i]->setText(factories->currentRelease(name));
lblUpdateRel[i]->setText(factories->updateRelease(name));
});
dlg->exec();
dlg->deleteLater();
});
grid->addWidget(btnOptions[i], row, col++);
}
}
ui->grpComponents->setLayout(grid);
connect(ui->buttonBox, &QDialogButtonBox::accepted, [=]() {
QMapIterator<QString, int> it(factories->sortedComponentsList());
while (it.hasNext()) {
it.next();
int i = it.value();
if (g.component[i].checkForUpdate()) {
if (chkUpdate[i]->isChecked()) {
const QString name = it.key();
UpdateParameters *runParams = factories->getRunParams(name);
runParams->data.flags |= UpdateInterface::UPDFLG_Update;
runParams->data.downloadDir = ui->leDownloadDir->text();
runParams->data.decompressUseDwnld = ui->chkDecompressUseDwnld->isChecked();
runParams->data.decompressDir = ui->leDecompressDir->text();
runParams->data.updateUseSD = ui->chkUpdateUseSD->isChecked();
runParams->data.updateDir = ui->leUpdateDir->text();
}
}
}
QDialog::accept();
});
connect(ui->buttonBox, &QDialogButtonBox::rejected, [=]() {
QDialog::reject();
});
ui->leDownloadDir->home(false);
status->hide();
status->deleteLater();
QTimer::singleShot(50, this, [=]() {
adjustSize();
});
}
UpdatesDialog::~UpdatesDialog()
{
delete ui;
}
@@ -1,7 +1,8 @@
/*
* Copyright (C) OpenTX
* 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
@@ -18,35 +19,39 @@
* GNU General Public License for more details.
*/
#ifndef _FWPREFERENCESDIALOG_H_
#define _FWPREFERENCESDIALOG_H_
#pragma once
#include "updateinterface.h"
#include "appdata.h"
#include <QDialog>
#include <QCheckBox>
#include "eeprominterface.h"
#include <QLabel>
#include <QPushButton>
namespace Ui {
class FirmwarePreferencesDialog;
class UpdatesDialog;
}
class Joystick;
class FirmwarePreferencesDialog : public QDialog
class UpdatesDialog : public QDialog
{
Q_OBJECT
public:
explicit FirmwarePreferencesDialog(QWidget *parent = 0);
~FirmwarePreferencesDialog();
UpdatesDialog(QWidget * parent, UpdateFactories * factories);
virtual ~UpdatesDialog();
private:
Ui::FirmwarePreferencesDialog *ui;
void initSettings();
signals:
private slots:
void on_fw_dnld_clicked();
void on_sd_dnld_clicked();
void on_checkFWUpdates_clicked();
private:
Ui::UpdatesDialog *ui;
const UpdateFactories *factories;
QCheckBox *chkUpdate[MAX_COMPONENTS];
QLabel *lblCurrentRel[MAX_COMPONENTS];
QLabel *lblUpdateRel[MAX_COMPONENTS];
QPushButton *btnOptions[MAX_COMPONENTS];
};
#endif // _FWPREFERENCESDIALOG_H_
+216
View File
@@ -0,0 +1,216 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>UpdatesDialog</class>
<widget class="QDialog" name="UpdatesDialog">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>525</width>
<height>289</height>
</rect>
</property>
<property name="windowTitle">
<string>Process Updates</string>
</property>
<property name="sizeGripEnabled">
<bool>true</bool>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="grpFolders">
<property name="title">
<string>Folders</string>
</property>
<layout class="QGridLayout" name="gridLayout_6">
<item row="0" column="0">
<widget class="QLabel" name="lblDownloadDir">
<property name="text">
<string>Download</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="leDownloadDir"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="lblUpdateDir">
<property name="text">
<string>Update</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="chkDecompressUseDwnld">
<property name="text">
<string>Create sub-folders in Download folder</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="leDecompressDir">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lblDecompressDir">
<property name="text">
<string>Decompress</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="chkUpdateUseSD">
<property name="text">
<string>Use Radio Profile SD structure</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="leUpdateDir">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QPushButton" name="btnUpdateSelect">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Select folder</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="btnDecompressSelect">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Select folder</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="btnDownloadSelect">
<property name="text">
<string>Select folder</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="grpComponents">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Components</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>UpdatesDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>UpdatesDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>
+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.
*/
#include "updatesounds.h"
UpdateSounds::UpdateSounds(QWidget * parent) :
UpdateInterface(parent)
{
setName("Sounds");
setRepo(QString(GH_REPOS_EDGETX).append("/edgetx-sdcard-sounds"));
/*
file: sounds.json
[
{
"language": "cs_CZ",
"name": "Czech Female",
"description": "Czech Female Voice (cs-CZ-Vlasta)",
"directory": "cz"
},
TODO support multiple language sub variants
// eg edgetx-sdcard-sounds-en_gb-libby-2.7.0.zip
ap.filter = QString("^edgetx-sdcard-sounds-%1").arg(language); //return all variants of each language
*/
UpdateParameters::AssetParams &ap = dfltParams->addAsset();
// only supports default for each language
// eg edgetx-sdcard-sounds-en-2.7.0.zip
ap.filterType = UpdateParameters::UFT_Startswith;
ap.filter = QString("edgetx-sdcard-sounds-%1-").arg(language);
// if user changes filter then more than one could be returned
//ap.maxExpected = 1;
ap.flags = dfltParams->data.flags | UPDFLG_CopyStructure;
}
+35
View File
@@ -0,0 +1,35 @@
/*
* 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"
class UpdateSounds : public UpdateInterface
{
Q_DECLARE_TR_FUNCTIONS(UpdateSounds)
public:
explicit UpdateSounds(QWidget * parent);
virtual ~UpdateSounds() {}
};
@@ -1,7 +1,8 @@
/*
* Copyright (C) OpenTX
* 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
@@ -18,32 +19,18 @@
* GNU General Public License for more details.
*/
#ifndef _RELEASENOTESFIRMWAREDIALOG_H_
#define _RELEASENOTESFIRMWAREDIALOG_H_
#include <QDialog>
#include "updatethemes.h"
#include "eeprominterface.h"
#include "downloaddialog.h"
namespace Ui {
class HtmlDialog;
}
class ReleaseNotesFirmwareDialog : public QDialog
UpdateThemes::UpdateThemes(QWidget * parent) :
UpdateInterface(parent)
{
Q_OBJECT
setName("Themes");
setRepo(QString(GH_REPOS_EDGETX).append("/themes"));
public:
explicit ReleaseNotesFirmwareDialog(QWidget *parent, const QString & rnurl);
~ReleaseNotesFirmwareDialog();
private:
Ui::HtmlDialog *ui;
QNetworkAccessManager *manager;
private slots:
void replyFinished(QNetworkReply * reply);
};
#endif // _RELEASENOTESFIRMWAREDIALOG_H_
UpdateParameters::AssetParams &ap = dfltParams->addAsset();
ap.filterType = UpdateParameters::UFT_Startswith;
ap.filter = "edgetx-themes";
ap.maxExpected = 1;
ap.flags = dfltParams->data.flags | UPDFLG_CopyStructure;
}
+35
View File
@@ -0,0 +1,35 @@
/*
* 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"
class UpdateThemes : public UpdateInterface
{
Q_DECLARE_TR_FUNCTIONS(UpdateThemes)
public:
explicit UpdateThemes(QWidget * parent);
virtual ~UpdateThemes() {}
};