diff --git a/companion/src/flash/flashfirmwaredialog.cpp b/companion/src/flash/flashfirmwaredialog.cpp index 038f32f4c..a5815526e 100644 --- a/companion/src/flash/flashfirmwaredialog.cpp +++ b/companion/src/flash/flashfirmwaredialog.cpp @@ -29,27 +29,42 @@ #include "progresswidget.h" #include "splashlibrarydialog.h" #include "storage.h" +#include "autocollapsiblesection.h" + +#include FlashFirmwareDialog::FlashFirmwareDialog(QWidget *parent) : QDialog(parent), ui(new Ui::FlashFirmwareDialog), - fwName(g.profile[g.id()].fwName()) + fwName(g.profile[g.id()].fwName()), + imageSource(FIRMWARE), + imageFile(""), + connectionMode(CONNECTION_NONE) { ui->setupUi(this); + ui->checkBackup->setChecked(g.backupOnFlash()); - if (!g.currentProfile().splashFile().isEmpty()){ + ui->advancedWidget->setTitle(tr("Advanced")); + ui->advancedWidget->setAnimationDuration(300); + QVBoxLayout *advLayout = new QVBoxLayout(); + chkHWComp = new QCheckBox(tr("check hardware compatibility (recommended)"), ui->advancedWidget); + chkHWComp->setChecked(true); + advLayout->addWidget(chkHWComp); + chkProfComp = new QCheckBox(tr("check profile compatibility"), ui->advancedWidget); + chkProfComp->setChecked(true); + advLayout->addWidget(chkProfComp); + ui->advancedWidget->setContentLayout(*advLayout); + + const QString splashFile(g.currentProfile().splashFile()); + + if (!splashFile.isEmpty() && QFile(splashFile).exists()){ imageSource = PROFILE; - imageFile = g.currentProfile().splashFile(); + imageFile = splashFile; ui->useProfileSplash->setChecked(true); } else { - imageSource = FIRMWARE; - imageFile = ""; ui->useProfileSplash->setDisabled(true); } - ui->checkHardwareCompatibility->setChecked(g.checkHardwareCompatibility()); - ui->checkBackup->setChecked(g.backupOnFlash()); - detectClicked(true); updateUI(); @@ -60,7 +75,8 @@ FlashFirmwareDialog::FlashFirmwareDialog(QWidget *parent) : connect(ui->useLibrarySplash, &QRadioButton::clicked, this, &FlashFirmwareDialog::useLibrarySplashClicked); connect(ui->useExternalSplash, &QRadioButton::clicked, this, &FlashFirmwareDialog::useExternalSplashClicked); connect(ui->writeButton, &QPushButton::clicked, this, &FlashFirmwareDialog::writeButtonClicked); - connect(ui->cancelButton, &QPushButton::clicked, [this]() { close(); } ); + connect(ui->cancelButton, &QPushButton::clicked, [this] () { close(); } ); + connect(ui->advancedWidget, &AutoCollapsibleSection::resized, this, &FlashFirmwareDialog::shrink); } FlashFirmwareDialog::~FlashFirmwareDialog() @@ -70,62 +86,65 @@ FlashFirmwareDialog::~FlashFirmwareDialog() void FlashFirmwareDialog::updateUI() { - ui->firmwareFilename->setText(fwName); - - if (connectionMode) { + if (connectionMode && isFileConnectionCompatible() && QFile(fwName).exists()) { + ui->firmwareFilename->setText(fwName); ui->loadButton->setEnabled(true); - ui->writeButton->setEnabled(isFileConnectionCompatible() && QFile(fwName).exists()); - } else { - ui->loadButton->setEnabled(false); - ui->writeButton->setEnabled(false); - } + ui->writeButton->setEnabled(true); - FirmwareInterface firmware(fwName); - if (firmware.isValid()) { - ui->firmwareInfoFrame->show(); - ui->date->setText(firmware.getDate() + " " + firmware.getTime()); - ui->version->setText(firmware.getVersion()); - ui->variant->setText(firmware.getFlavour()); - ui->date->setEnabled(true); - ui->version->setEnabled(true); - ui->variant->setEnabled(true); + FirmwareInterface firmware(fwName); - if (firmware.hasSplash()) { - ui->splashFrame->show(); - ui->splash->setFixedSize(firmware.getSplashWidth(), firmware.getSplashHeight()); + if (firmware.isValid()) { + ui->firmwareInfoFrame->show(); + ui->date->setText(firmware.getDate() + " " + firmware.getTime()); + ui->version->setText(firmware.getVersion()); + ui->variant->setText(firmware.getFlavour()); + + if (firmware.hasSplash()) { + ui->splashFrame->show(); + ui->splash->setFixedSize(firmware.getSplashWidth(), firmware.getSplashHeight()); + + QImage image; + switch (imageSource) { + case FIRMWARE: + ui->useFirmwareSplash->setChecked(true); + image = firmware.getSplash(); + break; + case PROFILE: + ui->useProfileSplash->setChecked(true); + image.load(g.currentProfile().splashFile()); + break; + case LIBRARY: + ui->useLibrarySplash->setChecked(true); + image.load(imageFile); + break; + case EXTERNAL: + ui->useExternalSplash->setChecked(true); + image.load(imageFile); + break; + } + + if (!image.isNull()) + ui->splash->setPixmap(makePixMap(image)); + else + ui->splash->setPixmap(QPixmap()); + + } else { + ui->splashFrame->hide(); + } } else { + ui->firmwareInfoFrame->hide(); ui->splashFrame->hide(); } } else { - imageSource = FIRMWARE; + ui->firmwareFilename->setText(""); + ui->loadButton->setEnabled(false); + ui->writeButton->setEnabled(false); ui->firmwareInfoFrame->hide(); ui->splashFrame->hide(); } - QImage image; - switch (imageSource) { - case FIRMWARE: - ui->useFirmwareSplash->setChecked(true); - image = firmware.getSplash(); - break; - case PROFILE: - ui->useProfileSplash->setChecked(true); - image.load(g.currentProfile().splashFile()); - break; - case LIBRARY: - ui->useLibrarySplash->setChecked(true); - image.load(imageFile); - break; - case EXTERNAL: - ui->useExternalSplash->setChecked(true); - image.load(imageFile); - break; - } - - if (!image.isNull()) - ui->splash->setPixmap(makePixMap(image)); - - QTimer::singleShot(0, [=]() { adjustSize(); }); + // add resize event to queue after changes above + QTimer::singleShot(0, [=]() { shrink(); }); } void FlashFirmwareDialog::loadClicked() @@ -133,6 +152,7 @@ void FlashFirmwareDialog::loadClicked() QString fileName = QFileDialog::getOpenFileName(this, tr("Open Firmware File"), g.flashDir(), getFirmwareFilesFilter()); + if (!fileName.isEmpty()) { if (!firmwareFileExtensions().contains(QFileInfo(fileName).suffix().toLower())) { QMessageBox::critical(this, tr("Open Firmware File"), @@ -141,32 +161,35 @@ void FlashFirmwareDialog::loadClicked() return; } - FirmwareInterface fw(fileName); + FirmwareInterface newfw(fileName); - if (!fw.isValid()) { - QMessageBox::critical(this, tr("Open Firmware File"), - tr("%1 is not a valid firmware file") - .arg(fileName)); - return; - } - - if (connectionMode == CONNECTION_UF2) { - const Uf2Info uf2(getUf2Info()); - if (fw.getFlavour() != uf2.board) { + if (chkHWComp->isChecked()) { + if (!newfw.isValid()) { QMessageBox::critical(this, tr("Open Firmware File"), - tr("%1 \nFirmware file is for %2 radio however connected radio is %3") - .arg(fileName) - .arg(fw.getFlavour()) - .arg(uf2.board)); + tr("Error - %1 is not a valid firmware file") + .arg(fileName)); return; } + + if (connectionMode == CONNECTION_UF2) { + const Uf2Info uf2(getUf2Info()); + if (newfw.getFlavour() != uf2.board) { + QMessageBox::critical(this, tr("Open Firmware File"), + tr("%1 \nIncompatability - File: '%2' Connected radio: '%3'") + .arg(fileName) + .arg(newfw.getFlavour()) + .arg(uf2.board)); + return; + } + } } - if (fw.getFlavour() != getCurrentFirmware()->getFlavour()) { + if (chkProfComp->isChecked() && + newfw.getFlavour() != getCurrentFirmware()->getFlavour()) { QMessageBox::critical(this, tr("Open Firmware File"), - tr("%1 \nFirmware file is for %2 radio however profile is for %3 radio") + tr("%1 \nIncompatability - File: '%2' Profile: '%3'") .arg(fileName) - .arg(fw.getFlavour()) + .arg(newfw.getFlavour()) .arg(getCurrentFirmware()->getFlavour())); return; } @@ -184,7 +207,7 @@ void FlashFirmwareDialog::useFirmwareSplashClicked() QMessageBox::warning(this, CPN_STR_TTL_ERROR, tr( "The firmware file is not valid." )); } else if (!firmware.hasSplash()) { QMessageBox::warning(this, CPN_STR_TTL_ERROR, - tr( "There is no start screen image in the firmware file." )); + tr( "The firmware file does not cntain a start screen image." )); } else { imageSource = FIRMWARE; } @@ -261,7 +284,6 @@ void FlashFirmwareDialog::writeButtonClicked() { g.flashDir(QFileInfo(fwName).dir().absolutePath()); g.currentProfile().fwName(fwName); - g.checkHardwareCompatibility(ui->checkHardwareCompatibility->isChecked()); g.backupOnFlash(ui->checkBackup->isChecked()); if (imageSource != FIRMWARE) { @@ -301,35 +323,54 @@ void FlashFirmwareDialog::startWrite(const QString &filename) CompanionIcon("write_flash.png")); auto progress = progressDialog.progress(); - bool checkHw = g.checkHardwareCompatibility(); + bool checkHw = chkHWComp->isChecked(); + bool checkProfile = chkProfComp->isChecked(); bool backup = g.backupOnFlash(); FirmwareInterface newfw(filename); - if (g.checkHardwareCompatibility() || backup) { + if (checkHw || checkProfile || backup) { progress->updateInfoAndMessages(tr("Reading old firmware...")); readFirmware( - [this, &newfw, progress, checkHw, backup](const QByteArray &_data) { + [this, &newfw, progress, checkHw, checkProfile, backup](const QByteArray &_data) { FirmwareInterface currfw(_data); - if (!currfw.isValid()) { - QString errMsg(tr("Firmware read from radio invalid")); - progress->addMessage(errMsg, QtFatalMsg); - progress->setInfo(errMsg); - return; - } - if (backup) { - progress->addMessage(tr("Backing up old firmware")); - if (!writeFirmwareToFile(this, _data, progress)) { + qDebug() << "profile:" << getCurrentFirmware()->getFlavour() + << "current:" << currfw.getFlavour() + << "new:" << newfw.getFlavour(); + + if (checkHw) { + if (!currfw.isValid()) { + QString errMsg(tr("Firmware read from radio invalid")); + progress->addMessage(errMsg, QtFatalMsg); + progress->setInfo(errMsg); + return; + } + + progress->addMessage(tr("Performing hardware compatibity check")); + if (!newfw.isHardwareCompatible(currfw)) { + QString errMsg(tr("New firmware is not compatible with current firmware")); + progress->updateInfoAndMessages(errMsg, QtFatalMsg); return; } } - if (checkHw) { - progress->addMessage(tr("Performing compatibity check")); - if (!newfw.isHardwareCompatible(currfw)) { - QString errMsg(tr("New firmware is not compatible with old firmware")); + if (checkProfile) { + progress->addMessage(tr("Performing profile compatibity check")); + if (currfw.getFlavour() != getCurrentFirmware()->getFlavour()) { + QString errMsg(tr("Current firmware is not compatible with profile")); progress->updateInfoAndMessages(errMsg, QtFatalMsg); return; + } else if (newfw.getFlavour() != getCurrentFirmware()->getFlavour()) { + QString errMsg(tr("New firmware is not compatible with profile")); + progress->updateInfoAndMessages(errMsg, QtFatalMsg); + return; + } + } + + if (backup) { + progress->addMessage(tr("Backing up current firmware")); + if (!writeFirmwareToFile(this, _data, progress)) { + return; } } @@ -364,7 +405,7 @@ void FlashFirmwareDialog::detectClicked(bool atLoad) connectionMsg = tr("Radio connection mode: DFU"); } else { connectionMode = CONNECTION_NONE; - connectionMsg = tr("ALERT: No radio connected"); + connectionMsg = tr("ALERT: No radio detected"); } ui->connectionMode->setText(connectionMsg); @@ -376,9 +417,6 @@ void FlashFirmwareDialog::detectClicked(bool atLoad) tr("Note: USB mode is not suitable for flashing firmware.")); } - if (!isFileConnectionCompatible()) - fwName.clear(); - if (!atLoad) updateUI(); } @@ -395,3 +433,9 @@ bool FlashFirmwareDialog::isFileConnectionCompatible() return false; } + +void FlashFirmwareDialog::shrink() +{ + this->adjustSize(); + this->resize(0, 0); +} diff --git a/companion/src/flash/flashfirmwaredialog.h b/companion/src/flash/flashfirmwaredialog.h index e066fe478..aa54d4f5d 100644 --- a/companion/src/flash/flashfirmwaredialog.h +++ b/companion/src/flash/flashfirmwaredialog.h @@ -50,13 +50,26 @@ class FlashFirmwareDialog : public QDialog void startWrite(const QString &filename); private: - enum ConnectionModes { CONNECTION_NONE, CONNECTION_UF2, CONNECTION_DFU }; - enum ImageSource {FIRMWARE, PROFILE, LIBRARY, EXTERNAL}; + enum ConnectionModes { + CONNECTION_NONE, + CONNECTION_UF2, + CONNECTION_DFU + }; + + enum ImageSource { + FIRMWARE, + PROFILE, + LIBRARY, + EXTERNAL + }; Ui::FlashFirmwareDialog *ui; QString fwName; ImageSource imageSource; QString imageFile; int connectionMode; + QCheckBox *chkHWComp; + QCheckBox *chkProfComp; bool isFileConnectionCompatible(); + void shrink(); }; diff --git a/companion/src/flash/flashfirmwaredialog.ui b/companion/src/flash/flashfirmwaredialog.ui index 0e24198b6..8972e3a25 100644 --- a/companion/src/flash/flashfirmwaredialog.ui +++ b/companion/src/flash/flashfirmwaredialog.ui @@ -7,7 +7,7 @@ 0 0 421 - 390 + 396 @@ -264,32 +264,19 @@ - - - - - 0 - 0 - - - - Checks the new firmware matches the connected radio. If not the process will be aborted. - - - Check hardware compatibility (recommended) - - - Read the connected radio firmware and write to the backup folder. - Backup radio firmware before flashing + Backup current radio firmware before flashing + + + @@ -354,6 +341,14 @@ + + + AutoCollapsibleSection + QWidget +
autocollapsiblesection.h
+ 1 +
+
writeButton firmwareFilename diff --git a/companion/src/shared/CMakeLists.txt b/companion/src/shared/CMakeLists.txt index 6fc3015ad..5da221b8d 100644 --- a/companion/src/shared/CMakeLists.txt +++ b/companion/src/shared/CMakeLists.txt @@ -6,6 +6,7 @@ set(${PROJECT_NAME}_NAMES autobitmappedcombobox autobitsetcheckbox autocheckbox + autocollapsiblesection autocombobox autodoublespinbox autohexspinbox diff --git a/companion/src/shared/autocollapsiblesection.cpp b/companion/src/shared/autocollapsiblesection.cpp new file mode 100644 index 000000000..1118210af --- /dev/null +++ b/companion/src/shared/autocollapsiblesection.cpp @@ -0,0 +1,139 @@ +/* + * 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. + * + * Based on + * + Elypson/qt-collapsible-section + (c) 2016 Michael A. Voelkel - michael.alexander.voelkel@gmail.com + + This file is part of Elypson/qt-collapsible section. + + Elypson/qt-collapsible-section is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Elypson/qt-collapsible-section 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 Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Elypson/qt-collapsible-section. If not, see . +*/ + +#include "autocollapsiblesection.h" + +#include + +AutoCollapsibleSection::AutoCollapsibleSection(QWidget * parent) : + QWidget(parent) +{ + toggleButton = new QToolButton(this); + headerLine = new QFrame(this); + toggleAnimation = new QParallelAnimationGroup(this); + contentArea = new QScrollArea(this); + mainLayout = new QGridLayout(this); + + toggleButton->setStyleSheet("QToolButton {border: none;}"); + toggleButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + toggleButton->setArrowType(Qt::ArrowType::RightArrow); + toggleButton->setText(title); + toggleButton->setCheckable(true); + toggleButton->setChecked(false); + + headerLine->setFrameShape(QFrame::HLine); + headerLine->setFrameShadow(QFrame::Sunken); + headerLine->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + + contentArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + + // start out collapsed + contentArea->setMaximumHeight(0); + contentArea->setMinimumHeight(0); + + // let the entire widget grow and shrink with its content + toggleAnimation->addAnimation(new QPropertyAnimation(this, "maximumHeight")); + toggleAnimation->addAnimation(new QPropertyAnimation(this, "minimumHeight")); + toggleAnimation->addAnimation(new QPropertyAnimation(contentArea, "maximumHeight")); + + mainLayout->setVerticalSpacing(0); + mainLayout->setContentsMargins(0, 0, 0, 0); + + int row = 0; + mainLayout->addWidget(toggleButton, row, 0, 1, 1, Qt::AlignLeft); + mainLayout->addWidget(headerLine, row++, 2, 1, 1); + mainLayout->addWidget(contentArea, row, 0, 1, 3); + setLayout(mainLayout); + + connect(toggleButton, &QToolButton::toggled, this, &AutoCollapsibleSection::toggle); + connect(toggleAnimation, &QParallelAnimationGroup::finished, [this] () { emit resized(); }); +} + +void AutoCollapsibleSection::setAnimationDuration(const int duration) +{ + animationDuration = duration; +} + +void AutoCollapsibleSection::setContentLayout(QLayout & contentLayout) +{ + delete contentArea->layout(); + contentArea->setLayout(&contentLayout); + collapsedHeight = sizeHint().height() - contentArea->maximumHeight(); + updateHeights(); +} + +void AutoCollapsibleSection::setTitle(const QString & title) +{ + toggleButton->setText(title); +} + +void AutoCollapsibleSection::toggle(bool expanded) +{ + toggleButton->setArrowType(expanded ? Qt::ArrowType::DownArrow : + Qt::ArrowType::RightArrow); + toggleAnimation->setDirection(expanded ? QAbstractAnimation::Forward : + QAbstractAnimation::Backward); + toggleAnimation->start(); + isExpanded = expanded; +} + +void AutoCollapsibleSection::updateHeights() +{ + int contentHeight = contentArea->layout()->sizeHint().height(); + + for (int i = 0; i < toggleAnimation->animationCount() - 1; ++i) { + QPropertyAnimation* SectionAnimation = static_cast + (toggleAnimation->animationAt(i)); + SectionAnimation->setDuration(animationDuration); + SectionAnimation->setStartValue(collapsedHeight); + SectionAnimation->setEndValue(collapsedHeight + contentHeight); + } + + QPropertyAnimation* contentAnimation = static_cast + (toggleAnimation->animationAt(toggleAnimation->animationCount() - 1)); + contentAnimation->setDuration(animationDuration); + contentAnimation->setStartValue(0); + contentAnimation->setEndValue(contentHeight); + + toggleAnimation->setDirection(isExpanded ? QAbstractAnimation::Forward : + QAbstractAnimation::Backward); + toggleAnimation->start(); +} diff --git a/companion/src/shared/autocollapsiblesection.h b/companion/src/shared/autocollapsiblesection.h new file mode 100644 index 000000000..a43b2e7bd --- /dev/null +++ b/companion/src/shared/autocollapsiblesection.h @@ -0,0 +1,87 @@ +/* + * 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. + * + * Based on + * + Elypson/qt-collapsible-section + (c) 2016 Michael A. Voelkel - michael.alexander.voelkel@gmail.com + + This file is part of Elypson/qt-collapsible section. + + Elypson/qt-collapsible-section is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Elypson/qt-collapsible-section 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 Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Elypson/qt-collapsible-section. If not, see . +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +class AutoCollapsibleSection : public QWidget +{ + Q_OBJECT + + public: + static const int DEFAULT_DURATION = 0; + + explicit AutoCollapsibleSection(QWidget * parent = nullptr); + + // setters + void setAnimationDuration(const int duration); + void setContentLayout(QLayout & contentLayout); + void setTitle(const QString & title); + + // getters + const int getAnimationDuration() { return animationDuration; } + + void updateHeights(); + + signals: + // inform parent that expand/collapse animation finished + void resized(); + + public slots: + void toggle(bool collapsed); + + private: + QGridLayout *mainLayout; + QToolButton *toggleButton; + QFrame *headerLine; + QParallelAnimationGroup *toggleAnimation; + QScrollArea *contentArea; + int animationDuration = DEFAULT_DURATION; + int collapsedHeight; + bool isExpanded = false; + QString title; +}; diff --git a/companion/src/storage/appdata.h b/companion/src/storage/appdata.h index c6f03c314..df476f1cb 100644 --- a/companion/src/storage/appdata.h +++ b/companion/src/storage/appdata.h @@ -776,7 +776,6 @@ class AppData: public CompStoreObj PROPERTY(bool, enableBackup, false) PROPERTY(bool, backupOnFlash, true) PROPERTY(bool, outputDisplayDetails, false) - PROPERTY(bool, checkHardwareCompatibility, true) PROPERTY(bool, removeModelSlots, true) PROPERTY(bool, maximized, false) PROPERTY(bool, tabbedMdi, false)