fix(cpn): radio specific name and label validators (#4670)

Only allows valid characters in names, drops invalid characters if present
This commit is contained in:
Neil Horne
2024-02-28 11:35:57 +10:00
committed by GitHub
parent 3807d7759b
commit 57c7aca2f6
31 changed files with 428 additions and 139 deletions
@@ -26,6 +26,7 @@
#include "yaml_ops.h"
#include "yaml_generalsettings.h"
#include "yaml_modeldata.h"
#include "labelvalidator.h"
#include <QMessageBox>
@@ -113,11 +114,14 @@ bool loadLabelsListFromYaml(RadioData::ModelLabels& labels,
std::string lbl = it->first.as<std::string>();
RadioData::LabelData ld;
ld.name = QString::fromStdString(lbl);
if (lbls[lbl]["selected"])
ld.selected = lbls[lbl]["selected"].as<bool>();
else
ld.selected = false;
labels.append(ld);
YamlValidateLabel(ld.name);
if (!ld.name.isEmpty()) {
if (lbls[lbl]["selected"])
ld.selected = lbls[lbl]["selected"].as<bool>();
else
ld.selected = false;
labels.append(ld);
}
}
}
@@ -32,6 +32,17 @@
#include <QMessageBox>
void YamlValidateNames(GeneralSettings& gs, Board::Type board)
{
for (int i = 0; i < CPN_MAX_INPUTS; i++) {
YamlValidateName(gs.inputConfig[i].name, board);
}
for (int i = 0; i < CPN_MAX_SWITCHES; i++) {
YamlValidateName(gs.switchConfig[i].name, board);
}
}
const YamlLookupTable beeperModeLut = {
{ GeneralSettings::BEEPER_QUIET, "mode_quiet" },
{ GeneralSettings::BEEPER_ALARMS_ONLY, "mode_alarms" },
@@ -599,7 +610,8 @@ bool convert<GeneralSettings>::decode(const Node& node, GeneralSettings& rhs)
node["labelMultiMode"] >> rhs.labelMultiMode;
node["favMultiMode"] >> rhs.favMultiMode;
// fix ups
// perform integrity checks and fix-ups
YamlValidateNames(rhs, fw->getBoard());
rhs.validateFlexSwitches();
return true;
@@ -41,6 +41,57 @@
SemanticVersion version; // used for data conversions
void YamlValidateLabelsNames(ModelData& model, Board::Type board)
{
YamlValidateName(model.name, board);
QStringList lst = QString(model.labels).split(',', Qt::SkipEmptyParts);
for (int i = lst.count() - 1; i >= 0; i--) {
YamlValidateLabel(lst[i]);
if (lst.at(i).isEmpty())
lst.removeAt(i);
}
strcpy(model.labels, QString(lst.join(',')).toLatin1().data());
for (int i = 0; i < CPN_MAX_CURVES; i++) {
YamlValidateName(model.curves[i].name, board);
}
for (int i = 0; i < CPN_MAX_EXPOS; i++) {
YamlValidateName(model.expoData[i].name, board);
}
for (int i = 0; i < CPN_MAX_GVARS; i++) {
YamlValidateName(model.gvarData[i].name, board);
}
for (int i = 0; i < CPN_MAX_FLIGHT_MODES; i++) {
YamlValidateName(model.flightModeData[i].name, board);
}
for (int i = 0; i < CPN_MAX_SWITCHES_FUNCTION; i++) {
YamlValidateName(model.functionSwitchNames[i], board);
}
for (int i = 0; i < CPN_MAX_INPUTS; i++) {
YamlValidateName(model.inputNames[i], board);
}
for (int i = 0; i < CPN_MAX_CHNOUT; i++) {
YamlValidateName(model.limitData[i].name, board);
}
for (int i = 0; i < CPN_MAX_MIXERS; i++) {
YamlValidateName(model.mixData[i].name, board);
}
for (int i = 0; i < CPN_MAX_SENSORS; i++) {
YamlValidateName(model.sensorData[i].label, board);
}
}
static const YamlLookupTable timerModeLut = {
{TimerData::TIMERMODE_OFF, "OFF"},
{TimerData::TIMERMODE_ON, "ON"},
@@ -1092,6 +1143,8 @@ bool convert<ModelData>::decode(const Node& node, ModelData& rhs)
{
if (!node.IsMap()) return false;
Board::Type board = getCurrentBoard();
unsigned int modelIds[CPN_MAX_MODULES];
memset(modelIds, 0, sizeof(modelIds));
@@ -1151,6 +1204,7 @@ bool convert<ModelData>::decode(const Node& node, ModelData& rhs)
node["limitData"] >> rhs.limitData;
node["inputNames"] >> rhs.inputNames;
node["expoData"] >> rhs.expoData;
node["curves"] >> rhs.curves;
@@ -1326,7 +1380,7 @@ bool convert<ModelData>::decode(const Node& node, ModelData& rhs)
}
// perform integrity checks and fix-ups
YamlValidateLabelsNames(rhs, board);
rhs.sortMixes(); // critical for Companion and radio that mix lines are in sequence
return true;
@@ -19,6 +19,8 @@
*/
#include "yaml_ops.h"
#include "labelvalidator.h"
#include "namevalidator.h"
SemanticVersion radioSettingsVersion;
SemanticVersion modelSettingsVersion;
@@ -57,6 +59,32 @@ void operator >> (const YAML::Node& node, bool& value)
}
}
void YamlValidateLabel(QString &input)
{
LabelValidator *lv = new LabelValidator();
if (!lv->isValid(input)) {
lv->fixup(input);
input = input.trimmed();
}
delete lv;
}
void YamlValidateName(char *input, Board::Type board)
{
NameValidator *nv = new NameValidator(board);
QString in(input);
if (!nv->isValid(in)) {
nv->fixup(in);
in = in.trimmed();
}
strcpy(input, in.toLatin1().data());
delete nv;
}
namespace YAML {
std::string LookupValue(const YamlLookupTable& lut, const int& value)
+6 -1
View File
@@ -21,9 +21,11 @@
#pragma once
#include <yaml-cpp/yaml.h>
#include "helpers.h"
#include "boards.h"
#include <algorithm>
#include <QString>
#include "helpers.h"
typedef std::pair<int, std::string> YamlLookupTableElmt;
typedef std::vector<YamlLookupTableElmt> YamlLookupTable;
@@ -122,6 +124,9 @@ void operator>>(const YAML::Node& node, T (&value)[N])
}
}
void YamlValidateName(char *input, Board::Type board);
void YamlValidateLabel(QString &input);
namespace YAML {
std::string LookupValue(const YamlLookupTable& lut, const int& value);
@@ -24,6 +24,7 @@
#include "eeprominterface.h"
#include "radiodataconversionstate.h"
#include "compounditemmodels.h"
#include "yaml_ops.h"
const uint8_t chout_ar[] = { // First number is 0..23 -> template setup, Second is relevant channel out
1,2,3,4 , 1,2,4,3 , 1,3,2,4 , 1,3,4,2 , 1,4,2,3 , 1,4,3,2,
@@ -836,6 +837,7 @@ bool GeneralSettings::convertLegacyConfiguration(Board::Type board)
{
for (int i = 0; i < CPN_MAX_STICKS && i < Boards::getCapability(board, Board::Sticks); i++) {
inputConfig[i].type = Board::AIT_STICK;
YamlValidateName(stickName[i], board);
strncpy(inputConfig[i].name, stickName[i], HARDWARE_NAME_LEN);
}
@@ -843,6 +845,7 @@ bool GeneralSettings::convertLegacyConfiguration(Board::Type board)
int idx = Boards::getInputPotIndex(i, board);
if (idx >= 0) {
inputConfig[idx].type = Board::AIT_FLEX;
YamlValidateName(potName[i], board);
strncpy(inputConfig[idx].name, potName[i], HARDWARE_NAME_LEN);
int ft = std::stoi(DataHelpers::getStringTagMappingTag(potTypesConversionTable, potConfig[i]));
if (ft > -1)
@@ -854,6 +857,7 @@ bool GeneralSettings::convertLegacyConfiguration(Board::Type board)
int idx = Boards::getInputSliderIndex(i, board);
if (idx >= 0) {
inputConfig[idx].type = Board::AIT_FLEX;
YamlValidateName(sliderName[i], board);
strncpy(inputConfig[idx].name, sliderName[i], HARDWARE_NAME_LEN);
int ft = std::stoi(DataHelpers::getStringTagMappingTag(sliderTypesConversionTable, sliderConfig[i]));
if (ft > -1)
@@ -869,6 +873,7 @@ bool GeneralSettings::convertLegacyConfiguration(Board::Type board)
for (int i = 0; i < CPN_MAX_SWITCHES && i < Boards::getCapability(board, Board::Switches); i++) {
switchConfig[i].type = (Board::SwitchType)swtchConfig[i];
YamlValidateName(swtchName[i], board);
strncpy(switchConfig[i].name, swtchName[i], HARDWARE_NAME_LEN);
}
+2 -5
View File
@@ -46,10 +46,6 @@ class AbstractStaticItemModel;
constexpr char AIM_MODELDATA_TRAINERMODE[] {"modeldata.trainermode"};
constexpr char AIM_MODELDATA_FUNCSWITCHCONFIG[] {"modeldata.funcswitchconfig"};
constexpr char AIM_MODELDATA_FUNCSWITCHSTART[] {"modeldata.funcswitchstart"};
constexpr int LABEL_LENGTH=16;
#define CHAR_FOR_NAMES " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-."
#define CHAR_FOR_NAMES_REGEX "[ A-Za-z0-9_.-,\"]*"
class RSSIAlarmData {
public:
@@ -87,6 +83,7 @@ enum TrainerMode {
TRAINER_MODE_LAST = TRAINER_MODE_MULTI
};
#define MODEL_NAME_LEN 15
#define INPUT_NAME_LEN 4
#define CPN_MAX_BITMAP_LEN 14
@@ -125,7 +122,7 @@ class ModelData {
char semver[8 + 1];
bool used;
char name[15+1];
char name[MODEL_NAME_LEN + 1];
char filename[16+1];
char labels[100];
int modelIndex; // Companion only, temporary index position managed by data model.
+10 -11
View File
@@ -109,8 +109,8 @@ void RadioData::convert(RadioDataConversionState & cstate)
void RadioData::addLabel(QString label)
{
label = unEscapeCSV(label);
// Truncate possible UTF-8 to 16char maximum
QByteArray output = label.toUtf8();
// Truncate possible UTF-8 to 16 char maximum
QByteArray output = label.toLatin1();
if (output.size() > LABEL_LENGTH) {
int truncateAt = 0;
for (int i = LABEL_LENGTH; i > 0; i--) {
@@ -135,12 +135,12 @@ bool RadioData::deleteLabel(QString label)
// Remove labels in the models
for(auto& model : models) {
QStringList modelLabels = fromCSV(QString::fromUtf8(model.labels));
QStringList modelLabels = fromCSV(QString::fromLatin1(model.labels));
if (modelLabels.indexOf(label) >= 0) {
deleted = true;
modelLabels.removeAll(label);
}
strcpy(model.labels, toCSV(modelLabels).toUtf8().data());
strcpy(model.labels, toCSV(modelLabels).toLatin1().data());
}
// Remove the label from the global list
@@ -186,8 +186,8 @@ bool RadioData::renameLabel(QString from, QString to)
if (ind != -1) {
modelLabels.replace(ind, csvTo);
QString outputcsv = QString(modelLabels.join(','));
if (outputcsv.toUtf8().size() < (int)sizeof(model.labels)) {
strcpy(model.labels, outputcsv.toUtf8().data());
if (outputcsv.toLatin1().size() < (int)sizeof(model.labels)) {
strcpy(model.labels, outputcsv.toLatin1().data());
} else { // Shouldn't ever get here, from check above
success = false;
throw std::length_error(model.name);
@@ -231,10 +231,10 @@ bool RadioData::addLabelToModel(int index, QString label)
char *modelLabelCsv = models[index].labels;
// Make sure it will fit
if (strlen(modelLabelCsv) + label.size() + 1 < sizeof(models[index].labels)-1) {
QStringList modelLabels = QString::fromUtf8(modelLabelCsv).split(',', Qt::SkipEmptyParts);
QStringList modelLabels = QString::fromLatin1(modelLabelCsv).split(',', Qt::SkipEmptyParts);
if (modelLabels.indexOf(label) == -1) {
modelLabels.append(label);
strcpy(models[index].labels, QString(modelLabels.join(',')).toUtf8().data());
strcpy(models[index].labels, QString(modelLabels.join(',')).toLatin1().data());
return true;
}
}
@@ -246,10 +246,10 @@ bool RadioData::removeLabelFromModel(int index, QString label)
{
if ((unsigned int)index >= models.size()) return false;
QStringList lbls = fromCSV(QString::fromUtf8(models[index].labels));
QStringList lbls = fromCSV(QString::fromLatin1(models[index].labels));
if (lbls.indexOf(label) >= 0) {
lbls.removeAll(label);
strcpy(models[index].labels, toCSV(lbls).toUtf8().data());
strcpy(models[index].labels, toCSV(lbls).toLatin1().data());
return true;
}
return false;
@@ -342,4 +342,3 @@ AbstractStaticItemModel * RadioData::modelSortOrderItemModel()
mdl->loadItemList();
return mdl;
}
+2
View File
@@ -11,6 +11,8 @@
// identiying names of static abstract item models
constexpr char AIM_RADIO_MODEL_SORT_ORDER[] {"radio.modelsortorder"};
constexpr int LABEL_LENGTH = 16;
class RadioDataConversionState;
class AbstractStaticItemModel;
+2 -2
View File
@@ -23,6 +23,7 @@
#include "compounditemmodels.h"
#include "filtereditemmodels.h"
#include "autocombobox.h"
#include "namevalidator.h"
GeneralSetupPanel::GeneralSetupPanel(QWidget * parent, GeneralSettings & generalSettings, Firmware * firmware):
GeneralPanel(parent, generalSettings, firmware),
@@ -287,8 +288,7 @@ ui(new Ui::GeneralSetup)
ui->pwrOnDelay->hide();
}
QRegExp rx(CHAR_FOR_NAMES_REGEX);
ui->registrationId->setValidator(new QRegExpValidator(rx, this));
ui->registrationId->setValidator(new NameValidator(board, this));
ui->registrationId->setMaxLength(REGISTRATION_ID_LEN);
setValues();
+5
View File
@@ -28,6 +28,7 @@
#include "autodoublespinbox.h"
#include "autobitmappedcombobox.h"
#include "autobitmappedcheckbox.h"
#include "namevalidator.h"
#include <QLabel>
#include <QGridLayout>
@@ -212,6 +213,7 @@ HardwarePanel::HardwarePanel(QWidget * parent, GeneralSettings & generalSettings
addLabel(tr("Bluetooth"));
AutoComboBox *bluetoothMode = new AutoComboBox(this);
bluetoothMode->setValidator(new NameValidator(board, this));
bluetoothMode->setModel(editorItemModels->getItemModel(btmodelid));
bluetoothMode->setField(generalSettings.bluetoothMode, this);
params->append(bluetoothMode);
@@ -414,6 +416,7 @@ void HardwarePanel::addStick(int index)
addLabel(Boards::getInputName(index, board));
AutoLineEdit *name = new AutoLineEdit(this);
name->setValidator(new NameValidator(board, this));
name->setField(config.name, HARDWARE_NAME_LEN, this);
params->append(name);
addParams();
@@ -426,6 +429,7 @@ void HardwarePanel::addFlex(int index)
addLabel(Boards::getInputName(index, board));
AutoLineEdit *name = new AutoLineEdit(this);
name->setValidator(new NameValidator(board, this));
name->setField(config.name, HARDWARE_NAME_LEN, this);
params->append(name);
@@ -467,6 +471,7 @@ void HardwarePanel::addSwitch(int index)
addLabel(Boards::getSwitchName(index));
AutoLineEdit *name = new AutoLineEdit(this);
name->setValidator(new NameValidator(board, this));
name->setField(config.name, HARDWARE_NAME_LEN, this);
params->append(name);
+27 -43
View File
@@ -1,4 +1,26 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "labels.h"
#include "radiodata.h"
LabelsModel::LabelsModel(QSortFilterProxyModel * modelsListProxyModel,
QItemSelectionModel *selectionModel,
@@ -241,48 +263,10 @@ QModelIndex LabelsModel::getDataIndex(QModelIndex viewIndex) const
return modelsListProxyModel->mapToSource(viewIndex);
}
QValidator::State LabelValidator::validate(QString &label, int &pos) const
QWidget * LabelEditTextDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
Q_UNUSED(pos)
QString lbl = RadioData::escapeCSV(label);
if (lbl.toUtf8().size() > LABEL_LENGTH)
return QValidator::Invalid;
if(lbl.contains('\\') || // TODO: Fix me to allow all, requires FW changes
lbl.contains('\"') ||
lbl.contains(':') ||
lbl.contains('-') ||
lbl.contains('\''))
return QValidator::Invalid;
if(lbl.size() == 0)
return QValidator::Intermediate;
return QValidator::Acceptable;
}
void LabelValidator::fixup(QString &input) const
{
QByteArray output = input.toUtf8();
if (output.size() > LABEL_LENGTH) {
int truncateAt = 0;
for (int i = LABEL_LENGTH; i > 0; i--) {
if ((output[i] & 0xC0) != 0x80) {
truncateAt = i;
break;
}
}
output.truncate(truncateAt);
}
input = QString(output);
input.remove('\\'); // TODO: Fix me to allow all, requires FW changes
input.remove('\"');
input.remove(':');
input.remove('-');
input.remove('\'');
QLineEdit *editor = new QLineEdit(parent);
editor->setValidator(new LabelValidator(parent));
editor->setMaxLength(LABEL_LENGTH);
return editor;
}
+25 -20
View File
@@ -1,16 +1,36 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#pragma once
#include "labelvalidator.h"
#include <QObject>
#include <QList>
#include <QAbstractItemModel>
#include <QItemSelectionModel>
#include <QValidator>
#include <QLineEdit>
#include <QStyledItemDelegate>
#include <QSortFilterProxyModel>
#include "radiodata.h"
class RadioData;
typedef struct {
@@ -58,27 +78,12 @@ class LabelsModel : public QAbstractItemModel
QModelIndex getDataIndex(QModelIndex viewIndex) const;
};
class LabelValidator : public QValidator
{
Q_OBJECT
public:
QValidator::State validate(QString &label, int &pos) const;
void fixup(QString &input) const;
};
class LabelEditTextDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
LabelEditTextDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) {}
LabelEditTextDelegate(QObject * parent = nullptr) : QStyledItemDelegate(parent) {}
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QLineEdit *editor = new QLineEdit(parent);
editor->setValidator(new LabelValidator);
return editor;
}
QWidget * createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const;
};
+5 -3
View File
@@ -22,6 +22,7 @@
#include "helpers.h"
#include "filtereditemmodels.h"
#include "curveimagewidget.h"
#include "namevalidator.h"
LimitsGroup::LimitsGroup(Firmware * firmware, TableLayout * tableLayout, int row, int col, int & value, const ModelData & model, GeneralSettings & generalSettings,
int min, int max, int deflt, FilteredItemModel * gvarModel, ModelPanel * panel):
@@ -91,6 +92,8 @@ ChannelsPanel::ChannelsPanel(QWidget * parent, ModelData & model, GeneralSetting
ModelPanel(parent, model, generalSettings, firmware),
sharedItemModels(sharedItemModels)
{
Board::Type board = firmware->getBoard();
chnCapability = firmware->getCapability(Outputs);
int channelNameMaxLen = firmware->getCapability(ChannelsName);
@@ -107,7 +110,7 @@ ChannelsPanel::ChannelsPanel(QWidget * parent, ModelData & model, GeneralSetting
headerLabels << tr("Name");
}
headerLabels << tr("Subtrim") << tr("Min") << tr("Max") << tr("Direction");
if (IS_HORUS_OR_TARANIS(firmware->getBoard()))
if (IS_HORUS_OR_TARANIS(board))
headerLabels << tr("Curve") << tr("Plot");
if (firmware->getCapability(PPMCenter))
headerLabels << tr("PPM Center");
@@ -134,8 +137,7 @@ ChannelsPanel::ChannelsPanel(QWidget * parent, ModelData & model, GeneralSetting
name[i] = new QLineEdit(this);
name[i]->setProperty("index", i);
name[i]->setMaxLength(channelNameMaxLen);
QRegExp rx(CHAR_FOR_NAMES_REGEX);
name[i]->setValidator(new QRegExpValidator(rx, this));
name[i]->setValidator(new NameValidator(board, this));
connect(name[i], SIGNAL(editingFinished()), this, SLOT(nameEdited()));
tableLayout->addWidget(i, col++, name[i]);
}
+4 -3
View File
@@ -22,6 +22,7 @@
#include "ui_expodialog.h"
#include "filtereditemmodels.h"
#include "helpers.h"
#include "namevalidator.h"
ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, GeneralSettings & generalSettings,
Firmware * firmware, QString & inputName, CompoundItemModelFactory * sharedItemModels) :
@@ -37,6 +38,7 @@ ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, G
{
ui->setupUi(this);
Board::Type board = firmware->getBoard();
dialogFilteredItemModels = new FilteredItemModelFactory();
int id;
@@ -48,7 +50,6 @@ ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, G
RawSourceType srcType = (firmware->getCapability(VirtualInputs) ? SOURCE_TYPE_VIRTUAL_INPUT : SOURCE_TYPE_STICK);
setWindowTitle(tr("Edit %1").arg(RawSource(srcType, ed->chn).toString(&model, &generalSettings)));
QRegExp rx(CHAR_FOR_NAMES_REGEX);
id = dialogFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_GVarRef)), "GVarRef");
gvWeightGroup = new GVarGroup(ui->weightGV, ui->weightSB, ui->weightCB, ed->weight, model, 100, -100, 100, 1.0,
@@ -108,7 +109,7 @@ ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, G
flags), "RawSource");
ui->sourceCB->setModel(dialogFilteredItemModels->getItemModel(id));
ui->sourceCB->setCurrentIndex(ui->sourceCB->findData(ed->srcRaw.toValue()));
ui->inputName->setValidator(new QRegExpValidator(rx, this));
ui->inputName->setValidator(new NameValidator(board, this));
ui->inputName->setText(inputName);
}
else {
@@ -140,7 +141,7 @@ ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, G
ui->lineName->setMaxLength(expolength);
}
ui->lineName->setValidator(new QRegExpValidator(rx, this));
ui->lineName->setValidator(new NameValidator(board, this));
ui->lineName->setText(ed->name);
updateScale();
+3 -2
View File
@@ -23,6 +23,7 @@
#include "filtereditemmodels.h"
#include "helpers.h"
#include "customdebug.h"
#include "namevalidator.h"
FlightModePanel::FlightModePanel(QWidget * parent, ModelData & model, int phaseIdx, GeneralSettings & generalSettings, Firmware * firmware,
FilteredItemModel * rawSwitchFilteredModel):
@@ -45,9 +46,8 @@ FlightModePanel::FlightModePanel(QWidget * parent, ModelData & model, int phaseI
trimCount = Boards::getCapability(board, Board::NumTrims);
// Flight mode name
QRegExp rx(CHAR_FOR_NAMES_REGEX);
if (fmCount) {
ui->name->setValidator(new QRegExpValidator(rx, this));
ui->name->setValidator(new NameValidator(board, this));
ui->name->setMaxLength(firmware->getCapability(FlightModesName));
connect(ui->name, SIGNAL(editingFinished()), this, SLOT(phaseName_editingFinished()));
}
@@ -231,6 +231,7 @@ FlightModePanel::FlightModePanel(QWidget * parent, ModelData & model, int phaseI
if (nameLen > 0) {
gvNames[i] = new QLineEdit(ui->gvGB);
gvNames[i]->setProperty("index", i);
gvNames[i]->setValidator(new NameValidator(board, this));
gvNames[i]->setMaxLength(nameLen);
connect(gvNames[i], SIGNAL(editingFinished()), this, SLOT(GVName_editingFinished()));
gvLayout->addWidget(gvNames[i], i + 1, col++, 1, 1);
+2 -2
View File
@@ -23,6 +23,7 @@
#include "radiodata.h"
#include "filtereditemmodels.h"
#include "helpers.h"
#include "namevalidator.h"
MixerDialog::MixerDialog(QWidget *parent, ModelData & model, MixData * mixdata, GeneralSettings & generalSettings, Firmware * firmware,
CompoundItemModelFactory * sharedItemModels) :
@@ -41,7 +42,6 @@ MixerDialog::MixerDialog(QWidget *parent, ModelData & model, MixData * mixdata,
dialogFilteredItemModels = new FilteredItemModelFactory();
int id;
QRegExp rx(CHAR_FOR_NAMES_REGEX);
QLabel * lb_fp[CPN_MAX_FLIGHT_MODES] = {ui->lb_FP0, ui->lb_FP1, ui->lb_FP2, ui->lb_FP3, ui->lb_FP4, ui->lb_FP5, ui->lb_FP6, ui->lb_FP7, ui->lb_FP8 };
QCheckBox * tmp[CPN_MAX_FLIGHT_MODES] = {ui->cb_FP0, ui->cb_FP1, ui->cb_FP2, ui->cb_FP3, ui->cb_FP4, ui->cb_FP5, ui->cb_FP6, ui->cb_FP7, ui->cb_FP8 };
for (int i = 0; i < CPN_MAX_FLIGHT_MODES; i++) {
@@ -100,7 +100,7 @@ MixerDialog::MixerDialog(QWidget *parent, ModelData & model, MixData * mixdata,
else {
ui->mixerName->setMaxLength(namelength);
}
ui->mixerName->setValidator(new QRegExpValidator(rx, this));
ui->mixerName->setValidator(new NameValidator(board, this));
ui->mixerName->setText(md->name);
if (!firmware->getCapability(FlightModes)) {
+7 -6
View File
@@ -29,6 +29,7 @@
#include "checklistdialog.h"
#include "helpers.h"
#include "moduledata.h"
#include "namevalidator.h"
#include <QDir>
@@ -55,6 +56,7 @@ TimerPanel::TimerPanel(QWidget * parent, ModelData & model, TimerData & timer, G
if (length == 0)
ui->name->hide();
else {
ui->name->setValidator(new NameValidator(firmware->getBoard(), this));
ui->name->setField(timer.name, length, this);
connect(ui->name, SIGNAL(currentDataChanged()), this, SLOT(onNameChanged()));
}
@@ -1253,11 +1255,11 @@ FunctionSwitchesPanel::FunctionSwitchesPanel(QWidget * parent, ModelData & model
AbstractStaticItemModel *fsConfig = ModelData::funcSwitchConfigItemModel();
AbstractStaticItemModel *fsStart = ModelData::funcSwitchStartItemModel();
Board::Type board = firmware->getBoard();
lock = true;
QRegExp rx(CHAR_FOR_NAMES_REGEX);
switchcnt = Boards::getCapability(firmware->getBoard(), Board::FunctionSwitches);
switchcnt = Boards::getCapability(board, Board::FunctionSwitches);
for (int i = 0; i < switchcnt; i++) {
QLabel * lblSwitchId = new QLabel(this);
@@ -1265,7 +1267,7 @@ FunctionSwitchesPanel::FunctionSwitchesPanel(QWidget * parent, ModelData & model
AutoLineEdit * aleName = new AutoLineEdit(this);
aleName->setProperty("index", i);
aleName->setValidator(new QRegExpValidator(rx, this));
aleName->setValidator(new NameValidator(board, this));
aleName->setField((char *)model.functionSwitchNames[i], 3);
QComboBox * cboConfig = new QComboBox(this);
@@ -1481,8 +1483,7 @@ SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & ge
memset(modules, 0, sizeof(modules));
QRegExp rx(CHAR_FOR_NAMES_REGEX);
ui->name->setValidator(new QRegExpValidator(rx, this));
ui->name->setValidator(new NameValidator(board, this));
ui->name->setMaxLength(firmware->getCapability(ModelName));
if (firmware->getCapability(HasModelImage)) {
+2
View File
@@ -23,6 +23,7 @@
#include "ui_telemetry_sensor.h"
#include "helpers.h"
#include "appdata.h"
#include "namevalidator.h"
constexpr char FIM_RAWSOURCE[] {"Raw Source"};
constexpr char FIM_TELEALLSRC[] {"Tele All Source"};
@@ -64,6 +65,7 @@ TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor,
ui->numLabel->setToolTip(tr("Popup menu available"));
ui->numLabel->setMouseTracking(true);
connect(ui->numLabel, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(on_customContextMenuRequested(QPoint)));
ui->name->setValidator(new NameValidator(firmware->getBoard(), this));
ui->name->setField(sensor.label, SENSOR_LABEL_LEN);
connect(ui->name, SIGNAL(currentDataChanged()), this, SLOT(on_nameDataChanged()));
ui->id->setField(sensor.id, this);
+8
View File
@@ -19,10 +19,18 @@ set(${PROJECT_NAME}_NAMES
curvedialog
genericpanel
verticalscrollarea
textvalidator
)
AddHeadersSources()
# these have no cpp
set(${PROJECT_NAME}_HDRS
${${PROJECT_NAME}_HDRS}
namevalidator.h
labelvalidator.h
)
# AUTOMOC does not detect so manually process
qt5_wrap_cpp(${PROJECT_NAME}_SRCS
${${PROJECT_NAME}_HDRS}
+7 -6
View File
@@ -39,19 +39,20 @@ AutoLineEdit::~AutoLineEdit()
void AutoLineEdit::setField(char * field, int len, GenericPanel * panel)
{
m_charField = field;
setPanel(panel);
setValidator(new QRegExpValidator(QRegExp("[ A-Za-z0-9_.-,]*"), this));
if (len)
setMaxLength(len);
updateValue();
setFieldInit(len, panel);
}
void AutoLineEdit::setField(QString & field, int len, GenericPanel * panel)
{
m_strField = &field;
setFieldInit(len, panel);
}
void AutoLineEdit::setFieldInit(int len, GenericPanel * panel)
{
setPanel(panel);
if (len)
setMaxLength(len);
setPanel(panel);
updateValue();
}
+2
View File
@@ -47,4 +47,6 @@ class AutoLineEdit: public QLineEdit, public AutoWidget
private:
char *m_charField;
QString *m_strField;
void setFieldInit(int len, GenericPanel * panel);
};
+2
View File
@@ -25,6 +25,7 @@
#include "helpers.h"
#include "filtereditemmodels.h"
#include "eeprominterface.h"
#include "namevalidator.h"
float curveLinear(float x, float coeff, float yMin, float yMid, float yMax)
{
@@ -133,6 +134,7 @@ CurveDialog::CurveDialog(QWidget * parent, ModelData & model, const int curveIdx
maxCurves = firmware->getCapability(NumCurves);
maxPoints = firmware->getCapability(NumCurvePoints);
ui->curveName->setValidator(new NameValidator(firmware->getBoard(), this));
ui->curveName->setField(curve.name, CURVEDATA_NAME_LEN);
connect(ui->curveName, &AutoLineEdit::currentDataChanged, this, &CurveDialog::on_curveNameChanged);
on_curveNameChanged();
+35
View File
@@ -0,0 +1,35 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#pragma once
#include "textvalidator.h"
constexpr char LABEL_VALID_PATTERN[] {"[ A-Za-z0-9\\_\\,\\.\\+\\/\\*\\=\\%\\!\\?\\#\\<\\>\\@\\$\\(\\)\\{\\}\\[\\]\\;]*"};
class LabelValidator : public TextValidator
{
Q_OBJECT
public:
explicit LabelValidator(QObject * parent = nullptr) : TextValidator(parent, LABEL_VALID_PATTERN) {}
virtual ~LabelValidator() {}
};
+41
View File
@@ -0,0 +1,41 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#pragma once
#include "boards.h"
#include "textvalidator.h"
// characters supportd by B&W radio firmware gui editor
constexpr char NAME_VALID_PATTERN_BW[] {"[ A-Za-z0-9\\-]*"};
// characters supported by color radio firmware keyboard widget
constexpr char NAME_VALID_PATTERN_COLOR[] {"[ A-Za-z0-9\\-\\_\\,\\.\"\\+\\/\\*\\=\\%\\!\\?\\#\\<\\>\\@\\$\\(\\)\\{\\}\\[\\]\\;\\:\\']*"};
class NameValidator : public TextValidator
{
Q_OBJECT
public:
explicit NameValidator(Board::Type board, QObject * parent = nullptr) :
TextValidator(parent, Boards::getCapability(board, Board::HasColorLcd) ? NAME_VALID_PATTERN_COLOR : NAME_VALID_PATTERN_BW) {}
virtual ~NameValidator() {}
};
+57
View File
@@ -0,0 +1,57 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "textvalidator.h"
#include <QDebug>
TextValidator::TextValidator(QObject * parent, const QString &pattern, const QChar invalidRepl, QRegularExpression::PatternOptions options):
QRegularExpressionValidator(parent),
invalidRepl(invalidRepl)
{
regexp.setPattern(pattern);
regexp.setPatternOptions(options);
if (!regexp.isValid())
qDebug() << "Error - Pattern:" << regexp.pattern() << "Error msg:" << regexp.errorString() << "Error offset:" << regexp.patternErrorOffset();
if (!isValid(invalidRepl))
qDebug() << "Error - Replacement character: '" << invalidRepl << "' is not valid for pattern:" << regexp.pattern();
setRegularExpression(regexp);
}
void TextValidator::fixup(QString &input) const
{
for (int i = 0; i < input.size(); i++) {
QRegularExpressionMatch match = regexp.match(input.at(i));
if (match.captured() != input.at(i)) // cannot rely on hasMatch
input.replace(i, 1, invalidRepl); // replacement character MUST be valid for regexp to avoid possible loop condition
}
input = input.trimmed();
}
bool TextValidator::isValid(const QString &input) const
{
QRegularExpressionMatch match = regexp.match(input);
return input == match.captured();
}
+44
View File
@@ -0,0 +1,44 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#pragma once
#include "boards.h"
#include <QRegularExpressionValidator>
class TextValidator : public QRegularExpressionValidator
{
Q_OBJECT
public:
explicit TextValidator(QObject * parent, const QString &pattern, const QChar invalidRepl = QChar(' '),
QRegularExpression::PatternOptions options = QRegularExpression::NoPatternOption);
virtual ~TextValidator() {}
void fixup(QString &input) const;
virtual bool isValid(const QString &input) const;
private:
QChar invalidRepl;
QRegularExpression regexp;
};
+7 -7
View File
@@ -76,13 +76,13 @@ void WizMix::addMix(ModelData &model, Input input, int weight, int channel, int
}
else if (input==FLAPS_INPUT){
// There ought to be some kind of constants for switches somewhere...
maxMixSwitch((char *)"Flaps Up", model.mixData[mixIndex++], channel+1, IS_SKY9X(getCurrentBoard()) ? -SWITCH_ELE : SWITCH_SA0, weight); //Taranis-Horus SA-UP, 9X ELE-UP
maxMixSwitch((char *)"Flaps Dn", model.mixData[mixIndex++], channel+1, IS_SKY9X(getCurrentBoard()) ? SWITCH_ELE : SWITCH_SA2, -weight); //Taranis-Horus SA-DOWN, 9X ELE-DOWN
maxMixSwitch((char *)tr("Flaps Up").toLatin1().data(), model.mixData[mixIndex++], channel+1, IS_SKY9X(getCurrentBoard()) ? -SWITCH_ELE : SWITCH_SA0, weight); //Taranis-Horus SA-UP, 9X ELE-UP
maxMixSwitch((char *)tr("Flaps Dn").toLatin1().data(), model.mixData[mixIndex++], channel+1, IS_SKY9X(getCurrentBoard()) ? SWITCH_ELE : SWITCH_SA2, -weight); //Taranis-Horus SA-DOWN, 9X ELE-DOWN
}
else if (input==AIRBRAKES_INPUT){
maxMixSwitch((char *)"AirbkOff", model.mixData[mixIndex++], channel+1, IS_SKY9X(getCurrentBoard()) ? -SWITCH_RUD : SWITCH_SE0, -weight); //Taranis-Horus SE-UP, 9X RUD-UP
maxMixSwitch((char *)"AirbkOn", model.mixData[mixIndex++], channel+1, IS_SKY9X(getCurrentBoard()) ? SWITCH_RUD : SWITCH_SE2, weight); //Tatanis-Horus SE-DOWN, 9X RUD-DOWN
maxMixSwitch((char *)tr("AirbkOff").toLatin1().data(), model.mixData[mixIndex++], channel+1, IS_SKY9X(getCurrentBoard()) ? -SWITCH_RUD : SWITCH_SE0, -weight); //Taranis-Horus SE-UP, 9X RUD-UP
maxMixSwitch((char *)tr("AirbkOn").toLatin1().data(), model.mixData[mixIndex++], channel+1, IS_SKY9X(getCurrentBoard()) ? SWITCH_RUD : SWITCH_SE2, weight); //Tatanis-Horus SE-DOWN, 9X RUD-DOWN
}
}
}
@@ -124,7 +124,7 @@ WizMix::operator ModelData()
mix.swtch.index = IS_SKY9X(getCurrentBoard()) ? SWITCH_THR : SWITCH_SF0;
mix.mltpx = MLTPX_REP;
memset(mix.name, 0, sizeof(mix.name));
strncpy(mix.name, "Cut", MIXDATA_NAME_LEN);
strncpy(mix.name, tr("Cut").toLatin1().data(), MIXDATA_NAME_LEN);
}
}
}
@@ -132,7 +132,7 @@ WizMix::operator ModelData()
// Add the Flight Timer option
if (options[FLIGHT_TIMER_OPTION] && throttleChannel >= 0){
memset(model.timers[timerIndex].name, 0, sizeof(model.timers[timerIndex].name));
strncpy(model.timers[timerIndex].name, "Flt", sizeof(model.timers[timerIndex].name)-1);
strncpy(model.timers[timerIndex].name, tr("Flt").toLatin1().data(), sizeof(model.timers[timerIndex].name)-1);
model.timers[timerIndex].mode = TimerData::TIMERMODE_START;
timerIndex++;
}
@@ -140,7 +140,7 @@ WizMix::operator ModelData()
// Add the Throttle Timer option
if (options[THROTTLE_TIMER_OPTION] && throttleChannel >= 0){
memset(model.timers[timerIndex].name, 0, sizeof(model.timers[timerIndex].name));
strncpy(model.timers[timerIndex].name, "Thr", sizeof(model.timers[timerIndex].name)-1);
strncpy(model.timers[timerIndex].name, tr("Thr").toLatin1().data(), sizeof(model.timers[timerIndex].name)-1);
model.timers[timerIndex].mode = TimerData::TIMERMODE_THR;
timerIndex++;
}
+7 -9
View File
@@ -18,15 +18,13 @@
* GNU General Public License for more details.
*/
#ifndef _WIZARDDATA_H_
#define _WIZARDDATA_H_
#pragma once
#include "eeprominterface.h"
#include <QtCore>
#define WIZ_MAX_CHANNELS 8
// TODO use a constant common to the whole companion
// TODO when in the wizard use the getCapacity(...) to know how long the name can be
#define WIZ_MODEL_NAME_LENGTH 12
enum Input {
NO_INPUT,
@@ -90,11 +88,13 @@ class Channel
void clear();
};
class WizMix
class WizMix : public QObject
{
Q_OBJECT
public:
bool complete;
char name[WIZ_MODEL_NAME_LENGTH + 1];
char name[MODEL_NAME_LEN + 1];
unsigned int modelId;
const GeneralSettings & settings;
const ModelData & originalModelData;
@@ -111,5 +111,3 @@ class WizMix
void maxMixSwitch(char *name, MixData &mix, int destCh, int sw, int weight);
};
#endif // _WIZARDDATA_H_
+5 -6
View File
@@ -19,8 +19,8 @@
*/
#include "wizarddialog.h"
#include "wizarddata.h"
#include "helpers.h"
#include "namevalidator.h"
WizardDialog::WizardDialog(const GeneralSettings & settings, unsigned int modelId, const ModelData & modelData, QWidget *parent):
QWizard(parent),
@@ -276,7 +276,10 @@ int StandardPage::nextId() const
ModelSelectionPage::ModelSelectionPage(WizardDialog *dlg, QString image, QString title, QString text)
: StandardPage(Page_Models, dlg, image, title, text)
{
Firmware *fw = getCurrentFirmware();
nameLineEdit = new QLineEdit;
nameLineEdit->setValidator(new NameValidator(fw->getBoard()));
nameLineEdit->setMaxLength(fw->getCapability(ModelName));
planeRB = new QRadioButton(tr("Plane"));
planeRB->setChecked(true);
multirotorRB = new QRadioButton(tr("Multirotor"));
@@ -301,12 +304,8 @@ void ModelSelectionPage::initializePage()
bool ModelSelectionPage::validatePage()
{
//Filter and insert model name in mix data
QString newName(nameLineEdit->text());
newName = (newName.normalized(QString::NormalizationForm_D));
newName = newName.replace(QRegExp("[^ A-Za-z0-9_.-,\\s]"), "");
memset(wizDlg->mix.name, 0, sizeof(wizDlg->mix.name));
strncpy( wizDlg->mix.name, newName.toLatin1(), sizeof(wizDlg->mix.name)-1);
strncpy( wizDlg->mix.name, nameLineEdit->text().toLatin1(), sizeof(wizDlg->mix.name) - 1);
if (multirotorRB->isChecked())
wizDlg->mix.vehicle = MULTICOPTER;
+1 -6
View File
@@ -18,8 +18,7 @@
* GNU General Public License for more details.
*/
#ifndef _WIZARDDIALOG_H_
#define _WIZARDDIALOG_H_
#pragma once
#include <QtWidgets>
#include "wizarddata.h"
@@ -370,7 +369,3 @@ private:
QString vehicleName( Vehicle );
QString printChannel( Input, int, Input, int );
};
#endif // _WIZARDDIALOG_H_