[Companion] Radio conversion improvements (#5534)
* Move MultiposPots & MultiposPotsPositions capability lookups and getAnalogInputName() to Boards class.
* Add RawSource::isAvailable() and RawSwitch::isAvailable(); Add board type param to various RawSource lookup functions.
* Add CustomFunctionData::toString(), FlightModeData::toString(), FactoryInstalledPots board capability, MAX_SWITCH_TYPE, & break out GeneralSettings source/switch defaults to own function.
* Add RadioDataConversionState class for tracking conversion actions.
* Implement RadioDataConversionState handler, add conversion process report, and improve/fix conversion issues:
* Make sure all switches/pots/knobs have default configs for destination board type;
* Try to move more custom control names, intelligently;
* Convert model timers and throttle source;
* Adjust for Horus 6P switch in place of Taranis S2 knob;
* Adjust for extra sliders on X12 and X9E to move LS and RS to proper slots as needed;
* Make sure ALL controls are validated, eg. to always account for extra trims/analogs/switches/etc which don't exist on destination;
* Properly indicate invalid RawSource::toString() items with "???".
* Allow user to cancel out of file conversions & provide ample warnings if switching radio type/profile with unsaved file(s) still open;
Also: Fix bug with some orphaned MdiChild windows never being deleted; Create static bool Storage::isBoardCompatible(b1, b2).
* Add maxLibQt "stub" (will not compile).
* Add ExportableTableView for better presentation of conversion results table.
* Move getFourCC() and isBoardCompatible() to Boards class.
This commit is contained in:
committed by
Bertrand Songis
parent
e63a877ed6
commit
0dbad182e8
@@ -166,6 +166,7 @@ set(common_SRCS
|
||||
eeprominterface.cpp
|
||||
helpers.cpp
|
||||
radiodata.cpp
|
||||
radiodataconversionstate.cpp
|
||||
translations.cpp
|
||||
firmwares/er9x/er9xeeprom.cpp
|
||||
firmwares/er9x/er9xinterface.cpp
|
||||
@@ -207,6 +208,7 @@ add_subdirectory(generaledit)
|
||||
add_subdirectory(simulation)
|
||||
add_subdirectory(storage)
|
||||
add_subdirectory(thirdparty/qcustomplot)
|
||||
add_subdirectory(thirdparty/maxlibqt/src/widgets)
|
||||
|
||||
############# Companion ###############
|
||||
|
||||
@@ -305,7 +307,7 @@ qt5_wrap_cpp(companion_SRCS ${companion_MOC_HDRS})
|
||||
|
||||
add_executable(${COMPANION_NAME} MACOSX_BUNDLE ${WIN_EXECUTABLE_TYPE} ${companion_SRCS} ${icon_RC})
|
||||
qt5_use_modules(${COMPANION_NAME} Core Widgets Network)
|
||||
target_link_libraries(${COMPANION_NAME} PRIVATE generaledit modeledit simulation ${CPN_COMMON_LIB} qcustomplot shared storage ${PTHREAD_LIBRARY} ${SDL_LIBRARY} ${WIN_LINK_LIBRARIES})
|
||||
target_link_libraries(${COMPANION_NAME} PRIVATE generaledit modeledit simulation ${CPN_COMMON_LIB} maxLibQtWidgets qcustomplot shared storage ${PTHREAD_LIBRARY} ${SDL_LIBRARY} ${WIN_LINK_LIBRARIES})
|
||||
|
||||
PrintTargetReport("${COMPANION_NAME}")
|
||||
|
||||
@@ -528,9 +530,9 @@ IF(APPLE)
|
||||
get_filename_component(AVRDUDECONF_ABSOLUTE_PATH ${AVRDUDE_CONF} REALPATH)
|
||||
install(PROGRAMS ${DFU_UTIL_ABSOLUTE_PATH} ${AVRDUDE_ABSOLUTE_PATH} DESTINATION ${companion_res_dir} COMPONENT Runtime)
|
||||
install(FILES ${AVRDUDECONF_ABSOLUTE_PATH} DESTINATION ${companion_res_dir} COMPONENT Runtime)
|
||||
|
||||
|
||||
set(bundle_tools_path "\${CMAKE_INSTALL_PREFIX}/${companion_res_dir}/dfu-util;\${CMAKE_INSTALL_PREFIX}/${companion_res_dir}/avrdude")
|
||||
|
||||
|
||||
|
||||
# Include depencies (adding frameworks, fixing the embbeded libraries)
|
||||
# I get write errors without setting BU_CHMOD_BUNDLE_ITEMS even though it is
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "mainwindow.h"
|
||||
#include "appdata.h"
|
||||
#include "helpers.h"
|
||||
#include "storage.h"
|
||||
#if defined(JOYSTICKS)
|
||||
#include "joystick.h"
|
||||
#include "joystickdialog.h"
|
||||
@@ -30,17 +31,17 @@
|
||||
|
||||
AppPreferencesDialog::AppPreferencesDialog(QWidget * parent) :
|
||||
QDialog(parent),
|
||||
updateLock(false),
|
||||
mainWinHasDirtyChild(false),
|
||||
ui(new Ui::AppPreferencesDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
updateLock=false;
|
||||
setWindowIcon(CompanionIcon("apppreferences.png"));
|
||||
|
||||
initSettings();
|
||||
connect(ui->downloadVerCB, SIGNAL(currentIndexChanged(int)), this, SLOT(baseFirmwareChanged()));
|
||||
connect(ui->opt_appDebugLog, &QCheckBox::toggled, this, &AppPreferencesDialog::toggleAppLogSettings);
|
||||
connect(ui->opt_fwTraceLog, &QCheckBox::toggled, this, &AppPreferencesDialog::toggleAppLogSettings);
|
||||
connect(this, SIGNAL(accepted()), this, SLOT(writeValues()));
|
||||
|
||||
#if !defined(JOYSTICKS)
|
||||
ui->joystickCB->hide();
|
||||
@@ -58,7 +59,12 @@ AppPreferencesDialog::~AppPreferencesDialog()
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void AppPreferencesDialog::writeValues()
|
||||
void AppPreferencesDialog::setMainWinHasDirtyChild(bool value)
|
||||
{
|
||||
mainWinHasDirtyChild = value;
|
||||
}
|
||||
|
||||
void AppPreferencesDialog::accept()
|
||||
{
|
||||
g.autoCheckApp(ui->autoCheckCompanion->isChecked());
|
||||
g.OpenTxBranch(DownloadBranchType(ui->OpenTxBranch->currentIndex()));
|
||||
@@ -102,15 +108,42 @@ void AppPreferencesDialog::writeValues()
|
||||
else
|
||||
g.profile[g.id()].name(ui->profileNameLE->text());
|
||||
|
||||
bool fwchange = false;
|
||||
Firmware * newFw = getFirmwareVariant();
|
||||
// If a new fw type has been choosen, several things need to reset
|
||||
Firmware::setCurrentVariant(getFirmwareVariant());
|
||||
QString id = Firmware::getCurrentVariant()->getId();
|
||||
if (g.profile[g.id()].fwType() != id) {
|
||||
if (Firmware::getCurrentVariant()->getId() != newFw->getId()) {
|
||||
// check if we're going to be converting to a new radio type and there are unsaved files in the main window
|
||||
if (mainWinHasDirtyChild && !Boards::isBoardCompatible(Firmware::getCurrentVariant()->getBoard(), newFw->getBoard())) {
|
||||
QString q = tr("<p><b>You cannot switch Radio Type or change Build Options while there are unsaved file changes. What do you wish to do?</b></p> <ul>" \
|
||||
"<li><i>Save All</i> - Save any open file(s) before saving Settings.<li>" \
|
||||
"<li><i>Reset</i> - Revert to the previous Radio Type and Build Options before saving Settings.</li>" \
|
||||
"<li><i>Cancel</i> - Return to the Settings editor dialog.</li></ul>");
|
||||
int resp = QMessageBox::question(this, windowTitle(), q, (QMessageBox::SaveAll | QMessageBox::Reset | QMessageBox::Cancel), QMessageBox::Cancel);
|
||||
if (resp == QMessageBox::SaveAll) {
|
||||
// signal main window to save files, need to do this before the fw actually changes
|
||||
emit firmwareProfileAboutToChange();
|
||||
}
|
||||
else if (resp == QMessageBox::Reset) {
|
||||
// bail out early before saving the radio type & firmware options
|
||||
QDialog::accept();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// we do not accept the dialog close
|
||||
return;
|
||||
}
|
||||
}
|
||||
Firmware::setCurrentVariant(newFw);
|
||||
g.profile[g.id()].fwName("");
|
||||
g.profile[g.id()].initFwVariables();
|
||||
g.profile[g.id()].fwType(id);
|
||||
emit firmwareProfileChanged(g.id());
|
||||
g.profile[g.id()].fwType(newFw->getId());
|
||||
fwchange = true;
|
||||
}
|
||||
|
||||
QDialog::accept();
|
||||
|
||||
if (fwchange)
|
||||
emit firmwareProfileChanged(g.id()); // important to do this after the accepted() signal
|
||||
}
|
||||
|
||||
void AppPreferencesDialog::on_snapshotPathButton_clicked()
|
||||
|
||||
@@ -38,14 +38,21 @@ class AppPreferencesDialog : public QDialog
|
||||
public:
|
||||
explicit AppPreferencesDialog(QWidget * parent = 0);
|
||||
~AppPreferencesDialog();
|
||||
|
||||
Joystick * joystick;
|
||||
|
||||
public slots:
|
||||
void accept() Q_DECL_OVERRIDE;
|
||||
void setMainWinHasDirtyChild(bool value);
|
||||
|
||||
signals:
|
||||
void firmwareProfileChanged(int profId);
|
||||
void firmwareProfileAboutToChange(bool saveFiles = true);
|
||||
|
||||
private:
|
||||
QList<QCheckBox *> optionsCheckBoxes;
|
||||
bool updateLock;
|
||||
bool mainWinHasDirtyChild;
|
||||
void showVoice(bool);
|
||||
void showVoice();
|
||||
void hideVoice();
|
||||
@@ -64,7 +71,6 @@ class AppPreferencesDialog : public QDialog
|
||||
void firmwareOptionChanged(bool state);
|
||||
void toggleAppLogSettings();
|
||||
|
||||
void writeValues();
|
||||
void on_libraryPathButton_clicked();
|
||||
void on_snapshotPathButton_clicked();
|
||||
void on_snapshotClipboardCKB_clicked();
|
||||
|
||||
@@ -53,6 +53,32 @@ void Boards::setBoardType(const Type & board)
|
||||
m_boardType = BOARD_UNKNOWN;
|
||||
}
|
||||
|
||||
uint32_t Boards::getFourCC(Type board)
|
||||
{
|
||||
switch (board) {
|
||||
case BOARD_X12S:
|
||||
return 0x3478746F;
|
||||
case BOARD_X10:
|
||||
return 0x3778746F;
|
||||
case BOARD_TARANIS_X7:
|
||||
return 0x3678746F;
|
||||
case BOARD_TARANIS_X9E:
|
||||
return 0x3578746F;
|
||||
case BOARD_TARANIS_X9D:
|
||||
case BOARD_TARANIS_X9DP:
|
||||
return 0x3378746F;
|
||||
case BOARD_SKY9X:
|
||||
case BOARD_AR9X:
|
||||
case BOARD_9XRPRO:
|
||||
return 0x3278746F;
|
||||
case BOARD_MEGA2560:
|
||||
case BOARD_GRUVIN9X:
|
||||
return 0x3178746F;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const int Boards::getEEpromSize(Board::Type board)
|
||||
{
|
||||
switch (board) {
|
||||
@@ -177,6 +203,12 @@ const int Boards::getCapability(Board::Type board, Board::Capability capability)
|
||||
else
|
||||
return 3;
|
||||
|
||||
case FactoryInstalledPots:
|
||||
if (IS_TARANIS_X9(board))
|
||||
return 2;
|
||||
else
|
||||
return getCapability(board, Pots);
|
||||
|
||||
case Sliders:
|
||||
if (IS_HORUS_X12S(board) || IS_TARANIS_X9E(board))
|
||||
return 4;
|
||||
@@ -191,6 +223,15 @@ const int Boards::getCapability(Board::Type board, Board::Capability capability)
|
||||
else
|
||||
return 0;
|
||||
|
||||
case MaxAnalogs:
|
||||
return getCapability(board, Board::Sticks) + getCapability(board, Board::Pots) + getCapability(board, Board::Sliders) + getCapability(board, Board::MouseAnalogs);
|
||||
|
||||
case MultiposPots:
|
||||
return IS_HORUS_OR_TARANIS(board) ? 3 : 0;
|
||||
|
||||
case MultiposPotsPositions:
|
||||
return IS_HORUS_OR_TARANIS(board) ? 6 : 0;
|
||||
|
||||
case Switches:
|
||||
if (IS_TARANIS_X9E(board))
|
||||
return 18;
|
||||
@@ -242,6 +283,89 @@ const QString Boards::getAxisName(int index)
|
||||
return QObject::tr("Unknown");
|
||||
}
|
||||
|
||||
const QString Boards::getAnalogInputName(Board::Type board, unsigned index)
|
||||
{
|
||||
if ((int)index < getBoardCapability(board, Board::Sticks)) {
|
||||
const QString sticks[] = {
|
||||
QObject::tr("Rud"),
|
||||
QObject::tr("Ele"),
|
||||
QObject::tr("Thr"),
|
||||
QObject::tr("Ail")
|
||||
};
|
||||
return sticks[index];
|
||||
}
|
||||
|
||||
index -= getCapability(board, Board::Sticks);
|
||||
|
||||
if (IS_9X(board) || IS_2560(board) || IS_SKY9X(board)) {
|
||||
const QString pots[] = {
|
||||
"P1",
|
||||
"P2",
|
||||
"P3"
|
||||
};
|
||||
if (index < DIM(pots))
|
||||
return pots[index];
|
||||
}
|
||||
else if (IS_TARANIS_X9E(board)) {
|
||||
const QString pots[] = {
|
||||
"F1",
|
||||
"F2",
|
||||
"F3",
|
||||
"F4",
|
||||
"S1",
|
||||
"S2",
|
||||
"LS",
|
||||
"RS"
|
||||
};
|
||||
if (index < DIM(pots))
|
||||
return pots[index];
|
||||
}
|
||||
else if (IS_TARANIS(board)) {
|
||||
const QString pots[] = {
|
||||
"S1",
|
||||
"S2",
|
||||
"S3",
|
||||
"LS",
|
||||
"RS"
|
||||
};
|
||||
if (index < DIM(pots))
|
||||
return pots[index];
|
||||
}
|
||||
else if (IS_HORUS_X12S(board)) {
|
||||
const QString pots[] = {
|
||||
"S1",
|
||||
"6P",
|
||||
"S2",
|
||||
"L1",
|
||||
"L2",
|
||||
"LS",
|
||||
"RS",
|
||||
"JSx",
|
||||
"JSy"
|
||||
};
|
||||
if (index < DIM(pots))
|
||||
return pots[index];
|
||||
}
|
||||
else if (IS_HORUS_X10(board)) {
|
||||
const QString pots[] = {
|
||||
"S1",
|
||||
"6P",
|
||||
"S2",
|
||||
"LS",
|
||||
"RS"
|
||||
};
|
||||
if (index < DIM(pots))
|
||||
return pots[index];
|
||||
}
|
||||
|
||||
return "???";
|
||||
}
|
||||
|
||||
const bool Boards::isBoardCompatible(Type board1, Type board2)
|
||||
{
|
||||
return (getFourCC(board1) == getFourCC(board2));
|
||||
}
|
||||
|
||||
/* Currently unused
|
||||
|
||||
const QString Boards::getBoardName(Board::Type board)
|
||||
|
||||
+15
-4
@@ -107,8 +107,12 @@ namespace Board {
|
||||
enum Capability {
|
||||
Sticks,
|
||||
Pots,
|
||||
FactoryInstalledPots,
|
||||
Sliders,
|
||||
MouseAnalogs,
|
||||
MaxAnalogs,
|
||||
MultiposPots,
|
||||
MultiposPotsPositions,
|
||||
Switches,
|
||||
SwitchPositions,
|
||||
FactoryInstalledSwitches,
|
||||
@@ -146,16 +150,22 @@ class Boards
|
||||
void setBoardType(const Board::Type & board);
|
||||
Board::Type getBoardType() const { return m_boardType; }
|
||||
|
||||
const int getEEpromSize() { return getEEpromSize(m_boardType); }
|
||||
const int getFlashSize() { return getFlashSize(m_boardType); }
|
||||
const Board::SwitchInfo getSwitchInfo(unsigned index) { return getSwitchInfo(m_boardType, index); }
|
||||
const int getCapability(Board::Capability capability) { return getCapability(m_boardType, capability); }
|
||||
const uint32_t getFourCC() const { return getFourCC(m_boardType); }
|
||||
const int getEEpromSize() const { return getEEpromSize(m_boardType); }
|
||||
const int getFlashSize() const { return getFlashSize(m_boardType); }
|
||||
const Board::SwitchInfo getSwitchInfo(unsigned index) const { return getSwitchInfo(m_boardType, index); }
|
||||
const int getCapability(Board::Capability capability) const { return getCapability(m_boardType, capability); }
|
||||
const QString getAnalogInputName(unsigned index) const { return getAnalogInputName(m_boardType, index); }
|
||||
const bool isBoardCompatible(Board::Type board2) const { return isBoardCompatible(m_boardType, board2); }
|
||||
|
||||
static uint32_t getFourCC(Board::Type board);
|
||||
static const int getEEpromSize(Board::Type board);
|
||||
static const int getFlashSize(Board::Type board);
|
||||
static const Board::SwitchInfo getSwitchInfo(Board::Type board, unsigned index);
|
||||
static const int getCapability(Board::Type board, Board::Capability capability);
|
||||
static const QString getAxisName(int index);
|
||||
static const QString getAnalogInputName(Board::Type board, unsigned index);
|
||||
static const bool isBoardCompatible(Board::Type board1, Board::Type board2);
|
||||
|
||||
protected:
|
||||
|
||||
@@ -178,6 +188,7 @@ class Boards
|
||||
#define IS_TARANIS_PLUS(board) (board==Board::BOARD_TARANIS_X9DP || board==Board::BOARD_TARANIS_X9E)
|
||||
#define IS_TARANIS_X9E(board) (board==Board::BOARD_TARANIS_X9E)
|
||||
#define IS_TARANIS(board) (IS_TARANIS_X9(board) || IS_TARANIS_X7(board))
|
||||
#define IS_TARANIS_NOT_X9E(board) (IS_TARANIS(board) && !IS_TARANIS_X9E(board))
|
||||
#define IS_HORUS_X12S(board) (board==Board::BOARD_X12S)
|
||||
#define IS_HORUS_X10(board) (board==Board::BOARD_X10)
|
||||
#define IS_HORUS(board) (IS_HORUS_X12S(board) || IS_HORUS_X10(board))
|
||||
|
||||
@@ -151,6 +151,11 @@ int TimToVal(float value)
|
||||
return (temp-129);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* SensorData
|
||||
*/
|
||||
|
||||
void SensorData::updateUnit()
|
||||
{
|
||||
if (type == TELEM_TYPE_CALCULATED) {
|
||||
@@ -215,13 +220,10 @@ QString SensorData::unitString() const
|
||||
}
|
||||
}
|
||||
|
||||
bool RawSource::isTimeBased() const
|
||||
{
|
||||
if (IS_ARM(getCurrentBoard()))
|
||||
return (type == SOURCE_TYPE_SPECIAL && index > 0);
|
||||
else
|
||||
return (type==SOURCE_TYPE_TELEMETRY && (index==TELEMETRY_SOURCE_TX_TIME || index==TELEMETRY_SOURCE_TIMER1 || index==TELEMETRY_SOURCE_TIMER2 || index==TELEMETRY_SOURCE_TIMER3));
|
||||
}
|
||||
|
||||
/*
|
||||
* RawSourceRange
|
||||
*/
|
||||
|
||||
float RawSourceRange::getValue(int value)
|
||||
{
|
||||
@@ -231,6 +233,11 @@ float RawSourceRange::getValue(int value)
|
||||
return min + float(value) * step;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* RawSource
|
||||
*/
|
||||
|
||||
RawSourceRange RawSource::getRange(const ModelData * model, const GeneralSettings & settings, unsigned int flags) const
|
||||
{
|
||||
RawSourceRange result;
|
||||
@@ -490,8 +497,12 @@ RawSourceRange RawSource::getRange(const ModelData * model, const GeneralSetting
|
||||
return result;
|
||||
}
|
||||
|
||||
QString RawSource::toString(const ModelData * model, const GeneralSettings * const generalSettings) const
|
||||
QString RawSource::toString(const ModelData * model, const GeneralSettings * const generalSettings, Board::Type board) const
|
||||
{
|
||||
if (board == Board::BOARD_UNKNOWN) {
|
||||
board = getCurrentBoard();
|
||||
}
|
||||
|
||||
static const QString trims[] = {
|
||||
QObject::tr("TrmR"), QObject::tr("TrmE"), QObject::tr("TrmT"), QObject::tr("TrmA"), QObject::tr("Trm5"), QObject::tr("Trm6")
|
||||
};
|
||||
@@ -518,12 +529,15 @@ QString RawSource::toString(const ModelData * model, const GeneralSettings * con
|
||||
static const QString rotary[] = { QObject::tr("REa"), QObject::tr("REb") };
|
||||
|
||||
if (index<0) {
|
||||
return QObject::tr("----");
|
||||
return QObject::tr("???");
|
||||
}
|
||||
|
||||
QString result;
|
||||
int genAryIdx = 0;
|
||||
switch (type) {
|
||||
case SOURCE_TYPE_NONE:
|
||||
return QObject::tr("----");
|
||||
|
||||
case SOURCE_TYPE_VIRTUAL_INPUT:
|
||||
{
|
||||
const char * name = NULL;
|
||||
@@ -545,7 +559,7 @@ QString RawSource::toString(const ModelData * model, const GeneralSettings * con
|
||||
result = QString(generalSettings->stickName[genAryIdx]);
|
||||
}
|
||||
if (result.isEmpty())
|
||||
result = getCurrentFirmware()->getAnalogInputName(index);;
|
||||
result = Boards::getAnalogInputName(board, index);
|
||||
return result;
|
||||
|
||||
case SOURCE_TYPE_TRIM:
|
||||
@@ -559,7 +573,7 @@ QString RawSource::toString(const ModelData * model, const GeneralSettings * con
|
||||
if (generalSettings)
|
||||
result = QString(generalSettings->switchName[index]);
|
||||
if (result.isEmpty())
|
||||
result = getSwitchInfo(getCurrentBoard(), index).name;
|
||||
result = Boards::getSwitchInfo(board, index).name;
|
||||
return result;
|
||||
|
||||
case SOURCE_TYPE_CUSTOM_SWITCH:
|
||||
@@ -583,7 +597,7 @@ QString RawSource::toString(const ModelData * model, const GeneralSettings * con
|
||||
return CHECK_IN_ARRAY(special, index);
|
||||
|
||||
case SOURCE_TYPE_TELEMETRY:
|
||||
if (IS_ARM(getCurrentBoard())) {
|
||||
if (IS_ARM(board)) {
|
||||
div_t qr = div(index, 3);
|
||||
result = getElementName(QCoreApplication::translate("Telemetry", "TELE"), qr.quot+1, model ? model->sensorData[qr.quot].label : NULL);
|
||||
if (qr.rem)
|
||||
@@ -603,13 +617,16 @@ QString RawSource::toString(const ModelData * model, const GeneralSettings * con
|
||||
}
|
||||
|
||||
default:
|
||||
return QObject::tr("----");
|
||||
return QObject::tr("???");
|
||||
}
|
||||
}
|
||||
|
||||
bool RawSource::isStick(int * stickIndex) const
|
||||
bool RawSource::isStick(int * stickIndex, Board::Type board) const
|
||||
{
|
||||
if (type == SOURCE_TYPE_STICK && index < getBoardCapability(getCurrentBoard(), Board::Sticks)) {
|
||||
if (board == Board::BOARD_UNKNOWN)
|
||||
board = getCurrentBoard();
|
||||
|
||||
if (type == SOURCE_TYPE_STICK && index < Boards::getCapability(board, Board::Sticks)) {
|
||||
if (stickIndex)
|
||||
*stickIndex = index;
|
||||
return true;
|
||||
@@ -617,30 +634,103 @@ bool RawSource::isStick(int * stickIndex) const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RawSource::isPot(int * potsIndex) const
|
||||
bool RawSource::isPot(int * potsIndex, Board::Type board) const
|
||||
{
|
||||
if (board == Board::BOARD_UNKNOWN)
|
||||
board = getCurrentBoard();
|
||||
|
||||
Boards b(board);
|
||||
if (type == SOURCE_TYPE_STICK &&
|
||||
index >= getBoardCapability(getCurrentBoard(), Board::Sticks) &&
|
||||
index < getBoardCapability(getCurrentBoard(), Board::Sticks) + getBoardCapability(getCurrentBoard(), Board::Pots)) {
|
||||
index >= b.getCapability(Board::Sticks) &&
|
||||
index < b.getCapability(Board::Sticks) + b.getCapability(Board::Pots)) {
|
||||
if (potsIndex)
|
||||
*potsIndex = index - getBoardCapability(getCurrentBoard(), Board::Sticks);
|
||||
*potsIndex = index - b.getCapability(Board::Sticks);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RawSource::isSlider(int * sliderIndex) const
|
||||
bool RawSource::isSlider(int * sliderIndex, Board::Type board) const
|
||||
{
|
||||
if (board == Board::BOARD_UNKNOWN)
|
||||
board = getCurrentBoard();
|
||||
|
||||
Boards b(board);
|
||||
if (type == SOURCE_TYPE_STICK &&
|
||||
index >= getBoardCapability(getCurrentBoard(), Board::Sticks) + getBoardCapability(getCurrentBoard(), Board::Pots) &&
|
||||
index < getBoardCapability(getCurrentBoard(), Board::Sticks) + getBoardCapability(getCurrentBoard(), Board::Pots) + getBoardCapability(getCurrentBoard(), Board::Sliders)) {
|
||||
index >= b.getCapability(Board::Sticks) + b.getCapability(Board::Pots) &&
|
||||
index < b.getCapability(Board::Sticks) + b.getCapability(Board::Pots) + b.getCapability(Board::Sliders)) {
|
||||
if (sliderIndex)
|
||||
*sliderIndex = index - getBoardCapability(getCurrentBoard(), Board::Sticks) - getBoardCapability(getCurrentBoard(), Board::Pots);
|
||||
*sliderIndex = index - b.getCapability(Board::Sticks) - b.getCapability(Board::Pots);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RawSource::isTimeBased(Board::Type board) const
|
||||
{
|
||||
if (board == Board::BOARD_UNKNOWN)
|
||||
board = getCurrentBoard();
|
||||
|
||||
if (IS_ARM(board))
|
||||
return (type == SOURCE_TYPE_SPECIAL && index > 0);
|
||||
else
|
||||
return (type==SOURCE_TYPE_TELEMETRY && (index==TELEMETRY_SOURCE_TX_TIME || index==TELEMETRY_SOURCE_TIMER1 || index==TELEMETRY_SOURCE_TIMER2 || index==TELEMETRY_SOURCE_TIMER3));
|
||||
}
|
||||
|
||||
bool RawSource::isAvailable(const ModelData * const model, const GeneralSettings * const gs, Board::Type board)
|
||||
{
|
||||
if (board == Board::BOARD_UNKNOWN)
|
||||
board = getCurrentBoard();
|
||||
|
||||
Boards b(board);
|
||||
|
||||
if (type == SOURCE_TYPE_STICK && index >= b.getCapability(Board::MaxAnalogs))
|
||||
return false;
|
||||
|
||||
if (type == SOURCE_TYPE_SWITCH && index >= b.getCapability(Board::Switches))
|
||||
return false;
|
||||
|
||||
if (model) {
|
||||
if (type == SOURCE_TYPE_VIRTUAL_INPUT && !model->isInputValid(index))
|
||||
return false;
|
||||
|
||||
if (type == SOURCE_TYPE_CUSTOM_SWITCH && model->logicalSw[index].isEmpty())
|
||||
return false;
|
||||
|
||||
if (type == SOURCE_TYPE_TELEMETRY) {
|
||||
if (IS_ARM(board) && !model->sensorData[div(index, 3).quot].isAvailable()) {
|
||||
return false;
|
||||
}
|
||||
else if (!IS_ARM(board)) {
|
||||
Firmware * fw = getCurrentFirmware();
|
||||
if (type == (int)TELEMETRY_SOURCE_TX_TIME && !fw->getCapability(RtcTime))
|
||||
return false;
|
||||
|
||||
if (type == (int)TELEMETRY_SOURCE_SWR && !fw->getCapability(SportTelemetry))
|
||||
return false;
|
||||
|
||||
if (type == (int)TELEMETRY_SOURCE_TIMER3 && fw->getCapability(Timers) < 3)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (gs) {
|
||||
int gsIdx = 0;
|
||||
if (type == SOURCE_TYPE_STICK && ((isPot(&gsIdx) && !gs->isPotAvailable(gsIdx)) || (isSlider(&gsIdx) && !gs->isSliderAvailable(gsIdx))))
|
||||
return false;
|
||||
|
||||
if (type == SOURCE_TYPE_SWITCH && IS_HORUS_OR_TARANIS(board) && !gs->switchSourceAllowedTaranis(index))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type == SOURCE_TYPE_TRIM && index >= b.getCapability(Board::NumTrims))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* RawSwitch
|
||||
*/
|
||||
@@ -704,13 +794,13 @@ QString RawSwitch::toString(Board::Type board, const GeneralSettings * const gen
|
||||
return getElementName(QCoreApplication::translate("Logic Switch", "L"), index, NULL, true);
|
||||
|
||||
case SWITCH_TYPE_MULTIPOS_POT:
|
||||
if (!getCurrentFirmware()->getCapability(MultiposPotsPositions))
|
||||
if (!Boards::getCapability(board, Board::MultiposPotsPositions))
|
||||
return QObject::tr("???");
|
||||
qr = div(index - 1, getCurrentFirmware()->getCapability(MultiposPotsPositions));
|
||||
qr = div(index - 1, Boards::getCapability(board, Board::MultiposPotsPositions));
|
||||
if (generalSettings && qr.quot < (int)DIM(generalSettings->potConfig))
|
||||
swName = QString(generalSettings->potName[qr.quot]);
|
||||
if (swName.isEmpty())
|
||||
swName = getCurrentFirmware()->getAnalogInputName(qr.quot + getBoardCapability(board, Board::Sticks));;
|
||||
swName = Boards::getAnalogInputName(board, qr.quot + Boards::getCapability(board, Board::Sticks));
|
||||
return swName + "_" + QString::number(qr.rem + 1);
|
||||
|
||||
case SWITCH_TYPE_TRIM:
|
||||
@@ -749,6 +839,36 @@ QString RawSwitch::toString(Board::Type board, const GeneralSettings * const gen
|
||||
}
|
||||
}
|
||||
|
||||
bool RawSwitch::isAvailable(const ModelData * const model, const GeneralSettings * const gs, Board::Type board)
|
||||
{
|
||||
if (board == Board::BOARD_UNKNOWN)
|
||||
board = getCurrentBoard();
|
||||
|
||||
Boards b(board);
|
||||
|
||||
if (type == SWITCH_TYPE_SWITCH && abs(index) > b.getCapability(Board::SwitchPositions))
|
||||
return false;
|
||||
|
||||
if (type == SWITCH_TYPE_TRIM && abs(index) > b.getCapability(Board::NumTrimSwitches))
|
||||
return false;
|
||||
|
||||
if (gs) {
|
||||
if (type == SWITCH_TYPE_SWITCH && IS_HORUS_OR_TARANIS(board) && !gs->switchPositionAllowedTaranis(abs(index)))
|
||||
return false;
|
||||
|
||||
if (type == SWITCH_TYPE_MULTIPOS_POT) {
|
||||
int pot = div(abs(index) - 1, b.getCapability(Board::MultiposPotsPositions)).quot;
|
||||
if (!gs->isPotAvailable(pot) || gs->potConfig[pot] != Board::POT_MULTIPOS_SWITCH)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (model && !model->isAvailable(*this))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* CurveReference
|
||||
@@ -887,6 +1007,11 @@ bool CustomFunctionData::isEmpty() const
|
||||
return (swtch.type == SWITCH_TYPE_NONE);
|
||||
}
|
||||
|
||||
QString CustomFunctionData::toString(int index, bool globalContext) const
|
||||
{
|
||||
return getElementName((globalContext ? QObject::tr("GF") : QObject::tr("SF")), index+1, 0, true);
|
||||
}
|
||||
|
||||
QString CustomFunctionData::funcToString(const ModelData * model) const
|
||||
{
|
||||
if (func >= FuncOverrideCH1 && func <= FuncOverrideCH32)
|
||||
@@ -1209,10 +1334,7 @@ GeneralSettings::GeneralSettings()
|
||||
vBatMax = -40; //8V
|
||||
}
|
||||
|
||||
|
||||
for (int i=0; i<getBoardCapability(board, Board::FactoryInstalledSwitches); i++) {
|
||||
switchConfig[i] = Boards::getSwitchInfo(board, i).config;
|
||||
}
|
||||
setDefaultControlTypes(board);
|
||||
|
||||
backlightMode = 3; // keys and sticks
|
||||
// backlightBright = 0; // 0 = 100%
|
||||
@@ -1221,36 +1343,6 @@ GeneralSettings::GeneralSettings()
|
||||
backlightOffBright = 20;
|
||||
}
|
||||
|
||||
if (IS_HORUS(board)) {
|
||||
potConfig[0] = Board::POT_WITH_DETENT;
|
||||
potConfig[1] = Board::POT_MULTIPOS_SWITCH;
|
||||
potConfig[2] = Board::POT_WITH_DETENT;
|
||||
}
|
||||
else if (IS_TARANIS_X7(board)) {
|
||||
potConfig[0] = Board::POT_WITHOUT_DETENT;
|
||||
potConfig[1] = Board::POT_WITH_DETENT;
|
||||
}
|
||||
else if (IS_TARANIS(board)) {
|
||||
potConfig[0] = Board::POT_WITH_DETENT;
|
||||
potConfig[1] = Board::POT_WITH_DETENT;
|
||||
}
|
||||
else {
|
||||
potConfig[0] = Board::POT_WITHOUT_DETENT;
|
||||
potConfig[1] = Board::POT_WITHOUT_DETENT;
|
||||
potConfig[2] = Board::POT_WITHOUT_DETENT;
|
||||
}
|
||||
|
||||
if (IS_HORUS_X12S(board) || IS_TARANIS_X9E(board)) {
|
||||
sliderConfig[0] = Board::SLIDER_WITH_DETENT;
|
||||
sliderConfig[1] = Board::SLIDER_WITH_DETENT;
|
||||
sliderConfig[2] = Board::SLIDER_WITH_DETENT;
|
||||
sliderConfig[3] = Board::SLIDER_WITH_DETENT;
|
||||
}
|
||||
else if (IS_TARANIS_X9(board) || IS_HORUS_X10(board)) {
|
||||
sliderConfig[0] = Board::SLIDER_WITH_DETENT;
|
||||
sliderConfig[1] = Board::SLIDER_WITH_DETENT;
|
||||
}
|
||||
|
||||
if (IS_ARM(board)) {
|
||||
speakerVolume = 12;
|
||||
}
|
||||
@@ -1365,6 +1457,44 @@ GeneralSettings::GeneralSettings()
|
||||
memcpy(&themeOptionValue[1], option2, sizeof(ThemeOptionData));
|
||||
}
|
||||
|
||||
void GeneralSettings::setDefaultControlTypes(Board::Type board)
|
||||
{
|
||||
for (int i=0; i<getBoardCapability(board, Board::FactoryInstalledSwitches); i++) {
|
||||
switchConfig[i] = Boards::getSwitchInfo(board, i).config;
|
||||
}
|
||||
|
||||
// TODO: move to Boards, like with switches
|
||||
if (IS_HORUS(board)) {
|
||||
potConfig[0] = Board::POT_WITH_DETENT;
|
||||
potConfig[1] = Board::POT_MULTIPOS_SWITCH;
|
||||
potConfig[2] = Board::POT_WITH_DETENT;
|
||||
}
|
||||
else if (IS_TARANIS_X7(board)) {
|
||||
potConfig[0] = Board::POT_WITHOUT_DETENT;
|
||||
potConfig[1] = Board::POT_WITH_DETENT;
|
||||
}
|
||||
else if (IS_TARANIS(board)) {
|
||||
potConfig[0] = Board::POT_WITH_DETENT;
|
||||
potConfig[1] = Board::POT_WITH_DETENT;
|
||||
}
|
||||
else {
|
||||
potConfig[0] = Board::POT_WITHOUT_DETENT;
|
||||
potConfig[1] = Board::POT_WITHOUT_DETENT;
|
||||
potConfig[2] = Board::POT_WITHOUT_DETENT;
|
||||
}
|
||||
|
||||
if (IS_HORUS_X12S(board) || IS_TARANIS_X9E(board)) {
|
||||
sliderConfig[0] = Board::SLIDER_WITH_DETENT;
|
||||
sliderConfig[1] = Board::SLIDER_WITH_DETENT;
|
||||
sliderConfig[2] = Board::SLIDER_WITH_DETENT;
|
||||
sliderConfig[3] = Board::SLIDER_WITH_DETENT;
|
||||
}
|
||||
else if (IS_TARANIS_X9(board) || IS_HORUS_X10(board)) {
|
||||
sliderConfig[0] = Board::SLIDER_WITH_DETENT;
|
||||
sliderConfig[1] = Board::SLIDER_WITH_DETENT;
|
||||
}
|
||||
}
|
||||
|
||||
int GeneralSettings::getDefaultStick(unsigned int channel) const
|
||||
{
|
||||
if (channel >= CPN_MAX_STICKS)
|
||||
@@ -1949,6 +2079,11 @@ void FlightModeData::clear(const int phase)
|
||||
}
|
||||
}
|
||||
|
||||
QString FlightModeData::toString(int index) const
|
||||
{
|
||||
return getElementName(QObject::tr("FM"), index, name, true);
|
||||
}
|
||||
|
||||
QString GVarData::unitToString() const
|
||||
{
|
||||
switch (unit) {
|
||||
|
||||
@@ -134,8 +134,6 @@ enum Capability {
|
||||
EnhancedCurves,
|
||||
HasFasOffset,
|
||||
HasMahPersistent,
|
||||
MultiposPots,
|
||||
MultiposPotsPositions,
|
||||
SimulatorVariant,
|
||||
MavlinkTelemetry,
|
||||
HasInputDiff,
|
||||
|
||||
@@ -141,7 +141,7 @@ bool OpenTxEepromInterface::saveToByteArray(const T & src, QByteArray & data, ui
|
||||
// manager.Dump();
|
||||
manager.Export(raw);
|
||||
data.resize(8);
|
||||
*((uint32_t*)&data.data()[0]) = StorageFormat::getFourCC(board);
|
||||
*((uint32_t*)&data.data()[0]) = Boards::getFourCC(board);
|
||||
data[4] = version;
|
||||
data[5] = 'M';
|
||||
*((uint16_t*)&data.data()[6]) = raw.size();
|
||||
@@ -164,12 +164,12 @@ template <class T, class M>
|
||||
bool OpenTxEepromInterface::loadFromByteArray(T & dest, const QByteArray & data)
|
||||
{
|
||||
uint32_t fourcc = *((uint32_t*)&data.data()[0]);
|
||||
if (StorageFormat::getFourCC(board) != fourcc) {
|
||||
if (Boards::getFourCC(board) != fourcc) {
|
||||
if (IS_HORUS(board) && fourcc == 0x3178396F) {
|
||||
qDebug() << QString().sprintf("%s: Deprecated fourcc used %x vs %x", getName(), fourcc, StorageFormat::getFourCC(board));
|
||||
qDebug() << QString().sprintf("%s: Deprecated fourcc used %x vs %x", getName(), fourcc, Boards::getFourCC(board));
|
||||
}
|
||||
else {
|
||||
qDebug() << QString().sprintf("%s: Wrong fourcc %x vs %x", getName(), fourcc, StorageFormat::getFourCC(board));
|
||||
qDebug() << QString().sprintf("%s: Wrong fourcc %x vs %x", getName(), fourcc, Boards::getFourCC(board));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -679,10 +679,6 @@ int OpenTxFirmware::getCapability(::Capability capability)
|
||||
return (IS_STOCK(board) ? false : true);
|
||||
case HasMahPersistent:
|
||||
return (IS_ARM(board) ? true : false);
|
||||
case MultiposPots:
|
||||
return IS_HORUS_OR_TARANIS(board) ? 3 : 0;
|
||||
case MultiposPotsPositions:
|
||||
return IS_HORUS_OR_TARANIS(board) ? 6 : 0;
|
||||
case SimulatorVariant:
|
||||
if (board == BOARD_STOCK)
|
||||
return SIMU_STOCK_VARIANTS;
|
||||
@@ -714,76 +710,7 @@ int OpenTxFirmware::getCapability(::Capability capability)
|
||||
|
||||
QString OpenTxFirmware::getAnalogInputName(unsigned int index)
|
||||
{
|
||||
if ((int)index < getBoardCapability(board, Board::Sticks)) {
|
||||
const QString sticks[] = {
|
||||
QObject::tr("Rud"),
|
||||
QObject::tr("Ele"),
|
||||
QObject::tr("Thr"),
|
||||
QObject::tr("Ail")
|
||||
};
|
||||
return sticks[index];
|
||||
}
|
||||
|
||||
index -= getBoardCapability(board, Board::Sticks);
|
||||
|
||||
if (IS_9X(board) || IS_2560(board) || IS_SKY9X(board)) {
|
||||
const QString pots[] = {
|
||||
QObject::tr("P1"),
|
||||
QObject::tr("P2"),
|
||||
QObject::tr("P3")
|
||||
};
|
||||
return CHECK_IN_ARRAY(pots, index);
|
||||
}
|
||||
else if (IS_TARANIS_X9E(board)) {
|
||||
const QString pots[] = {
|
||||
QObject::tr("F1"),
|
||||
QObject::tr("F2"),
|
||||
QObject::tr("F3"),
|
||||
QObject::tr("F4"),
|
||||
QObject::tr("S1"),
|
||||
QObject::tr("S2"),
|
||||
QObject::tr("LS"),
|
||||
QObject::tr("RS")
|
||||
};
|
||||
return CHECK_IN_ARRAY(pots, index);
|
||||
}
|
||||
else if (IS_TARANIS(board)) {
|
||||
const QString pots[] = {
|
||||
QObject::tr("S1"),
|
||||
QObject::tr("S2"),
|
||||
QObject::tr("S3"),
|
||||
QObject::tr("LS"),
|
||||
QObject::tr("RS")
|
||||
};
|
||||
return CHECK_IN_ARRAY(pots, index);
|
||||
}
|
||||
else if (IS_HORUS_X12S(board)) {
|
||||
const QString pots[] = {
|
||||
QObject::tr("S1"),
|
||||
QObject::tr("6P"),
|
||||
QObject::tr("S2"),
|
||||
QObject::tr("L1"),
|
||||
QObject::tr("L2"),
|
||||
QObject::tr("LS"),
|
||||
QObject::tr("RS"),
|
||||
QObject::tr("JSx"),
|
||||
QObject::tr("JSy")
|
||||
};
|
||||
return CHECK_IN_ARRAY(pots, index);
|
||||
}
|
||||
else if (IS_HORUS_X10(board)) {
|
||||
const QString pots[] = {
|
||||
QObject::tr("S1"),
|
||||
QObject::tr("6P"),
|
||||
QObject::tr("S2"),
|
||||
QObject::tr("LS"),
|
||||
QObject::tr("RS")
|
||||
};
|
||||
return CHECK_IN_ARRAY(pots, index);
|
||||
}
|
||||
else {
|
||||
return "???";
|
||||
}
|
||||
return Boards::getAnalogInputName(board, index);
|
||||
}
|
||||
|
||||
QTime OpenTxFirmware::getMaxTimerStart()
|
||||
|
||||
@@ -405,30 +405,16 @@ void Helpers::getFileComboBoxValue(QComboBox * b, char * dest, int length)
|
||||
}
|
||||
|
||||
void Helpers::addRawSourceItems(QStandardItemModel * itemModel, const RawSourceType & type, int count, const GeneralSettings * const generalSettings,
|
||||
const ModelData * const model, const int start, const QList<int> exclude)
|
||||
const ModelData * const model, const int start)
|
||||
{
|
||||
for (int i = start; i < start + count; i++) {
|
||||
if (exclude.contains(i))
|
||||
continue;
|
||||
|
||||
RawSource src = RawSource(type, i);
|
||||
if (model) {
|
||||
if (type == SOURCE_TYPE_VIRTUAL_INPUT && !model->isInputValid(i))
|
||||
continue;
|
||||
}
|
||||
if (generalSettings) {
|
||||
int genAryIdx = 0;
|
||||
if (type == SOURCE_TYPE_STICK && ((src.isPot(&genAryIdx) && !generalSettings->isPotAvailable(genAryIdx)) || (src.isSlider(&genAryIdx) && !generalSettings->isSliderAvailable(genAryIdx))))
|
||||
continue;
|
||||
}
|
||||
if (!src.isAvailable(model, generalSettings, getCurrentBoard()))
|
||||
continue;
|
||||
|
||||
QStandardItem * modelItem = new QStandardItem(src.toString(model, generalSettings));
|
||||
modelItem->setData(src.toValue(), Qt::UserRole);
|
||||
itemModel->appendRow(modelItem);
|
||||
|
||||
if (type == SOURCE_TYPE_SWITCH && generalSettings && IS_HORUS_OR_TARANIS(getCurrentBoard()) && !generalSettings->switchSourceAllowedTaranis(i)) {
|
||||
modelItem->setData(0, Qt::UserRole - 1); // trick to disable an item
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -478,31 +464,16 @@ QStandardItemModel * Helpers::getRawSourceItemModel(const GeneralSettings * cons
|
||||
}
|
||||
|
||||
if (flags & POPULATE_TELEMETRY) {
|
||||
int count = 0;
|
||||
if (IS_ARM(board.getBoardType())) {
|
||||
addRawSourceItems(itemModel, SOURCE_TYPE_SPECIAL, 5, generalSettings, model);
|
||||
|
||||
if (model) {
|
||||
QList<int> exclude;
|
||||
for (int i=0; i < CPN_MAX_SENSORS * 3; ++i) {
|
||||
//this conditon must be false if we populate Global Functions where model = 0
|
||||
if (!model->sensorData[div(i, 3).quot].isAvailable())
|
||||
exclude << i;
|
||||
}
|
||||
if (exclude.size() < CPN_MAX_SENSORS * 3)
|
||||
addRawSourceItems(itemModel, SOURCE_TYPE_TELEMETRY, CPN_MAX_SENSORS * 3, generalSettings, model, 0, exclude);
|
||||
}
|
||||
count = CPN_MAX_SENSORS * 3;
|
||||
}
|
||||
else {
|
||||
QList<int> exclude;
|
||||
if (!fw->getCapability(RtcTime))
|
||||
exclude << TELEMETRY_SOURCE_TX_TIME;
|
||||
if (!fw->getCapability(SportTelemetry))
|
||||
exclude << TELEMETRY_SOURCE_SWR;
|
||||
if (!IS_ARM(board.getBoardType()))
|
||||
exclude << TELEMETRY_SOURCE_TIMER3;
|
||||
int count = ((flags & POPULATE_TELEMETRYEXT) ? TELEMETRY_SOURCES_STATUS_COUNT : TELEMETRY_SOURCES_COUNT);
|
||||
addRawSourceItems(itemModel, SOURCE_TYPE_TELEMETRY, count, generalSettings, model, 0, exclude);
|
||||
count = ((flags & POPULATE_TELEMETRYEXT) ? TELEMETRY_SOURCES_STATUS_COUNT : TELEMETRY_SOURCES_COUNT);
|
||||
}
|
||||
if (model && count)
|
||||
addRawSourceItems(itemModel, SOURCE_TYPE_TELEMETRY, count, generalSettings, model);
|
||||
}
|
||||
|
||||
if (flags & POPULATE_GVARS) {
|
||||
|
||||
@@ -130,7 +130,7 @@ class CurveGroup : public QObject {
|
||||
namespace Helpers
|
||||
{
|
||||
void addRawSourceItems(QStandardItemModel * itemModel, const RawSourceType & type, int count, const GeneralSettings * const generalSettings = NULL,
|
||||
const ModelData * const model = NULL, const int start = 0, const QList<int> exclude = QList<int>());
|
||||
const ModelData * const model = NULL, const int start = 0);
|
||||
QStandardItemModel * getRawSourceItemModel(const GeneralSettings * const generalSettings = NULL, const ModelData * const model = NULL, unsigned int flags = 0);
|
||||
|
||||
void populateGvarUseCB(QComboBox *b, unsigned int phase);
|
||||
|
||||
@@ -163,6 +163,9 @@ MainWindow::MainWindow():
|
||||
child->close();
|
||||
}
|
||||
}
|
||||
else {
|
||||
child->closeFile(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (printing) {
|
||||
@@ -685,6 +688,9 @@ void MainWindow::openFile(const QString & fileName, bool updateLastUsedDir)
|
||||
statusBar()->showMessage(tr("File loaded"), 2000);
|
||||
child->show();
|
||||
}
|
||||
else {
|
||||
child->closeFile(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -708,6 +714,15 @@ void MainWindow::saveAs()
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::saveAll()
|
||||
{
|
||||
foreach (QMdiSubWindow * window, mdiArea->subWindowList()) {
|
||||
MdiChild * child;
|
||||
if ((child = qobject_cast<MdiChild *>(window->widget())) && child->isWindowModified())
|
||||
child->save();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::closeFile()
|
||||
{
|
||||
if (mdiArea->activeSubWindow())
|
||||
@@ -723,16 +738,28 @@ void MainWindow::openRecentFile()
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::loadProfileId(const unsigned pid) // TODO Load all variables - Also HW!
|
||||
bool MainWindow::loadProfileId(const unsigned pid) // TODO Load all variables - Also HW!
|
||||
{
|
||||
if (pid >= MAX_PROFILES)
|
||||
return;
|
||||
return false;
|
||||
|
||||
Firmware * newFw = Firmware::getFirmwareForId(g.profile[pid].fwType());
|
||||
// warn if we're switching between incompatible board types and any files have been modified
|
||||
if (!Boards::isBoardCompatible(Firmware::getCurrentVariant()->getBoard(), newFw->getBoard()) && anyChildrenDirty()) {
|
||||
if (QMessageBox::question(this, tr("Companion"),
|
||||
tr("There are unsaved file changes which you may lose when switching radio types.\n\nDo you wish to continue?"),
|
||||
(QMessageBox::Yes | QMessageBox::No), QMessageBox::No) != QMessageBox::Yes) {
|
||||
updateProfilesActions();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the new profile number
|
||||
g.id(pid);
|
||||
Firmware::setCurrentVariant(Firmware::getFirmwareForId(g.profile[pid].fwType()));
|
||||
Firmware::setCurrentVariant(newFw);
|
||||
emit firmwareChanged();
|
||||
updateMenus();
|
||||
return true;
|
||||
}
|
||||
|
||||
void MainWindow::loadProfile()
|
||||
@@ -749,6 +776,8 @@ void MainWindow::loadProfile()
|
||||
void MainWindow::appPrefs()
|
||||
{
|
||||
AppPreferencesDialog * dialog = new AppPreferencesDialog(this);
|
||||
dialog->setMainWinHasDirtyChild(anyChildrenDirty());
|
||||
connect(dialog, &AppPreferencesDialog::firmwareProfileAboutToChange, this, &MainWindow::saveAll);
|
||||
connect(dialog, &AppPreferencesDialog::firmwareProfileChanged, this, &MainWindow::loadProfileId);
|
||||
dialog->exec();
|
||||
dialog->deleteLater();
|
||||
@@ -1275,6 +1304,7 @@ MdiChild * MainWindow::createMdiChild()
|
||||
connect(child, &MdiChild::windowTitleChanged, this, &MainWindow::onSubwindowTitleChanged);
|
||||
connect(child, &MdiChild::modified, this, &MainWindow::onSubwindowModified);
|
||||
connect(child, &MdiChild::newStatusMessage, statusBar(), &QStatusBar::showMessage);
|
||||
connect(child, &MdiChild::destroyed, win, &QMdiSubWindow::close);
|
||||
connect(win, &QMdiSubWindow::destroyed, this, &MainWindow::updateWindowActions);
|
||||
|
||||
updateWindowActions();
|
||||
@@ -1648,6 +1678,16 @@ QMdiSubWindow *MainWindow::findMdiChild(const QString &fileName)
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool MainWindow::anyChildrenDirty()
|
||||
{
|
||||
foreach (QMdiSubWindow * window, mdiArea->subWindowList()) {
|
||||
MdiChild * child;
|
||||
if ((child = qobject_cast<MdiChild *>(window->widget())) && child->isWindowModified())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MainWindow::updateRecentFileActions()
|
||||
{
|
||||
// Hide all document slots
|
||||
@@ -1769,9 +1809,8 @@ int MainWindow::newProfile(bool loadProfile)
|
||||
g.profile[i].name(tr("New Radio"));
|
||||
|
||||
if (loadProfile) {
|
||||
g.id(i);
|
||||
loadProfileId(i);
|
||||
appPrefs();
|
||||
if (loadProfileId(i))
|
||||
appPrefs();
|
||||
}
|
||||
|
||||
return i;
|
||||
@@ -1788,17 +1827,20 @@ void MainWindow::copyProfile()
|
||||
|
||||
if (newId > -1) {
|
||||
g.profile[newId] = g.profile[g.id()];
|
||||
g.id(newId);
|
||||
g.profile[newId].name(g.profile[newId].name() + tr(" - Copy"));
|
||||
loadProfileId(newId);
|
||||
appPrefs();
|
||||
if (loadProfileId(newId))
|
||||
appPrefs();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::deleteProfile(const int pid)
|
||||
{
|
||||
if (pid == g.id() && anyChildrenDirty()) {
|
||||
QMessageBox::warning(this, tr("Companion :: Open files warning"), tr("Please save or close modified file(s) before deleting the active profile."));
|
||||
return;
|
||||
}
|
||||
if (pid == 0) {
|
||||
QMessageBox::information(this, tr("Not possible to remove profile"), tr("The default profile can not be removed."));
|
||||
QMessageBox::warning(this, tr("Not possible to remove profile"), tr("The default profile can not be removed."));
|
||||
return;
|
||||
}
|
||||
int ret = QMessageBox::question(this,
|
||||
|
||||
@@ -100,9 +100,10 @@ class MainWindow : public QMainWindow
|
||||
void openFile();
|
||||
void save();
|
||||
void saveAs();
|
||||
void saveAll();
|
||||
void closeFile();
|
||||
void openRecentFile();
|
||||
void loadProfileId(const unsigned pid);
|
||||
bool loadProfileId(const unsigned pid);
|
||||
void loadProfile();
|
||||
void logFile();
|
||||
void writeEeprom();
|
||||
@@ -155,6 +156,7 @@ class MainWindow : public QMainWindow
|
||||
MdiChild * createMdiChild();
|
||||
MdiChild * activeMdiChild();
|
||||
QMdiSubWindow * findMdiChild(const QString & fileName);
|
||||
bool anyChildrenDirty();
|
||||
|
||||
bool readEepromFromRadio(const QString & filename);
|
||||
bool readFirmwareFromRadio(const QString & filename);
|
||||
|
||||
+77
-16
@@ -31,8 +31,10 @@
|
||||
#include "flashfirmwaredialog.h"
|
||||
#include "storage.h"
|
||||
#include "radiointerface.h"
|
||||
#include "radiodataconversionstate.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <ExportableTableView>
|
||||
|
||||
MdiChild::MdiChild(QWidget * parent, QWidget * parentWin, Qt::WindowFlags f):
|
||||
QWidget(parent, f),
|
||||
@@ -46,6 +48,7 @@ MdiChild::MdiChild(QWidget * parent, QWidget * parentWin, Qt::WindowFlags f):
|
||||
lastSelectedModel(-1),
|
||||
isUntitled(true),
|
||||
showCatToolbar(true),
|
||||
forceCloseFlag(false),
|
||||
stateDataVersion(1)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
@@ -113,7 +116,7 @@ MdiChild::~MdiChild()
|
||||
|
||||
void MdiChild::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
if (!maybeSave()) {
|
||||
if (!maybeSave() && !forceCloseFlag) {
|
||||
event->ignore();
|
||||
return;
|
||||
}
|
||||
@@ -702,8 +705,11 @@ void MdiChild::onFirmwareChanged()
|
||||
Firmware * previous = firmware;
|
||||
firmware = getCurrentFirmware();
|
||||
//qDebug() << "onFirmwareChanged" << previous->getName() << "=>" << firmware->getName();
|
||||
if (StorageFormat::getFourCC(previous->getBoard()) != StorageFormat::getFourCC(firmware->getBoard())) {
|
||||
convertStorage(previous->getBoard(), firmware->getBoard());
|
||||
if (!Boards::isBoardCompatible(previous->getBoard(), firmware->getBoard())) {
|
||||
if (!convertStorage(previous->getBoard(), firmware->getBoard())) {
|
||||
closeFile(true);
|
||||
return;
|
||||
}
|
||||
setModified();
|
||||
}
|
||||
}
|
||||
@@ -1327,8 +1333,9 @@ bool MdiChild::loadFile(const QString & filename, bool resetCurrentFile)
|
||||
setCurrentFile(filename);
|
||||
}
|
||||
|
||||
if (!storage.isBoardCompatible(getCurrentBoard())) {
|
||||
convertStorage(storage.getBoard(), getCurrentBoard());
|
||||
if (!Boards::isBoardCompatible(storage.getBoard(), getCurrentBoard())) {
|
||||
if (!convertStorage(storage.getBoard(), getCurrentBoard(), true))
|
||||
return false;
|
||||
setModified();
|
||||
}
|
||||
else {
|
||||
@@ -1362,10 +1369,8 @@ bool MdiChild::saveAs(bool isNew)
|
||||
if (fileName.isEmpty())
|
||||
return false;
|
||||
g.eepromDir( QFileInfo(fileName).dir().absolutePath() );
|
||||
if (isNew)
|
||||
return saveFile(fileName);
|
||||
else
|
||||
return saveFile(fileName, true);
|
||||
|
||||
return saveFile(fileName, true);
|
||||
}
|
||||
|
||||
bool MdiChild::saveFile(const QString & filename, bool setCurrent)
|
||||
@@ -1384,15 +1389,27 @@ bool MdiChild::saveFile(const QString & filename, bool setCurrent)
|
||||
return true;
|
||||
}
|
||||
|
||||
void MdiChild::closeFile(bool force)
|
||||
{
|
||||
forceCloseFlag = force;
|
||||
if (parentWindow)
|
||||
parentWindow->close();
|
||||
else
|
||||
this->close();
|
||||
}
|
||||
|
||||
bool MdiChild::maybeSave()
|
||||
{
|
||||
if (isWindowModified()) {
|
||||
int ret = askQuestion(tr("%1 has been modified.\nDo you want to save your changes?").arg(userFriendlyCurrentFile()),
|
||||
QMessageBox::Save, QMessageBox::Discard, QMessageBox::Cancel | QMessageBox::Default);
|
||||
(QMessageBox::Save | QMessageBox::Discard | (forceCloseFlag ? QMessageBox::NoButton : QMessageBox::Cancel)),
|
||||
(forceCloseFlag ? QMessageBox::Save : QMessageBox::Cancel));
|
||||
|
||||
if (ret == QMessageBox::Save)
|
||||
return save();
|
||||
else if (ret == QMessageBox::Cancel)
|
||||
else if (ret == QMessageBox::Discard)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -1428,13 +1445,57 @@ void MdiChild::forceNewFilename(const QString & suffix, const QString & ext)
|
||||
curFile.replace(QRegExp("\\.(eepe|bin|hex|otx)$"), suffix + "." + ext);
|
||||
}
|
||||
|
||||
void MdiChild::convertStorage(Board::Type from, Board::Type to)
|
||||
bool MdiChild::convertStorage(Board::Type from, Board::Type to, bool newFile)
|
||||
{
|
||||
showWarning(tr("Models and settings will be automatically converted.\nIf that is not what you intended, please close the file\nand choose the correct radio type/profile before reopening it."));
|
||||
radioData.convert(from, to);
|
||||
QMessageBox::StandardButtons btns;
|
||||
QMessageBox::StandardButton dfltBtn;
|
||||
QString q = tr("<p><b>Current radio type is not compatible with file %1, models and settings need to be converted.</b></p>").arg(userFriendlyCurrentFile());
|
||||
if (newFile) {
|
||||
q.append(tr("Do you wish to continue with the conversion?"));
|
||||
btns = (QMessageBox::Yes | QMessageBox::No);
|
||||
dfltBtn = QMessageBox::Yes;
|
||||
}
|
||||
else{
|
||||
q.append(tr("Choose <i>Apply</i> to convert the file, or <i>Close</i> to close it without conversion."));
|
||||
btns = (QMessageBox::Apply | QMessageBox::Close);
|
||||
dfltBtn = QMessageBox::Apply;
|
||||
}
|
||||
if (askQuestion(q, btns, dfltBtn) != dfltBtn)
|
||||
return false;
|
||||
|
||||
RadioDataConversionState cstate(from, to, &radioData);
|
||||
if (!cstate.convert())
|
||||
return false;
|
||||
forceNewFilename("_converted");
|
||||
initModelsList();
|
||||
isUntitled = true;
|
||||
|
||||
if (cstate.hasLogEntries(RadioDataConversionState::EVT_INF)) {
|
||||
QDialog * msgBox = new QDialog(Q_NULLPTR, Qt::Dialog | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
|
||||
|
||||
ExportableTableView * tv = new ExportableTableView(msgBox);
|
||||
tv->setSortingEnabled(true);
|
||||
tv->verticalHeader()->hide();
|
||||
tv->setModel(cstate.getLogModel(RadioDataConversionState::EVT_INF, tv));
|
||||
tv->resizeColumnsToContents();
|
||||
tv->resizeRowsToContents();
|
||||
|
||||
QDialogButtonBox * btnBox = new QDialogButtonBox(QDialogButtonBox::Ok, this);
|
||||
|
||||
QVBoxLayout * lo = new QVBoxLayout(msgBox);
|
||||
lo->addWidget(new QLabel(tr("<b>The conversion generated some important messages, please review them below.</b>")));
|
||||
lo->addWidget(tv);
|
||||
lo->addWidget(btnBox);
|
||||
|
||||
connect(btnBox, &QDialogButtonBox::accepted, msgBox, &QDialog::accept);
|
||||
connect(btnBox, &QDialogButtonBox::rejected, msgBox, &QDialog::reject);
|
||||
|
||||
msgBox->setWindowTitle(tr("Companion :: Conversion Result for %1").arg(curFile));
|
||||
msgBox->setAttribute(Qt::WA_DeleteOnClose);
|
||||
msgBox->show(); // modeless
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MdiChild::showWarning(const QString & msg)
|
||||
@@ -1443,9 +1504,9 @@ void MdiChild::showWarning(const QString & msg)
|
||||
QMessageBox::warning(this, "Companion", msg);
|
||||
}
|
||||
|
||||
int MdiChild::askQuestion(const QString & msg, int button0, int button1, int button2)
|
||||
int MdiChild::askQuestion(const QString & msg, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton)
|
||||
{
|
||||
return QMessageBox::question(this, tr("Companion"), msg, button0, button1, button2);
|
||||
return QMessageBox::question(this, tr("Companion"), msg, buttons, defaultButton);
|
||||
}
|
||||
|
||||
void MdiChild::writeEeprom() // write to Tx
|
||||
|
||||
@@ -86,6 +86,7 @@ class MdiChild : public QWidget
|
||||
bool save();
|
||||
bool saveAs(bool isNew=false);
|
||||
bool saveFile(const QString & fileName, bool setCurrent=true);
|
||||
void closeFile(bool force = false);
|
||||
void writeEeprom();
|
||||
void print(int model=-1, const QString & filename="");
|
||||
void onFirmwareChanged();
|
||||
@@ -179,9 +180,9 @@ class MdiChild : public QWidget
|
||||
bool maybeSave();
|
||||
void setCurrentFile(const QString & fileName);
|
||||
void forceNewFilename(const QString & suffix = "", const QString & ext = "otx");
|
||||
void convertStorage(Board::Type from, Board::Type to);
|
||||
bool convertStorage(Board::Type from, Board::Type to, bool newFile = false);
|
||||
void showWarning(const QString & msg);
|
||||
int askQuestion(const QString & msg, int button0 = QMessageBox::Yes, int button1 = QMessageBox::No | QMessageBox::Default, int button2 = 0);
|
||||
int askQuestion(const QString & msg, QMessageBox::StandardButtons buttons = (QMessageBox::Yes | QMessageBox::No), QMessageBox::StandardButton defaultButton = QMessageBox::No);
|
||||
|
||||
Ui::MdiChild * ui;
|
||||
TreeModel * modelsListModel;
|
||||
@@ -200,6 +201,7 @@ class MdiChild : public QWidget
|
||||
int lastSelectedModel;
|
||||
bool isUntitled;
|
||||
bool showCatToolbar;
|
||||
bool forceCloseFlag;
|
||||
const quint16 stateDataVersion;
|
||||
};
|
||||
|
||||
|
||||
@@ -38,14 +38,14 @@ RawSwitchItemModel::RawSwitchItemModel(const GeneralSettings * const generalSett
|
||||
add(SWITCH_TYPE_VIRTUAL, -fw->getCapability(LogicalSwitches));
|
||||
add(SWITCH_TYPE_ROTARY_ENCODER, -fw->getCapability(RotaryEncoders));
|
||||
add(SWITCH_TYPE_TRIM, -board.getCapability(Board::NumTrimSwitches));
|
||||
add(SWITCH_TYPE_MULTIPOS_POT, -(fw->getCapability(MultiposPots) * fw->getCapability(MultiposPotsPositions)));
|
||||
add(SWITCH_TYPE_MULTIPOS_POT, -(board.getCapability(Board::MultiposPots) * board.getCapability(Board::MultiposPotsPositions)));
|
||||
add(SWITCH_TYPE_SWITCH, -board.getCapability(Board::SwitchPositions));
|
||||
|
||||
// Ascending switch direction (including zero)
|
||||
add(SWITCH_TYPE_TIMER_MODE, 5);
|
||||
add(SWITCH_TYPE_NONE, 1);
|
||||
add(SWITCH_TYPE_SWITCH, board.getCapability(Board::SwitchPositions));
|
||||
add(SWITCH_TYPE_MULTIPOS_POT, fw->getCapability(MultiposPots) * fw->getCapability(MultiposPotsPositions));
|
||||
add(SWITCH_TYPE_MULTIPOS_POT, board.getCapability(Board::MultiposPots) * board.getCapability(Board::MultiposPotsPositions));
|
||||
add(SWITCH_TYPE_TRIM, board.getCapability(Board::NumTrimSwitches));
|
||||
add(SWITCH_TYPE_ROTARY_ENCODER, fw->getCapability(RotaryEncoders));
|
||||
add(SWITCH_TYPE_VIRTUAL, fw->getCapability(LogicalSwitches));
|
||||
@@ -73,15 +73,6 @@ void RawSwitchItemModel::add(const RawSwitchType & type, int count)
|
||||
rawIdxAdj = -1;
|
||||
|
||||
for ( ; i < maxCount; ++i) {
|
||||
if (generalSettings) {
|
||||
if (type == SWITCH_TYPE_SWITCH && IS_HORUS_OR_TARANIS(board) && !generalSettings->switchPositionAllowedTaranis(abs(i)))
|
||||
continue;
|
||||
if (type == SWITCH_TYPE_MULTIPOS_POT) {
|
||||
int pot = div(abs(i) - 1, getCurrentFirmware()->getCapability(MultiposPotsPositions)).quot;
|
||||
if (!generalSettings->isPotAvailable(pot) || generalSettings->potConfig[pot] != Board::POT_MULTIPOS_SWITCH)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
RawSwitch rs(type, i + rawIdxAdj);
|
||||
QStandardItem * modelItem = new QStandardItem(rs.toString(board, generalSettings, modelData));
|
||||
modelItem->setData(rs.toValue(), Qt::UserRole);
|
||||
@@ -131,7 +122,7 @@ bool RawSwitchFilterItemModel::filterAcceptsRow(int sourceRow, const QModelIndex
|
||||
if ((swtch.type == SWITCH_TYPE_ON || swtch.type == SWITCH_TYPE_ONE) && (context != SpecialFunctionsContext && context != GlobalFunctionsContext))
|
||||
return false;
|
||||
|
||||
if (!modelData->isAvailable(swtch))
|
||||
if (!swtch.isAvailable(modelData, generalSettings, getCurrentBoard()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
||||
@@ -490,13 +490,7 @@ QString ModelPrinter::printFlightModeSwitch(const RawSwitch & swtch)
|
||||
|
||||
QString ModelPrinter::printFlightModeName(int index)
|
||||
{
|
||||
const FlightModeData & fm = model.flightModeData[index];
|
||||
if (strlen(fm.name) > 0) {
|
||||
return QString("%1").arg(fm.name);
|
||||
}
|
||||
else {
|
||||
return tr("FM%1").arg(index);
|
||||
}
|
||||
return model.flightModeData[index].toString(index);
|
||||
}
|
||||
|
||||
QString ModelPrinter::printFlightModes(unsigned int flightModes)
|
||||
|
||||
+249
-121
@@ -19,43 +19,70 @@
|
||||
*/
|
||||
|
||||
#include "radiodata.h"
|
||||
#include "radiodataconversionstate.h"
|
||||
#include "eeprominterface.h"
|
||||
|
||||
// TODO here we will move a lot of functions from eeprominterface.cpp when no merge risk
|
||||
|
||||
RawSource RawSource::convert(Board::Type before, Board::Type after)
|
||||
RawSource RawSource::convert(RadioDataConversionState & cstate)
|
||||
{
|
||||
if (type == SOURCE_TYPE_STICK && index >= 4 + getBoardCapability(before, Board::Pots)) {
|
||||
// 1st slider alignment
|
||||
index += getBoardCapability(after, Board::Pots) - getBoardCapability(before, Board::Pots);
|
||||
}
|
||||
cstate.setItemType("SRC", 1);
|
||||
RadioDataConversionState::EventType evt = RadioDataConversionState::EVT_NONE;
|
||||
RadioDataConversionState::LogField oldData(index, toString(cstate.fromModel(), cstate.fromGS(), cstate.fromType));
|
||||
|
||||
if (IS_HORUS(after)) {
|
||||
if (IS_TARANIS_X9D(before)) {
|
||||
if (type == SOURCE_TYPE_STICK && index >= 7) {
|
||||
// LS and RS on Horus are after sliders L1 and L2
|
||||
index += 2;
|
||||
if (type == SOURCE_TYPE_STICK) {
|
||||
if (cstate.toBoard.getCapability(Board::Sliders)) {
|
||||
if (index >= cstate.fromBoard.getCapability(Board::Sticks) + cstate.fromBoard.getCapability(Board::Pots)) {
|
||||
// 1st slider alignment
|
||||
index += cstate.toBoard.getCapability(Board::Pots) - cstate.fromBoard.getCapability(Board::Pots);
|
||||
}
|
||||
|
||||
if (isSlider(0, cstate.fromType)) {
|
||||
// LS and RS sliders are after 2 aux sliders on X12 and X9E
|
||||
if ((IS_HORUS_X12S(cstate.toType) || IS_TARANIS_X9E(cstate.toType)) && !IS_HORUS_X12S(cstate.fromType) && !IS_TARANIS_X9E(cstate.fromType)) {
|
||||
if (index >= 7) {
|
||||
index += 2; // LS/RS to LS/RS
|
||||
}
|
||||
}
|
||||
else if (!IS_TARANIS_X9E(cstate.toType) && !IS_HORUS_X12S(cstate.toType) && (IS_HORUS_X12S(cstate.fromType) || IS_TARANIS_X9E(cstate.fromType))) {
|
||||
if (index >= 7 && index <= 8) {
|
||||
index += 2; // aux sliders to spare analogs (which may not exist, this is validated later)
|
||||
evt = RadioDataConversionState::EVT_CVRT;
|
||||
}
|
||||
else if (index >= 9 && index <= 10) {
|
||||
index -= 2; // LS/RS to LS/RS
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SWI to SWR don't exist on !X9E board
|
||||
if (!IS_TARANIS_X9E(after) && IS_TARANIS_X9E(before)) {
|
||||
if (type == SOURCE_TYPE_SWITCH && index >= 8) {
|
||||
index = index % 8;
|
||||
if (IS_TARANIS(cstate.toType) && IS_HORUS(cstate.fromType)) {
|
||||
if (index == 6)
|
||||
index = 5; // pot S2 to S2
|
||||
else if (index == 5)
|
||||
index = -1; // 6P on Horus doesn't exist on Taranis
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_TARANIS_X7(after)) {
|
||||
// No S3, LS and RS on X7 board
|
||||
if (type == SOURCE_TYPE_STICK && index >= 6) {
|
||||
index = 5;
|
||||
else if (IS_HORUS(cstate.toType) && IS_TARANIS(cstate.fromType) && index == 5)
|
||||
{
|
||||
index = 6; // pot S2 to S2
|
||||
}
|
||||
|
||||
// No SE and SG on X7 board
|
||||
if ((IS_TARANIS_X9(before) || IS_HORUS(before)) && type == SOURCE_TYPE_SWITCH) {
|
||||
} // SOURCE_TYPE_STICK
|
||||
|
||||
if (type == SOURCE_TYPE_SWITCH) {
|
||||
// SWI to SWR don't exist on !X9E board
|
||||
if (!IS_TARANIS_X9E(cstate.toType) && IS_TARANIS_X9E(cstate.fromType)) {
|
||||
if (index >= 8) {
|
||||
index = index % 8;
|
||||
evt = RadioDataConversionState::EVT_CVRT;
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_TARANIS_X7(cstate.toType) && (IS_TARANIS_X9(cstate.fromType) || IS_HORUS(cstate.fromType))) {
|
||||
// No SE and SG on X7 board
|
||||
if (index == 4 || index == 6) {
|
||||
index = 3; // SG and SE to SD
|
||||
evt = RadioDataConversionState::EVT_CVRT;
|
||||
}
|
||||
else if (index == 5) {
|
||||
index = 4; // SF to SF
|
||||
@@ -64,170 +91,269 @@ RawSource RawSource::convert(Board::Type before, Board::Type after)
|
||||
index = 5; // SH to SH
|
||||
}
|
||||
}
|
||||
}
|
||||
// Compensate for SE and SG on X9/Horus board if converting from X7
|
||||
else if ((IS_TARANIS_X9(cstate.toType) || IS_HORUS(cstate.toType)) && IS_TARANIS_X7(cstate.fromType)) {
|
||||
if (index == 4) {
|
||||
index = 5; // SF to SF
|
||||
}
|
||||
else if (index == 5) {
|
||||
index = 7; // SH to SH
|
||||
}
|
||||
}
|
||||
} // SOURCE_TYPE_SWITCH
|
||||
|
||||
// Compensate for SE and SG on X9/Horus board if converting from X7
|
||||
if ((IS_TARANIS_X9(after) || IS_HORUS(after)) && IS_TARANIS_X7(before) && type == SOURCE_TYPE_SWITCH) {
|
||||
if (index == 4) {
|
||||
index = 5; // SF to SF
|
||||
}
|
||||
else if (index == 5) {
|
||||
index = 7; // SH to SH
|
||||
}
|
||||
// final validation (we do not pass model to isAvailable() because we don't know what has or hasn't been converted)
|
||||
if (!isAvailable(NULL, cstate.toGS(), cstate.toType)) {
|
||||
cstate.setInvalid(oldData);
|
||||
index = -1; // TODO: better way to flag invalid sources?
|
||||
type = MAX_SOURCE_TYPE;
|
||||
}
|
||||
else if (evt == RadioDataConversionState::EVT_CVRT) {
|
||||
cstate.setConverted(oldData, RadioDataConversionState::LogField(index, toString(cstate.toModel(), cstate.toGS(), cstate.toType)));
|
||||
}
|
||||
else if (oldData.id != index) {
|
||||
// provide info by default if anything changed
|
||||
cstate.setMoved(oldData, RadioDataConversionState::LogField(index, toString(cstate.toModel(), cstate.toGS(), cstate.toType)));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
RawSwitch RawSwitch::convert(Board::Type before, Board::Type after)
|
||||
RawSwitch RawSwitch::convert(RadioDataConversionState & cstate)
|
||||
{
|
||||
if (!index || type != SWITCH_TYPE_SWITCH) {
|
||||
return *this; // no changes
|
||||
if (!index)
|
||||
return *this;
|
||||
|
||||
cstate.setItemType("SW", 2);
|
||||
RadioDataConversionState::EventType evt = RadioDataConversionState::EVT_NONE;
|
||||
RadioDataConversionState::LogField oldData(index, toString(cstate.fromType, cstate.fromGS(), cstate.fromModel()));
|
||||
|
||||
if (type == SWITCH_TYPE_SWITCH) {
|
||||
int srcIdx = div(abs(index)-1, 3).quot; // raw source index
|
||||
int delta = 0;
|
||||
|
||||
// SWI to SWR don't exist on !X9E board
|
||||
if (!IS_TARANIS_X9E(cstate.toType) && IS_TARANIS_X9E(cstate.fromType)) {
|
||||
if (srcIdx > 7) {
|
||||
index %= 24;
|
||||
evt = RadioDataConversionState::EVT_CVRT;
|
||||
}
|
||||
}
|
||||
|
||||
// No SE and SG on X7 board
|
||||
if (IS_TARANIS_X7(cstate.toType) && (IS_TARANIS_X9(cstate.fromType) || IS_HORUS(cstate.fromType))) {
|
||||
if (srcIdx == 4 || srcIdx == 5) {
|
||||
delta = 3; // SE to SD & SF to SF
|
||||
if (srcIdx == 4)
|
||||
evt = RadioDataConversionState::EVT_CVRT;
|
||||
}
|
||||
else if (srcIdx == 6) {
|
||||
delta = 9; // SG to SD
|
||||
evt = RadioDataConversionState::EVT_CVRT;
|
||||
}
|
||||
else if (srcIdx == 7) {
|
||||
delta = 6; // SH to SH
|
||||
}
|
||||
}
|
||||
// Compensate for SE and SG on X9/Horus board if converting from X7
|
||||
else if ((IS_TARANIS_X9(cstate.toType) || IS_HORUS(cstate.toType)) && IS_TARANIS_X7(cstate.fromType)) {
|
||||
if (srcIdx == 4) {
|
||||
delta = -3; // SF to SF
|
||||
}
|
||||
else if (srcIdx == 5) {
|
||||
delta = -6; // SH to SH
|
||||
}
|
||||
}
|
||||
|
||||
if (index < 0) {
|
||||
delta = -delta; // invert for !switch
|
||||
}
|
||||
|
||||
index -= delta;
|
||||
|
||||
} // SWITCH_TYPE_SWITCH
|
||||
|
||||
// final validation (we do not pass model to isAvailable() because we don't know what has or hasn't been converted)
|
||||
if (!isAvailable(NULL, cstate.toGS(), cstate.toType)) {
|
||||
cstate.setInvalid(oldData);
|
||||
type = MAX_SWITCH_TYPE; // TODO: better way to flag invalid switches?
|
||||
}
|
||||
|
||||
// SWI to SWR don't exist on !X9E board
|
||||
if (!IS_TARANIS_X9E(after) && IS_TARANIS_X9E(before)) {
|
||||
if (abs(index) > 24) {
|
||||
index = index % 24;
|
||||
}
|
||||
else if (evt == RadioDataConversionState::EVT_CVRT) {
|
||||
cstate.setConverted(oldData, RadioDataConversionState::LogField(index, toString(cstate.toType, cstate.toGS(), cstate.toModel())));
|
||||
}
|
||||
|
||||
int srcIdx = div(abs(index)-1, 3).quot; // raw source index
|
||||
int delta = 0;
|
||||
|
||||
// No SE and SG on X7 board
|
||||
if (IS_TARANIS_X7(after) && (IS_TARANIS_X9(before) || IS_HORUS(before))) {
|
||||
if (srcIdx == 4 || srcIdx == 5) {
|
||||
delta = 3; // SE to SD & SF to SF
|
||||
}
|
||||
else if (srcIdx == 6) {
|
||||
delta = 9; // SG to SD
|
||||
}
|
||||
else if (srcIdx == 7) {
|
||||
delta = 6; // SH to SH
|
||||
}
|
||||
else if (oldData.id != index) {
|
||||
// provide info by default if anything changed
|
||||
cstate.setMoved(oldData, RadioDataConversionState::LogField(index, toString(cstate.toType, cstate.toGS(), cstate.toModel())));
|
||||
}
|
||||
|
||||
// Compensate for SE and SG on X9/Horus board if converting from X7
|
||||
if ((IS_TARANIS_X9(after) || IS_HORUS(after)) && IS_TARANIS_X7(before)) {
|
||||
if (srcIdx == 4) {
|
||||
delta = -3; // SF to SF
|
||||
}
|
||||
else if (srcIdx == 5) {
|
||||
delta = -6; // SH to SH
|
||||
}
|
||||
}
|
||||
|
||||
if (index < 0) {
|
||||
delta = -delta; // invert for !switch
|
||||
}
|
||||
|
||||
index -= delta;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void ExpoData::convert(Board::Type before, Board::Type after)
|
||||
void ExpoData::convert(RadioDataConversionState & cstate)
|
||||
{
|
||||
srcRaw.convert(before, after);
|
||||
swtch.convert(before, after);
|
||||
cstate.setComponent(QObject::tr("INP"), 3);
|
||||
cstate.setSubComp(RawSource(SOURCE_TYPE_VIRTUAL_INPUT, chn).toString(cstate.fromModel(), cstate.fromGS(), cstate.fromType) % QObject::tr(" (@%1)").arg(cstate.subCompIdx));
|
||||
srcRaw.convert(cstate);
|
||||
swtch.convert(cstate);
|
||||
}
|
||||
|
||||
void MixData::convert(Board::Type before, Board::Type after)
|
||||
void MixData::convert(RadioDataConversionState & cstate)
|
||||
{
|
||||
srcRaw.convert(before, after);
|
||||
swtch.convert(before, after);
|
||||
cstate.setComponent(QObject::tr("MIX"), 4);
|
||||
cstate.setSubComp(RawSource(SOURCE_TYPE_CH, destCh-1).toString(cstate.fromModel(), cstate.fromGS(), cstate.fromType) % QObject::tr(" (@%1)").arg(cstate.subCompIdx));
|
||||
srcRaw.convert(cstate);
|
||||
swtch.convert(cstate);
|
||||
}
|
||||
|
||||
void LogicalSwitchData::convert(Board::Type before, Board::Type after)
|
||||
void LogicalSwitchData::convert(RadioDataConversionState & cstate)
|
||||
{
|
||||
cstate.setComponent("LSW", 7);
|
||||
cstate.setSubComp(RawSwitch(SWITCH_TYPE_VIRTUAL, cstate.subCompIdx + 1).toString(cstate.fromType, cstate.fromGS(), cstate.fromModel()));
|
||||
CSFunctionFamily family = getFunctionFamily();
|
||||
switch(family) {
|
||||
case LS_FAMILY_VOFS:
|
||||
val1 = RawSource(val1).convert(before, after).toValue();
|
||||
val1 = RawSource(val1).convert(cstate.withComponentField("V1")).toValue();
|
||||
break;
|
||||
case LS_FAMILY_STICKY:
|
||||
case LS_FAMILY_VBOOL:
|
||||
val1 = RawSwitch(val1).convert(before, after).toValue();
|
||||
val2 = RawSwitch(val2).convert(before, after).toValue();
|
||||
val1 = RawSwitch(val1).convert(cstate.withComponentField("V1")).toValue();
|
||||
val2 = RawSwitch(val2).convert(cstate.withComponentField("V2")).toValue();
|
||||
break;
|
||||
case LS_FAMILY_EDGE:
|
||||
val1 = RawSwitch(val1).convert(before, after).toValue();
|
||||
val1 = RawSwitch(val1).convert(cstate.withComponentField("V1")).toValue();
|
||||
break;
|
||||
case LS_FAMILY_VCOMP:
|
||||
val1 = RawSource(val1).convert(before, after).toValue();
|
||||
val2 = RawSource(val2).convert(before, after).toValue();
|
||||
val1 = RawSource(val1).convert(cstate.withComponentField("V1")).toValue();
|
||||
val2 = RawSource(val2).convert(cstate.withComponentField("V2")).toValue();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
andsw = RawSwitch(andsw).convert(before, after).toValue();
|
||||
andsw = RawSwitch(andsw).convert(cstate.withComponentField("AND")).toValue();
|
||||
}
|
||||
|
||||
void CustomFunctionData::convert(Board::Type before, Board::Type after)
|
||||
void CustomFunctionData::convert(RadioDataConversionState & cstate)
|
||||
{
|
||||
swtch.convert(before, after);
|
||||
if (func == FuncVolume || func == FuncPlayValue || (func >= FuncAdjustGV1 && func <= FuncAdjustGVLast && adjustMode == 1))
|
||||
param = RawSource(param).convert(before, after).toValue();
|
||||
cstate.setComponent("CFN", 8);
|
||||
cstate.setSubComp(toString(cstate.subCompIdx, (cstate.toModel() ? false : true)));
|
||||
swtch.convert(cstate);
|
||||
if (func == FuncVolume || func == FuncPlayValue || (func >= FuncAdjustGV1 && func <= FuncAdjustGVLast && adjustMode == 1)) {
|
||||
param = RawSource(param).convert(cstate.withComponentField("PARAM")).toValue();
|
||||
}
|
||||
}
|
||||
|
||||
void FlightModeData::convert(Board::Type before, Board::Type after)
|
||||
void FlightModeData::convert(RadioDataConversionState & cstate)
|
||||
{
|
||||
swtch.convert(before, after);
|
||||
cstate.setComponent("FMD", 2);
|
||||
cstate.setSubComp(toString(cstate.subCompIdx));
|
||||
swtch.convert(cstate);
|
||||
}
|
||||
|
||||
void ModelData::convert(Board::Type before, Board::Type after)
|
||||
void TimerData::convert(RadioDataConversionState & cstate)
|
||||
{
|
||||
cstate.setComponent("TMR", 1);
|
||||
cstate.setSubComp(QObject::tr("Timer %1").arg(cstate.subCompIdx + 1));
|
||||
mode.convert(cstate);
|
||||
}
|
||||
|
||||
void ModelData::convert(RadioDataConversionState & cstate)
|
||||
{
|
||||
// Here we can add explicit conversions when moving from one board to another
|
||||
|
||||
QString origin = QString(name);
|
||||
if (origin.isEmpty())
|
||||
origin = QString::number(cstate.modelIdx+1);
|
||||
cstate.setOrigin(QObject::tr("Model: ") % origin);
|
||||
|
||||
cstate.setComponent("SET", 0);
|
||||
if (thrTraceSrc && (int)thrTraceSrc < cstate.fromBoard.getCapability(Board::Pots) + cstate.fromBoard.getCapability(Board::Sliders)) {
|
||||
cstate.setSubComp(QObject::tr("Throttle Source"));
|
||||
thrTraceSrc = RawSource(SOURCE_TYPE_STICK, (int)thrTraceSrc + 3).convert(cstate).index - 3;
|
||||
}
|
||||
|
||||
for (int i=0; i<CPN_MAX_TIMERS; i++) {
|
||||
timers[i].convert(cstate.withComponentIndex(i));
|
||||
}
|
||||
|
||||
for (int i=0; i<CPN_MAX_MIXERS; i++) {
|
||||
mixData[i].convert(before, after);
|
||||
mixData[i].convert(cstate.withComponentIndex(i));
|
||||
}
|
||||
|
||||
for (int i=0; i<CPN_MAX_EXPOS; i++) {
|
||||
expoData[i].convert(before, after);
|
||||
expoData[i].convert(cstate.withComponentIndex(i));
|
||||
}
|
||||
|
||||
for (int i=0; i<CPN_MAX_LOGICAL_SWITCHES; i++) {
|
||||
logicalSw[i].convert(before, after);
|
||||
logicalSw[i].convert(cstate.withComponentIndex(i));
|
||||
}
|
||||
|
||||
for (int i=0; i<CPN_MAX_SPECIAL_FUNCTIONS; i++) {
|
||||
customFn[i].convert(before, after);
|
||||
customFn[i].convert(cstate.withComponentIndex(i));
|
||||
}
|
||||
|
||||
for (int i=0; i<CPN_MAX_FLIGHT_MODES; i++) {
|
||||
flightModeData[i].convert(before, after);
|
||||
flightModeData[i].convert(cstate.withComponentIndex(i));
|
||||
}
|
||||
}
|
||||
|
||||
void GeneralSettings::convert(Board::Type before, Board::Type after)
|
||||
void GeneralSettings::convert(RadioDataConversionState & cstate)
|
||||
{
|
||||
// Here we can add explicit conversions when moving from one board to another
|
||||
|
||||
for (int i=0; i<CPN_MAX_SPECIAL_FUNCTIONS; i++) {
|
||||
customFn[i].convert(before, after);
|
||||
}
|
||||
cstate.setOrigin(QObject::tr("Radio Settings"));
|
||||
|
||||
// No SE and SG on X7 board
|
||||
if (IS_TARANIS_X7(after)) {
|
||||
if (IS_TARANIS_X9(before) || IS_HORUS(before)) {
|
||||
switchConfig[4] = switchConfig[5];
|
||||
memcpy(switchName[4], switchName[5], sizeof(switchName[4]));
|
||||
switchConfig[5] = switchConfig[7];
|
||||
memcpy(switchName[5], switchName[7], sizeof(switchName[5]));
|
||||
setDefaultControlTypes(cstate.toType); // start with default switches/pots/sliders
|
||||
|
||||
// Try to intelligently copy any custom control names
|
||||
|
||||
// SE and SG are skipped on X7 board
|
||||
if (IS_TARANIS_X7(cstate.toType)) {
|
||||
if (IS_TARANIS_X9(cstate.fromType) || IS_HORUS(cstate.fromType)) {
|
||||
strncpy(switchName[4], switchName[5], sizeof(switchName[0]));
|
||||
strncpy(switchName[5], switchName[7], sizeof(switchName[0]));
|
||||
}
|
||||
}
|
||||
else if (IS_TARANIS_X7(cstate.fromType)) {
|
||||
if (IS_TARANIS_X9(cstate.toType) || IS_HORUS(cstate.toType)) {
|
||||
strncpy(switchName[5], switchName[4], sizeof(switchName[0]));
|
||||
strncpy(switchName[7], switchName[5], sizeof(switchName[0]));
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_TARANIS(after)) {
|
||||
unsigned int min_contrast = getCurrentFirmware()->getCapability(MinContrast);
|
||||
unsigned int max_contrast = getCurrentFirmware()->getCapability(MaxContrast);
|
||||
|
||||
if (contrast < min_contrast)
|
||||
contrast = min_contrast;
|
||||
else if (contrast > max_contrast)
|
||||
contrast = max_contrast;
|
||||
// LS and RS sliders are after 2 aux sliders on X12 and X9E
|
||||
if ((IS_HORUS_X12S(cstate.toType) || IS_TARANIS_X9E(cstate.toType)) && !IS_HORUS_X12S(cstate.fromType) && !IS_TARANIS_X9E(cstate.fromType)) {
|
||||
strncpy(sliderName[0], sliderName[2], sizeof(sliderName[0]));
|
||||
strncpy(sliderName[1], sliderName[3], sizeof(sliderName[0]));
|
||||
}
|
||||
else if (!IS_TARANIS_X9E(cstate.toType) && !IS_HORUS_X12S(cstate.toType) && (IS_HORUS_X12S(cstate.fromType) || IS_TARANIS_X9E(cstate.fromType))) {
|
||||
strncpy(sliderName[2], sliderName[0], sizeof(sliderName[0]));
|
||||
strncpy(sliderName[3], sliderName[1], sizeof(sliderName[0]));
|
||||
}
|
||||
|
||||
if (IS_HORUS(cstate.toType)) {
|
||||
// 6P switch is only on Horus (by default)
|
||||
if (cstate.fromBoard.getCapability(Board::FactoryInstalledPots) == 2) {
|
||||
strncpy(potName[2], potName[1], sizeof(potName[0]));
|
||||
potName[1][0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_TARANIS(cstate.toType)) {
|
||||
// No S3 pot on Taranis boards by default
|
||||
if (cstate.fromBoard.getCapability(Board::FactoryInstalledPots) > 2)
|
||||
strncpy(potName[1], potName[2], sizeof(potName[0]));
|
||||
|
||||
contrast = qBound<int>(getCurrentFirmware()->getCapability(MinContrast), contrast, getCurrentFirmware()->getCapability(MaxContrast));
|
||||
}
|
||||
|
||||
// TODO: Would be nice at this point to have GUI pause and ask the user to set up any custom hardware they have on the destination radio.
|
||||
|
||||
// Convert all global functions (do this after HW adjustments)
|
||||
for (int i=0; i<CPN_MAX_SPECIAL_FUNCTIONS; i++) {
|
||||
customFn[i].convert(cstate.withComponentIndex(i));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -297,12 +423,14 @@ QString RadioData::getNextModelFilename()
|
||||
return filename;
|
||||
}
|
||||
|
||||
void RadioData::convert(Board::Type before, Board::Type after)
|
||||
void RadioData::convert(RadioDataConversionState & cstate)
|
||||
{
|
||||
generalSettings.convert(before, after);
|
||||
generalSettings.convert(cstate.withModelIndex(-1));
|
||||
|
||||
for (unsigned i=0; i<models.size(); i++) {
|
||||
models[i].convert(before, after);
|
||||
models[i].convert(cstate.withModelIndex(i));
|
||||
}
|
||||
|
||||
if (categories.size() == 0) {
|
||||
categories.push_back(CategoryData(qPrintable(QObject::tr("Models"))));
|
||||
for (unsigned i=0; i<models.size(); i++) {
|
||||
@@ -310,7 +438,7 @@ void RadioData::convert(Board::Type before, Board::Type after)
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_HORUS(after)) {
|
||||
if (IS_HORUS(cstate.toType)) {
|
||||
fixModelFilenames();
|
||||
}
|
||||
|
||||
|
||||
+25
-21
@@ -7,6 +7,9 @@
|
||||
#include <QComboBox>
|
||||
|
||||
class Firmware;
|
||||
class ModelData;
|
||||
class GeneralSettings;
|
||||
class RadioDataConversionState;
|
||||
|
||||
enum Switches {
|
||||
SWITCH_NONE,
|
||||
@@ -80,9 +83,6 @@ enum HeliSwashTypes {
|
||||
HELI_SWASH_TYPE_90
|
||||
};
|
||||
|
||||
class ModelData;
|
||||
class GeneralSettings;
|
||||
|
||||
enum TelemetrySource {
|
||||
TELEMETRY_SOURCE_TX_BATT,
|
||||
TELEMETRY_SOURCE_TX_TIME,
|
||||
@@ -211,16 +211,19 @@ class RawSource {
|
||||
{
|
||||
}
|
||||
|
||||
RawSource convert(Board::Type before, Board::Type after);
|
||||
|
||||
inline const int toValue() const
|
||||
{
|
||||
return index >= 0 ? (type * 65536 + index) : -(type * 65536 - index);
|
||||
}
|
||||
|
||||
QString toString(const ModelData * model = NULL, const GeneralSettings * const generalSettings = NULL) const;
|
||||
|
||||
RawSource convert(RadioDataConversionState & cstate);
|
||||
QString toString(const ModelData * model = NULL, const GeneralSettings * const generalSettings = NULL, Board::Type board = Board::BOARD_UNKNOWN) const;
|
||||
RawSourceRange getRange(const ModelData * model, const GeneralSettings & settings, unsigned int flags=0) const;
|
||||
bool isStick(int * potsIndex = NULL, Board::Type board = Board::BOARD_UNKNOWN) const;
|
||||
bool isPot(int * potsIndex = NULL, Board::Type board = Board::BOARD_UNKNOWN) const;
|
||||
bool isSlider(int * sliderIndex = NULL, Board::Type board = Board::BOARD_UNKNOWN) const;
|
||||
bool isTimeBased(Board::Type board = Board::BOARD_UNKNOWN) const;
|
||||
bool isAvailable(const ModelData * const model = NULL, const GeneralSettings * const gs = NULL, Board::Type board = Board::BOARD_UNKNOWN);
|
||||
|
||||
bool operator == ( const RawSource & other) {
|
||||
return (this->type == other.type) && (this->index == other.index);
|
||||
@@ -230,11 +233,6 @@ class RawSource {
|
||||
return (this->type != other.type) || (this->index != other.index);
|
||||
}
|
||||
|
||||
bool isTimeBased() const;
|
||||
bool isStick(int * potsIndex = NULL) const;
|
||||
bool isPot(int * potsIndex = NULL) const;
|
||||
bool isSlider(int * sliderIndex = NULL) const;
|
||||
|
||||
RawSourceType type;
|
||||
int index;
|
||||
};
|
||||
@@ -253,6 +251,7 @@ enum RawSwitchType {
|
||||
SWITCH_TYPE_TIMER_MODE,
|
||||
SWITCH_TYPE_TELEMETRY,
|
||||
SWITCH_TYPE_SENSOR,
|
||||
MAX_SWITCH_TYPE
|
||||
};
|
||||
|
||||
class RawSwitch {
|
||||
@@ -280,7 +279,9 @@ class RawSwitch {
|
||||
return index >= 0 ? (type * 256 + index) : -(type * 256 - index);
|
||||
}
|
||||
|
||||
RawSwitch convert(RadioDataConversionState & cstate);
|
||||
QString toString(Board::Type board = Board::BOARD_UNKNOWN, const GeneralSettings * const generalSettings = NULL, const ModelData * const modelData = NULL) const;
|
||||
bool isAvailable(const ModelData * const model = NULL, const GeneralSettings * const gs = NULL, Board::Type board = Board::BOARD_UNKNOWN);
|
||||
|
||||
bool operator== ( const RawSwitch& other) {
|
||||
return (this->type == other.type) && (this->index == other.index);
|
||||
@@ -290,7 +291,6 @@ class RawSwitch {
|
||||
return (this->type != other.type) || (this->index != other.index);
|
||||
}
|
||||
|
||||
RawSwitch convert(Board::Type before, Board::Type after);
|
||||
|
||||
RawSwitchType type;
|
||||
int index;
|
||||
@@ -343,7 +343,7 @@ class ExpoData {
|
||||
int carryTrim;
|
||||
char name[10+1];
|
||||
void clear() { memset(this, 0, sizeof(ExpoData)); }
|
||||
void convert(Board::Type before, Board::Type after);
|
||||
void convert(RadioDataConversionState & cstate);
|
||||
};
|
||||
|
||||
class CurvePoint {
|
||||
@@ -401,7 +401,7 @@ enum MltpxValue {
|
||||
class MixData {
|
||||
public:
|
||||
MixData() { clear(); }
|
||||
void convert(Board::Type before, Board::Type after);
|
||||
void convert(RadioDataConversionState & cstate);
|
||||
|
||||
unsigned int destCh; // 1..CPN_MAX_CHNOUT
|
||||
RawSource srcRaw;
|
||||
@@ -478,7 +478,7 @@ class LogicalSwitchData { // Logical Switches data
|
||||
CSFunctionFamily getFunctionFamily() const;
|
||||
unsigned int getRangeFlags() const;
|
||||
QString funcToString() const;
|
||||
void convert(Board::Type before, Board::Type after);
|
||||
void convert(RadioDataConversionState & cstate);
|
||||
};
|
||||
|
||||
enum AssignFunc {
|
||||
@@ -540,6 +540,7 @@ class CustomFunctionData { // Function Switches data
|
||||
|
||||
void clear();
|
||||
bool isEmpty() const;
|
||||
QString toString(int index, bool globalContext = false) const;
|
||||
QString funcToString(const ModelData * model = NULL) const;
|
||||
QString paramToString(const ModelData * model) const;
|
||||
QString repeatToString() const;
|
||||
@@ -549,7 +550,7 @@ class CustomFunctionData { // Function Switches data
|
||||
static void populatePlaySoundParams(QStringList & qs);
|
||||
static void populateHapticParams(QStringList & qs);
|
||||
|
||||
void convert(Board::Type before, Board::Type after);
|
||||
void convert(RadioDataConversionState & cstate);
|
||||
|
||||
};
|
||||
|
||||
@@ -566,7 +567,8 @@ class FlightModeData {
|
||||
int rotaryEncoders[CPN_MAX_ENCODERS];
|
||||
int gvars[CPN_MAX_GVARS];
|
||||
void clear(const int phase);
|
||||
void convert(Board::Type before, Board::Type after);
|
||||
QString toString(int index) const;
|
||||
void convert(RadioDataConversionState & cstate);
|
||||
};
|
||||
|
||||
class SwashRingData { // Swash Ring data
|
||||
@@ -754,6 +756,7 @@ class TimerData {
|
||||
unsigned int persistent;
|
||||
int pvalue;
|
||||
void clear() { memset(this, 0, sizeof(TimerData)); mode = RawSwitch(SWITCH_TYPE_TIMER_MODE, 0); }
|
||||
void convert(RadioDataConversionState & cstate);
|
||||
};
|
||||
|
||||
enum PulsesProtocol {
|
||||
@@ -1071,7 +1074,7 @@ class ModelData {
|
||||
ModelData(const ModelData & src);
|
||||
ModelData & operator = (const ModelData & src);
|
||||
|
||||
void convert(Board::Type before, Board::Type after);
|
||||
void convert(RadioDataConversionState & cstate);
|
||||
|
||||
ExpoData * insertInput(const int idx);
|
||||
void removeInput(const int idx);
|
||||
@@ -1197,8 +1200,9 @@ class GeneralSettings {
|
||||
};
|
||||
|
||||
GeneralSettings();
|
||||
void convert(Board::Type before, Board::Type after);
|
||||
void convert(RadioDataConversionState & cstate);
|
||||
|
||||
void setDefaultControlTypes(Board::Type board);
|
||||
int getDefaultStick(unsigned int channel) const;
|
||||
RawSource getDefaultSource(unsigned int channel) const;
|
||||
int getDefaultChannel(unsigned int stick) const;
|
||||
@@ -1314,7 +1318,7 @@ class RadioData {
|
||||
std::vector<CategoryData> categories;
|
||||
std::vector<ModelData> models;
|
||||
|
||||
void convert(Board::Type before, Board::Type after);
|
||||
void convert(RadioDataConversionState & cstate);
|
||||
|
||||
void setCurrentModel(unsigned int index);
|
||||
void fixModelFilenames();
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* 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 "radiodataconversionstate.h"
|
||||
#include "radiodata.h"
|
||||
|
||||
#include <QStandardItemModel>
|
||||
|
||||
RadioDataConversionState::RadioDataConversionState(Board::Type before, Board::Type after, RadioData * rdata) :
|
||||
fromType(before),
|
||||
toType(after),
|
||||
fromBoard(Boards(before)),
|
||||
toBoard(Boards(after)),
|
||||
rd(NULL),
|
||||
rdCopy(NULL),
|
||||
maxEventType(EVT_NONE),
|
||||
modelIdx(-1),
|
||||
subCompIdx(-1)
|
||||
{
|
||||
setRadioData(rdata);
|
||||
}
|
||||
|
||||
RadioDataConversionState::~RadioDataConversionState()
|
||||
{
|
||||
if (rdCopy)
|
||||
delete rdCopy;
|
||||
}
|
||||
|
||||
RadioDataConversionState & RadioDataConversionState::withModelIndex(int index)
|
||||
{
|
||||
modelIdx = index;
|
||||
subCompIdx = -1;
|
||||
setLogField(FLD_ORIGIN);
|
||||
return *this;
|
||||
}
|
||||
|
||||
RadioDataConversionState & RadioDataConversionState::withComponentIndex(int index)
|
||||
{
|
||||
subCompIdx = index;
|
||||
setLogField(FLD_SUB_COMP);
|
||||
return *this;
|
||||
}
|
||||
|
||||
RadioDataConversionState &RadioDataConversionState::withComponentField(const QString & name)
|
||||
{
|
||||
setLogField(FLD_COMP_FIELD, LogField(0, name));
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool RadioDataConversionState::convert()
|
||||
{
|
||||
if (rd) {
|
||||
rd->convert(*this);
|
||||
return true;
|
||||
}
|
||||
|
||||
setErr(("No radio data to convert."));
|
||||
return false;
|
||||
}
|
||||
|
||||
void RadioDataConversionState::setRadioData(RadioData * rdata)
|
||||
{
|
||||
rd = rdata;
|
||||
if (rdCopy)
|
||||
delete rdCopy;
|
||||
rdCopy = new RadioData(*rdata);
|
||||
logCache = LogRecord();
|
||||
}
|
||||
|
||||
void RadioDataConversionState::setLogField(LogFieldType type, const LogField & data, bool clearRest)
|
||||
{
|
||||
if (type >= FLD_ENUM_COUNT)
|
||||
return;
|
||||
|
||||
logCache.fields[type].id = data.id;
|
||||
logCache.fields[type].name = data.name;
|
||||
if (clearRest && type + 1 < FLD_ENUM_COUNT)
|
||||
setLogField(LogFieldType(type + 1));
|
||||
}
|
||||
|
||||
void RadioDataConversionState::addLogEntry(EventType event, const QString & msg)
|
||||
{
|
||||
if (event != EVT_NONE) {
|
||||
setLogField(FLD_EVT_TYPE, LogField(event, eventTypeToString(event)), false);
|
||||
setLogField(FLD_MSG, LogField(event, msg), false);
|
||||
log.append(logCache);
|
||||
}
|
||||
maxEventType = qMax(event, maxEventType);
|
||||
logCache.fields[FLD_MSG].clear();
|
||||
}
|
||||
|
||||
void RadioDataConversionState::setInvalid(const LogField & item)
|
||||
{
|
||||
setLogField(FLD_ITM_BEFORE, item);
|
||||
addLogEntry(EVT_INV, tr("is invalid"));
|
||||
}
|
||||
|
||||
void RadioDataConversionState::setConverted(const LogField & from, const LogField & to)
|
||||
{
|
||||
setLogField(FLD_ITM_BEFORE, from);
|
||||
setLogField(FLD_ITM_AFTER, to);
|
||||
addLogEntry(EVT_CVRT, tr("converted to"));
|
||||
}
|
||||
|
||||
void RadioDataConversionState::setMoved(const LogField & from, const LogField & to)
|
||||
{
|
||||
setLogField(FLD_ITM_BEFORE, from);
|
||||
setLogField(FLD_ITM_AFTER, to);
|
||||
addLogEntry(EVT_INF, tr("verify is"));
|
||||
}
|
||||
|
||||
const ModelData * RadioDataConversionState::toModel() const
|
||||
{
|
||||
if (rd && modelIdx > -1 && modelIdx < (int)rd->models.size())
|
||||
return &rd->models[modelIdx];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const ModelData *RadioDataConversionState::fromModel() const
|
||||
{
|
||||
if (rdCopy && modelIdx > -1 && modelIdx < (int)rdCopy->models.size())
|
||||
return &rdCopy->models[modelIdx];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const GeneralSettings * RadioDataConversionState::toGS() const
|
||||
{
|
||||
if (rd)
|
||||
return &rd->generalSettings;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const GeneralSettings *RadioDataConversionState::fromGS() const
|
||||
{
|
||||
if (rdCopy)
|
||||
return &rdCopy->generalSettings;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool RadioDataConversionState::hasLogEntries(EventType logLevel) const
|
||||
{
|
||||
return (maxEventType >= logLevel);
|
||||
}
|
||||
|
||||
QString RadioDataConversionState::eventTypeToString(int type) const
|
||||
{
|
||||
// enum EventType { EVT_NONE, EVT_DBG, EVT_INF, EVT_WRN, EVT_CVRT, EVT_ERR, EVT_INV
|
||||
static const QStringList evtTypes = QStringList() << "" << tr("[DBG]") << tr("[NFO]") << tr("[WRN]") << tr("[CVT]") << tr("[ERR]") << tr("[INV]");
|
||||
return (type < evtTypes.size() ? evtTypes.at(type) : "");
|
||||
}
|
||||
|
||||
QString RadioDataConversionState::eventTypeToColor(int type) const
|
||||
{
|
||||
// enum EventType { EVT_NONE, EVT_DBG, EVT_INF, EVT_WRN, EVT_CVRT, EVT_ERR, EVT_INV
|
||||
static const QStringList evtColors = QStringList() << "black" << "dimgrey" << "black" << "#ea7104" << "#ea7104" << "red" << "red";
|
||||
return (type < evtColors.size() ? evtColors.at(type) : "black");
|
||||
}
|
||||
|
||||
QString RadioDataConversionState::getFieldName(int field) const
|
||||
{
|
||||
static const QStringList dataFields = QStringList() << tr("Event") << tr("Origin") << tr("Comp") << tr("Sub-Component") << tr("Field") << tr("Type") << tr("Old") << tr("Action") << tr("New");
|
||||
return (field < dataFields.size() ? dataFields.at(field) : "");
|
||||
}
|
||||
|
||||
QStandardItemModel * RadioDataConversionState::getLogModel(RadioDataConversionState::EventType logLevel, QObject * parent) const
|
||||
{
|
||||
QStandardItemModel * stdmdl = new QStandardItemModel(parent);
|
||||
const QString tooltip = "%1: %2 (ID: %3)";
|
||||
const int sortRole = Qt::UserRole + 1;
|
||||
const int seqdigits = floorf(logf(log.size()) / logf(10.0f)) + 1;
|
||||
|
||||
stdmdl->setSortRole(sortRole);
|
||||
|
||||
QStringList header;
|
||||
header << tr("Seq");
|
||||
for (int i=0; i < FLD_ENUM_COUNT; ++i)
|
||||
header << getFieldName(i);
|
||||
stdmdl->setHorizontalHeaderLabels(header);
|
||||
|
||||
QFont fnt("Courier");
|
||||
fnt.setStyleHint(QFont::TypeWriter);
|
||||
//fnt.setPointSize(fnt.pointSize()+1);
|
||||
|
||||
int row = 0;
|
||||
foreach (const LogRecord & lr, log) {
|
||||
if (lr.fields.at(FLD_EVT_TYPE).id < logLevel)
|
||||
continue;
|
||||
QBrush fg(QColor(eventTypeToColor(lr.fields.at(FLD_EVT_TYPE).id)));
|
||||
stdmdl->setItem(row, 0, new QStandardItem(QString("%1").arg(row+1, seqdigits, 10, QChar('0'))));
|
||||
stdmdl->item(row, 0)->setData(row, sortRole);
|
||||
stdmdl->item(row, 0)->setData(fnt, Qt::FontRole);
|
||||
for (int i=0; i < FLD_ENUM_COUNT; ++i) {
|
||||
QStandardItem * itm = new QStandardItem(lr.fields.at(i).name);
|
||||
itm->setData(lr.fields.at(i).id, sortRole);
|
||||
itm->setData(fnt, Qt::FontRole);
|
||||
itm->setData(tooltip.arg(getFieldName(i)).arg(lr.fields.at(i).name).arg(lr.fields.at(i).id), Qt::ToolTipRole);
|
||||
itm->setEditable(false);
|
||||
if (i == FLD_EVT_TYPE || i == FLD_MSG)
|
||||
itm->setForeground(fg);
|
||||
stdmdl->setItem(row, i+1, itm);
|
||||
}
|
||||
++row;
|
||||
}
|
||||
|
||||
return stdmdl;
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef RADIODATACONVERSIONSTATE_H
|
||||
#define RADIODATACONVERSIONSTATE_H
|
||||
|
||||
#include "boards.h"
|
||||
#include <QtCore>
|
||||
|
||||
class ModelData;
|
||||
class GeneralSettings;
|
||||
class RadioData;
|
||||
class QStandardItemModel;
|
||||
|
||||
class RadioDataConversionState
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(RadioDataConversionState)
|
||||
|
||||
public:
|
||||
enum EventType {
|
||||
EVT_NONE, // does not get logged
|
||||
EVT_DBG, // debug
|
||||
EVT_INF, // misc. info
|
||||
EVT_WRN, // misc. warning, place warning-level events after this and before EVT_ERR
|
||||
EVT_CVRT, // something was converted (A->B)
|
||||
EVT_ERR, // misc. error, place error-level events after this
|
||||
EVT_INV, // invalid, control/etc not available on destination radio
|
||||
};
|
||||
|
||||
enum LogFieldType {
|
||||
FLD_EVT_TYPE, // EventType
|
||||
FLD_ORIGIN, // General Settings or Model with name
|
||||
FLD_COMP, // Input/Mix/LS/CF/etc
|
||||
FLD_SUB_COMP, // Mix channel/Input line/LS number/etc
|
||||
FLD_COMP_FIELD, // optional item within subcomponent, eg. LS V1/V2/AND, etc
|
||||
FLD_ITM_TYPE, // Source/Switch
|
||||
FLD_ITM_BEFORE, // item before conversion
|
||||
FLD_MSG, // conversion details
|
||||
FLD_ITM_AFTER, // item after conversion
|
||||
FLD_ENUM_COUNT
|
||||
};
|
||||
|
||||
struct LogField {
|
||||
LogField(int id = 0, const QString & name = QString()) : id(id), name(name) {}
|
||||
void clear() { id = 0; name.clear(); }
|
||||
int id;
|
||||
QString name;
|
||||
};
|
||||
|
||||
struct LogRecord
|
||||
{
|
||||
LogRecord() { fields.resize(FLD_ENUM_COUNT); }
|
||||
QVector<LogField> fields; // collected data fields indexed by LogColumn value
|
||||
};
|
||||
|
||||
RadioDataConversionState(Board::Type before = Board::BOARD_UNKNOWN, Board::Type after = Board::BOARD_UNKNOWN, RadioData * rdata = NULL);
|
||||
~RadioDataConversionState();
|
||||
|
||||
RadioDataConversionState & withModelIndex(int index);
|
||||
RadioDataConversionState & withComponentIndex(int index);
|
||||
RadioDataConversionState & withComponentField(const QString & name);
|
||||
|
||||
bool convert();
|
||||
void setRadioData(RadioData * rdata);
|
||||
void setLogField(LogFieldType type, const LogField & data = LogField(), bool clearRest = true);
|
||||
inline void setOrigin(const QString & name = QString()) { setLogField(FLD_ORIGIN, LogField(modelIdx, name)); }
|
||||
inline void setSubComp(const QString & name = QString()) { setLogField(FLD_SUB_COMP, LogField(subCompIdx, name)); }
|
||||
inline void setComponent(const QString & name = QString(), int id = 0) { setLogField(FLD_COMP, LogField(id, name)); }
|
||||
inline void setItemType(const QString & name = QString(), int id = 0) { setLogField(FLD_ITM_TYPE, LogField(id, name)); }
|
||||
|
||||
void addLogEntry(EventType event, const QString & msg = QString());
|
||||
void setInvalid(const LogField & item);
|
||||
void setConverted(const LogField & from, const LogField & to);
|
||||
void setMoved(const LogField & from, const LogField & to);
|
||||
inline void setDbg(const QString & msg) { addLogEntry(EVT_DBG, msg); }
|
||||
inline void setInf(const QString & msg) { addLogEntry(EVT_INF, msg); }
|
||||
inline void setWrn(const QString & msg) { addLogEntry(EVT_WRN, msg); }
|
||||
inline void setErr(const QString & msg) { addLogEntry(EVT_ERR, msg); }
|
||||
|
||||
const ModelData * toModel() const;
|
||||
const ModelData * fromModel() const;
|
||||
const GeneralSettings * toGS() const;
|
||||
const GeneralSettings * fromGS() const;
|
||||
bool hasLogEntries(EventType logLevel = EVT_INF) const;
|
||||
|
||||
QString eventTypeToString(int type) const;
|
||||
QString eventTypeToColor(int type) const;
|
||||
QString getFieldName(int field) const;
|
||||
QStandardItemModel * getLogModel(EventType logLevel = EVT_INF, QObject *parent = Q_NULLPTR) const;
|
||||
|
||||
Board::Type fromType; // board type before conversion
|
||||
Board::Type toType; // board type after
|
||||
Boards fromBoard;
|
||||
Boards toBoard;
|
||||
RadioData * rd; // pointer to data struct being converted
|
||||
RadioData * rdCopy; // copy of original radio data before conversion
|
||||
QVector<LogRecord> log; // logged events
|
||||
LogRecord logCache; // buffer for holding event data before logging
|
||||
EventType maxEventType; // most severe event type recorded
|
||||
int modelIdx; // model index in radio data array, -1 if none
|
||||
int subCompIdx; // current row index within component (eg. model.mixData[componentIdx]), -1 if none
|
||||
};
|
||||
|
||||
#endif // RADIODATACONVERSIONSTATE_H
|
||||
@@ -67,42 +67,6 @@ class StorageFormat
|
||||
return board;
|
||||
}
|
||||
|
||||
static uint32_t getFourCC(Board::Type board)
|
||||
{
|
||||
switch (board) {
|
||||
case Board::BOARD_X12S:
|
||||
return 0x3478746F;
|
||||
case Board::BOARD_X10:
|
||||
return 0x3778746F;
|
||||
case Board::BOARD_TARANIS_X7:
|
||||
return 0x3678746F;
|
||||
case Board::BOARD_TARANIS_X9E:
|
||||
return 0x3578746F;
|
||||
case Board::BOARD_TARANIS_X9D:
|
||||
case Board::BOARD_TARANIS_X9DP:
|
||||
return 0x3378746F;
|
||||
case Board::BOARD_SKY9X:
|
||||
case Board::BOARD_AR9X:
|
||||
case Board::BOARD_9XRPRO:
|
||||
return 0x3278746F;
|
||||
case Board::BOARD_MEGA2560:
|
||||
case Board::BOARD_GRUVIN9X:
|
||||
return 0x3178746F;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t getFourCC()
|
||||
{
|
||||
return getFourCC(board);
|
||||
}
|
||||
|
||||
virtual bool isBoardCompatible(Board::Type board)
|
||||
{
|
||||
return getFourCC() == getFourCC(board);
|
||||
}
|
||||
|
||||
protected:
|
||||
void setError(const QString & error)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
cmake_policy(SET CMP0020 NEW)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
if (CMAKE_VERSION VERSION_LESS "3.1" AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
list(APPEND CMAKE_CXX_FLAGS --std=gnu++11)
|
||||
endif ()
|
||||
|
||||
find_package(Qt5Core)
|
||||
find_package(Qt5Widgets)
|
||||
|
||||
project(maxLibQtWidgets)
|
||||
|
||||
set(SRCS
|
||||
"${CMAKE_CURRENT_LIST_DIR}/ExportableTableView.cpp"
|
||||
# "${CMAKE_CURRENT_LIST_DIR}/ScrollableMessageBox.cpp"
|
||||
# "${CMAKE_CURRENT_LIST_DIR}/TimerEdit.cpp"
|
||||
# "${CMAKE_CURRENT_LIST_DIR}/TreeComboBox.cpp"
|
||||
)
|
||||
|
||||
set(HDRS
|
||||
"${CMAKE_CURRENT_LIST_DIR}/ExportableTableView.h"
|
||||
# "${CMAKE_CURRENT_LIST_DIR}/ScrollableMessageBox.h"
|
||||
# "${CMAKE_CURRENT_LIST_DIR}/TimerEdit.h"
|
||||
# "${CMAKE_CURRENT_LIST_DIR}/TreeComboBox.h"
|
||||
)
|
||||
|
||||
qt5_wrap_cpp(SRCS ${HDRS})
|
||||
|
||||
add_library(${PROJECT_NAME} ${SRCS} ${HDRS})
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Core Qt5::Widgets)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE QT_USE_QSTRINGBUILDER)
|
||||
target_include_directories(${PROJECT_NAME} SYSTEM PUBLIC "${CMAKE_CURRENT_LIST_DIR}")
|
||||
|
||||
# need to push this upstream
|
||||
set(BUILT_LIBRARIES ${BUILT_LIBRARIES} ${PROJECT_NAME} PARENT_SCOPE)
|
||||
@@ -0,0 +1 @@
|
||||
#include "ExportableTableView.h"
|
||||
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
ExportableTableView
|
||||
|
||||
COPYRIGHT: (c)2017 Maxim Paperno; All Right Reserved.
|
||||
Contact: http://www.WorldDesign.com/contact
|
||||
|
||||
LICENSE:
|
||||
|
||||
Commercial License Usage
|
||||
Licensees holding valid commercial licenses may use this file in
|
||||
accordance with the terms contained in a written agreement between
|
||||
you and the copyright holder.
|
||||
|
||||
GNU General Public License Usage
|
||||
Alternatively, this file may be used under the terms of the GNU
|
||||
General Public License as published by the Free Software Foundation,
|
||||
either version 3 of the License, or (at your option) any later version.
|
||||
|
||||
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.
|
||||
|
||||
A copy of the GNU General Public License is available at <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ExportableTableView.h"
|
||||
|
||||
ExportableTableView::ExportableTableView(QWidget *parent) : QTableView(parent)
|
||||
{
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
setHtmlTemplate(getDefaultHtmlTemplate());
|
||||
|
||||
QAction * selAll = new QAction(tr("Select All"), this);
|
||||
selAll->setShortcut(QKeySequence::SelectAll);
|
||||
addAction(selAll);
|
||||
|
||||
QAction * cpyTab = new QAction(tr("Copy selection as TAB-delimited text"), this);
|
||||
cpyTab->setShortcut(QKeySequence::Copy);
|
||||
cpyTab->setProperty("delim", "\t");
|
||||
addAction(cpyTab);
|
||||
|
||||
QAction * cpyCsv = new QAction(tr("Copy selection as cooma-delimited text (CSV)"), this);
|
||||
cpyCsv->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_C);
|
||||
cpyCsv->setProperty("delim", ", ");
|
||||
addAction(cpyCsv);
|
||||
|
||||
QAction * cpyPipe = new QAction(tr("Copy selection as pipe-delimited text"), this);
|
||||
cpyPipe->setShortcut(Qt::CTRL + Qt::ALT + Qt::Key_C);
|
||||
cpyPipe->setProperty("delim", " | ");
|
||||
addAction(cpyPipe);
|
||||
|
||||
QAction * cpyHtml = new QAction(tr("Copy selection as HTML"), this);
|
||||
cpyHtml->setProperty("delim", "html");
|
||||
addAction(cpyHtml);
|
||||
|
||||
QAction * saveFile = new QAction(tr("Save selection to file"), this);
|
||||
saveFile->setShortcut(QKeySequence::Save);
|
||||
addAction(saveFile);
|
||||
|
||||
connect(selAll, &QAction::triggered, this, &QTableView::selectAll);
|
||||
connect(cpyTab, &QAction::triggered, this, &ExportableTableView::copy);
|
||||
connect(cpyCsv, &QAction::triggered, this, &ExportableTableView::copy);
|
||||
connect(cpyPipe, &QAction::triggered, this, &ExportableTableView::copy);
|
||||
connect(cpyHtml, &QAction::triggered, this, &ExportableTableView::copy);
|
||||
connect(saveFile, &QAction::triggered, this, &ExportableTableView::save);
|
||||
connect(this, &QTableView::customContextMenuRequested, this, &ExportableTableView::onCustomContextMenuRequested);
|
||||
}
|
||||
|
||||
QString ExportableTableView::getDefaultHtmlTemplate()
|
||||
{
|
||||
return "<!DOCTYPE html>\n<html>\n<head><meta charset='utf-8'/><style>" \
|
||||
"th, td {font-family: sans-serif; padding: 3px 15px 3px 3px;}" \
|
||||
"</style></head>\n<body>\n" \
|
||||
"<table border=0 cellspacing=2>\n" \
|
||||
"%1" \
|
||||
"</table>\n" \
|
||||
"</body></html>";
|
||||
}
|
||||
|
||||
void ExportableTableView::setHtmlTemplate(const QString &value)
|
||||
{
|
||||
m_htmlTemplate = value;
|
||||
}
|
||||
|
||||
QString ExportableTableView::toPlainText(const QModelIndexList &indexList, const QString &delim) const
|
||||
{
|
||||
QString ret, header;
|
||||
const QChar rowDelim = '\n';
|
||||
bool firstRow = true;
|
||||
for (int i = 0; i < indexList.count(); ++i) {
|
||||
const QModelIndex & idx = indexList.at(i);
|
||||
|
||||
if (firstRow)
|
||||
header.append(model()->headerData(idx.column(), Qt::Horizontal).toString());
|
||||
|
||||
ret.append(idx.data(Qt::DisplayRole).toString());
|
||||
|
||||
if (i + 1 == indexList.count() || indexList.at(i+1).row() != idx.row()) {
|
||||
ret.append(rowDelim);
|
||||
if (firstRow && !header.isEmpty())
|
||||
header.append(rowDelim);
|
||||
firstRow = false;
|
||||
}
|
||||
else {
|
||||
ret.append(delim);
|
||||
if (firstRow && !header.isEmpty())
|
||||
header.append(delim);
|
||||
}
|
||||
}
|
||||
if (!header.isEmpty())
|
||||
ret.prepend(header);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
QString ExportableTableView::toHtml(const QModelIndexList &indexList) const
|
||||
{
|
||||
QString ret, header, row;
|
||||
bool firstRow = true;
|
||||
|
||||
for (int i = 0; i < indexList.count(); ++i) {
|
||||
const QModelIndex & idx = indexList.at(i);
|
||||
const int algn = (idx.data(Qt::TextAlignmentRole).isValid() ? idx.data(Qt::TextAlignmentRole).toInt() : (Qt::AlignLeft | Qt::AlignVCenter));
|
||||
const QString fg = (idx.data(Qt::ForegroundRole).isValid() ? idx.data(Qt::ForegroundRole).value<QBrush>().color().name(QColor::HexRgb) : "initial");
|
||||
const QString bg = (idx.data(Qt::BackgroundRole).isValid() ? idx.data(Qt::BackgroundRole).value<QBrush>().color().name(QColor::HexRgb) : "initial");
|
||||
const QString ttl = (idx.data(Qt::ToolTipRole).isValid() ? idx.data(Qt::ToolTipRole).toString().replace("\"", "\"\"") : QString());
|
||||
|
||||
QString fnt;
|
||||
if (idx.data(Qt::FontRole).isValid()) {
|
||||
const QFont font = idx.data(Qt::FontRole).value<QFont>();
|
||||
fnt = "font: \"" % font.family() % "\" " % QString::number(font.pointSize()) % "pt;";
|
||||
}
|
||||
|
||||
if (firstRow)
|
||||
header.append(QString("<th align='left'>%1</th>\n").arg(model()->headerData(idx.column(), Qt::Horizontal).toString()));
|
||||
|
||||
row.append("<td style='color: %2; background-color: %3; %4' align='%5' valign='%6' %7>%1</td>\n"); // cell template
|
||||
row = row.arg(idx.data(Qt::DisplayRole).toString()).arg(fg).arg(bg).arg(fnt);
|
||||
row = row.arg((algn & Qt::AlignRight) ? "right" : (algn & Qt::AlignHCenter) ? "center" : "left");
|
||||
row = row.arg((algn & Qt::AlignTop) ? "top" : (algn & Qt::AlignBottom) ? "bottom" : "middle");
|
||||
row = row.arg(ttl.isEmpty() ? ttl : QString("title=\"%1\"").arg(ttl));
|
||||
|
||||
if (i + 1 == indexList.count() || indexList.at(i+1).row() != idx.row()) {
|
||||
ret.append(QString("<tr>\n%1</tr>\n").arg(row));
|
||||
row.clear();
|
||||
firstRow = false;
|
||||
}
|
||||
}
|
||||
|
||||
ret = QString("<tbody>\n%1</tbody>\n").arg(ret);
|
||||
if (!header.isEmpty())
|
||||
ret.prepend(QString("<thead>\n<tr>\n%1</tr>\n</thead>\n").arg(header));
|
||||
ret = m_htmlTemplate.arg(ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ExportableTableView::saveToFile(const QModelIndexList &indexList, const QString &fileName)
|
||||
{
|
||||
static QString lastDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
|
||||
|
||||
if (indexList.isEmpty())
|
||||
return false;
|
||||
|
||||
QString fname = fileName;
|
||||
if (fname.isEmpty()) {
|
||||
QString types = tr("Tab-delimited text") % " (*.tab);;" % tr("Comma-delimited text") % " (*.csv);;" % tr("Pipe-delimited text") % " (*.txt);;" % tr("HTML") % " (*.html)";
|
||||
fname = QFileDialog::getSaveFileName(this, tr("Save to file"), lastDir, types);
|
||||
}
|
||||
|
||||
if (fname.isEmpty())
|
||||
return false;
|
||||
|
||||
QFile file(fname);
|
||||
QFileInfo fi(file);
|
||||
lastDir = fi.absolutePath();
|
||||
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
|
||||
return false;
|
||||
|
||||
if (fi.suffix() == "html")
|
||||
file.write(toHtml(indexList).toUtf8());
|
||||
else
|
||||
file.write(toPlainText(indexList, (fi.suffix() == "tab" ? "\t" : fi.suffix() == "csv" ? ", " : " | ")).toUtf8());
|
||||
file.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QSize ExportableTableView::sizeHint() const
|
||||
{
|
||||
int w = 0;
|
||||
for (int i = 0; i < model()->columnCount(); ++i)
|
||||
w += sizeHintForColumn(i);
|
||||
return QSize(w + verticalScrollBar()->sizeHint().width() + 60, sizeHintForRow(0) * model()->rowCount());
|
||||
}
|
||||
|
||||
void ExportableTableView::copyText(const QString &delim)
|
||||
{
|
||||
QApplication::clipboard()->setText(toPlainText(getSelectedOrAll(), delim));
|
||||
}
|
||||
|
||||
void ExportableTableView::copyHtml()
|
||||
{
|
||||
QMimeData * mdata = new QMimeData;
|
||||
mdata->setHtml(toHtml(getSelectedOrAll()));
|
||||
QApplication::clipboard()->setMimeData(mdata);
|
||||
}
|
||||
|
||||
void ExportableTableView::copy()
|
||||
{
|
||||
QString delim = "\t";
|
||||
if (sender() && sender()->property("delim").isValid())
|
||||
delim = sender()->property("delim").toString();
|
||||
if (delim == "html")
|
||||
copyHtml();
|
||||
else
|
||||
copyText(delim);
|
||||
}
|
||||
|
||||
void ExportableTableView::save()
|
||||
{
|
||||
saveToFile(getSelectedOrAll());
|
||||
}
|
||||
|
||||
QModelIndexList ExportableTableView::getSelectedOrAll()
|
||||
{
|
||||
QModelIndexList sel = selectionModel()->selectedIndexes();
|
||||
if (sel.isEmpty()) {
|
||||
selectAll();
|
||||
sel = selectionModel()->selectedIndexes();
|
||||
}
|
||||
return sel;
|
||||
}
|
||||
|
||||
void ExportableTableView::onCustomContextMenuRequested(const QPoint & pos)
|
||||
{
|
||||
QMenu menu;
|
||||
menu.addActions(actions());
|
||||
menu.exec(mapToGlobal(pos));
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
ExportableTableView
|
||||
https://github.com/mpaperno/maxLibQt
|
||||
|
||||
COPYRIGHT: (c)2017 Maxim Paperno; All Right Reserved.
|
||||
Contact: http://www.WorldDesign.com/contact
|
||||
|
||||
LICENSE:
|
||||
|
||||
Commercial License Usage
|
||||
Licensees holding valid commercial licenses may use this file in
|
||||
accordance with the terms contained in a written agreement between
|
||||
you and the copyright holder.
|
||||
|
||||
GNU General Public License Usage
|
||||
Alternatively, this file may be used under the terms of the GNU
|
||||
General Public License as published by the Free Software Foundation,
|
||||
either version 3 of the License, or (at your option) any later version.
|
||||
|
||||
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.
|
||||
|
||||
A copy of the GNU General Public License is available at <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef EXPORTABLETABLEVIEW_H
|
||||
#define EXPORTABLETABLEVIEW_H
|
||||
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QFile>
|
||||
#include <QFileDialog>
|
||||
#include <QMenu>
|
||||
#include <QMimeData>
|
||||
#include <QScrollBar>
|
||||
#include <QStandardPaths>
|
||||
#include <QTableView>
|
||||
|
||||
/**
|
||||
\class ExportableTableView
|
||||
\version 1.0.0
|
||||
|
||||
\brief The ExportableTableView class provides a regular QTableView but with features to export the data
|
||||
as plain text or HTML.
|
||||
|
||||
Any selection of data can be exported. The horizontal headings, if any, are included in the export.
|
||||
The export functions are available to the user via a custom context menu or keyboard shortcuts.
|
||||
3 field delimiter choices are available for plain-text export (tab, comma, or pipe).
|
||||
Data can be saved to the clipboard or to a file.
|
||||
|
||||
The export functions can also be accessed programmatically via \c toPlainText(), \c toHtml(),
|
||||
and \c saveToFile().
|
||||
|
||||
The overall HTML template can be customized by setting \c setHtmlTemplate(). The data itself is always
|
||||
formatted as a basic HTML table and then inserted into the template at the \c %1 placeholder.
|
||||
HTML version tries to preserve many data role attributes of the model items:
|
||||
|
||||
\li \c Qt::FontRole
|
||||
\li \c Qt::ForegroundRole
|
||||
\li \c Qt::BackgroundRole
|
||||
\li \c Qt::TextAlignmentRole
|
||||
\li \c Qt::ToolTipRole
|
||||
|
||||
Note that \c Qt::EditRole data is not specifically preserved (unless it already matches \c Qt::DisplayRole ).
|
||||
|
||||
*/
|
||||
class ExportableTableView : public QTableView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ExportableTableView(QWidget *parent = Q_NULLPTR);
|
||||
|
||||
static QString getDefaultHtmlTemplate();
|
||||
void setHtmlTemplate(const QString &value);
|
||||
|
||||
QString toPlainText(const QModelIndexList &indexList, const QString &delim = "\t") const;
|
||||
QString toHtml(const QModelIndexList &indexList) const;
|
||||
bool saveToFile(const QModelIndexList &indexList, const QString &fileName = QString());
|
||||
|
||||
QSize sizeHint() const Q_DECL_OVERRIDE;
|
||||
|
||||
public slots:
|
||||
void copyText(const QString &delim = QString("\t"));
|
||||
void copyHtml();
|
||||
void copy();
|
||||
void save();
|
||||
QModelIndexList getSelectedOrAll();
|
||||
|
||||
protected:
|
||||
void onCustomContextMenuRequested(const QPoint &pos);
|
||||
|
||||
QString m_htmlTemplate;
|
||||
};
|
||||
|
||||
#endif // EXPORTABLETABLEVIEW_H
|
||||
Reference in New Issue
Block a user