feat(color): New label based Model Selector (#1374)
* Model Labels Selector * Portrait Layout * Receiver ID Check * Fix Labels .yml format * Companion Initial Commit commit 967ddf3979b4954402fe62c858faba4b7a416bba Author: dlktdr <cblackburn320@gmail.com> Date: Mon Aug 29 01:49:15 2022 -0700 Fix Labels .yml format commit 534221cad9e01146c47a9ac1080a655e04f1f6c8 Author: dlktdr <cblackburn320@gmail.com> Date: Mon Aug 29 01:20:57 2022 -0700 Receiver ID Check commit f32c0939d44e9ae60bc0705a9176354c23fec198 Author: dlktdr <cblackburn320@gmail.com> Date: Sun Aug 28 23:13:25 2022 -0700 Portrait Layout commit 961181217c7841ff3f21299ac6aa419b810f80e9 Author: dlktdr <cblackburn320@gmail.com> Date: Sat Dec 11 16:56:13 2021 -0800 Model Labels Selector commitdc99dd40d2Author: David PHAM-VAN <1387855+DavBfr@users.noreply.github.com> Date: Fri Aug 26 03:04:34 2022 -0300 feat: Allow Lua widget to override built-in widget with same name (#2208) * Allow CPP Widgets Override with LUA * Reset a widget option it the type differs commit4b747caff9Author: eshifri <46511024+eshifri@users.noreply.github.com> Date: Thu Aug 25 19:47:53 2022 -0700 fix: Write model name into models.yml when duplicating model (#2224) * Update model name in the models.yml file (#2214) * Fix the length of the name. commite97f23a305Author: eshifri <46511024+eshifri@users.noreply.github.com> Date: Wed Aug 24 21:19:52 2022 -0700 fix(cpn): Fix full path to model notes for Simulator (#2223) * Correct Highlighted Column * labels .yml indentation * Update da.h New defines translated to Danish * Update dataconstants.h Omit warnings due to difference of LEN_MODEL_FILENAME and LEN_MODEL_NAME in modellist.cpp * Update modelslist.cpp Wonder if this are the right file in PR #1374 (?), seems a lot are changed to the better in #1374 * Update dataconstants.h Restore #define for LEN_MODEL_FILENAME to 16 * Rebase, Correct Issues Delete now deletes instead of select New model stores correct information Duplicate save all information in cell Rework items to use cell over button Reword function variables * CMPN: Add context menu Fix output not functional. Removed Yaml::null at some point :) Fix some compiler warnings * CMPN: Compiler warning, replace ,->_ in list * FW: Various Changes Change view to a grid Don't allow comma in label name Add scroll indicators Save sort order Co-authored-by: HThuren <99370924+HThuren@users.noreply.github.com>
This commit is contained in:
@@ -211,6 +211,7 @@ set(companion_SRCS
|
||||
helpers_html.cpp
|
||||
mdichild.cpp
|
||||
modelslist.cpp
|
||||
labels.cpp
|
||||
apppreferencesdialog.cpp
|
||||
fwpreferencesdialog.cpp
|
||||
burnconfigdialog.cpp
|
||||
@@ -269,6 +270,7 @@ set(companion_MOC_HDRS
|
||||
modelprinter.h
|
||||
multimodelprinter.h
|
||||
modelslist.h
|
||||
labels.h
|
||||
styleeditdialog.h
|
||||
helpers_html.h
|
||||
dialogs/filesyncdialog.h
|
||||
|
||||
@@ -75,7 +75,7 @@ bool CompareDialog::handleMimeData(const QMimeData * mimeData)
|
||||
{
|
||||
QVector<ModelData> mList;
|
||||
GeneralSettings gs;
|
||||
if (!TreeModel::decodeMimeData(mimeData, &mList, &gs) || mList.isEmpty())
|
||||
if (!ModelsListModel::decodeMimeData(mimeData, &mList, &gs) || mList.isEmpty())
|
||||
return false;
|
||||
for (int i=0; i < mList.size(); ++i) {
|
||||
GMData data;
|
||||
|
||||
@@ -89,39 +89,23 @@ static void writeYamlToByteArray(const YAML::Node& node, QByteArray& data, bool
|
||||
qDebug() << data.toStdString().c_str();
|
||||
}
|
||||
|
||||
bool loadModelsListFromYaml(std::vector<CategoryData>& categories,
|
||||
bool loadLabelsListFromYaml(QStringList& labels,
|
||||
EtxModelfiles& modelFiles,
|
||||
const QByteArray& data)
|
||||
{
|
||||
labels.clear();
|
||||
if (data.size() == 0)
|
||||
return true;
|
||||
|
||||
YAML::Node node = loadYamlFromByteArray(data);
|
||||
if (!node.IsSequence()) return false;
|
||||
|
||||
int modelIdx = 0;
|
||||
for (const auto& cat : node) {
|
||||
if (!cat.IsMap()) continue;
|
||||
|
||||
for (const auto& cat_map : cat) {
|
||||
categories.push_back(cat_map.first.Scalar().c_str());
|
||||
|
||||
const auto& models = cat_map.second;
|
||||
if (!models.IsSequence()) continue;
|
||||
|
||||
for (const auto& model : models) {
|
||||
std::string filename, name;
|
||||
model["filename"] >> filename;
|
||||
model["name"] >> name;
|
||||
modelFiles.push_back(
|
||||
{filename, name, (int)categories.size() - 1, modelIdx++});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!node.IsMap()) return false; // Root Map (Labels, Models)
|
||||
for (const auto& lbl : node["Labels"]) {
|
||||
labels.append(QString::fromStdString(lbl.first.as<std::string>()));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool loadModelFromYaml(ModelData& model, const QByteArray& data)
|
||||
{
|
||||
YAML::Node node = loadYamlFromByteArray(data);
|
||||
@@ -168,46 +152,12 @@ bool loadRadioSettingsFromYaml(GeneralSettings& settings, const QByteArray& data
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// 'modelFiles' should be ordered by Category index to avoid
|
||||
// turning the sequence into a map.
|
||||
//
|
||||
bool writeModelsListToYaml(const std::vector<CategoryData>& categories,
|
||||
const EtxModelfiles& modelFiles,
|
||||
QByteArray& data)
|
||||
bool writeLabelsListToYaml(const RadioData &radioData, QByteArray& data)
|
||||
{
|
||||
YAML::Node node;
|
||||
std::vector<CategoryData> cats = categories;
|
||||
std::vector<EtxModelMetadata> files = { modelFiles.begin(), modelFiles.end() };
|
||||
|
||||
std::stable_sort(files.begin(), files.end(),
|
||||
[](const EtxModelMetadata &a, const EtxModelMetadata &b) {
|
||||
return a.category < b.category;
|
||||
});
|
||||
|
||||
int catIdx = 0;
|
||||
for (const auto& cat : cats) {
|
||||
node[catIdx++][cat.name] = YAML::Node();
|
||||
foreach(QString label, radioData.labels) {
|
||||
node["Labels"][label.toStdString()] = YAML::Null;
|
||||
}
|
||||
|
||||
for (const auto& modelFile: modelFiles) {
|
||||
|
||||
YAML::Node cat_attrs;
|
||||
cat_attrs["filename"] = modelFile.filename;
|
||||
cat_attrs["name"] = modelFile.name;
|
||||
|
||||
catIdx = modelFile.category;
|
||||
if (catIdx >= (int)cats.size()) {
|
||||
catIdx = 0;
|
||||
if (cats.size() == 0) {
|
||||
cats.push_back("Models");
|
||||
}
|
||||
}
|
||||
|
||||
const std::string cat_name = cats[catIdx].name;
|
||||
node[catIdx][cat_name].push_back(cat_attrs);
|
||||
}
|
||||
|
||||
writeYamlToByteArray(node, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -32,22 +32,19 @@ constexpr unsigned int CPN_CURRENT_SETTINGS_VERSION = { 221 };
|
||||
struct EtxModelMetadata {
|
||||
std::string filename;
|
||||
std::string name;
|
||||
int category;
|
||||
int modelIdx;
|
||||
};
|
||||
|
||||
typedef std::list<EtxModelMetadata> EtxModelfiles;
|
||||
|
||||
bool loadModelsListFromYaml(std::vector<CategoryData>& categories,
|
||||
bool loadLabelsListFromYaml(QStringList& labels,
|
||||
EtxModelfiles& modelFiles,
|
||||
const QByteArray& data);
|
||||
|
||||
bool loadModelFromYaml(ModelData& model, const QByteArray& data);
|
||||
bool loadRadioSettingsFromYaml(GeneralSettings& settings, const QByteArray& data);
|
||||
|
||||
bool writeModelsListToYaml(const std::vector<CategoryData>& categories,
|
||||
const EtxModelfiles& modelFiles,
|
||||
QByteArray& data);
|
||||
bool writeLabelsListToYaml(const RadioData &radioData, QByteArray& data);
|
||||
|
||||
bool writeModelToYaml(const ModelData& model, QByteArray& data);
|
||||
bool writeRadioSettingsToYaml(const GeneralSettings& settings, QByteArray& data);
|
||||
|
||||
@@ -743,6 +743,7 @@ Node convert<ModelData>::encode(const ModelData& rhs)
|
||||
Node header;
|
||||
header["name"] = rhs.name;
|
||||
header["bitmap"] = rhs.bitmap;
|
||||
header["labels"] = rhs.labels;
|
||||
|
||||
for (int i=0; i<CPN_MAX_MODULES; i++) {
|
||||
if (rhs.moduleData[i].protocol != PULSES_OFF) {
|
||||
@@ -986,6 +987,7 @@ bool convert<ModelData>::decode(const Node& node, ModelData& rhs)
|
||||
if (header.IsMap()) {
|
||||
header["name"] >> rhs.name;
|
||||
header["bitmap"] >> rhs.bitmap;
|
||||
header["labels"] >> rhs.labels;
|
||||
header["modelId"] >> modelIds;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ enum Capability {
|
||||
HasMixerExpo,
|
||||
HasBatMeterRange,
|
||||
DangerousFunctions,
|
||||
HasModelCategories,
|
||||
HasModelLabels,
|
||||
HasSwitchableJack,
|
||||
HasSportConnector,
|
||||
PwrButtonPress,
|
||||
|
||||
@@ -111,9 +111,9 @@ class ModelData {
|
||||
|
||||
char semver[8 + 1];
|
||||
bool used;
|
||||
int category;
|
||||
char name[15+1];
|
||||
char filename[16+1];
|
||||
char labels[100];
|
||||
int modelIndex; // Companion only, temporary index position managed by data model.
|
||||
|
||||
TimerData timers[CPN_MAX_TIMERS];
|
||||
|
||||
@@ -742,7 +742,7 @@ int OpenTxFirmware::getCapability(::Capability capability)
|
||||
return (IS_HORUS_OR_TARANIS(board) ? true : id.contains("battgraph"));
|
||||
case DangerousFunctions:
|
||||
return id.contains("danger") ? 1 : 0;
|
||||
case HasModelCategories:
|
||||
case HasModelLabels:
|
||||
return IS_FAMILY_HORUS_OR_T16(board);
|
||||
case HasSwitchableJack:
|
||||
return IS_TARANIS_XLITES(board);
|
||||
|
||||
@@ -94,13 +94,6 @@ void RadioData::convert(RadioDataConversionState & cstate)
|
||||
models[i].convert(cstate.withModelIndex(i));
|
||||
}
|
||||
|
||||
if (categories.size() == 0) {
|
||||
categories.push_back(CategoryData(qPrintable(tr("Models"))));
|
||||
for (unsigned i=0; i<models.size(); i++) {
|
||||
models[i].category = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_FAMILY_HORUS_OR_T16(cstate.toType)) {
|
||||
fixModelFilenames();
|
||||
}
|
||||
@@ -110,3 +103,104 @@ void RadioData::convert(RadioDataConversionState & cstate)
|
||||
models.resize(getCurrentFirmware()->getCapability(Models));
|
||||
}
|
||||
}
|
||||
|
||||
void RadioData::addLabel(QString label)
|
||||
{
|
||||
if(!labels.contains(label))
|
||||
labels.append(label);
|
||||
}
|
||||
|
||||
bool RadioData::deleteLabel(QString label)
|
||||
{
|
||||
bool deleted = false;
|
||||
// Remove labels in the models
|
||||
for(auto& model : models) {
|
||||
QStringList modelLabels = QString(model.labels).split(',',QString::SkipEmptyParts);
|
||||
if(modelLabels.contains(label))
|
||||
deleted = true;
|
||||
modelLabels.removeAll(label);
|
||||
strcpy(model.labels, QString(modelLabels.join(',')).toLocal8Bit().data());
|
||||
}
|
||||
|
||||
// Remove the label from the global list
|
||||
labels.removeAll(label);
|
||||
|
||||
// If no labels remain, add a Favorites one
|
||||
if(!labels.size()) {
|
||||
addLabel(tr("Favorites"));
|
||||
}
|
||||
return deleted;
|
||||
}
|
||||
|
||||
bool RadioData::deleteLabel(int index)
|
||||
{
|
||||
if(index >= labels.size()) return false;
|
||||
QString modelLabel = labels.at(index);
|
||||
return deleteLabel(modelLabel);
|
||||
}
|
||||
|
||||
bool RadioData::renameLabel(QString from, QString to)
|
||||
{
|
||||
bool success = true;
|
||||
int lengthdiff = to.size() - from.size();
|
||||
|
||||
// Check that rename is possible, not too long
|
||||
for(auto& model : models) {
|
||||
if((int)strlen(model.labels) + lengthdiff > (int)sizeof(model.labels) - 1) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
if(success) {
|
||||
for(auto& model : models) {
|
||||
QStringList modelLabels = QString(model.labels).split(',',QString::SkipEmptyParts);
|
||||
int ind = modelLabels.indexOf(from); // TOOD: Make sure it will fit
|
||||
if(ind != -1) {
|
||||
modelLabels.replace(ind, to);
|
||||
strcpy(model.labels, QString(modelLabels.join(',')).toLocal8Bit().data());
|
||||
}
|
||||
}
|
||||
int ind = labels.indexOf(from);
|
||||
if(ind != -1) {
|
||||
labels.replace(ind, to);
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool RadioData::renameLabel(int index, QString to)
|
||||
{
|
||||
if(index >= labels.size()) return false;
|
||||
QString from = labels.at(index);
|
||||
return renameLabel(from, to);
|
||||
}
|
||||
|
||||
bool RadioData::addLabelToModel(int index, QString label)
|
||||
{
|
||||
if(index >= models.size()) return false;
|
||||
|
||||
char *modelLabelCsv = models[index].labels;
|
||||
// Make sure it will fit
|
||||
if(strlen(modelLabelCsv) + label.size() + 1 < sizeof(models[index].labels)-1) {
|
||||
QStringList modelLabels = QString(modelLabelCsv).split(',',QString::SkipEmptyParts);
|
||||
if(!modelLabels.contains(label)) {
|
||||
modelLabels.append(label);
|
||||
strcpy(models[index].labels, QString(modelLabels.join(',')).toLocal8Bit().data());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RadioData::removeLabelFromModel(int index, QString label)
|
||||
{
|
||||
if(index >= models.size()) return false;
|
||||
|
||||
char *modelLabelCsv = models[index].labels;
|
||||
QStringList modelLabels = QString(modelLabelCsv).split(',',QString::SkipEmptyParts);
|
||||
if(modelLabels.contains(label)) {
|
||||
modelLabels.removeAll(label);
|
||||
strcpy(models[index].labels, QString(modelLabels.join(',')).toLocal8Bit().data());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "generalsettings.h"
|
||||
#include "modeldata.h"
|
||||
#include "labels.h"
|
||||
|
||||
#include "datahelpers.h" // required for getElementName
|
||||
|
||||
@@ -9,15 +10,6 @@
|
||||
|
||||
class RadioDataConversionState;
|
||||
|
||||
class CategoryData {
|
||||
public:
|
||||
CategoryData(const char * name) {
|
||||
memset(this->name, 0, sizeof(CategoryData::name));
|
||||
strncpy(this->name, name, sizeof(CategoryData::name)-1);
|
||||
}
|
||||
char name[15+1];
|
||||
};
|
||||
|
||||
class RadioData {
|
||||
Q_DECLARE_TR_FUNCTIONS(RadioData)
|
||||
|
||||
@@ -25,11 +17,19 @@ class RadioData {
|
||||
RadioData();
|
||||
|
||||
GeneralSettings generalSettings;
|
||||
std::vector<CategoryData> categories;
|
||||
QStringList labels;
|
||||
std::vector<ModelData> models;
|
||||
|
||||
void convert(RadioDataConversionState & cstate);
|
||||
|
||||
void addLabel(QString label);
|
||||
bool deleteLabel(QString label);
|
||||
bool deleteLabel(int index);
|
||||
bool renameLabel(QString from, QString to);
|
||||
bool renameLabel(int index, QString to);
|
||||
bool addLabelToModel(int index, QString label);
|
||||
bool removeLabelFromModel(int index, QString label);
|
||||
|
||||
void setCurrentModel(unsigned int index);
|
||||
void fixModelFilenames();
|
||||
QString getNextModelFilename();
|
||||
|
||||
@@ -0,0 +1,276 @@
|
||||
#include "labels.h"
|
||||
|
||||
LabelsModel::LabelsModel(QItemSelectionModel *selectionModel,
|
||||
RadioData *radioData, QObject *parent) :
|
||||
QAbstractItemModel(parent),
|
||||
modelsSelection(selectionModel),
|
||||
radioData(radioData),
|
||||
selectedModel(-1)
|
||||
{
|
||||
connect(modelsSelection, &QItemSelectionModel::currentChanged,
|
||||
this, &LabelsModel::modelsSelectionChanged );
|
||||
|
||||
buildLabelsList();
|
||||
}
|
||||
|
||||
LabelsModel::~LabelsModel()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Qt::ItemFlags LabelsModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
Qt::ItemFlags flags = Qt::ItemIsEnabled;
|
||||
|
||||
if(index.column() == 0) {
|
||||
flags |= Qt::ItemIsUserCheckable;
|
||||
flags |= Qt::ItemIsSelectable;
|
||||
flags |= Qt::ItemIsDropEnabled;
|
||||
flags |= Qt::ItemIsEditable;
|
||||
if(index.isValid())
|
||||
flags |= Qt::ItemIsDragEnabled;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
bool LabelsModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
if(!index.isValid() || index.column() != 0 ||
|
||||
index.row() >= radioData->labels.size() ||
|
||||
selectedModel == -1)
|
||||
return false;
|
||||
|
||||
if(role == Qt::CheckStateRole) {
|
||||
if(value==Qt::Unchecked) {
|
||||
if(radioData->removeLabelFromModel(selectedModel, radioData->labels.at(index.row())))
|
||||
emit modelChanged(selectedModel);
|
||||
} else {
|
||||
if(radioData->addLabelToModel(selectedModel, radioData->labels.at(index.row())))
|
||||
emit modelChanged(selectedModel);
|
||||
}
|
||||
emit dataChanged(this->index(index.row(), 0),
|
||||
this->index(index.row(), 0));
|
||||
return true;
|
||||
} else if(role == Qt::EditRole) {
|
||||
// Don't allow comma, replace with _
|
||||
if(radioData->renameLabel(index.row(),value.toString().replace(',','_'))) {
|
||||
labels[index.row()].label = value.toString().replace(',','_');
|
||||
emit dataChanged(this->index(index.row(), 0),
|
||||
this->index(index.row(), 0));
|
||||
emit modelChanged(selectedModel);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariant LabelsModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
// if(index.row() >= radioData->labels.size() || !index.isValid() )
|
||||
if(index.row() >= labels.size() || !index.isValid() )
|
||||
return QVariant();
|
||||
|
||||
QString label = radioData->labels.at(index.row());
|
||||
|
||||
if(role == Qt::DisplayRole || role == Qt::EditRole) {
|
||||
if(index.column() == 0) {
|
||||
return labels.at(index.row()).label;
|
||||
//return label;
|
||||
}
|
||||
} else if (role == Qt::CheckStateRole) {
|
||||
if(index.column() == 0 && selectedModel >= 0 &&
|
||||
selectedModel < (int)radioData->models.size()) {
|
||||
return QString(radioData->models.at(selectedModel).labels).contains(label)?Qt::Checked:Qt::Unchecked;
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QModelIndex LabelsModel::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
|
||||
if(row < labels.size())
|
||||
return createIndex(row,0);
|
||||
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
QModelIndex LabelsModel::parent(const QModelIndex &index) const
|
||||
{
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
int LabelsModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
//return radioData->labels.size();
|
||||
return labels.size();
|
||||
}
|
||||
|
||||
int LabelsModel::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
return 1;
|
||||
}
|
||||
|
||||
QVariant LabelsModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if(role != Qt::DisplayRole ||
|
||||
orientation != Qt::Horizontal ||
|
||||
section != 0)
|
||||
return QVariant();
|
||||
return tr("Labels");
|
||||
}
|
||||
|
||||
Qt::DropActions LabelsModel::supportedDropActions() const
|
||||
{
|
||||
return Qt::MoveAction;
|
||||
}
|
||||
|
||||
QStringList LabelsModel::mimeTypes() const
|
||||
{
|
||||
QStringList types;
|
||||
types << "application/edglbl.text.list";
|
||||
return types;
|
||||
}
|
||||
|
||||
QMimeData *LabelsModel::mimeData(const QModelIndexList &indexes) const
|
||||
{
|
||||
QMimeData *mimeData = new QMimeData;
|
||||
QByteArray encodedData;
|
||||
|
||||
QDataStream stream(&encodedData, QIODevice::WriteOnly);
|
||||
|
||||
for (const QModelIndex &index : indexes) {
|
||||
if (index.isValid()) {
|
||||
QString text = data(index, Qt::DisplayRole).toString();
|
||||
stream << text;
|
||||
}
|
||||
}
|
||||
|
||||
mimeData->setData("application/edglbl.text.list", encodedData);
|
||||
return mimeData;
|
||||
}
|
||||
|
||||
bool LabelsModel::canDropMimeData(const QMimeData *data, Qt::DropAction action,
|
||||
int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(action);
|
||||
Q_UNUSED(row);
|
||||
Q_UNUSED(parent);
|
||||
|
||||
if (!data->hasFormat("application/edglbl.text.list"))
|
||||
return false;
|
||||
|
||||
if (column > 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LabelsModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
|
||||
{
|
||||
// TODO: This needs to be completed
|
||||
if (!canDropMimeData(data, action, row, column, parent))
|
||||
return false;
|
||||
|
||||
if(row < 0) return false;
|
||||
|
||||
if (action == Qt::IgnoreAction)
|
||||
return true;
|
||||
else if (action != Qt::MoveAction)
|
||||
return false;
|
||||
|
||||
QByteArray encodedData = data->data("application/edglbl.text.list");
|
||||
QDataStream stream(&encodedData, QIODevice::ReadOnly);
|
||||
QStringList newItems;
|
||||
int rows = 0;
|
||||
|
||||
while (!stream.atEnd()) {
|
||||
QString text;
|
||||
stream >> text;
|
||||
newItems << text;
|
||||
++rows;
|
||||
}
|
||||
|
||||
/*insertRows(row, rows, QModelIndex());
|
||||
for (const QString &text : qAsConst(newItems))
|
||||
{
|
||||
QModelIndex idx = index(row, 0, QModelIndex());
|
||||
row++;
|
||||
}*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LabelsModel::insertRows(int row, int count, const QModelIndex &parent)
|
||||
{
|
||||
if (parent.isValid())
|
||||
return false;
|
||||
|
||||
// Find a New Number
|
||||
for (int i = 0; i < count; i++) {
|
||||
int newno=0;
|
||||
QString newstr;
|
||||
do {
|
||||
newstr = QString(tr("New%1").arg(newno));
|
||||
if(newno == 0)
|
||||
newstr = QString(tr("New"));
|
||||
newno++;
|
||||
} while(radioData->labels.contains(newstr));
|
||||
// Add it to radioData
|
||||
radioData->labels.insert(row+i, newstr);
|
||||
}
|
||||
buildLabelsList();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LabelsModel::removeRows(int row, int count, const QModelIndex &parent)
|
||||
{
|
||||
if (parent.isValid())
|
||||
return false;
|
||||
bool deleted=false;
|
||||
beginRemoveRows(parent, row, row + count - 1);
|
||||
for (int i = 0; i != count; ++i)
|
||||
if(radioData->deleteLabel(row+i)) {
|
||||
deleted = true;
|
||||
}
|
||||
endRemoveRows();
|
||||
// Refresh all
|
||||
if(deleted)
|
||||
emit modelChanged(-1);
|
||||
buildLabelsList();
|
||||
return true;
|
||||
}
|
||||
|
||||
void LabelsModel::buildLabelsList()
|
||||
{
|
||||
labels.clear();
|
||||
|
||||
int i=0;
|
||||
foreach(QString lbl, radioData->labels) {
|
||||
LabelItem itm;
|
||||
itm.label = lbl;
|
||||
itm.radioLabelIndex = i++;
|
||||
labels.append(itm);
|
||||
}
|
||||
if (i) {
|
||||
emit dataChanged(index(0,0),
|
||||
index(i-1,0));
|
||||
}
|
||||
}
|
||||
|
||||
void LabelsModel::modelsSelectionChanged()
|
||||
{
|
||||
QModelIndex index = modelsSelection->currentIndex();
|
||||
if (index.isValid()) {
|
||||
int mi = modelsSelection->currentIndex().row();
|
||||
if(mi < (int)radioData->models.size()) {
|
||||
selectedModel = mi;
|
||||
buildLabelsList();
|
||||
}
|
||||
}
|
||||
else
|
||||
selectedModel = -1;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QList>
|
||||
#include <QAbstractItemModel>
|
||||
#include <QItemSelectionModel>
|
||||
|
||||
#include "radiodata.h"
|
||||
|
||||
class RadioData;
|
||||
|
||||
typedef struct {
|
||||
QString label;
|
||||
QModelIndex index;
|
||||
int radioLabelIndex;
|
||||
} LabelItem;
|
||||
|
||||
class LabelsModel : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
LabelsModel(QItemSelectionModel *selectionModel, RadioData *radioData, QObject *parent = nullptr);
|
||||
~LabelsModel();
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
|
||||
QModelIndex parent(const QModelIndex &index) const override;
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||
Qt::DropActions supportedDropActions() const override;
|
||||
QStringList mimeTypes() const override;
|
||||
QMimeData *mimeData(const QModelIndexList &indexes) const override;
|
||||
bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const override;
|
||||
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
|
||||
bool insertRows(int row, int count, const QModelIndex &parent) override;
|
||||
bool removeRows(int row, int count, const QModelIndex &parent) override;
|
||||
|
||||
private slots:
|
||||
void buildLabelsList();
|
||||
void modelsSelectionChanged();
|
||||
|
||||
signals:
|
||||
void modelChanged(int index);
|
||||
|
||||
private:
|
||||
QItemSelectionModel *modelsSelection;
|
||||
RadioData *radioData;
|
||||
int selectedModel;
|
||||
QList<QModelIndex> modelIndices;
|
||||
QList<LabelItem> labels;
|
||||
};
|
||||
+124
-159
@@ -33,6 +33,7 @@
|
||||
#include "radiointerface.h"
|
||||
#include "radiodataconversionstate.h"
|
||||
#include "filtereditemmodels.h"
|
||||
#include "labels.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <ExportableTableView>
|
||||
@@ -41,14 +42,16 @@ MdiChild::MdiChild(QWidget * parent, QWidget * parentWin, Qt::WindowFlags f):
|
||||
QWidget(parent, f),
|
||||
ui(new Ui::MdiChild),
|
||||
modelsListModel(NULL),
|
||||
labelsListModel(NULL),
|
||||
parentWindow(parentWin),
|
||||
radioToolbar(NULL),
|
||||
categoriesToolbar(NULL),
|
||||
modelsToolbar(NULL),
|
||||
labelsToolbar(NULL),
|
||||
lblLabels(NULL),
|
||||
|
||||
firmware(getCurrentFirmware()),
|
||||
lastSelectedModel(-1),
|
||||
isUntitled(true),
|
||||
showCatToolbar(true),
|
||||
forceCloseFlag(false),
|
||||
stateDataVersion(1)
|
||||
{
|
||||
@@ -73,6 +76,7 @@ MdiChild::MdiChild(QWidget * parent, QWidget * parentWin, Qt::WindowFlags f):
|
||||
ui->modelsList->setDragDropMode(QAbstractItemView::DragDrop);
|
||||
ui->modelsList->setStyle(new ItemViewProxyStyle(ui->modelsList->style()));
|
||||
ui->modelsList->setStyleSheet("QTreeView::item {margin: 2px 0;}"); // a little more space for our drop indicators
|
||||
ui->lstLabels->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
retranslateUi();
|
||||
|
||||
@@ -82,6 +86,7 @@ MdiChild::MdiChild(QWidget * parent, QWidget * parentWin, Qt::WindowFlags f):
|
||||
connect(ui->modelsList, &QTreeView::pressed, this, &MdiChild::onItemSelected);
|
||||
connect(ui->modelsList->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &MdiChild::onCurrentItemChanged);
|
||||
connect(QGuiApplication::clipboard(), &QClipboard::dataChanged, this, &MdiChild::updateNavigation);
|
||||
connect(ui->lstLabels, &QTreeView::customContextMenuRequested, this, &MdiChild::showLabelsContextMenu);
|
||||
|
||||
if (!(isMaximized() || isMinimized())) {
|
||||
QByteArray geo = g.mdiWinGeo();
|
||||
@@ -103,8 +108,7 @@ MdiChild::MdiChild(QWidget * parent, QWidget * parentWin, Qt::WindowFlags f):
|
||||
stream >> ver;
|
||||
if (ver <= stateDataVersion) {
|
||||
bool visMdl, visGen;
|
||||
stream >> showCatToolbar >> visMdl >> visGen;
|
||||
categoriesToolbar->setVisible(showCatToolbar);
|
||||
stream >> showLabelToolbar >> visMdl >> visGen;
|
||||
modelsToolbar->setVisible(visMdl);
|
||||
radioToolbar->setVisible(visGen);
|
||||
}
|
||||
@@ -141,7 +145,7 @@ void MdiChild::closeEvent(QCloseEvent *event)
|
||||
QByteArray state;
|
||||
QDataStream stream(&state, QIODevice::WriteOnly);
|
||||
stream << stateDataVersion
|
||||
<< (firmware->getCapability(Capability::HasModelCategories) ? categoriesToolbar->isVisible() : showCatToolbar)
|
||||
<< false
|
||||
<< modelsToolbar->isVisible()
|
||||
<< radioToolbar->isVisible();
|
||||
g.mdiWinState(state);
|
||||
@@ -212,16 +216,13 @@ void MdiChild::setupNavigation()
|
||||
addAct(ACT_ITM_EDT, "edit.png", SLOT(edit()), Qt::Key_Enter);
|
||||
addAct(ACT_ITM_DEL, "clear.png", SLOT(confirmDelete()), QKeySequence::Delete);
|
||||
|
||||
addAct(ACT_CAT_ADD, "add.png", SLOT(categoryAdd()), tr("Alt+C"));
|
||||
//addAct(ACT_CAT_EDT, "edit.png", SLOT(edit()), Qt::Key_Enter);
|
||||
//addAct(ACT_CAT_DEL, "clear.png", SLOT(confirmDelete()), QKeySequence::Delete);
|
||||
action[ACT_CAT_SEP] = new QAction(this);
|
||||
action[ACT_CAT_SEP]->setSeparator(true);
|
||||
|
||||
addAct(ACT_MDL_ADD, "add.png", SLOT(modelAdd()), tr("Alt+A"));
|
||||
addAct(ACT_MDL_RTR, "open.png", SLOT(loadBackup()), tr("Alt+R"));
|
||||
addAct(ACT_MDL_WIZ, "wizard.png", SLOT(wizardEdit()), tr("Alt+W"));
|
||||
|
||||
addAct(ACT_LBL_ADD, "add.png", SLOT(labelAdd()), tr("Alt-L"));
|
||||
addAct(ACT_LBL_DEL, "clear.png", SLOT(labelDelete()), tr("Alt-L"));
|
||||
|
||||
addAct(ACT_MDL_DFT, "currentmodel.png", SLOT(setDefault()), tr("Alt+U"));
|
||||
addAct(ACT_MDL_PRT, "print.png", SLOT(print()), QKeySequence::Print);
|
||||
addAct(ACT_MDL_SIM, "simulate.png", SLOT(modelSimulate()), tr("Alt+S"));
|
||||
@@ -243,16 +244,24 @@ void MdiChild::setupNavigation()
|
||||
QSize tbIcnSz(16, 16);
|
||||
QString tbCss = "QToolBar {border: 1px solid palette(midlight);}";
|
||||
|
||||
if (categoriesToolbar)
|
||||
categoriesToolbar->deleteLater();
|
||||
categoriesToolbar = new QToolBar(this);
|
||||
categoriesToolbar->setObjectName("TB_CATEGORIES");
|
||||
categoriesToolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||
categoriesToolbar->setFloatable(false);
|
||||
categoriesToolbar->setIconSize(tbIcnSz);
|
||||
categoriesToolbar->setStyleSheet(tbCss);
|
||||
categoriesToolbar->addAction(getAction(ACT_CAT_ADD));
|
||||
ui->topToolbarLayout->addWidget(categoriesToolbar);
|
||||
// Add labels Label to bottom layout
|
||||
if(lblLabels)
|
||||
lblLabels->deleteLater();
|
||||
lblLabels = new QLabel(tr("Labels"));
|
||||
lblLabels->setStyleSheet("font-weight: bold");
|
||||
ui->bottomLayout->addWidget(lblLabels);
|
||||
|
||||
if(labelsToolbar)
|
||||
labelsToolbar->deleteLater();
|
||||
labelsToolbar = new QToolBar(this);
|
||||
labelsToolbar->setObjectName("TB_LABELS");
|
||||
labelsToolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||
labelsToolbar->setFloatable(false);
|
||||
labelsToolbar->setIconSize(tbIcnSz);
|
||||
labelsToolbar->setStyleSheet(tbCss);
|
||||
labelsToolbar->addAction(getAction(ACT_LBL_ADD));
|
||||
labelsToolbar->addAction(getAction(ACT_LBL_DEL));
|
||||
ui->bottomLayout->addWidget(labelsToolbar);
|
||||
|
||||
if (radioToolbar)
|
||||
radioToolbar->deleteLater();
|
||||
@@ -282,7 +291,7 @@ void MdiChild::setupNavigation()
|
||||
modelsToolbar->setFloatable(false);
|
||||
modelsToolbar->setIconSize(tbIcnSz);
|
||||
modelsToolbar->setStyleSheet(tbCss);
|
||||
modelsToolbar->addActions(getEditActions(false));
|
||||
modelsToolbar->addActions(getEditActions());
|
||||
modelsToolbar->addSeparator();
|
||||
modelsToolbar->addActions(getModelActions());
|
||||
if ((btn = qobject_cast<QToolButton *>(modelsToolbar->widgetForAction(action[ACT_MDL_ADD])))) {
|
||||
@@ -293,7 +302,6 @@ void MdiChild::setupNavigation()
|
||||
}
|
||||
ui->botToolbarLayout->addWidget(modelsToolbar);
|
||||
|
||||
connect(categoriesToolbar, &QToolBar::visibilityChanged, this, &MdiChild::adjustToolbarLayout);
|
||||
connect(radioToolbar, &QToolBar::visibilityChanged, this, &MdiChild::adjustToolbarLayout);
|
||||
connect(modelsToolbar, &QToolBar::visibilityChanged, this, &MdiChild::adjustToolbarLayout);
|
||||
}
|
||||
@@ -301,73 +309,32 @@ void MdiChild::setupNavigation()
|
||||
void MdiChild::updateNavigation()
|
||||
{
|
||||
const int modelsSelected = countSelectedModels();
|
||||
const int catsSelected = countSelectedCats();
|
||||
const bool singleModelSelected = (modelsSelected == 1);
|
||||
const bool hasModelSlotSelcted = (getCurrentModel() > -1);
|
||||
const bool hasCats = firmware->getCapability(Capability::HasModelCategories);
|
||||
const bool hasCatSelected = hasCats && modelsListModel->isCategoryType(getCurrentIndex());
|
||||
const bool hasLabels = firmware->getCapability(Capability::HasModelLabels);
|
||||
const int numOnClipbrd = modelsListModel->countModelsInMimeData(QApplication::clipboard()->mimeData());
|
||||
const QString modelsRemvTxt = tr("%n Model(s)", "As in \"Copy 3 Models\" or \"Cut 1 Model\" or \"Delete 3 Models\" action).", modelsSelected);
|
||||
const QString modelsAddTxt = tr("%n Model(s)", "As in \"Paste 3 Models\" or \"Insert 1 Model.\"", numOnClipbrd);
|
||||
const QString catsRemvTxt = tr("%n Category(ies)", "As in \"Delete 3 Categories\" or \"Delete 1 Category.\"", catsSelected);
|
||||
static const QString noSelection = tr("Nothing selected");
|
||||
static const QString sp = " ";
|
||||
static const QString ns;
|
||||
|
||||
categoriesToolbar->setVisible(hasCats && showCatToolbar);
|
||||
|
||||
labelsToolbar->setVisible(hasLabels);
|
||||
ui->lstLabels->setVisible(hasLabels);
|
||||
lblLabels->setVisible(hasLabels);
|
||||
action[ACT_GEN_PST]->setEnabled(hasClipboardData(1));
|
||||
|
||||
action[ACT_ITM_EDT]->setEnabled(singleModelSelected || catsSelected == 1);
|
||||
action[ACT_ITM_EDT]->setText((hasCatSelected ? tr("Rename Category") : modelsSelected ? tr("Edit Model") : noSelection));
|
||||
action[ACT_ITM_DEL]->setEnabled(modelsSelected || catsSelected);
|
||||
action[ACT_ITM_DEL]->setText((action[ACT_ITM_DEL]->isEnabled() ? tr("Delete") % " " % (hasCatSelected ? catsRemvTxt : modelsRemvTxt) : noSelection));
|
||||
|
||||
action[ACT_CAT_ADD]->setVisible(hasCats);
|
||||
action[ACT_CAT_SEP]->setVisible(hasCats);
|
||||
// action[ACT_CAT_EDT]->setVisible(hasCats);
|
||||
// action[ACT_CAT_EDT]->setEnabled(catsSelected == 1);
|
||||
// action[ACT_CAT_DEL]->setVisible(hasCats);
|
||||
// action[ACT_CAT_DEL]->setEnabled(catsSelected);
|
||||
// action[ACT_CAT_DEL]->setText((hasCatSelected ? tr("Delete") % " " % catsRemTxt : noSelection));
|
||||
|
||||
action[ACT_MDL_CUT]->setEnabled(modelsSelected);
|
||||
action[ACT_MDL_CUT]->setText(tr("Cut") % (modelsSelected ? sp % modelsRemvTxt : ns));
|
||||
action[ACT_MDL_CPY]->setEnabled(modelsSelected);
|
||||
action[ACT_MDL_CPY]->setText(tr("Copy") % (modelsSelected ? sp % modelsRemvTxt : ns));
|
||||
action[ACT_MDL_PST]->setEnabled(numOnClipbrd);
|
||||
action[ACT_MDL_PST]->setText(tr("Paste") % (numOnClipbrd ? sp % modelsAddTxt : ns));
|
||||
action[ACT_MDL_INS]->setEnabled(numOnClipbrd && (hasModelSlotSelcted || catsSelected));
|
||||
action[ACT_MDL_INS]->setEnabled(numOnClipbrd && hasModelSlotSelcted);
|
||||
action[ACT_MDL_INS]->setText(tr("Insert") % QString(action[ACT_MDL_INS]->isEnabled() ? sp % modelsAddTxt : ns));
|
||||
action[ACT_MDL_SAV]->setEnabled(modelsSelected);
|
||||
action[ACT_MDL_SAV]->setText(tr("Save") % (modelsSelected ? sp % modelsRemvTxt : ns));
|
||||
|
||||
if (hasCats && action[ACT_MDL_MOV]->menu()) {
|
||||
action[ACT_MDL_MOV]->setVisible(true);
|
||||
QModelIndex modelIndex = getCurrentIndex();
|
||||
QMenu * catsMenu = action[ACT_MDL_MOV]->menu();
|
||||
catsMenu->clear();
|
||||
if (modelsSelected && modelsListModel && radioData.categories.size() > 1) {
|
||||
action[ACT_MDL_MOV]->setEnabled(true);
|
||||
for (unsigned i=0; i < radioData.categories.size(); ++i) {
|
||||
QAction * act = catsMenu->addAction(QString(radioData.categories[i].name), this, SLOT(onModelMoveToCategory()));
|
||||
act->setProperty("categoryId", i);
|
||||
if ((int)i < modelsListModel->getCategoryIndex(modelIndex))
|
||||
act->setIcon(CompanionIcon("moveup.png"));
|
||||
else if ((int)i > modelsListModel->getCategoryIndex(modelIndex))
|
||||
act->setIcon(CompanionIcon("movedown.png"));
|
||||
else
|
||||
act->setEnabled(false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
action[ACT_MDL_MOV]->setDisabled(true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
action[ACT_MDL_MOV]->setVisible(false);
|
||||
}
|
||||
|
||||
action[ACT_MDL_MOV]->setVisible(false);
|
||||
action[ACT_MDL_DUP]->setEnabled(singleModelSelected);
|
||||
action[ACT_MDL_RTR]->setEnabled(singleModelSelected);
|
||||
action[ACT_MDL_WIZ]->setEnabled(singleModelSelected);
|
||||
@@ -383,9 +350,11 @@ void MdiChild::retranslateUi()
|
||||
action[ACT_GEN_PST]->setText(tr("Paste Radio Settings"));
|
||||
action[ACT_GEN_SIM]->setText(tr("Simulate Radio"));
|
||||
|
||||
action[ACT_CAT_ADD]->setText(tr("Add Category"));
|
||||
action[ACT_CAT_ADD]->setIconText(tr("Category"));
|
||||
//action[ACT_CAT_EDT]->setText(tr("Rename Category"));
|
||||
action[ACT_ITM_EDT]->setText(tr("Edit Model"));
|
||||
action[ACT_ITM_DEL]->setText(tr("Delete"));
|
||||
|
||||
action[ACT_LBL_ADD]->setText(tr("Add Label"));
|
||||
action[ACT_LBL_DEL]->setText(tr("Delete Label"));
|
||||
|
||||
action[ACT_MDL_ADD]->setText(tr("Add Model"));
|
||||
action[ACT_MDL_ADD]->setIconText(tr("Model"));
|
||||
@@ -398,9 +367,6 @@ void MdiChild::retranslateUi()
|
||||
action[ACT_MDL_SIM]->setText(tr("Simulate Model"));
|
||||
action[ACT_MDL_DUP]->setText(tr("Duplicate Model"));
|
||||
|
||||
action[ACT_MDL_MOV]->setText(tr("Move to Category"));
|
||||
|
||||
categoriesToolbar->setWindowTitle(tr("Show Category Actions Toolbar"));
|
||||
radioToolbar->setWindowTitle(tr("Show Radio Actions Toolbar"));
|
||||
modelsToolbar->setWindowTitle(tr("Show Model Actions Toolbar"));
|
||||
}
|
||||
@@ -415,13 +381,9 @@ QList<QAction *> MdiChild::getGeneralActions()
|
||||
return actGrp;
|
||||
}
|
||||
|
||||
QList<QAction *> MdiChild::getEditActions(bool incCatNew)
|
||||
QList<QAction *> MdiChild::getEditActions()
|
||||
{
|
||||
QList<QAction *> actGrp;
|
||||
if (incCatNew) {
|
||||
actGrp.append(getAction(ACT_CAT_ADD));
|
||||
actGrp.append(getAction(ACT_CAT_SEP));
|
||||
}
|
||||
actGrp.append(action[ACT_MDL_ADD]);
|
||||
QAction * sep2 = new QAction(this);
|
||||
sep2->setSeparator(true);
|
||||
@@ -449,15 +411,13 @@ QList<QAction *> MdiChild::getModelActions()
|
||||
return actGrp;
|
||||
}
|
||||
|
||||
//QList<QAction *> MdiChild::getCategoryActions()
|
||||
//{
|
||||
// QList<QAction *> actGrp;
|
||||
// actGrp.append(getAction(ACT_CAT_ADD));
|
||||
// actGrp.append(getAction(ACT_CAT_EDT));
|
||||
// actGrp.append(getAction(ACT_CAT_DEL));
|
||||
// actGrp.append(getAction(ACT_CAT_SEP));
|
||||
// return actGrp;
|
||||
//}
|
||||
QList<QAction *> MdiChild::getLabelsActions()
|
||||
{
|
||||
QList<QAction *> actGrp;
|
||||
actGrp.append(getAction(ACT_LBL_ADD));
|
||||
actGrp.append(getAction(ACT_LBL_DEL));
|
||||
return actGrp;
|
||||
}
|
||||
|
||||
QAction * MdiChild::getAction(const MdiChild::Actions type)
|
||||
{
|
||||
@@ -474,15 +434,6 @@ void MdiChild::showModelsListContextMenu(const QPoint & pos)
|
||||
|
||||
updateNavigation();
|
||||
|
||||
if (firmware->getCapability(Capability::HasModelCategories)) {
|
||||
contextMenu.addAction(action[ACT_CAT_ADD]);
|
||||
if(modelsListModel->isCategoryType(modelIndex)) {
|
||||
contextMenu.addAction(action[ACT_ITM_EDT]);
|
||||
contextMenu.addAction(action[ACT_ITM_DEL]);
|
||||
}
|
||||
contextMenu.addSeparator();
|
||||
}
|
||||
|
||||
if (modelsListModel->isModelType(modelIndex)) {
|
||||
contextMenu.addActions(getEditActions());
|
||||
if (countSelectedModels() == 1) {
|
||||
@@ -500,11 +451,20 @@ void MdiChild::showModelsListContextMenu(const QPoint & pos)
|
||||
contextMenu.exec(ui->modelsList->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void MdiChild::showLabelsContextMenu(const QPoint &pos)
|
||||
{
|
||||
QMenu contextMenu;
|
||||
|
||||
contextMenu.addAction(action[ACT_LBL_ADD]);
|
||||
contextMenu.addAction(action[ACT_LBL_DEL]);
|
||||
|
||||
if (!contextMenu.isEmpty())
|
||||
contextMenu.exec(ui->lstLabels->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void MdiChild::showContextMenu(const QPoint & pos)
|
||||
{
|
||||
QMenu contextMenu;
|
||||
if (firmware->getCapability(Capability::HasModelCategories))
|
||||
contextMenu.addAction(categoriesToolbar->toggleViewAction());
|
||||
contextMenu.addAction(modelsToolbar->toggleViewAction());
|
||||
contextMenu.addAction(radioToolbar->toggleViewAction());
|
||||
if (!contextMenu.isEmpty())
|
||||
@@ -534,26 +494,39 @@ void MdiChild::initModelsList()
|
||||
if (modelsListModel)
|
||||
delete modelsListModel;
|
||||
|
||||
modelsListModel = new TreeModel(&radioData, this);
|
||||
connect(modelsListModel, &TreeModel::modelsDropped, this, &MdiChild::pasteModelData);
|
||||
connect(modelsListModel, &TreeModel::modelsRemoved, this, &MdiChild::deleteModels);
|
||||
connect(modelsListModel, &TreeModel::refreshRequested, this, &MdiChild::refresh);
|
||||
modelsListModel = new ModelsListModel(&radioData, this);
|
||||
connect(modelsListModel, &ModelsListModel::modelsDropped, this, &MdiChild::pasteModelData);
|
||||
connect(modelsListModel, &ModelsListModel::modelsRemoved, this, &MdiChild::deleteModels);
|
||||
connect(modelsListModel, &ModelsListModel::refreshRequested, this, &MdiChild::refresh);
|
||||
connect(modelsListModel, &QAbstractItemModel::dataChanged, this, &MdiChild::onDataChanged);
|
||||
|
||||
ui->modelsList->setModel(modelsListModel);
|
||||
if (IS_FAMILY_HORUS_OR_T16(board)) {
|
||||
ui->modelsList->setIndentation(20);
|
||||
// ui->modelsList->resetIndentation(); // introduced in next Qt versions ...
|
||||
}
|
||||
else {
|
||||
ui->modelsList->setIndentation(0);
|
||||
}
|
||||
ui->modelsList->selectionModel()->currentIndex().row();
|
||||
|
||||
// Labels Editor + Model
|
||||
if(labelsListModel)
|
||||
delete labelsListModel;
|
||||
labelsListModel = new LabelsModel(ui->modelsList->selectionModel(),
|
||||
&radioData, this);
|
||||
connect(labelsListModel, &LabelsModel::modelChanged, this, &MdiChild::refresh);
|
||||
ui->lstLabels->setModel(labelsListModel);
|
||||
ui->lstLabels->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
ui->lstLabels->setDragEnabled(true);
|
||||
ui->lstLabels->setAcceptDrops(true);
|
||||
ui->lstLabels->setDropIndicatorShown(true);
|
||||
ui->lstLabels->setDragDropOverwriteMode(false);
|
||||
ui->lstLabels->setDragDropMode(QAbstractItemView::InternalMove);
|
||||
ui->lstLabels->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::EditKeyPressed);
|
||||
|
||||
ui->modelsList->setIndentation(0);
|
||||
|
||||
refresh();
|
||||
|
||||
if (firmware->getCapability(Capability::HasModelCategories)) {
|
||||
if (firmware->getCapability(Capability::HasModelLabels)) {
|
||||
ui->modelsList->header()->resizeSection(0, ui->modelsList->header()->sectionSize(0) * 2); // pad out categories and model names
|
||||
}
|
||||
else {
|
||||
} else if (firmware->getCapability(Capability::HasModelLabels)) {
|
||||
|
||||
} else {
|
||||
ui->modelsList->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); // minimise Index
|
||||
ui->modelsList->header()->resizeSection(1, ui->modelsList->header()->sectionSize(1) * 1.5); // pad out model names
|
||||
}
|
||||
@@ -602,7 +575,10 @@ void MdiChild::onCurrentItemChanged(const QModelIndex &, const QModelIndex &)
|
||||
|
||||
void MdiChild::onDataChanged(const QModelIndex & index)
|
||||
{
|
||||
if (!modelsListModel->isCategoryType(index))
|
||||
return;
|
||||
|
||||
// TODO ***
|
||||
/*if (!modelsListModel->isCategoryType(index))
|
||||
return;
|
||||
|
||||
int categoryIndex = modelsListModel->getCategoryIndex(index);
|
||||
@@ -612,7 +588,7 @@ void MdiChild::onDataChanged(const QModelIndex & index)
|
||||
strcpy(radioData.categories[categoryIndex].name, modelsListModel->data(index, 0).toString().left(sizeof(CategoryData::name)-1).toStdString().c_str());
|
||||
|
||||
setWindowModified(true);
|
||||
emit modified();
|
||||
emit modified();*/
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -624,7 +600,7 @@ QModelIndex MdiChild::getCurrentIndex() const
|
||||
return ui->modelsList->selectionModel()->currentIndex();
|
||||
}
|
||||
|
||||
int MdiChild::getCurrentCategory() const
|
||||
/*int MdiChild::getCurrentCategory() const
|
||||
{
|
||||
return modelsListModel->getCategoryIndex(getCurrentIndex());
|
||||
}
|
||||
@@ -653,7 +629,7 @@ QVector<int> MdiChild::getSelectedCategories() const
|
||||
cats.append(modelsListModel->getCategoryIndex(index));
|
||||
}
|
||||
return cats;
|
||||
}
|
||||
}*/
|
||||
|
||||
int MdiChild::getCurrentModel() const
|
||||
{
|
||||
@@ -736,9 +712,10 @@ void MdiChild::onFirmwareChanged()
|
||||
* Categories CRUD
|
||||
*/
|
||||
|
||||
/*
|
||||
void MdiChild::categoryAdd()
|
||||
{
|
||||
/*: Translators do NOT use accent for this, this is the default category name on Horus. */
|
||||
// Translators do NOT use accent for this, this is the default category name on Horus.
|
||||
CategoryData category(qPrintable(tr("New category")));
|
||||
radioData.categories.push_back(category);
|
||||
setModified();
|
||||
@@ -799,7 +776,7 @@ void MdiChild::deleteSelectedCats()
|
||||
}
|
||||
if (modified)
|
||||
setModified();
|
||||
}
|
||||
}*/
|
||||
|
||||
/*
|
||||
* Models CRUD
|
||||
@@ -827,8 +804,8 @@ void MdiChild::findNewDefaultModel(const unsigned startAt)
|
||||
radioData.setCurrentModel(0);
|
||||
}
|
||||
|
||||
// NOTE: insertModelRows() does not update the TreeModel, only modifies radioData.models[] array by inserting row(s) of blank model(s).
|
||||
// TreeModel::refresh() needs to be called at some point afterwards to sync the data.
|
||||
// NOTE: insertModelRows() does not update the ModelsListModel, only modifies radioData.models[] array by inserting row(s) of blank model(s).
|
||||
// ModelsListModel::refresh() needs to be called at some point afterwards to sync the data.
|
||||
// This invalidates any model indices stored previously.
|
||||
bool MdiChild::insertModelRows(int atModelIdx, int count)
|
||||
{
|
||||
@@ -859,7 +836,7 @@ bool MdiChild::insertModelRows(int atModelIdx, int count)
|
||||
|
||||
// Finds the first empty slot and inserts the model into it. In case of category-style models, will append to end of list.
|
||||
// Return -1 if no slot was found, otherwise new array index.
|
||||
// TreeModel::refresh() needs to be called at some point afterwards to sync the data.
|
||||
// ModelsListModel::refresh() needs to be called at some point afterwards to sync the data.
|
||||
int MdiChild::modelAppend(const ModelData model)
|
||||
{
|
||||
int newIdx = -1;
|
||||
@@ -895,12 +872,6 @@ int MdiChild::newModel(int modelIndex, int categoryIndex)
|
||||
|
||||
bool isNewModel = radioData.models[modelIndex].isEmpty();
|
||||
checkAndInitModel(modelIndex);
|
||||
if (isNewModel && firmware->getCapability(Capability::HasModelCategories) && categoryIndex > -1) {
|
||||
radioData.models[modelIndex].category = categoryIndex;
|
||||
strcpy(radioData.models[modelIndex].filename, radioData.getNextModelFilename().toStdString().c_str());
|
||||
/*: Translators: do NOT use accents here, this is a default model name. */
|
||||
strcpy(radioData.models[modelIndex].name, qPrintable(tr("New model"))); // TODO: Why not just use existing default model name?
|
||||
}
|
||||
// Only set the default model if we just added the first one.
|
||||
if (countUsedModels() == 1) {
|
||||
radioData.setCurrentModel(modelIndex);
|
||||
@@ -926,11 +897,11 @@ int MdiChild::newModel(int modelIndex, int categoryIndex)
|
||||
}
|
||||
|
||||
|
||||
// NOTE: deleteModelss() does not update the TreeModel, only modifies radioData.models[] array by clearing the model data.
|
||||
// NOTE: deleteModelss() does not update the ModelsListModel, only modifies radioData.models[] array by clearing the model data.
|
||||
// If (removeModelSlotsWhenDeleting == true) then removes array rows entirely (and pads w/blank model at the end if needed).
|
||||
// TreeModel::refresh() needs to be called at some point afterwards to sync the data
|
||||
// ModelsListModel::refresh() needs to be called at some point afterwards to sync the data
|
||||
// We delete using stored indexes because actual indexes may change during inserts/deletes.
|
||||
// Obviously this only works before the stored indexes get updated in TreeModel::refresh().
|
||||
// Obviously this only works before the stored indexes get updated in ModelsListModel::refresh().
|
||||
unsigned MdiChild::deleteModels(const QVector<int> modelIndices)
|
||||
{
|
||||
unsigned deletes = 0;
|
||||
@@ -975,6 +946,7 @@ void MdiChild::deleteSelectedModels()
|
||||
deleteModels(getSelectedModels());
|
||||
}
|
||||
|
||||
/*
|
||||
void MdiChild::moveModelsToCategory(const QVector<int> models, const int toCategoryId)
|
||||
{
|
||||
if (toCategoryId < 0 || !models.size())
|
||||
@@ -998,14 +970,14 @@ void MdiChild::moveModelsToCategory(const QVector<int> models, const int toCateg
|
||||
void MdiChild::moveSelectedModelsToCat(const int toCategoryId)
|
||||
{
|
||||
moveModelsToCategory(getSelectedModels(), toCategoryId);
|
||||
}
|
||||
}*/
|
||||
|
||||
unsigned MdiChild::countUsedModels(const int categoryId)
|
||||
{
|
||||
unsigned count = 0;
|
||||
for (unsigned i=0; i < radioData.models.size(); ++i) {
|
||||
ModelData & model = radioData.models.at(i);
|
||||
if (!model.isEmpty() && (categoryId < 0 || model.category == categoryId))
|
||||
if (!model.isEmpty())
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
@@ -1014,12 +986,12 @@ unsigned MdiChild::countUsedModels(const int categoryId)
|
||||
void MdiChild::pasteModelData(const QMimeData * mimeData, const QModelIndex row, bool insert, bool move)
|
||||
{
|
||||
QVector<ModelData> modelsList;
|
||||
if (!TreeModel::decodeMimeData(mimeData, &modelsList))
|
||||
if (!ModelsListModel::decodeMimeData(mimeData, &modelsList))
|
||||
return;
|
||||
|
||||
bool modified = false;
|
||||
int modelIdx = modelsListModel->getModelIndex(row);
|
||||
int categoryIdx = modelsListModel->getCategoryIndex(row);
|
||||
// int categoryIdx = modelsListModel->getCategoryIndex(row);
|
||||
unsigned inserts = 0;
|
||||
QVector<int> deletesList;
|
||||
|
||||
@@ -1084,9 +1056,6 @@ void MdiChild::pasteModelData(const QMimeData * mimeData, const QModelIndex row,
|
||||
// We don't want to create an index value conflict so use an invalid one (it will get updated after we're done here)
|
||||
// this is esp. important because otherwise we may delete this model during a move operation (eg. after a cut)
|
||||
radioData.models[modelIdx].modelIndex = -modelIdx;
|
||||
// Set the destination category, so a user can copy/paste across categories.
|
||||
if (categoryIdx > -1)
|
||||
radioData.models[modelIdx].category = categoryIdx;
|
||||
strcpy(radioData.models[modelIdx].filename, radioData.getNextModelFilename().toStdString().c_str());
|
||||
lastSelectedModel = modelIdx; // after refresh the last pasted model will be selected
|
||||
modified = true;
|
||||
@@ -1117,7 +1086,7 @@ void MdiChild::pasteGeneralData(const QMimeData * mimeData)
|
||||
GeneralSettings gs;
|
||||
bool hasGenSettings = false;
|
||||
|
||||
if (!TreeModel::decodeMimeData(mimeData, NULL, &gs, &hasGenSettings))
|
||||
if (!ModelsListModel::decodeMimeData(mimeData, NULL, &gs, &hasGenSettings))
|
||||
return;
|
||||
|
||||
if (hasGenSettings && askQuestion(tr("Do you want to overwrite radio general settings?")) == QMessageBox::Yes) {
|
||||
@@ -1225,11 +1194,6 @@ void MdiChild::confirmDelete()
|
||||
deleteSelectedModels();
|
||||
}
|
||||
}
|
||||
else if (hasSelectedCat()) {
|
||||
if (askQuestion(tr("Delete %n selected category(ies)?", 0, countSelectedCats())) == QMessageBox::Yes) {
|
||||
deleteSelectedCats();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MdiChild::modelAdd()
|
||||
@@ -1261,7 +1225,7 @@ void MdiChild::modelDuplicate()
|
||||
}
|
||||
}
|
||||
|
||||
void MdiChild::onModelMoveToCategory()
|
||||
/*void MdiChild::onModelMoveToCategory()
|
||||
{
|
||||
if (!sender()) {
|
||||
return;
|
||||
@@ -1271,7 +1235,7 @@ void MdiChild::onModelMoveToCategory()
|
||||
if (ok && toCatId >= 0) {
|
||||
moveSelectedModelsToCat(toCatId);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
void MdiChild::modelEdit()
|
||||
{
|
||||
@@ -1368,10 +1332,6 @@ void MdiChild::newFile(bool createDefaults)
|
||||
isUntitled = true;
|
||||
curFile = QString("document%1.etx").arg(sequenceNumber++);
|
||||
updateTitle();
|
||||
|
||||
if (createDefaults && firmware->getCapability(Capability::HasModelCategories)) {
|
||||
categoryAdd();
|
||||
}
|
||||
}
|
||||
|
||||
bool MdiChild::loadFile(const QString & filename, bool resetCurrentFile)
|
||||
@@ -1775,11 +1735,6 @@ void MdiChild::openModelTemplate(int row)
|
||||
for (int i = 0; i < CPN_MAX_MODULES; i++) {
|
||||
radioData.models[row].moduleData[i].modelId = row + 1;
|
||||
}
|
||||
|
||||
setModified();
|
||||
setSelectedModel(row);
|
||||
|
||||
openModelEditWindow(row);
|
||||
}
|
||||
|
||||
void MdiChild::openModelPrompt(int row)
|
||||
@@ -1826,6 +1781,17 @@ void MdiChild::modelSave()
|
||||
saveSelectedModels();
|
||||
}
|
||||
|
||||
void MdiChild::labelAdd()
|
||||
{
|
||||
labelsListModel->insertRow(0);
|
||||
}
|
||||
|
||||
void MdiChild::labelDelete()
|
||||
{
|
||||
int row = ui->lstLabels->selectionModel()->currentIndex().row();
|
||||
labelsListModel->removeRow(row);
|
||||
}
|
||||
|
||||
unsigned MdiChild::saveModels(const QVector<int> modelIndices)
|
||||
{
|
||||
unsigned saves = 0;
|
||||
@@ -1854,8 +1820,7 @@ unsigned MdiChild::saveModels(const QVector<int> modelIndices)
|
||||
|
||||
++saves;
|
||||
}
|
||||
|
||||
return saves;
|
||||
return saves;
|
||||
}
|
||||
|
||||
bool MdiChild::saveModel(const int modelIndex)
|
||||
|
||||
+18
-19
@@ -23,12 +23,15 @@
|
||||
|
||||
#include "eeprominterface.h"
|
||||
#include "modelslist.h"
|
||||
#include "labels.h"
|
||||
|
||||
#include <QActionGroup>
|
||||
#include <QtGui>
|
||||
#include <QMessageBox>
|
||||
#include <QProxyStyle>
|
||||
#include <QWidget>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QListWidget>
|
||||
|
||||
class QToolBar;
|
||||
|
||||
@@ -36,6 +39,9 @@ namespace Ui {
|
||||
class MdiChild;
|
||||
}
|
||||
|
||||
class LabelsDelegate;
|
||||
class LabelsProxy;
|
||||
|
||||
class MdiChild : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -48,10 +54,8 @@ class MdiChild : public QWidget
|
||||
ACT_GEN_SIM,
|
||||
ACT_ITM_EDT, // edit model/rename category
|
||||
ACT_ITM_DEL, // delete model or cat
|
||||
ACT_CAT_ADD, // category actions...
|
||||
//ACT_CAT_EDT, // not sure these are needed...
|
||||
//ACT_CAT_DEL, // the ACT_ITM_* actions do the same thing
|
||||
ACT_CAT_SEP, // convenience separator shown/hidden with category actions
|
||||
ACT_LBL_ADD,
|
||||
ACT_LBL_DEL,
|
||||
ACT_MDL_ADD, // model actions...
|
||||
ACT_MDL_CPY,
|
||||
ACT_MDL_CUT,
|
||||
@@ -76,9 +80,9 @@ class MdiChild : public QWidget
|
||||
QVector<int> getSelectedCategories() const;
|
||||
QVector<int> getSelectedModels() const;
|
||||
QList<QAction *> getGeneralActions();
|
||||
QList<QAction *> getEditActions(bool incCatNew = true);
|
||||
QList<QAction *> getEditActions();
|
||||
QList<QAction *> getModelActions();
|
||||
//QList<QAction *> getCategoryActions();
|
||||
QList<QAction *> getLabelsActions();
|
||||
QAction * getAction(const Actions type);
|
||||
|
||||
public slots:
|
||||
@@ -109,6 +113,7 @@ class MdiChild : public QWidget
|
||||
void setModified();
|
||||
void retranslateUi();
|
||||
void showModelsListContextMenu(const QPoint & pos);
|
||||
void showLabelsContextMenu(const QPoint & pos);
|
||||
void showContextMenu(const QPoint & pos);
|
||||
void adjustToolbarLayout();
|
||||
|
||||
@@ -130,13 +135,13 @@ class MdiChild : public QWidget
|
||||
void insert();
|
||||
void edit();
|
||||
void confirmDelete();
|
||||
void categoryAdd();
|
||||
void modelAdd();
|
||||
void modelEdit();
|
||||
void modelSave();
|
||||
void labelAdd();
|
||||
void labelDelete();
|
||||
void wizardEdit();
|
||||
void modelDuplicate();
|
||||
void onModelMoveToCategory();
|
||||
|
||||
void openModelWizard(int row = -1);
|
||||
void openModelEditWindow(int row = -1);
|
||||
@@ -159,12 +164,6 @@ class MdiChild : public QWidget
|
||||
int countSelectedModels() const;
|
||||
bool hasSelectedModel();
|
||||
bool setSelectedModel(const int modelIndex);
|
||||
int getCurrentCategory() const;
|
||||
int countSelectedCats() const;
|
||||
bool hasSelectedCat();
|
||||
|
||||
bool deleteCategory(int categoryIndex = -1, QString * error = NULL);
|
||||
void deleteSelectedCats();
|
||||
|
||||
void checkAndInitModel(int row);
|
||||
void findNewDefaultModel(const unsigned startAt = 0);
|
||||
@@ -174,8 +173,6 @@ class MdiChild : public QWidget
|
||||
unsigned deleteModels(const QVector<int> modelIndices);
|
||||
bool deleteModel(const int modelIndex);
|
||||
void deleteSelectedModels();
|
||||
void moveModelsToCategory(const QVector<int> models, const int toCategoryId);
|
||||
void moveSelectedModelsToCat(const int toCategoryId);
|
||||
unsigned countUsedModels(const int categoryId = -1);
|
||||
unsigned saveModels(const QVector<int> modelIndices);
|
||||
bool saveModel(const int modelIndex);
|
||||
@@ -197,22 +194,24 @@ class MdiChild : public QWidget
|
||||
QList<QDialog *> * getModelEditDialogsList();
|
||||
|
||||
Ui::MdiChild * ui;
|
||||
TreeModel * modelsListModel;
|
||||
ModelsListModel * modelsListModel;
|
||||
LabelsModel * labelsListModel;
|
||||
QWidget * parentWindow;
|
||||
|
||||
QString curFile;
|
||||
QVector<int> cutModels;
|
||||
QVector<QAction *> action;
|
||||
QToolBar * radioToolbar;
|
||||
QToolBar * categoriesToolbar;
|
||||
QToolBar * modelsToolbar;
|
||||
QToolBar * labelsToolbar;
|
||||
QLabel *lblLabels;
|
||||
|
||||
Firmware * firmware;
|
||||
RadioData radioData;
|
||||
|
||||
int lastSelectedModel;
|
||||
bool isUntitled;
|
||||
bool showCatToolbar;
|
||||
bool showLabelToolbar;
|
||||
bool forceCloseFlag;
|
||||
const quint16 stateDataVersion;
|
||||
};
|
||||
|
||||
@@ -10,10 +10,7 @@
|
||||
<height>575</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,1">
|
||||
<property name="spacing">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="topToolbarLayout">
|
||||
<property name="spacing">
|
||||
@@ -35,6 +32,25 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="bottomLayout"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListView" name="lstLabels">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>150</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
||||
+105
-126
@@ -20,7 +20,7 @@
|
||||
|
||||
#include "modelslist.h"
|
||||
|
||||
TreeItem::TreeItem(const QVector<QVariant> & itemData):
|
||||
ModelListItem::ModelListItem(const QVector<QVariant> & itemData):
|
||||
itemData(itemData),
|
||||
parentItem(NULL),
|
||||
categoryIndex(-1),
|
||||
@@ -30,60 +30,60 @@ TreeItem::TreeItem(const QVector<QVariant> & itemData):
|
||||
{
|
||||
}
|
||||
|
||||
TreeItem::TreeItem(TreeItem * parent, int categoryIndex, int modelIndex):
|
||||
TreeItem(QVector<QVariant>(parent->columnCount()))
|
||||
ModelListItem::ModelListItem(ModelListItem * parent, int categoryIndex, int modelIndex):
|
||||
ModelListItem(QVector<QVariant>(parent->columnCount()))
|
||||
{
|
||||
setParent(parent);
|
||||
setCategoryIndex(categoryIndex);
|
||||
setModelIndex(modelIndex);
|
||||
}
|
||||
|
||||
TreeItem::~TreeItem()
|
||||
ModelListItem::~ModelListItem()
|
||||
{
|
||||
qDeleteAll(childItems);
|
||||
}
|
||||
|
||||
TreeItem * TreeItem::child(int number)
|
||||
ModelListItem * ModelListItem::child(int number)
|
||||
{
|
||||
return childItems.value(number);
|
||||
}
|
||||
|
||||
int TreeItem::childCount() const
|
||||
int ModelListItem::childCount() const
|
||||
{
|
||||
return childItems.count();
|
||||
}
|
||||
|
||||
int TreeItem::childNumber() const
|
||||
int ModelListItem::childNumber() const
|
||||
{
|
||||
if (parentItem)
|
||||
return parentItem->childItems.indexOf(const_cast<TreeItem*>(this));
|
||||
return parentItem->childItems.indexOf(const_cast<ModelListItem*>(this));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TreeItem::columnCount() const
|
||||
int ModelListItem::columnCount() const
|
||||
{
|
||||
return itemData.count();
|
||||
}
|
||||
|
||||
QVariant TreeItem::data(int column) const
|
||||
QVariant ModelListItem::data(int column) const
|
||||
{
|
||||
return itemData.value(column);
|
||||
}
|
||||
|
||||
TreeItem *TreeItem::insertChild(const int row, int categoryIndex, int modelIndex)
|
||||
ModelListItem *ModelListItem::insertChild(const int row, int categoryIndex, int modelIndex)
|
||||
{
|
||||
TreeItem * item = new TreeItem(this, categoryIndex, modelIndex);
|
||||
ModelListItem * item = new ModelListItem(this, categoryIndex, modelIndex);
|
||||
childItems.insert(row, item);
|
||||
return item;
|
||||
}
|
||||
|
||||
TreeItem * TreeItem::appendChild(int categoryIndex, int modelIndex)
|
||||
ModelListItem * ModelListItem::appendChild(int categoryIndex, int modelIndex)
|
||||
{
|
||||
return insertChild(childItems.size(), categoryIndex, modelIndex);
|
||||
}
|
||||
|
||||
bool TreeItem::removeChildren(int position, int count)
|
||||
bool ModelListItem::removeChildren(int position, int count)
|
||||
{
|
||||
if (position < 0 || position + count > childItems.size())
|
||||
return false;
|
||||
@@ -94,7 +94,7 @@ bool TreeItem::removeChildren(int position, int count)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TreeItem::insertChildren(int row, int count)
|
||||
bool ModelListItem::insertChildren(int row, int count)
|
||||
{
|
||||
for (int i=0; i < count; ++i) {
|
||||
insertChild(row + i, -1, -1);
|
||||
@@ -102,7 +102,7 @@ bool TreeItem::insertChildren(int row, int count)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TreeItem::setData(int column, const QVariant & value)
|
||||
bool ModelListItem::setData(int column, const QVariant & value)
|
||||
{
|
||||
if (column < 0 || column >= itemData.size())
|
||||
return false;
|
||||
@@ -111,7 +111,7 @@ bool TreeItem::setData(int column, const QVariant & value)
|
||||
return true;
|
||||
}
|
||||
|
||||
void TreeItem::setFlag(const quint16 & flag, const bool on)
|
||||
void ModelListItem::setFlag(const quint16 & flag, const bool on)
|
||||
{
|
||||
if (on)
|
||||
flags |= flag;
|
||||
@@ -119,57 +119,60 @@ void TreeItem::setFlag(const quint16 & flag, const bool on)
|
||||
flags &= ~flag;
|
||||
}
|
||||
|
||||
bool TreeItem::isCategory() const
|
||||
bool ModelListItem::isCategory() const
|
||||
{
|
||||
return (modelIndex < 0 && categoryIndex > -1);
|
||||
}
|
||||
|
||||
bool TreeItem::isModel() const
|
||||
bool ModelListItem::isModel() const
|
||||
{
|
||||
return (modelIndex > -1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* TreeModel
|
||||
* ModelsListModel
|
||||
*/
|
||||
|
||||
TreeModel::TreeModel(RadioData * radioData, QObject * parent):
|
||||
ModelsListModel::ModelsListModel(RadioData * radioData, QObject * parent):
|
||||
QAbstractItemModel(parent),
|
||||
radioData(radioData)
|
||||
{
|
||||
hasCategories = getCurrentFirmware()->getCapability(Capability::HasModelCategories);
|
||||
hasLabels = getCurrentFirmware()->getCapability(Capability::HasModelLabels);
|
||||
QVector<QVariant> labels;
|
||||
if (!hasCategories)
|
||||
if (!hasLabels)
|
||||
labels << tr("Index");
|
||||
labels << tr("Name");
|
||||
labels << tr("RX #");
|
||||
rootItem = new TreeItem(labels);
|
||||
if (hasLabels)
|
||||
labels << tr("Labels");
|
||||
|
||||
rootItem = new ModelListItem(labels);
|
||||
// uniqueId and version for drag/drop operations (see encodeHeaderData())
|
||||
mimeHeaderData.instanceId = QUuid::createUuid();
|
||||
mimeHeaderData.dataVersion = 1;
|
||||
|
||||
refresh();
|
||||
//connect(this, &QAbstractItemModel::rowsAboutToBeRemoved, this, &TreeModel::onRowsAboutToBeRemoved);
|
||||
connect(this, &QAbstractItemModel::rowsRemoved, this, &TreeModel::onRowsRemoved);
|
||||
//connect(this, &QAbstractItemModel::rowsAboutToBeRemoved, this, &ModelsListModel::onRowsAboutToBeRemoved);
|
||||
connect(this, &QAbstractItemModel::rowsRemoved, this, &ModelsListModel::onRowsRemoved);
|
||||
}
|
||||
|
||||
TreeModel::~TreeModel()
|
||||
ModelsListModel::~ModelsListModel()
|
||||
{
|
||||
delete rootItem;
|
||||
}
|
||||
|
||||
int TreeModel::columnCount(const QModelIndex & /* parent */) const
|
||||
int ModelsListModel::columnCount(const QModelIndex & /* parent */) const
|
||||
{
|
||||
return rootItem->columnCount();
|
||||
}
|
||||
|
||||
QVariant TreeModel::data(const QModelIndex & index, int role) const
|
||||
QVariant ModelsListModel::data(const QModelIndex & index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
|
||||
TreeItem * item = getItem(index);
|
||||
ModelListItem * item = getItem(index);
|
||||
|
||||
if (role == Qt::DisplayRole || role == Qt::EditRole) {
|
||||
return item->data(index.column());
|
||||
@@ -181,12 +184,15 @@ QVariant TreeModel::data(const QModelIndex & index, int role) const
|
||||
return font;
|
||||
}
|
||||
|
||||
if (role == Qt::ForegroundRole && (item->getFlags() & TreeItem::MarkedForCut)) {
|
||||
if (role == Qt::ForegroundRole && (item->getFlags() & ModelListItem::MarkedForCut)) {
|
||||
return QPalette().brush(QPalette::Disabled, QPalette::Text);
|
||||
}
|
||||
|
||||
if (role == Qt::ForegroundRole && item->isModel()) {
|
||||
if (index.column() == (item->columnCount() - 1) && item->isHighlightRX()) {
|
||||
int col = item->columnCount() - 1;
|
||||
if(hasLabels)
|
||||
col --;
|
||||
if (index.column() == col && item->isHighlightRX()) {
|
||||
QBrush brush;
|
||||
brush.setColor(Qt::red);
|
||||
return brush;
|
||||
@@ -196,23 +202,18 @@ QVariant TreeModel::data(const QModelIndex & index, int role) const
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
|
||||
Qt::ItemFlags ModelsListModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
Qt::ItemFlags f = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
|
||||
|
||||
if (index.isValid()) {
|
||||
if (getItem(index)->isCategory())
|
||||
f |= Qt::ItemIsEditable;
|
||||
else
|
||||
f |= Qt::ItemIsDragEnabled; // TODO drag/drop categories
|
||||
}
|
||||
f |= Qt::ItemIsDragEnabled;
|
||||
f |= Qt::ItemIsDropEnabled;
|
||||
|
||||
//qDebug() << f;
|
||||
return f;
|
||||
}
|
||||
|
||||
QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
QVariant ModelsListModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
|
||||
return rootItem->data(section);
|
||||
@@ -220,36 +221,27 @@ QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int rol
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QModelIndex TreeModel::index(int row, int column, const QModelIndex & parent) const
|
||||
QModelIndex ModelsListModel::index(int row, int column, const QModelIndex & parent) const
|
||||
{
|
||||
if (parent.isValid() && parent.column() != 0)
|
||||
return QModelIndex();
|
||||
|
||||
TreeItem * parentItem = getItem(parent);
|
||||
TreeItem * childItem = parentItem->child(row);
|
||||
ModelListItem * parentItem = getItem(parent);
|
||||
ModelListItem * childItem = parentItem->child(row);
|
||||
if (childItem)
|
||||
return createIndex(row, column, childItem);
|
||||
else
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
QModelIndex TreeModel::parent(const QModelIndex & index) const
|
||||
QModelIndex ModelsListModel::parent(const QModelIndex & index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QModelIndex();
|
||||
|
||||
TreeItem * childItem = getItem(index);
|
||||
TreeItem * parentItem = childItem->parent();
|
||||
|
||||
if (parentItem == rootItem)
|
||||
return QModelIndex();
|
||||
|
||||
return createIndex(parentItem->childNumber(), 0, parentItem);
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
bool TreeModel::removeRows(int position, int rows, const QModelIndex & parent)
|
||||
bool ModelsListModel::removeRows(int position, int rows, const QModelIndex & parent)
|
||||
{
|
||||
TreeItem * parentItem = getItem(parent);
|
||||
ModelListItem * parentItem = getItem(parent);
|
||||
if (!parentItem)
|
||||
return false;
|
||||
|
||||
@@ -264,9 +256,9 @@ bool TreeModel::removeRows(int position, int rows, const QModelIndex & parent)
|
||||
}
|
||||
|
||||
/* unused but possibly useful in future
|
||||
bool TreeModel::insertRows(int row, int count, const QModelIndex & parent)
|
||||
bool ModelsListModel::insertRows(int row, int count, const QModelIndex & parent)
|
||||
{
|
||||
TreeItem * parentItem = getItem(parent);
|
||||
ModelListItem * parentItem = getItem(parent);
|
||||
if (!parentItem)
|
||||
return false;
|
||||
|
||||
@@ -280,13 +272,13 @@ bool TreeModel::insertRows(int row, int count, const QModelIndex & parent)
|
||||
return success;
|
||||
} */
|
||||
|
||||
int TreeModel::rowCount(const QModelIndex &parent) const
|
||||
int ModelsListModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
TreeItem * parentItem = getItem(parent);
|
||||
ModelListItem * parentItem = getItem(parent);
|
||||
return parentItem->childCount();
|
||||
}
|
||||
|
||||
bool TreeModel::setData(const QModelIndex & index, const QVariant & value, int role)
|
||||
bool ModelsListModel::setData(const QModelIndex & index, const QVariant & value, int role)
|
||||
{
|
||||
if (role != Qt::EditRole)
|
||||
return false;
|
||||
@@ -294,7 +286,7 @@ bool TreeModel::setData(const QModelIndex & index, const QVariant & value, int r
|
||||
if (!index.isValid())
|
||||
return false;
|
||||
|
||||
TreeItem * item = getItem(index);
|
||||
ModelListItem * item = getItem(index);
|
||||
bool result = item->setData(index.column(), value);
|
||||
|
||||
if (result) {
|
||||
@@ -304,7 +296,7 @@ bool TreeModel::setData(const QModelIndex & index, const QVariant & value, int r
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList TreeModel::mimeTypes() const
|
||||
QStringList ModelsListModel::mimeTypes() const
|
||||
{
|
||||
QStringList types;
|
||||
types << "application/x-companion-modeldata";
|
||||
@@ -313,18 +305,18 @@ QStringList TreeModel::mimeTypes() const
|
||||
return types;
|
||||
}
|
||||
|
||||
Qt::DropActions TreeModel::supportedDropActions() const
|
||||
Qt::DropActions ModelsListModel::supportedDropActions() const
|
||||
{
|
||||
return Qt::CopyAction | Qt::MoveAction;
|
||||
}
|
||||
|
||||
Qt::DropActions TreeModel::supportedDragActions() const
|
||||
Qt::DropActions ModelsListModel::supportedDragActions() const
|
||||
{
|
||||
return Qt::CopyAction | Qt::MoveAction;
|
||||
}
|
||||
|
||||
// This method encodes all the data on default drag operation, including general radio settings. This is useful for eg. Compare dialog/model printer.
|
||||
QMimeData * TreeModel::mimeData(const QModelIndexList & indexes) const
|
||||
QMimeData * ModelsListModel::mimeData(const QModelIndexList & indexes) const
|
||||
{
|
||||
QMimeData * mimeData = new QMimeData();
|
||||
getModelsMimeData(indexes, mimeData);
|
||||
@@ -333,7 +325,7 @@ QMimeData * TreeModel::mimeData(const QModelIndexList & indexes) const
|
||||
return mimeData;
|
||||
}
|
||||
|
||||
bool TreeModel::canDropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) const
|
||||
bool ModelsListModel::canDropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) const
|
||||
{
|
||||
Q_UNUSED(action);
|
||||
//qDebug() << action << row << column << parent.row();
|
||||
@@ -345,7 +337,7 @@ bool TreeModel::canDropMimeData(const QMimeData * data, Qt::DropAction action, i
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TreeModel::dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent)
|
||||
bool ModelsListModel::dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent)
|
||||
{
|
||||
if (!canDropMimeData(data, action, row, column, parent))
|
||||
return false;
|
||||
@@ -380,7 +372,7 @@ bool TreeModel::dropMimeData(const QMimeData * data, Qt::DropAction action, int
|
||||
return true;
|
||||
}
|
||||
|
||||
QMimeData *TreeModel::getModelsMimeData(const QModelIndexList & indexes, QMimeData * mimeData) const
|
||||
QMimeData *ModelsListModel::getModelsMimeData(const QModelIndexList & indexes, QMimeData * mimeData) const
|
||||
{
|
||||
if (!mimeData)
|
||||
mimeData = new QMimeData();
|
||||
@@ -390,7 +382,7 @@ QMimeData *TreeModel::getModelsMimeData(const QModelIndexList & indexes, QMimeDa
|
||||
return mimeData;
|
||||
}
|
||||
|
||||
QMimeData *TreeModel::getGeneralMimeData(QMimeData * mimeData) const
|
||||
QMimeData *ModelsListModel::getGeneralMimeData(QMimeData * mimeData) const
|
||||
{
|
||||
if (!mimeData)
|
||||
mimeData = new QMimeData();
|
||||
@@ -400,7 +392,7 @@ QMimeData *TreeModel::getGeneralMimeData(QMimeData * mimeData) const
|
||||
return mimeData;
|
||||
}
|
||||
|
||||
QMimeData *TreeModel::getHeaderMimeData(QMimeData * mimeData) const
|
||||
QMimeData *ModelsListModel::getHeaderMimeData(QMimeData * mimeData) const
|
||||
{
|
||||
if (!mimeData)
|
||||
mimeData = new QMimeData();
|
||||
@@ -410,14 +402,14 @@ QMimeData *TreeModel::getHeaderMimeData(QMimeData * mimeData) const
|
||||
return mimeData;
|
||||
}
|
||||
|
||||
QUuid TreeModel::getMimeDataSourceId(const QMimeData * mimeData) const
|
||||
QUuid ModelsListModel::getMimeDataSourceId(const QMimeData * mimeData) const
|
||||
{
|
||||
MimeHeaderData header;
|
||||
decodeHeaderData(mimeData, &header);
|
||||
return header.instanceId;
|
||||
}
|
||||
|
||||
bool TreeModel::hasSupportedMimeData(const QMimeData * mimeData) const
|
||||
bool ModelsListModel::hasSupportedMimeData(const QMimeData * mimeData) const
|
||||
{
|
||||
foreach (const QString & mtype, mimeTypes()) {
|
||||
if (mimeData->hasFormat(mtype))
|
||||
@@ -426,28 +418,28 @@ bool TreeModel::hasSupportedMimeData(const QMimeData * mimeData) const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TreeModel::hasModelsMimeData(const QMimeData * mimeData) const
|
||||
bool ModelsListModel::hasModelsMimeData(const QMimeData * mimeData) const
|
||||
{
|
||||
return mimeData->hasFormat("application/x-companion-modeldata");
|
||||
}
|
||||
|
||||
bool TreeModel::hasGenralMimeData(const QMimeData * mimeData) const
|
||||
bool ModelsListModel::hasGenralMimeData(const QMimeData * mimeData) const
|
||||
{
|
||||
return mimeData->hasFormat("application/x-companion-generaldata");
|
||||
}
|
||||
|
||||
bool TreeModel::hasHeaderMimeData(const QMimeData * mimeData) const
|
||||
bool ModelsListModel::hasHeaderMimeData(const QMimeData * mimeData) const
|
||||
{
|
||||
return mimeData->hasFormat("application/x-companion-radiodata-header");
|
||||
}
|
||||
|
||||
// returns true if mime data origin was this data model (vs. from another file window)
|
||||
bool TreeModel::hasOwnMimeData(const QMimeData * mimeData) const
|
||||
bool ModelsListModel::hasOwnMimeData(const QMimeData * mimeData) const
|
||||
{
|
||||
return (getMimeDataSourceId(mimeData) == mimeHeaderData.instanceId);
|
||||
}
|
||||
|
||||
void TreeModel::encodeModelsData(const QModelIndexList & indexes, QByteArray * data) const
|
||||
void ModelsListModel::encodeModelsData(const QModelIndexList & indexes, QByteArray * data) const
|
||||
{
|
||||
foreach (const QModelIndex &index, indexes) {
|
||||
if (index.isValid() && index.column() == 0) {
|
||||
@@ -459,15 +451,15 @@ void TreeModel::encodeModelsData(const QModelIndexList & indexes, QByteArray * d
|
||||
}
|
||||
}
|
||||
|
||||
void TreeModel::encodeGeneralData(QByteArray * data) const
|
||||
void ModelsListModel::encodeGeneralData(QByteArray * data) const
|
||||
{
|
||||
data->append('G');
|
||||
data->append((char *)&radioData->generalSettings, sizeof(GeneralSettings));
|
||||
}
|
||||
|
||||
void TreeModel::encodeHeaderData(QByteArray * data) const
|
||||
void ModelsListModel::encodeHeaderData(QByteArray * data) const
|
||||
{
|
||||
// We use a unique ID representing this TreeModel instance (a unique file).
|
||||
// We use a unique ID representing this ModelsListModel instance (a unique file).
|
||||
// This can be used eg. to detect cross-file drop operations.
|
||||
QDataStream stream(data, QIODevice::WriteOnly);
|
||||
stream << mimeHeaderData.dataVersion;
|
||||
@@ -475,7 +467,7 @@ void TreeModel::encodeHeaderData(QByteArray * data) const
|
||||
}
|
||||
|
||||
// static
|
||||
bool TreeModel::decodeHeaderData(const QMimeData * mimeData, MimeHeaderData * header)
|
||||
bool ModelsListModel::decodeHeaderData(const QMimeData * mimeData, MimeHeaderData * header)
|
||||
{
|
||||
if (header && mimeData->hasFormat("application/x-companion-radiodata-header")) {
|
||||
QByteArray data = mimeData->data("application/x-companion-radiodata-header");
|
||||
@@ -487,7 +479,7 @@ bool TreeModel::decodeHeaderData(const QMimeData * mimeData, MimeHeaderData * he
|
||||
}
|
||||
|
||||
// static
|
||||
bool TreeModel::decodeMimeData(const QMimeData * mimeData, QVector<ModelData> * models, GeneralSettings * gs, bool * hasGenSet)
|
||||
bool ModelsListModel::decodeMimeData(const QMimeData * mimeData, QVector<ModelData> * models, GeneralSettings * gs, bool * hasGenSet)
|
||||
{
|
||||
bool ret = false;
|
||||
char * gData;
|
||||
@@ -528,7 +520,7 @@ bool TreeModel::decodeMimeData(const QMimeData * mimeData, QVector<ModelData> *
|
||||
}
|
||||
|
||||
// static
|
||||
int TreeModel::countModelsInMimeData(const QMimeData * mimeData)
|
||||
int ModelsListModel::countModelsInMimeData(const QMimeData * mimeData)
|
||||
{
|
||||
int ret = 0;
|
||||
if (mimeData->hasFormat("application/x-companion-modeldata")) {
|
||||
@@ -539,10 +531,10 @@ int TreeModel::countModelsInMimeData(const QMimeData * mimeData)
|
||||
}
|
||||
|
||||
|
||||
TreeItem * TreeModel::getItem(const QModelIndex & index) const
|
||||
ModelListItem * ModelsListModel::getItem(const QModelIndex & index) const
|
||||
{
|
||||
if (index.isValid()) {
|
||||
TreeItem * item = static_cast<TreeItem *>(index.internalPointer());
|
||||
ModelListItem * item = static_cast<ModelListItem *>(index.internalPointer());
|
||||
if (item) {
|
||||
return item;
|
||||
}
|
||||
@@ -551,7 +543,7 @@ TreeItem * TreeModel::getItem(const QModelIndex & index) const
|
||||
}
|
||||
|
||||
// recursive
|
||||
QModelIndex TreeModel::getIndexForModel(const int modelIndex, QModelIndex parent)
|
||||
QModelIndex ModelsListModel::getIndexForModel(const int modelIndex, QModelIndex parent)
|
||||
{
|
||||
for (int i = 0; i < rowCount(parent); ++i) {
|
||||
QModelIndex idx = index(i, 0, parent);
|
||||
@@ -563,7 +555,7 @@ QModelIndex TreeModel::getIndexForModel(const int modelIndex, QModelIndex parent
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
QModelIndex TreeModel::getIndexForCategory(const int categoryIndex)
|
||||
QModelIndex ModelsListModel::getIndexForCategory(const int categoryIndex)
|
||||
{
|
||||
for (int i = 0; i < rowCount(); ++i) {
|
||||
if (getItem(index(i, 0))->getCategoryIndex() == categoryIndex)
|
||||
@@ -572,51 +564,51 @@ QModelIndex TreeModel::getIndexForCategory(const int categoryIndex)
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
int TreeModel::getModelIndex(const QModelIndex & index) const
|
||||
int ModelsListModel::getModelIndex(const QModelIndex & index) const
|
||||
{
|
||||
return getItem(index)->getModelIndex();
|
||||
}
|
||||
|
||||
int TreeModel::getCategoryIndex(const QModelIndex & index) const
|
||||
int ModelsListModel::getCategoryIndex(const QModelIndex & index) const
|
||||
{
|
||||
return getItem(index)->getCategoryIndex();
|
||||
}
|
||||
|
||||
int TreeModel::rowNumber(const QModelIndex & index) const
|
||||
int ModelsListModel::rowNumber(const QModelIndex & index) const
|
||||
{
|
||||
return getItem(index)->childNumber();
|
||||
}
|
||||
|
||||
bool TreeModel::isCategoryType(const QModelIndex & index) const
|
||||
bool ModelsListModel::isCategoryType(const QModelIndex & index) const
|
||||
{
|
||||
return index.isValid() && getItem(index)->isCategory();
|
||||
}
|
||||
|
||||
bool TreeModel::isModelType(const QModelIndex & index) const
|
||||
bool ModelsListModel::isModelType(const QModelIndex & index) const
|
||||
{
|
||||
return index.isValid() && getItem(index)->isModel();
|
||||
}
|
||||
|
||||
void TreeModel::markItemForCut(const QModelIndex & index, bool on)
|
||||
void ModelsListModel::markItemForCut(const QModelIndex & index, bool on)
|
||||
{
|
||||
if (index.isValid() && index.column() == 0)
|
||||
getItem(index)->setFlag(TreeItem::MarkedForCut, on);
|
||||
getItem(index)->setFlag(ModelListItem::MarkedForCut, on);
|
||||
}
|
||||
|
||||
void TreeModel::markItemsForCut(const QModelIndexList & indexes, bool on)
|
||||
void ModelsListModel::markItemsForCut(const QModelIndexList & indexes, bool on)
|
||||
{
|
||||
foreach (const QModelIndex &index, indexes)
|
||||
markItemForCut(index, on);
|
||||
}
|
||||
|
||||
// onRowsAboutToBeRemoved could be a way to deal with models being drag-drop moved to another window/file.
|
||||
// TreeModel detects these as removals and runs removeRows(), which deletes the Model indexes
|
||||
// ModelsListModel detects these as removals and runs removeRows(), which deletes the Model indexes
|
||||
// but not the actual models from the RadioData::models array.
|
||||
// BUT this also runs when moving rows within our own tree, and if there is an error during the move,
|
||||
// or the user cancels the operation, removeRows() is still called automatically somewhere inside QAbstractItemModel().
|
||||
// If a solution could be found to this problem then we could enable DnD-moving models between file windows.
|
||||
/*
|
||||
void TreeModel::onRowsAboutToBeRemoved(const QModelIndex & parent, int first, int last)
|
||||
void ModelsListModel::onRowsAboutToBeRemoved(const QModelIndex & parent, int first, int last)
|
||||
{
|
||||
qDebug() << parent << first << last;
|
||||
QVector<int> modelIndices;
|
||||
@@ -628,55 +620,38 @@ void TreeModel::onRowsAboutToBeRemoved(const QModelIndex & parent, int first, in
|
||||
}
|
||||
*/
|
||||
|
||||
void TreeModel::onRowsRemoved(const QModelIndex & parent, int first, int last)
|
||||
void ModelsListModel::onRowsRemoved(const QModelIndex & parent, int first, int last)
|
||||
{
|
||||
// This is a workaround to deal with models being DnD moved to another window/file or if user cancels a DnD move within our own.
|
||||
// TreeModel detects these as removals and runs removeRows(), which deletes the Model indexes but not our actual models. See notes above.
|
||||
// ModelsListModel detects these as removals and runs removeRows(), which deletes the Model indexes but not our actual models. See notes above.
|
||||
//qDebug() << parent << first << last;
|
||||
emit refreshRequested(); // request refresh from View because it may have it's own ideas
|
||||
}
|
||||
|
||||
void TreeModel::refresh()
|
||||
void ModelsListModel::refresh()
|
||||
{
|
||||
TreeItem * defaultCategoryItem = NULL;
|
||||
ModelListItem * defaultCategoryItem = NULL;
|
||||
|
||||
this->blockSignals(true); // make sure onRowsRemoved is not triggered
|
||||
removeRows(0, rowCount());
|
||||
this->blockSignals(false);
|
||||
|
||||
if (hasCategories) {
|
||||
/*if (hasCategories) {
|
||||
for (unsigned i = 0; i < radioData->categories.size(); i++) {
|
||||
TreeItem * current = rootItem->appendChild(i, -1);
|
||||
ModelListItem * current = rootItem->appendChild(i, -1);
|
||||
current->setData(0, QString(radioData->categories[i].name));
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
for (unsigned i = 0; i < radioData->models.size(); i++) {
|
||||
ModelData & model = radioData->models[i];
|
||||
int currentColumn = 0;
|
||||
TreeItem * current = NULL;
|
||||
ModelListItem * current = NULL;
|
||||
|
||||
model.modelIndex = i;
|
||||
|
||||
if (hasCategories) {
|
||||
if (!model.isEmpty()) {
|
||||
TreeItem * categoryItem;
|
||||
// TODO category should be set to -1 if not Horus
|
||||
if (model.category >= 0 && model.category < rootItem->childCount()) {
|
||||
categoryItem = rootItem->child(model.category);
|
||||
}
|
||||
else {
|
||||
model.category = 0;
|
||||
if (!defaultCategoryItem) {
|
||||
defaultCategoryItem = rootItem->appendChild(0, -1);
|
||||
/*: Translators do NOT use accent for this, this is the default category name on Horus. */
|
||||
defaultCategoryItem->setData(0, tr("Models"));
|
||||
radioData->categories.push_back(qPrintable(tr("Models")));
|
||||
}
|
||||
categoryItem = defaultCategoryItem;
|
||||
}
|
||||
current = categoryItem->appendChild(model.category, i);
|
||||
}
|
||||
if (hasLabels) {
|
||||
current = rootItem->appendChild(0, i);
|
||||
}
|
||||
else {
|
||||
current = rootItem->appendChild(0, i);
|
||||
@@ -715,10 +690,13 @@ void TreeModel::refresh()
|
||||
}
|
||||
current->setData(currentColumn++, rxs);
|
||||
}
|
||||
if (hasLabels) {
|
||||
current->setData(currentColumn++, QString(model.labels));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TreeModel::isModelIdUnique(unsigned modelIdx, unsigned module, unsigned protocol)
|
||||
bool ModelsListModel::isModelIdUnique(unsigned modelIdx, unsigned module, unsigned protocol)
|
||||
{
|
||||
int cnt = 0;
|
||||
if (protocol== PULSES_PXX_XJT_D8)
|
||||
@@ -736,3 +714,4 @@ bool TreeModel::isModelIdUnique(unsigned modelIdx, unsigned module, unsigned pro
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+19
-19
@@ -25,29 +25,29 @@
|
||||
#include <QMimeData>
|
||||
#include <QUuid>
|
||||
|
||||
class TreeItem
|
||||
class ModelListItem
|
||||
{
|
||||
public:
|
||||
enum TreeItemFlags { MarkedForCut = 0x01 };
|
||||
enum ModelListItemFlags { MarkedForCut = 0x01 };
|
||||
|
||||
explicit TreeItem(const QVector<QVariant> & itemData);
|
||||
explicit TreeItem(TreeItem * parent, int categoryIndex, int modelIndex);
|
||||
~TreeItem();
|
||||
explicit ModelListItem(const QVector<QVariant> & itemData);
|
||||
explicit ModelListItem(ModelListItem * parent, int categoryIndex, int modelIndex);
|
||||
~ModelListItem();
|
||||
|
||||
TreeItem * child(int number);
|
||||
ModelListItem * child(int number);
|
||||
int childCount() const;
|
||||
int columnCount() const;
|
||||
QVariant data(int column) const;
|
||||
TreeItem * appendChild(int categoryIndex, int modelIndex);
|
||||
TreeItem * insertChild(const int row, int categoryIndex, int modelIndex);
|
||||
ModelListItem * appendChild(int categoryIndex, int modelIndex);
|
||||
ModelListItem * insertChild(const int row, int categoryIndex, int modelIndex);
|
||||
bool removeChildren(int position, int count);
|
||||
bool insertChildren(int row, int count);
|
||||
|
||||
int childNumber() const;
|
||||
bool setData(int column, const QVariant &value);
|
||||
|
||||
TreeItem * parent() { return parentItem; }
|
||||
void setParent(TreeItem * p) { parentItem = p; }
|
||||
ModelListItem * parent() { return parentItem; }
|
||||
void setParent(ModelListItem * p) { parentItem = p; }
|
||||
int getModelIndex() const { return modelIndex; }
|
||||
void setModelIndex(int value) { modelIndex = value; }
|
||||
int getCategoryIndex() const { return categoryIndex; }
|
||||
@@ -63,9 +63,9 @@ class TreeItem
|
||||
bool isModel() const;
|
||||
|
||||
private:
|
||||
QList<TreeItem*> childItems;
|
||||
QList<ModelListItem*> childItems;
|
||||
QVector<QVariant> itemData;
|
||||
TreeItem * parentItem;
|
||||
ModelListItem * parentItem;
|
||||
int categoryIndex;
|
||||
int modelIndex;
|
||||
quint16 flags;
|
||||
@@ -73,7 +73,7 @@ class TreeItem
|
||||
};
|
||||
|
||||
|
||||
class TreeModel : public QAbstractItemModel
|
||||
class ModelsListModel : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@@ -83,8 +83,8 @@ class TreeModel : public QAbstractItemModel
|
||||
quint16 dataVersion;
|
||||
};
|
||||
|
||||
TreeModel(RadioData * radioData, QObject *parent = 0);
|
||||
virtual ~TreeModel();
|
||||
ModelsListModel(RadioData * radioData, QObject *parent = 0);
|
||||
virtual ~ModelsListModel();
|
||||
|
||||
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
|
||||
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
|
||||
@@ -92,7 +92,7 @@ class TreeModel : public QAbstractItemModel
|
||||
|
||||
virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
|
||||
virtual QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE;
|
||||
virtual Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;
|
||||
virtual Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;
|
||||
|
||||
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
|
||||
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
|
||||
@@ -146,11 +146,11 @@ class TreeModel : public QAbstractItemModel
|
||||
void onRowsRemoved(const QModelIndex & parent, int first, int last);
|
||||
|
||||
private:
|
||||
TreeItem * getItem(const QModelIndex & index) const;
|
||||
ModelListItem * getItem(const QModelIndex & index) const;
|
||||
bool isModelIdUnique(unsigned modelId, unsigned module, unsigned protocol);
|
||||
|
||||
TreeItem * rootItem;
|
||||
ModelListItem * rootItem;
|
||||
RadioData * radioData;
|
||||
MimeHeaderData mimeHeaderData;
|
||||
bool hasCategories;
|
||||
bool hasLabels;
|
||||
};
|
||||
|
||||
@@ -10,7 +10,7 @@ set(storage_NAMES
|
||||
bineeprom
|
||||
eepe
|
||||
hexeeprom
|
||||
categorized
|
||||
labeled
|
||||
sdcard
|
||||
etx
|
||||
otx
|
||||
|
||||
@@ -43,7 +43,7 @@ bool EtxFormat::load(RadioData & radioData)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = CategorizedStorageFormat::load(radioData);
|
||||
bool result = LabelsStorageFormat::load(radioData);
|
||||
mz_zip_reader_end(&zip_archive);
|
||||
return result;
|
||||
}
|
||||
@@ -58,7 +58,7 @@ bool EtxFormat::write(const RadioData & radioData)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = CategorizedStorageFormat::write(radioData);
|
||||
bool result = LabelsStorageFormat::write(radioData);
|
||||
if (result) {
|
||||
// finalize archive and get contents
|
||||
char * archiveContents;
|
||||
|
||||
@@ -20,17 +20,17 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "categorized.h"
|
||||
#include "labeled.h"
|
||||
|
||||
#include <QtCore>
|
||||
|
||||
class EtxFormat : public CategorizedStorageFormat
|
||||
class EtxFormat : public LabelsStorageFormat
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(EtxFormat)
|
||||
|
||||
public:
|
||||
EtxFormat(const QString & filename):
|
||||
CategorizedStorageFormat(filename)
|
||||
LabelsStorageFormat(filename)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -18,14 +18,14 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "categorized.h"
|
||||
#include "labeled.h"
|
||||
#include "firmwares/opentx/opentxinterface.h"
|
||||
#include "firmwares/edgetx/edgetxinterface.h"
|
||||
#include "miniz.c" // Can only be included once!
|
||||
|
||||
#include <regex>
|
||||
|
||||
bool CategorizedStorageFormat::load(RadioData & radioData)
|
||||
bool LabelsStorageFormat::load(RadioData & radioData)
|
||||
{
|
||||
StorageType st = getStorageType(filename);
|
||||
if (st == STORAGE_TYPE_UNKNOWN) {
|
||||
@@ -39,7 +39,7 @@ bool CategorizedStorageFormat::load(RadioData & radioData)
|
||||
}
|
||||
}
|
||||
|
||||
bool CategorizedStorageFormat::write(const RadioData & radioData)
|
||||
bool LabelsStorageFormat::write(const RadioData & radioData)
|
||||
{
|
||||
StorageType st = getStorageType(filename);
|
||||
if (st == STORAGE_TYPE_ETX || st == STORAGE_TYPE_YML ||
|
||||
@@ -50,7 +50,7 @@ bool CategorizedStorageFormat::write(const RadioData & radioData)
|
||||
return false;
|
||||
}
|
||||
|
||||
StorageType CategorizedStorageFormat::probeFormat()
|
||||
StorageType LabelsStorageFormat::probeFormat()
|
||||
{
|
||||
if (QFile(filename + "/RADIO/radio.yml").exists()) // converted
|
||||
return getStorageType("radio.yml");
|
||||
@@ -60,7 +60,7 @@ StorageType CategorizedStorageFormat::probeFormat()
|
||||
return getStorageType(filename);
|
||||
}
|
||||
|
||||
bool CategorizedStorageFormat::loadBin(RadioData & radioData)
|
||||
bool LabelsStorageFormat::loadBin(RadioData & radioData)
|
||||
{
|
||||
QByteArray radioSettingsBuffer;
|
||||
if (!loadFile(radioSettingsBuffer, "RADIO/radio.bin")) {
|
||||
@@ -82,24 +82,15 @@ bool CategorizedStorageFormat::loadBin(RadioData & radioData)
|
||||
}
|
||||
|
||||
QList<QByteArray> lines = modelsListBuffer.split('\n');
|
||||
int modelIndex = 0;
|
||||
int categoryIndex = -1;
|
||||
foreach (const QByteArray & lineArray, lines) {
|
||||
QString line = QString(lineArray).trimmed();
|
||||
if (line.isEmpty()) continue;
|
||||
// qDebug() << "parsing line" << line;
|
||||
int modelIndex = 0;
|
||||
foreach (const QByteArray & lineArray, lines) {
|
||||
QString line = QString(lineArray).trimmed();
|
||||
if (line.isEmpty()) continue;
|
||||
// qDebug() << "parsing line" << line;
|
||||
|
||||
if (line.startsWith('[') && line.endsWith(']')) {
|
||||
// ignore categories for boards that do not support them
|
||||
if (getCurrentFirmware()->getCapability(HasModelCategories)) {
|
||||
QString name = line.mid(1, line.size() - 2);
|
||||
CategoryData category(qPrintable(name));
|
||||
radioData.categories.push_back(category);
|
||||
categoryIndex++;
|
||||
qDebug() << "added category" << name;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (line.startsWith('[') && line.endsWith(']')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// determine if we have a model number
|
||||
QStringList parts = line.split(QRegExp("\\s+"), QString::SkipEmptyParts);
|
||||
@@ -136,9 +127,6 @@ bool CategorizedStorageFormat::loadBin(RadioData & radioData)
|
||||
radioData.generalSettings.currModelIndex = modelIndex;
|
||||
qDebug() << "currModelIndex =" << modelIndex;
|
||||
}
|
||||
if (getCurrentFirmware()->getCapability(HasModelCategories)) {
|
||||
radioData.models[modelIndex].category = categoryIndex;
|
||||
}
|
||||
radioData.models[modelIndex].used = true;
|
||||
modelIndex++;
|
||||
continue;
|
||||
@@ -152,12 +140,11 @@ bool CategorizedStorageFormat::loadBin(RadioData & radioData)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CategorizedStorageFormat::writeBin(const RadioData & radioData)
|
||||
bool LabelsStorageFormat::writeBin(const RadioData & radioData)
|
||||
{
|
||||
QByteArray modelsList; // models.txt
|
||||
QByteArray radioSettingsData; // radio.bin
|
||||
size_t numModels = radioData.models.size();
|
||||
size_t numCategories = radioData.categories.size();
|
||||
size_t numCategories = radioData.labels.size();
|
||||
std::vector<std::vector<QString>> sortedModels(numCategories);
|
||||
|
||||
writeRadioSettingsToByteArray(radioData.generalSettings, radioSettingsData);
|
||||
@@ -175,41 +162,12 @@ bool CategorizedStorageFormat::writeBin(const RadioData & radioData)
|
||||
if (!writeFile(modelData, modelFilename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// For firmware that doesn't support categories, we can just construct
|
||||
// models.txt as we iterate thru the models vector. For firmware that does
|
||||
// support categories, we have a bit of work to do in order to sort the
|
||||
// models first by category index, so we use the sortedModels data
|
||||
// structure to do that.
|
||||
if (!getCurrentFirmware()->getCapability(HasModelCategories)) {
|
||||
// Use format with model number and file name. This is needed because
|
||||
// radios without category support can have unused model slots
|
||||
modelsList.append(QString("%1 %2\n").arg(m).arg(model.filename).toLocal8Bit());
|
||||
} else {
|
||||
sortedModels[model.category].push_back(QString("%1\n").arg(model.filename));
|
||||
}
|
||||
}
|
||||
|
||||
if (getCurrentFirmware()->getCapability(HasModelCategories)) {
|
||||
for (size_t c=0; c<numCategories; c++) {
|
||||
modelsList.append(QString()
|
||||
.sprintf("[%s]\n", radioData.categories[c].name)
|
||||
.toLocal8Bit());
|
||||
numModels = sortedModels[c].size();
|
||||
for (size_t m=0; m<numModels; m++) {
|
||||
modelsList.append(sortedModels[c][m].toLocal8Bit());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!writeFile(modelsList, "RADIO/models.txt")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CategorizedStorageFormat::loadYaml(RadioData & radioData)
|
||||
bool LabelsStorageFormat::loadYaml(RadioData & radioData)
|
||||
{
|
||||
if (getStorageType(filename) == STORAGE_TYPE_UNKNOWN && probeFormat() == STORAGE_TYPE_ETX) {
|
||||
if (!QFile(filename + "/" + "RADIO/radio.yml").exists())
|
||||
@@ -250,43 +208,23 @@ bool CategorizedStorageFormat::loadYaml(RadioData & radioData)
|
||||
// get models
|
||||
EtxModelfiles modelFiles;
|
||||
|
||||
QByteArray modelslistBuffer;
|
||||
if (loadFile(modelslistBuffer, "MODELS/models.yml")) {
|
||||
// Labels - Read the labels from labels.yml file
|
||||
// - Always scan for /MODELS/modelXX.yml
|
||||
// - Update possible labels with all found in models too
|
||||
|
||||
QByteArray labelslistBuffer;
|
||||
if (loadFile(labelslistBuffer, "MODELS/labels.yml")) {
|
||||
try {
|
||||
if (!loadModelsListFromYaml(radioData.categories, modelFiles, modelslistBuffer)) {
|
||||
setError(tr("Cannot load MODELS/models.yml"));
|
||||
return false;
|
||||
if (!loadLabelsListFromYaml(radioData.labels, modelFiles, labelslistBuffer)) {
|
||||
setError(tr("Can't load MODELS/labels.yml"));
|
||||
}
|
||||
} catch(const std::runtime_error& e) {
|
||||
setError(tr("Cannot load MODELS/models.yml") + ":\n" + QString(e.what()));
|
||||
//return false;
|
||||
|
||||
//TODO: fall back to directory scan
|
||||
std::list<std::string> filelist;
|
||||
if (!getFileList(filelist)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// push default category
|
||||
radioData.categories.push_back("Models");
|
||||
|
||||
const std::regex yml_regex("MODELS/(model([0-9]+)\\.yml)");
|
||||
for(const auto& f : filelist) {
|
||||
std::smatch match;
|
||||
if (std::regex_match(f, match, yml_regex)) {
|
||||
if (match.size() == 3) {
|
||||
std::ssub_match modelFile = match[1];
|
||||
std::ssub_match modelIdx = match[2];
|
||||
modelFiles.push_back({ modelFile.str(), "", 0, std::stoi(modelIdx.str()) });
|
||||
}
|
||||
}
|
||||
}
|
||||
setError(tr("Can't load MODELS/labels.yml") + ":\n" + QString(e.what()));
|
||||
}
|
||||
} else {
|
||||
// fetch "MODELS/modelXX.yml"
|
||||
|
||||
// Scan for all models
|
||||
std::list<std::string> filelist;
|
||||
if (!getFileList(filelist)) {
|
||||
setError(tr("Cannot list files"));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -297,18 +235,18 @@ bool CategorizedStorageFormat::loadYaml(RadioData & radioData)
|
||||
if (match.size() == 3) {
|
||||
std::ssub_match modelFile = match[1];
|
||||
std::ssub_match modelIdx = match[2];
|
||||
modelFiles.push_back({ modelFile.str(), "", 0, std::stoi(modelIdx.str()) });
|
||||
modelFiles.push_back({ modelFile.str(), "", std::stoi(modelIdx.str()) });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int modelIdx = 0;
|
||||
bool hasCategories = getCurrentFirmware()->getCapability(HasModelCategories);
|
||||
bool hasLabels = getCurrentFirmware()->getCapability(HasModelLabels);
|
||||
|
||||
radioData.models.resize(modelFiles.size());
|
||||
for (const auto& mc : modelFiles) {
|
||||
qDebug() << "Filename: " << mc.filename.c_str() << " / Category: " << mc.category;
|
||||
qDebug() << "Filename: " << mc.filename.c_str();
|
||||
|
||||
QByteArray modelBuffer;
|
||||
QString filename = "MODELS/" + QString::fromStdString(mc.filename);
|
||||
@@ -332,24 +270,34 @@ bool CategorizedStorageFormat::loadYaml(RadioData & radioData)
|
||||
return false;
|
||||
}
|
||||
|
||||
model.category = mc.category;
|
||||
model.modelIndex = modelIdx;
|
||||
strncpy(model.filename, mc.filename.c_str(), sizeof(model.filename)-1);
|
||||
|
||||
if (hasCategories && !strncmp(radioData.generalSettings.currModelFilename,
|
||||
if (hasLabels && !strncmp(radioData.generalSettings.currModelFilename,
|
||||
model.filename, sizeof(model.filename))) {
|
||||
radioData.generalSettings.currModelIndex = modelIdx;
|
||||
qDebug() << "currModelIndex =" << modelIdx;
|
||||
radioData.generalSettings.currModelIndex = modelIdx;
|
||||
}
|
||||
|
||||
if (hasLabels) {
|
||||
QStringList labels = QString(model.labels).split(',',QString::SkipEmptyParts);
|
||||
foreach(QString label, labels) {
|
||||
radioData.addLabel(label);
|
||||
}
|
||||
}
|
||||
|
||||
model.used = true;
|
||||
modelIdx++;
|
||||
}
|
||||
|
||||
// If no labels, add a Favorites
|
||||
if(hasLabels && !radioData.labels.size()) {
|
||||
radioData.labels.append(tr("Favorites"));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CategorizedStorageFormat::writeYaml(const RadioData & radioData)
|
||||
bool LabelsStorageFormat::writeYaml(const RadioData & radioData)
|
||||
{
|
||||
QByteArray radioSettingsBuffer;
|
||||
if (!writeRadioSettingsToYaml(radioData.generalSettings, radioSettingsBuffer)) {
|
||||
@@ -360,12 +308,12 @@ bool CategorizedStorageFormat::writeYaml(const RadioData & radioData)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hasCategories = getCurrentFirmware()->getCapability(HasModelCategories);
|
||||
bool hasLabels = getCurrentFirmware()->getCapability(HasModelLabels);
|
||||
|
||||
// B&W radios do not use the models.yml file and scan the MODELS folder for modelxx.yml files
|
||||
// models have been deleted or reordered in Companion so delete all old modelxx.yml and just in case models.yml files
|
||||
// from radio MODELS folder before writing new modelxx.yml files
|
||||
if (!hasCategories) {
|
||||
if (!hasLabels) {
|
||||
// fetch "MODELS/modelXX.yml"
|
||||
std::list<std::string> filelist;
|
||||
if (!getFileList(filelist)) {
|
||||
@@ -393,12 +341,12 @@ bool CategorizedStorageFormat::writeYaml(const RadioData & radioData)
|
||||
continue;
|
||||
|
||||
QString modelFilename;
|
||||
if (hasCategories) {
|
||||
if (hasLabels) {
|
||||
std::string ymlFilename = patchFilenameToYaml(model.filename);
|
||||
modelFilename =
|
||||
QString("MODELS/%1").arg(QString::fromStdString(ymlFilename));
|
||||
modelFiles.push_back(
|
||||
{ymlFilename, std::string(model.name), model.category});
|
||||
{ymlFilename, std::string(model.name)});
|
||||
} else {
|
||||
modelFilename = QString("MODELS/model%1.yml")
|
||||
.arg(model.modelIndex, 2, 10, QLatin1Char('0'));
|
||||
@@ -411,11 +359,10 @@ bool CategorizedStorageFormat::writeYaml(const RadioData & radioData)
|
||||
}
|
||||
}
|
||||
|
||||
if (hasCategories) {
|
||||
// TODO: sort 'modelFiles' by category index
|
||||
QByteArray modelslistBuffer;
|
||||
if (!writeModelsListToYaml(radioData.categories, modelFiles, modelslistBuffer)
|
||||
|| !writeFile(modelslistBuffer, "MODELS/models.yml")) {
|
||||
if (hasLabels) {
|
||||
QByteArray labelsListBuffer;
|
||||
if (!writeLabelsListToYaml(radioData, labelsListBuffer)
|
||||
|| !writeFile(labelsListBuffer, "MODELS/labels.yml")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -27,12 +27,12 @@
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
class CategorizedStorageFormat : public StorageFormat
|
||||
class LabelsStorageFormat : public StorageFormat
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(CategorizedStorageFormat)
|
||||
Q_DECLARE_TR_FUNCTIONS(LabelsStorageFormat)
|
||||
|
||||
public:
|
||||
CategorizedStorageFormat(const QString & filename):
|
||||
LabelsStorageFormat(const QString & filename):
|
||||
StorageFormat(filename)
|
||||
{
|
||||
}
|
||||
@@ -43,7 +43,7 @@ bool OtxFormat::load(RadioData & radioData)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = CategorizedStorageFormat::load(radioData);
|
||||
bool result = LabelsStorageFormat::load(radioData);
|
||||
mz_zip_reader_end(&zip_archive);
|
||||
return result;
|
||||
}
|
||||
@@ -58,7 +58,7 @@ bool OtxFormat::write(const RadioData & radioData)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = CategorizedStorageFormat::write(radioData);
|
||||
bool result = LabelsStorageFormat::write(radioData);
|
||||
if (result) {
|
||||
// finalize archive and get contents
|
||||
char * archiveContents;
|
||||
|
||||
@@ -20,17 +20,17 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "categorized.h"
|
||||
#include "labeled.h"
|
||||
|
||||
#include <QtCore>
|
||||
|
||||
class OtxFormat : public CategorizedStorageFormat
|
||||
class OtxFormat : public LabelsStorageFormat
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(OtxFormat)
|
||||
|
||||
public:
|
||||
OtxFormat(const QString & filename):
|
||||
CategorizedStorageFormat(filename)
|
||||
LabelsStorageFormat(filename)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ bool SdcardFormat::write(const RadioData & radioData)
|
||||
QDir dir(filename);
|
||||
dir.mkdir("RADIO");
|
||||
dir.mkdir("MODELS");
|
||||
return CategorizedStorageFormat::write(radioData);
|
||||
return LabelsStorageFormat::write(radioData);
|
||||
}
|
||||
|
||||
bool SdcardFormat::loadFile(QByteArray & filedata, const QString & filename)
|
||||
|
||||
@@ -21,17 +21,17 @@
|
||||
#ifndef _SDCARD_H2_
|
||||
#define _SDCARD_H2_
|
||||
|
||||
#include "categorized.h"
|
||||
#include "labeled.h"
|
||||
|
||||
#include <QtCore>
|
||||
|
||||
class SdcardFormat : public CategorizedStorageFormat
|
||||
class SdcardFormat : public LabelsStorageFormat
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(SdcardFormat)
|
||||
|
||||
public:
|
||||
SdcardFormat(const QString & filename):
|
||||
CategorizedStorageFormat(filename)
|
||||
LabelsStorageFormat(filename)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -55,8 +55,8 @@ bool YamlFormat::writeFile(const QByteArray & filedata)
|
||||
}
|
||||
|
||||
bool YamlFormat::load(RadioData & radioData)
|
||||
{
|
||||
bool hasCategories = getCurrentFirmware()->getCapability(HasModelCategories);
|
||||
{
|
||||
//bool hasLabels = getCurrentFirmware()->getCapability(HasModelLabels);
|
||||
int modelIdx = 0;
|
||||
|
||||
QByteArray data;
|
||||
@@ -78,10 +78,10 @@ bool YamlFormat::load(RadioData & radioData)
|
||||
if (node["header"].IsMap()) {
|
||||
qDebug() << "File" << filename << "appears to contain model data";
|
||||
|
||||
if (hasCategories) {
|
||||
/*if (hasLabels) {
|
||||
CategoryData category(qPrintable(tr("New category")));
|
||||
radioData.categories.push_back(category);
|
||||
}
|
||||
}*/
|
||||
|
||||
radioData.models.resize(1);
|
||||
|
||||
@@ -97,7 +97,7 @@ bool YamlFormat::load(RadioData & radioData)
|
||||
return false;
|
||||
}
|
||||
|
||||
model.category = 0;
|
||||
/*model.category = 0;*/
|
||||
model.modelIndex = modelIdx;
|
||||
strncpy(model.filename, qPrintable(filename), sizeof(model.filename) - 1);
|
||||
model.used = true;
|
||||
|
||||
@@ -92,7 +92,7 @@ WizMix::operator ModelData()
|
||||
int throttleChannel = -1;
|
||||
|
||||
ModelData model;
|
||||
model.category = originalModelData.category;
|
||||
model.labels[0] = '\0';
|
||||
model.used = true;
|
||||
model.moduleData[0].modelId = modelId;
|
||||
model.setDefaultInputs(settings);
|
||||
|
||||
+38
-33
@@ -46,6 +46,7 @@ option(TRACE_SIMPGMSPACE "Turn on traces in simpgmspace.cpp" ON)
|
||||
option(TRACE_LUA_INTERNALS "Turn on traces for Lua internals" OFF)
|
||||
option(DEBUG_WINDOWS "Turn on windows traces" OFF)
|
||||
option(DEBUG_YAML "Turn on YAML traces" OFF)
|
||||
option(DEBUG_LABELS "Turn on Labels traces" OFF)
|
||||
option(FRSKY_STICKS "Reverse sticks for FrSky sticks" OFF)
|
||||
option(NANO "Use nano newlib and binalloc")
|
||||
option(TEST_BUILD_WARNING "Warn this is a test build" OFF)
|
||||
@@ -210,11 +211,11 @@ if(LUA)
|
||||
if(LUA_MIXER)
|
||||
add_definitions(-DLUA_MODEL_SCRIPTS)
|
||||
endif()
|
||||
|
||||
|
||||
# TODO: LUA CMAKE
|
||||
set(SRC ${SRC}
|
||||
lua/interface.cpp
|
||||
lua/api_general.cpp
|
||||
set(SRC ${SRC}
|
||||
lua/interface.cpp
|
||||
lua/api_general.cpp
|
||||
lua/api_model.cpp
|
||||
lua/api_filesystem.cpp
|
||||
lua/lua_event.cpp
|
||||
@@ -226,37 +227,37 @@ if(LUA)
|
||||
set(SRC ${SRC} lua/api_stdlcd.cpp)
|
||||
endif()
|
||||
|
||||
set(LUA_SRC
|
||||
lapi.c
|
||||
lcode.c
|
||||
lctype.c
|
||||
ldebug.c
|
||||
ldo.c
|
||||
ldump.c
|
||||
lfunc.c
|
||||
lgc.c
|
||||
llex.c
|
||||
lmem.c
|
||||
lobject.c
|
||||
lopcodes.c
|
||||
set(LUA_SRC
|
||||
lapi.c
|
||||
lcode.c
|
||||
lctype.c
|
||||
ldebug.c
|
||||
ldo.c
|
||||
ldump.c
|
||||
lfunc.c
|
||||
lgc.c
|
||||
llex.c
|
||||
lmem.c
|
||||
lobject.c
|
||||
lopcodes.c
|
||||
lparser.c
|
||||
lstate.c
|
||||
lstring.c
|
||||
ltable.c
|
||||
lrotable.c
|
||||
ltm.c
|
||||
lundump.c
|
||||
lvm.c
|
||||
lzio.c
|
||||
lstate.c
|
||||
lstring.c
|
||||
ltable.c
|
||||
lrotable.c
|
||||
ltm.c
|
||||
lundump.c
|
||||
lvm.c
|
||||
lzio.c
|
||||
linit.c
|
||||
lbaselib.c
|
||||
lmathlib.c
|
||||
lbitlib.c
|
||||
loadlib.c
|
||||
lauxlib.c
|
||||
ltablib.c
|
||||
lcorolib.c
|
||||
liolib.c
|
||||
lbaselib.c
|
||||
lmathlib.c
|
||||
lbitlib.c
|
||||
loadlib.c
|
||||
lauxlib.c
|
||||
ltablib.c
|
||||
lcorolib.c
|
||||
liolib.c
|
||||
lstrlib.c
|
||||
)
|
||||
|
||||
@@ -393,6 +394,10 @@ if(DEBUG_YAML)
|
||||
add_definitions(-DDEBUG_YAML)
|
||||
endif()
|
||||
|
||||
if(DEBUG_LABELS)
|
||||
add_definitions(-DDEBUG_LABELS)
|
||||
endif()
|
||||
|
||||
if(FRSKY_STICKS)
|
||||
add_definitions(-DFRSKY_STICKS)
|
||||
endif()
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 271 B |
Binary file not shown.
|
After Width: | Height: | Size: 274 B |
Binary file not shown.
|
After Width: | Height: | Size: 237 B |
Binary file not shown.
|
After Width: | Height: | Size: 232 B |
@@ -37,6 +37,9 @@
|
||||
#define LUA_EXPORT_EXTRA(...)
|
||||
#endif
|
||||
|
||||
#define LABELS_LENGTH 100 // Maximum length of the label string
|
||||
#define LABEL_LENGTH 20 // Maximum length of a single label
|
||||
|
||||
#if defined(PCBHORUS) || defined(PCBNV14)
|
||||
#define MAX_MODELS 60
|
||||
#define MAX_OUTPUT_CHANNELS 32 // number of real output channels CH1-CH32
|
||||
|
||||
@@ -83,7 +83,7 @@ static inline void check_struct()
|
||||
CHKSIZE(FlightModeData, 44);
|
||||
CHKSIZE(TimerData, 17);
|
||||
CHKSIZE(SwashRingData, 8);
|
||||
CHKSIZE(ModelHeader, 31);
|
||||
CHKSIZE(ModelHeader, 131);
|
||||
CHKSIZE(CurveHeader, 4);
|
||||
CHKSIZE(CustomScreenData, 850);
|
||||
CHKTYPE(TopBarPersistentData, 300);
|
||||
@@ -137,14 +137,14 @@ static inline void check_struct()
|
||||
#elif defined(PCBHORUS)
|
||||
#if defined(PCBX10)
|
||||
CHKSIZE(RadioData, 924);
|
||||
CHKSIZE(ModelData, 11028);
|
||||
CHKSIZE(ModelData, 11128);
|
||||
#else
|
||||
CHKSIZE(RadioData, 906);
|
||||
CHKSIZE(ModelData, 11026);
|
||||
CHKSIZE(ModelData, 11126);
|
||||
#endif
|
||||
#elif defined(PCBNV14)
|
||||
CHKSIZE(RadioData, 852);
|
||||
CHKSIZE(ModelData, 10842);
|
||||
CHKSIZE(ModelData, 10942);
|
||||
#endif
|
||||
|
||||
#undef CHKSIZE
|
||||
|
||||
@@ -550,6 +550,9 @@ PACK(struct ModelHeader {
|
||||
char name[LEN_MODEL_NAME]; // must be first for eeLoadModelName
|
||||
uint8_t modelId[NUM_MODULES];
|
||||
MODEL_HEADER_BITMAP_FIELD
|
||||
#if defined(STORAGE_MODELSLIST)
|
||||
char labels[LABELS_LENGTH];
|
||||
#endif
|
||||
});
|
||||
|
||||
#if defined(COLORLCD)
|
||||
|
||||
@@ -389,6 +389,7 @@ enum DebugTimers {
|
||||
debugTimerAudioIterval,
|
||||
debugTimerAudioDuration,
|
||||
debugTimerAudioConsume,
|
||||
debugTimerYamlScan,
|
||||
|
||||
DEBUG_TIMERS_COUNT
|
||||
};
|
||||
|
||||
@@ -67,6 +67,7 @@ set(GUI_SRC
|
||||
model_outputs.cpp
|
||||
model_curves.cpp
|
||||
model_gvars.cpp
|
||||
model_templates.cpp
|
||||
model_logical_switches.cpp
|
||||
special_functions.cpp
|
||||
model_telemetry.cpp
|
||||
|
||||
@@ -595,8 +595,8 @@ void drawTextLines(BitmapBuffer * dc, coord_t left, coord_t top, coord_t width,
|
||||
case '(':
|
||||
case '{':
|
||||
case '[':
|
||||
case ' ':
|
||||
nxt++;
|
||||
case ' ':
|
||||
case '\n':
|
||||
case '\0':
|
||||
done = true;
|
||||
|
||||
@@ -107,6 +107,18 @@ void ListBase::setSelected(int selected)
|
||||
select(selected, 0);
|
||||
}
|
||||
|
||||
void ListBase::setSelected(std::set<uint32_t> selected)
|
||||
{
|
||||
if(!multiSelect) return;
|
||||
|
||||
for(int i=0; i < getRowCount(); i++) {
|
||||
if(selected.find(i) != selected.end())
|
||||
lv_table_add_cell_ctrl(lvobj, i, 0, LV_TABLE_CELL_CTRL_CUSTOM_1);
|
||||
else
|
||||
lv_table_clear_cell_ctrl(lvobj, i, 0, LV_TABLE_CELL_CTRL_CUSTOM_1);
|
||||
}
|
||||
}
|
||||
|
||||
int ListBase::getSelected() const
|
||||
{
|
||||
uint16_t row, col;
|
||||
@@ -115,6 +127,17 @@ int ListBase::getSelected() const
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::set<uint32_t> ListBase::getSelection()
|
||||
{
|
||||
if(!multiSelect) return std::set<uint32_t>();
|
||||
std::set<uint32_t> selectedIndexes;
|
||||
for(int i=0; i < getRowCount(); i++) {
|
||||
if(lv_table_has_cell_ctrl(lvobj, i, 0, LV_TABLE_CELL_CTRL_CUSTOM_1))
|
||||
selectedIndexes.insert(i);
|
||||
}
|
||||
return selectedIndexes;
|
||||
}
|
||||
|
||||
void ListBase::setActiveItem(int item)
|
||||
{
|
||||
if (item != activeItem) {
|
||||
@@ -133,7 +156,20 @@ void ListBase::onPress(uint16_t row, uint16_t col)
|
||||
if (row == LV_TABLE_CELL_NONE) return;
|
||||
|
||||
TRACE("SHORT_PRESS");
|
||||
if (pressHandler) { pressHandler(); }
|
||||
|
||||
if(multiSelect && row < getRowCount()) {
|
||||
std::set<uint32_t> lastSelection = getSelection();
|
||||
|
||||
bool chk = lv_table_has_cell_ctrl(lvobj, row, 0, LV_TABLE_CELL_CTRL_CUSTOM_1);
|
||||
if(chk) lv_table_clear_cell_ctrl(lvobj, row, 0, LV_TABLE_CELL_CTRL_CUSTOM_1);
|
||||
else lv_table_add_cell_ctrl(lvobj, row, 0, LV_TABLE_CELL_CTRL_CUSTOM_1);
|
||||
|
||||
if(_multiSelectHandler) {
|
||||
_multiSelectHandler(getSelection(),lastSelection);
|
||||
}
|
||||
} else {
|
||||
if (pressHandler) { pressHandler(); }
|
||||
}
|
||||
}
|
||||
|
||||
void ListBase::onLongPressed()
|
||||
@@ -147,6 +183,7 @@ void ListBase::onClicked()
|
||||
{
|
||||
if (!getAutoEdit()) {
|
||||
lv_group_set_editing((lv_group_t*)lv_obj_get_group(lvobj), true);
|
||||
|
||||
} else {
|
||||
TableField::onClicked();
|
||||
}
|
||||
@@ -164,7 +201,9 @@ void ListBase::onCancel()
|
||||
|
||||
void ListBase::onDrawEnd(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc)
|
||||
{
|
||||
if (row != activeItem) return;
|
||||
if ((multiSelect == false && row != activeItem) ||
|
||||
(multiSelect == true && !lv_table_has_cell_ctrl(lvobj, dsc->id, 0, LV_TABLE_CELL_CTRL_CUSTOM_1)))
|
||||
return;
|
||||
|
||||
lv_area_t coords;
|
||||
lv_coord_t area_h = lv_area_get_height(dsc->draw_area);
|
||||
|
||||
@@ -35,6 +35,7 @@ class ListBase : public TableField
|
||||
{
|
||||
std::function<void()> longPressHandler = nullptr;
|
||||
std::function<void()> pressHandler = nullptr;
|
||||
std::function<void(std::set<uint32_t>, std::set<uint32_t>)> _multiSelectHandler = nullptr;
|
||||
bool autoEdit = false;
|
||||
|
||||
public:
|
||||
@@ -49,11 +50,23 @@ class ListBase : public TableField
|
||||
void setLineHeight(uint8_t height);
|
||||
|
||||
virtual void setSelected(int selected);
|
||||
virtual void setSelected(std::set<uint32_t> selected);
|
||||
|
||||
int getSelected() const;
|
||||
std::set<uint32_t> getSelection();
|
||||
|
||||
void setMultiSelect(bool mode) {
|
||||
multiSelect = mode;
|
||||
}
|
||||
|
||||
virtual void setActiveItem(int item);
|
||||
int getActiveItem() const;
|
||||
|
||||
|
||||
void setMultiSelectHandler(std::function<void(std::set<uint32_t>, std::set<uint32_t>)> handler)
|
||||
{
|
||||
_multiSelectHandler = std::move(handler);
|
||||
}
|
||||
|
||||
void setLongPressHandler(std::function<void()> handler)
|
||||
{
|
||||
longPressHandler = std::move(handler);
|
||||
@@ -71,6 +84,7 @@ class ListBase : public TableField
|
||||
protected:
|
||||
static void event_cb(lv_event_t* e);
|
||||
int activeItem = -1;
|
||||
bool multiSelect = false;
|
||||
|
||||
void onPress(uint16_t row, uint16_t col) override;
|
||||
void onLongPressed();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,12 +22,100 @@
|
||||
#ifndef _MODEL_SELECT_H_
|
||||
#define _MODEL_SELECT_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
#include "libopenui.h"
|
||||
#include "listbox.h"
|
||||
#include "storage/modelslist.h"
|
||||
#include "tabsgroup.h"
|
||||
|
||||
class ModelSelectMenu: public TabsGroup {
|
||||
public:
|
||||
ModelSelectMenu();
|
||||
void build(int index=-1);
|
||||
constexpr int MAX_LABEL_SIZE = 20;
|
||||
|
||||
class ModelButton;
|
||||
|
||||
class ModelsPageBody : public FormWindow
|
||||
{
|
||||
public:
|
||||
ModelsPageBody(Window *parent, const rect_t &rect);
|
||||
|
||||
void setLabels(LabelsVector labels)
|
||||
{
|
||||
selectedLabels = labels;
|
||||
update();
|
||||
}
|
||||
void update(int selected = -1);
|
||||
|
||||
inline void setSortOrder(ModelsSortBy sortOrder)
|
||||
{
|
||||
modelslabels.setSortOrder(sortOrder);
|
||||
update();
|
||||
}
|
||||
|
||||
void setLblRefreshFunc(std::function<void()> fnc)
|
||||
{
|
||||
refreshLabels = std::move(fnc);
|
||||
}
|
||||
|
||||
protected:
|
||||
ModelsSortBy _sortOrder;
|
||||
bool isDirty = false;
|
||||
bool refresh = false;
|
||||
std::string selectedLabel;
|
||||
LabelsVector selectedLabels;
|
||||
std::function<void()> refreshLabels = nullptr;
|
||||
|
||||
void selectModel(ModelCell* model);
|
||||
void duplicateModel(ModelCell* model);
|
||||
void deleteModel(ModelCell* model);
|
||||
void editLabels(ModelCell* model);
|
||||
};
|
||||
|
||||
#endif // _MODEL_SELECT_H_
|
||||
class ModelLabelsWindow : public Page
|
||||
{
|
||||
public:
|
||||
ModelLabelsWindow();
|
||||
|
||||
protected:
|
||||
ModelsSortBy sort = DEFAULT_MODEL_SORT;
|
||||
char tmpLabel[MAX_LABEL_SIZE + 1] = "\0";
|
||||
ListBox *lblselector;
|
||||
ModelsPageBody *mdlselector;
|
||||
std::string currentLabel;
|
||||
|
||||
LabelsVector getLabels()
|
||||
{
|
||||
auto labels = modelslabels.getLabels();
|
||||
if (modelslabels.getUnlabeledModels().size() > 0)
|
||||
labels.emplace_back(STR_UNLABELEDMODEL);
|
||||
return labels;
|
||||
}
|
||||
|
||||
void newModel();
|
||||
void buildHead(PageHeader *window);
|
||||
void buildBody(FormWindow *window);
|
||||
void updateFilteredLabels(std::set<uint32_t> selected, bool setdirty = true);
|
||||
void labelRefreshRequest();
|
||||
};
|
||||
|
||||
class ProgressDialog : public Dialog
|
||||
{
|
||||
uint32_t lastUpdate = 0;
|
||||
Progress *progress;
|
||||
|
||||
std::function<void()> onClose;
|
||||
|
||||
public:
|
||||
ProgressDialog(Window *parent, std::string title,
|
||||
std::function<void()> onClose);
|
||||
|
||||
void updateProgress(const char *filename, int percentage);
|
||||
|
||||
// disable keys
|
||||
void onEvent(event_t) override;
|
||||
|
||||
protected:
|
||||
std::string _title;
|
||||
};
|
||||
|
||||
#endif // _MODEL_SELECT_H_
|
||||
|
||||
@@ -45,11 +45,9 @@ ModelSetupPage::ModelSetupPage() :
|
||||
|
||||
static void onModelNameChanged()
|
||||
{
|
||||
modelslist.load();
|
||||
auto model = modelslist.getCurrentModel();
|
||||
if (model) {
|
||||
model->setModelName(g_model.header.name);
|
||||
modelslist.save();
|
||||
}
|
||||
SET_DIRTY();
|
||||
}
|
||||
@@ -233,6 +231,31 @@ void ModelSetupPage::build(FormWindow * window)
|
||||
new StaticText(line, rect_t{}, STR_MODELNAME, 0, COLOR_THEME_PRIMARY1);
|
||||
new ModelNameEdit(line, rect_t{});
|
||||
|
||||
// Model labels
|
||||
line = window->newLine(&grid);
|
||||
new StaticText(line, rect_t{}, STR_LABELS, 0, COLOR_THEME_PRIMARY1);
|
||||
auto curmod = modelslist.getCurrentModel();
|
||||
labelTextButton =
|
||||
new TextButton(line, rect_t{}, modelslabels.getLabelString(curmod ,STR_UNLABELEDMODEL), [=] () {
|
||||
Menu *menu = new Menu(window, true);
|
||||
menu->setTitle(STR_LABELS);
|
||||
for (auto &label: modelslabels.getLabels()) {
|
||||
menu->addLine(label,
|
||||
[=] () {
|
||||
if (!modelslabels.isLabelSelected(label, curmod))
|
||||
modelslabels.addLabelToModel(label, curmod);
|
||||
else
|
||||
modelslabels.removeLabelFromModel(label, curmod);
|
||||
labelTextButton->setText(modelslabels.getLabelString(curmod,STR_UNLABELEDMODEL));
|
||||
strcpy(g_model.header.labels, modelslabels.getLabelString(curmod,STR_UNLABELEDMODEL).c_str());
|
||||
SET_DIRTY();
|
||||
}, [=] () {
|
||||
return modelslabels.isLabelSelected(label, curmod);
|
||||
});
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
// Bitmap
|
||||
line = window->newLine(&grid);
|
||||
new StaticText(line, rect_t{}, STR_BITMAP, 0, COLOR_THEME_PRIMARY1);
|
||||
|
||||
@@ -26,4 +26,6 @@ class ModelSetupPage: public PageTab {
|
||||
ModelSetupPage();
|
||||
|
||||
void build(FormWindow * window) override;
|
||||
private:
|
||||
TextButton *labelTextButton = nullptr;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* 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 "model_templates.h"
|
||||
#include "standalone_lua.h"
|
||||
#include "str_functions.h"
|
||||
|
||||
char TemplatePage::path[LEN_PATH + 1];
|
||||
std::function<void(void)> TemplatePage::update = nullptr;
|
||||
|
||||
|
||||
void TemplatePage::updateInfo()
|
||||
{
|
||||
FIL fp;
|
||||
FRESULT res = f_open (&fp, buffer, FA_READ);
|
||||
unsigned int bytesRead = 0;
|
||||
if (res == FR_OK) {
|
||||
f_read (&fp, infoText, LEN_INFO_TEXT, &bytesRead);
|
||||
f_close(&fp);
|
||||
}
|
||||
infoText[bytesRead] = '\0';
|
||||
invalidate();
|
||||
}
|
||||
|
||||
#if defined(HARDWARE_KEYS)
|
||||
void TemplatePage::onEvent(event_t event)
|
||||
{
|
||||
if (event == EVT_KEY_LONG(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_EXIT)) {
|
||||
killEvents(event);
|
||||
deleteLater();
|
||||
} else {
|
||||
Page::onEvent(event);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void TemplatePage::paint(BitmapBuffer *dc)
|
||||
{
|
||||
Page::paint(dc);
|
||||
rect_t rect = body.getRect();
|
||||
coord_t x = LCD_W / 2 + PAGE_PADDING;
|
||||
coord_t y = rect.y + PAGE_PADDING;
|
||||
coord_t w = LCD_W / 2 - 2 * PAGE_PADDING;
|
||||
coord_t h = rect.h - 2 * PAGE_PADDING;
|
||||
if (count > 0) {
|
||||
if (infoText[0] == '\0')
|
||||
drawTextLines(dc, x, y, w, h, STR_NO_INFORMATION, COLOR_THEME_DISABLED);
|
||||
else
|
||||
drawTextLines(dc, x, y, w, h, infoText, COLOR_THEME_PRIMARY1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SelectTemplate::SelectTemplate(TemplatePage* tp)
|
||||
: templateFolderPage(tp)
|
||||
{
|
||||
rect_t rect = {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + 10, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT};
|
||||
new StaticText(&header, rect, STR_SELECT_TEMPLATE, 0, COLOR_THEME_PRIMARY2);
|
||||
|
||||
FormGridLayout grid;
|
||||
grid.spacer(PAGE_PADDING);
|
||||
std::list<std::string> files;
|
||||
FILINFO fno;
|
||||
DIR dir;
|
||||
FRESULT res = f_opendir(&dir, path);
|
||||
|
||||
if (res == FR_OK) {
|
||||
// read all entries
|
||||
bool firstTime = true;
|
||||
for (;;) {
|
||||
res = sdReadDir(&dir, &fno, firstTime);
|
||||
firstTime = false;
|
||||
if (res != FR_OK || fno.fname[0] == 0)
|
||||
break; // Break on error or end of dir
|
||||
if (strlen((const char*)fno.fname) > SD_SCREEN_FILE_LENGTH)
|
||||
continue;
|
||||
if (fno.fname[0] == '.')
|
||||
continue;
|
||||
if (!(fno.fattrib & AM_DIR)) {
|
||||
const char *ext = getFileExtension(fno.fname);
|
||||
if(ext && !strcasecmp(ext, YAML_EXT)) {
|
||||
int len = ext - fno.fname;
|
||||
if (len < FF_MAX_LFN) {
|
||||
char name[FF_MAX_LFN] = { 0 };
|
||||
strncpy(name, fno.fname, len);
|
||||
files.push_back(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
files.sort(compare_nocase);
|
||||
|
||||
for (auto name: files) {
|
||||
auto tb = new TemplateButton(&body, grid.getLabelSlot(), name, [=]() -> uint8_t {
|
||||
// Read model template
|
||||
loadModelTemplate((name + YAML_EXT).c_str(), path);
|
||||
storageDirty(EE_MODEL);
|
||||
storageCheck(true);
|
||||
// Dismiss template pages
|
||||
deleteLater();
|
||||
templateFolderPage->deleteLater();
|
||||
#if defined(LUA)
|
||||
// If there is a wizard Lua script, fire it up
|
||||
snprintf(buffer, LEN_BUFFER, "%s%c%s%s", path, '/', name.c_str(), SCRIPT_EXT);
|
||||
if (f_stat(buffer, 0) == FR_OK) {
|
||||
luaExec(buffer);
|
||||
// Need to update() the ModelCategoryPageBody before attaching StandaloneLuaWindow to not mess up focus
|
||||
update();
|
||||
update = nullptr;
|
||||
StandaloneLuaWindow::instance()->attach();
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
});
|
||||
tb->setFocusHandler([=](bool active) {
|
||||
if (active) {
|
||||
snprintf(buffer, LEN_BUFFER, "%s%c%s%s", path, '/', name.c_str(), TEXT_EXT);
|
||||
updateInfo();
|
||||
}
|
||||
});
|
||||
tb->setHeight(PAGE_LINE_HEIGHT * 2);
|
||||
grid.spacer(tb->height() + 5);
|
||||
}
|
||||
}
|
||||
|
||||
f_closedir(&dir);
|
||||
count = files.size();
|
||||
if (count == 0) {
|
||||
rect_t rect = body.getRect();
|
||||
rect.x = PAGE_PADDING;
|
||||
rect.y = PAGE_PADDING;
|
||||
rect.w = rect.w - (2 * PAGE_PADDING);
|
||||
|
||||
#if LCD_W > LCD_H
|
||||
rect.h = PAGE_LINE_HEIGHT;
|
||||
int charBreak = 60;
|
||||
#else
|
||||
rect.h = PAGE_LINE_HEIGHT * 2;
|
||||
int charBreak = 40;
|
||||
#endif
|
||||
new StaticText(&body, rect, wrap(STR_NO_TEMPLATES, charBreak), 0, COLOR_THEME_PRIMARY1);
|
||||
|
||||
// The following button is needed because the EXIT key does not work without...
|
||||
rect = body.getRect();
|
||||
rect.x = rect.w - PAGE_PADDING - 100;
|
||||
rect.y = rect.h - PAGE_PADDING - (PAGE_LINE_HEIGHT * 2);
|
||||
rect.w = 100;
|
||||
rect.h = PAGE_LINE_HEIGHT * 2;
|
||||
new TextButton(&body, rect, STR_EXIT, [=]() -> uint8_t { deleteLater(); return 0; });
|
||||
} else {
|
||||
snprintf(buffer, LEN_BUFFER, "%s%c%s%s", path, '/', files.front().c_str(), TEXT_EXT);
|
||||
updateInfo();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SelectTemplateFolder::SelectTemplateFolder(std::function<void(void)> update)
|
||||
{
|
||||
TemplatePage::update = update;
|
||||
rect_t rect = {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + 10, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT};
|
||||
new StaticText(&header, rect, STR_SELECT_TEMPLATE_FOLDER, 0, COLOR_THEME_PRIMARY2);
|
||||
|
||||
FormGridLayout grid;
|
||||
grid.spacer(PAGE_PADDING);
|
||||
|
||||
auto tfb = new TemplateButton(&body, grid.getLabelSlot(), STR_BLANK_MODEL, [=]() -> uint8_t {
|
||||
deleteLater();
|
||||
return 0;
|
||||
});
|
||||
snprintf(infoText, LEN_INFO_TEXT, "%s", STR_BLANK_MODEL_INFO);
|
||||
tfb->setFocusHandler([=](bool active) {
|
||||
if (active) {
|
||||
snprintf(infoText, LEN_INFO_TEXT, "%s", STR_BLANK_MODEL_INFO);
|
||||
invalidate();
|
||||
}
|
||||
});
|
||||
tfb->setHeight(PAGE_LINE_HEIGHT * 2);
|
||||
grid.spacer(tfb->height() + 5);
|
||||
|
||||
std::list<std::string> directories;
|
||||
FILINFO fno;
|
||||
DIR dir;
|
||||
FRESULT res = f_opendir(&dir, TEMPLATES_PATH);
|
||||
|
||||
if (res == FR_OK) {
|
||||
// read all entries
|
||||
bool firstTime = true;
|
||||
for (;;) {
|
||||
res = sdReadDir(&dir, &fno, firstTime);
|
||||
firstTime = false;
|
||||
if (res != FR_OK || fno.fname[0] == 0)
|
||||
break; // Break on error or end of dir
|
||||
if (strlen((const char*)fno.fname) > SD_SCREEN_FILE_LENGTH)
|
||||
continue;
|
||||
if (fno.fname[0] == '.')
|
||||
continue;
|
||||
if (fno.fattrib & AM_DIR)
|
||||
directories.push_back((char*)fno.fname);
|
||||
}
|
||||
|
||||
directories.sort(compare_nocase);
|
||||
|
||||
for (auto name: directories) {
|
||||
#if not defined(LUA)
|
||||
// Don't show wizards dir if no lua
|
||||
if (!strcasecmp(name.c_str(), "WIZARD") == 0) {
|
||||
#endif
|
||||
auto tfb = new TemplateButton(&body, grid.getLabelSlot(), name,
|
||||
[=]() -> uint8_t {
|
||||
snprintf(path, LEN_PATH, "%s%c%s", TEMPLATES_PATH, '/', name.c_str());
|
||||
new SelectTemplate(this);
|
||||
return 0;
|
||||
});
|
||||
tfb->setFocusHandler([=](bool active) {
|
||||
if (active) {
|
||||
snprintf(buffer, LEN_BUFFER, "%s%c%s%c%s%s", TEMPLATES_PATH, '/', name.c_str(), '/', "about", TEXT_EXT);
|
||||
updateInfo();
|
||||
}
|
||||
});
|
||||
tfb->setHeight(PAGE_LINE_HEIGHT * 2);
|
||||
grid.spacer(tfb->height() + 5);
|
||||
}
|
||||
#if not defined(LUA)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
f_closedir(&dir);
|
||||
count = directories.size();
|
||||
if (count == 0) {
|
||||
rect_t rect = grid.getLabelSlot();
|
||||
rect.w = body.getRect().w - 2 * PAGE_PADDING;
|
||||
new StaticText(&body, rect, STR_NO_TEMPLATES, 0, COLOR_THEME_PRIMARY1);
|
||||
}
|
||||
}
|
||||
|
||||
SelectTemplateFolder::~SelectTemplateFolder()
|
||||
{
|
||||
if (update)
|
||||
update();
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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 <functional>
|
||||
#include "opentx.h"
|
||||
|
||||
constexpr size_t LEN_INFO_TEXT = 300;
|
||||
constexpr size_t LEN_PATH = sizeof(TEMPLATES_PATH) + TEXT_FILENAME_MAXLEN;
|
||||
constexpr size_t LEN_BUFFER = sizeof(TEMPLATES_PATH) + 2 * TEXT_FILENAME_MAXLEN + 1;
|
||||
|
||||
class TemplatePage : public Page
|
||||
{
|
||||
public:
|
||||
|
||||
TemplatePage() : Page(ICON_MODEL_SELECT)
|
||||
{ }
|
||||
|
||||
void updateInfo();
|
||||
|
||||
#if defined(HARDWARE_KEYS)
|
||||
void onEvent(event_t event) override;
|
||||
#endif
|
||||
|
||||
#if defined(DEBUG_WINDOWS)
|
||||
std::string getName() const { return "TemplatePage"; }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
|
||||
static char path[LEN_PATH + 1];
|
||||
char buffer[LEN_BUFFER + 1];
|
||||
char infoText[LEN_INFO_TEXT + 1] = { 0 };
|
||||
unsigned int count = 0;
|
||||
static std::function<void(void)> update;
|
||||
|
||||
void paint(BitmapBuffer *dc) override;
|
||||
};
|
||||
|
||||
class TemplateButton : public TextButton
|
||||
{
|
||||
public:
|
||||
TemplateButton(FormGroup* parent, const rect_t& rect, std::string name, std::function<uint8_t(void)> pressHandler = nullptr)
|
||||
: TextButton(parent, rect, name, pressHandler)
|
||||
{ }
|
||||
};
|
||||
|
||||
class SelectTemplate : public TemplatePage
|
||||
{
|
||||
public:
|
||||
SelectTemplate(TemplatePage* tp);
|
||||
|
||||
protected:
|
||||
TemplatePage* templateFolderPage;
|
||||
};
|
||||
|
||||
class SelectTemplateFolder : public TemplatePage
|
||||
{
|
||||
public:
|
||||
SelectTemplateFolder(std::function<void(void)> update);
|
||||
~SelectTemplateFolder();
|
||||
};
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "custom_failsafe.h"
|
||||
#include "ppm_settings.h"
|
||||
#include "channel_range.h"
|
||||
#include "storage/modelslist.h"
|
||||
|
||||
#if defined(PXX2)
|
||||
#include "access_settings.h"
|
||||
@@ -70,7 +71,7 @@ class ModuleWindow : public FormGroup
|
||||
void updateSubType();
|
||||
|
||||
uint8_t getModuleIdx() const { return moduleIdx; }
|
||||
|
||||
|
||||
protected:
|
||||
uint8_t moduleIdx;
|
||||
|
||||
@@ -82,8 +83,11 @@ class ModuleWindow : public FormGroup
|
||||
TextButton *registerButton = nullptr;
|
||||
Window *fsLine = nullptr;
|
||||
Choice *rfPower = nullptr;
|
||||
StaticText *idUnique = nullptr;
|
||||
|
||||
void startRSSIDialog(std::function<void()> closeHandler = nullptr);
|
||||
|
||||
void updateIDStaticText(int mdIdx);
|
||||
};
|
||||
|
||||
struct FailsafeChoice : public FormGroup {
|
||||
@@ -138,6 +142,22 @@ ModuleWindow::ModuleWindow(Window* parent, uint8_t moduleIdx) :
|
||||
updateModule();
|
||||
}
|
||||
|
||||
void ModuleWindow::updateIDStaticText(int mdIdx)
|
||||
{
|
||||
if(idUnique == nullptr)
|
||||
return;
|
||||
char buffer[50];
|
||||
std::string idStr = STR_MODELIDUNIQUE;
|
||||
if(!modelslist.isModelIdUnique(mdIdx, buffer, sizeof(buffer))) {
|
||||
idStr = STR_MODELIDUSED;
|
||||
idStr = idStr + buffer;
|
||||
idUnique->setTextFlags(COLOR_THEME_WARNING);
|
||||
} else {
|
||||
idUnique->setTextFlags(COLOR_THEME_PRIMARY1);
|
||||
}
|
||||
idUnique->setText(idStr);
|
||||
}
|
||||
|
||||
void ModuleWindow::updateModule()
|
||||
{
|
||||
FlexGridLayout grid(col_dsc, row_dsc, 2);
|
||||
@@ -194,6 +214,15 @@ void ModuleWindow::updateModule()
|
||||
// Bind and Range buttons
|
||||
if (!isModuleRFAccess(moduleIdx) && (isModuleModelIndexAvailable(moduleIdx) ||
|
||||
isModuleBindRangeAvailable(moduleIdx))) {
|
||||
// Is Reciever ID Unique
|
||||
if(isModuleModelIndexAvailable(moduleIdx)) {
|
||||
auto line = newLine(&grid);
|
||||
new StaticText(line, rect_t{},"");
|
||||
auto box = new FormGroup(line, rect_t{});
|
||||
box->setFlexLayout(LV_FLEX_FLOW_ROW, lv_dpx(8));
|
||||
idUnique = new StaticText(box, rect_t{}, "", 0, 0);
|
||||
updateIDStaticText(moduleIdx);
|
||||
}
|
||||
|
||||
auto line = newLine(&grid);
|
||||
new StaticText(line, rect_t{}, STR_RECEIVER, 0, COLOR_THEME_PRIMARY1);
|
||||
@@ -207,6 +236,8 @@ void ModuleWindow::updateModule()
|
||||
GET_DEFAULT(*modelId), [=](int32_t newValue) {
|
||||
if (newValue != *modelId) {
|
||||
*modelId = newValue;
|
||||
modelslist.updateCurrentModelCell();
|
||||
updateIDStaticText(moduleIdx);
|
||||
#if defined(CROSSFIRE)
|
||||
if (isModuleCrossfire(moduleIdx)) {
|
||||
moduleState[moduleIdx].counter =
|
||||
@@ -217,6 +248,7 @@ void ModuleWindow::updateModule()
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (isModuleBindRangeAvailable(moduleIdx)) {
|
||||
bindButton = new TextButton(box, rect_t{},STR_MODULE_BIND);
|
||||
bindButton->setPressHandler([=]() -> uint8_t {
|
||||
@@ -303,7 +335,7 @@ void ModuleWindow::updateModule()
|
||||
#if defined(PXX2)
|
||||
else if (isModuleRFAccess(moduleIdx)) {
|
||||
|
||||
// Register and Range buttons
|
||||
// Register and Range buttons
|
||||
auto line = newLine(&grid);
|
||||
new StaticText(line, rect_t{}, STR_MODULE, 0, COLOR_THEME_PRIMARY1);
|
||||
|
||||
@@ -517,7 +549,7 @@ void ModuleSubTypeChoice::update()
|
||||
setValues(STR_R9M_REGION);
|
||||
setGetValueHandler(GET_DEFAULT(md->subType));
|
||||
setSetValueHandler(SET_DEFAULT(md->subType));
|
||||
setAvailableHandler(nullptr);
|
||||
setAvailableHandler(nullptr);
|
||||
}
|
||||
#if defined(PXX2)
|
||||
else if (isModulePXX2(moduleIdx)) {
|
||||
@@ -526,7 +558,7 @@ void ModuleSubTypeChoice::update()
|
||||
setValues(STR_ISRM_RF_PROTOCOLS);
|
||||
setGetValueHandler(GET_DEFAULT(md->subType));
|
||||
setSetValueHandler(SET_DEFAULT(md->subType));
|
||||
setAvailableHandler(nullptr);
|
||||
setAvailableHandler(nullptr);
|
||||
}
|
||||
#endif
|
||||
#if defined(AFHDS2) || defined(AFHDS3)
|
||||
@@ -590,9 +622,9 @@ void ModuleSubTypeChoice::update()
|
||||
lv_obj_add_flag(lvobj, LV_OBJ_FLAG_HIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_HIDDEN);
|
||||
|
||||
|
||||
// update choice value
|
||||
lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr);
|
||||
}
|
||||
@@ -669,7 +701,7 @@ ModulePage::ModulePage(uint8_t moduleIdx) : Page(ICON_MODEL_SETUP)
|
||||
: isExternalModuleAvailable(moduleType);
|
||||
});
|
||||
|
||||
auto subTypeChoice = new ModuleSubTypeChoice(box, moduleIdx);
|
||||
auto subTypeChoice = new ModuleSubTypeChoice(box, moduleIdx);
|
||||
auto moduleWindow = new ModuleWindow(form, moduleIdx);
|
||||
|
||||
// This needs to be after moduleWindow has been created
|
||||
|
||||
@@ -240,7 +240,7 @@ void ViewMain::onEvent(event_t event)
|
||||
break;
|
||||
|
||||
case EVT_KEY_LONG(KEY_MODEL):
|
||||
new ModelSelectMenu();
|
||||
new ModelLabelsWindow();
|
||||
break;
|
||||
|
||||
// TODO:
|
||||
|
||||
@@ -46,7 +46,7 @@ ViewMainMenu::ViewMainMenu(Window* parent) :
|
||||
auto carousel = new SelectFabCarousel(this);
|
||||
carousel->addButton(ICON_MODEL_SELECT, STR_MAIN_MENU_SELECT_MODEL, [=]() -> uint8_t {
|
||||
deleteLater();
|
||||
new ModelSelectMenu();
|
||||
new ModelLabelsWindow();
|
||||
return 0;
|
||||
});
|
||||
|
||||
|
||||
@@ -284,5 +284,6 @@ PACK(union u_int8int16_t {
|
||||
|
||||
constexpr uint8_t EE_GENERAL = 0x01;
|
||||
constexpr uint8_t EE_MODEL = 0x02;
|
||||
constexpr uint8_t EE_LABELS = 0x04;
|
||||
|
||||
#endif // _MYEEPROM_H_
|
||||
|
||||
+6
-2
@@ -34,6 +34,7 @@ extern FIL g_oLogFile;
|
||||
#define PATH_SEPARATOR "/"
|
||||
#define ROOT_PATH PATH_SEPARATOR
|
||||
#define MODELS_PATH ROOT_PATH "MODELS" // no trailing slash = important
|
||||
#define DELETED_MODELS_PATH MODELS_PATH PATH_SEPARATOR "DELETED"
|
||||
#define RADIO_PATH ROOT_PATH "RADIO" // no trailing slash = important
|
||||
#define TEMPLATES_PATH ROOT_PATH "TEMPLATES"
|
||||
#define PERS_TEMPL_PATH TEMPLATES_PATH "/PERSONAL"
|
||||
@@ -65,8 +66,11 @@ extern FIL g_oLogFile;
|
||||
const char RADIO_MODELSLIST_PATH[] = RADIO_PATH PATH_SEPARATOR "models.txt";
|
||||
const char RADIO_SETTINGS_PATH[] = RADIO_PATH PATH_SEPARATOR RADIO_FILENAME;
|
||||
#if defined(SDCARD_YAML)
|
||||
const char MODELSLIST_YAML_PATH[] = MODELS_PATH PATH_SEPARATOR "models.yml";
|
||||
const char FALLBACK_MODELSLIST_YAML_PATH[] = RADIO_PATH PATH_SEPARATOR "models.yml";
|
||||
#define LABELS_FILENAME "labels.yml"
|
||||
#define MODELS_FILENAME "models.yml"
|
||||
const char MODELSLIST_YAML_PATH[] = MODELS_PATH PATH_SEPARATOR MODELS_FILENAME;
|
||||
const char FALLBACK_MODELSLIST_YAML_PATH[] = RADIO_PATH PATH_SEPARATOR MODELS_FILENAME;
|
||||
const char LABELSLIST_YAML_PATH[] = MODELS_PATH PATH_SEPARATOR LABELS_FILENAME;
|
||||
const char RADIO_SETTINGS_YAML_PATH[] = RADIO_PATH PATH_SEPARATOR "radio.yml";
|
||||
const char RADIO_SETTINGS_TMPFILE_YAML_PATH[] = RADIO_PATH PATH_SEPARATOR "radio_new.yml";
|
||||
const char RADIO_SETTINGS_ERRORFILE_YAML_PATH[] = RADIO_PATH PATH_SEPARATOR "radio_error.yml";
|
||||
|
||||
@@ -105,56 +105,37 @@ void convertBinRadioData(const char * path, int version)
|
||||
#endif
|
||||
|
||||
const char* error = nullptr;
|
||||
for (auto category_ptr : modelslist.getCategories()) {
|
||||
for(auto model_it = modelslist.begin(); model_it != modelslist.end(); ++model_it) {
|
||||
uint8_t model_version = 0;
|
||||
auto* model_ptr = *model_it;
|
||||
char* filename = model_ptr->modelFilename;
|
||||
|
||||
auto model_it = category_ptr->begin();
|
||||
TRACE("converting '%s' (%d/%d)", filename, converted, to_convert);
|
||||
drawProgressScreen(filename, converted, to_convert);
|
||||
|
||||
while(model_it != category_ptr->end()) {
|
||||
// read only the version number (size=0)
|
||||
error = readModelBin(filename, nullptr, 0, &model_version);
|
||||
if (!error) {
|
||||
// TODO: error handling
|
||||
error = convertBinModelData(filename, model_version);
|
||||
|
||||
uint8_t model_version = 0;
|
||||
auto* model_ptr = *model_it;
|
||||
char* filename = model_ptr->modelFilename;
|
||||
|
||||
TRACE("converting '%s' (%d/%d)", filename, converted, to_convert);
|
||||
drawProgressScreen(filename, converted, to_convert);
|
||||
|
||||
// read only the version number (size=0)
|
||||
error = readModelBin(filename, nullptr, 0, &model_version);
|
||||
if (!error) {
|
||||
// TODO: error handling
|
||||
error = convertBinModelData(filename, model_version);
|
||||
++model_it;
|
||||
|
||||
if (error) {
|
||||
TRACE("ERROR converting '%s': %s", filename, error);
|
||||
category_ptr->removeModel(model_ptr);
|
||||
} else {
|
||||
PartialModel partial;
|
||||
memclear(&partial, sizeof(PartialModel));
|
||||
|
||||
readModelYaml(filename, reinterpret_cast<uint8_t*>(&partial), sizeof(partial));
|
||||
model_ptr->setModelName(partial.header.name);
|
||||
}
|
||||
} else {
|
||||
TRACE("ERROR reading '%s': %s", filename, error);
|
||||
|
||||
// remove that file from the models list
|
||||
++model_it;
|
||||
category_ptr->removeModel(model_ptr);
|
||||
if (error) {
|
||||
TRACE("ERROR converting '%s': %s", filename, error);
|
||||
}
|
||||
} else {
|
||||
TRACE("ERROR reading '%s': %s", filename, error);
|
||||
}
|
||||
|
||||
converted++;
|
||||
converted++;
|
||||
|
||||
#if defined(SIMU)
|
||||
RTOS_WAIT_MS(200);
|
||||
RTOS_WAIT_MS(200);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(SDCARD_YAML) || defined(STORAGE_MODELSLIST)
|
||||
modelslist.save();
|
||||
// trigger models list reload
|
||||
modelslist.clear();
|
||||
modelslist.load();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -179,7 +160,7 @@ const char* convertBinModelData(char* filename, int version)
|
||||
memcpy(path, MODELS_PATH, sizeof(MODELS_PATH)-1);
|
||||
path[sizeof(MODELS_PATH)-1] = '/';
|
||||
strcpy(&path[sizeof(MODELS_PATH)], filename);
|
||||
|
||||
|
||||
#if STORAGE_CONVERSIONS < 220
|
||||
if (version == 219) {
|
||||
const char* error = convertModelData_219_to_220(path);
|
||||
@@ -206,7 +187,7 @@ const char* convertBinModelData(char* filename, int version)
|
||||
void eeConvertModel(int id, int version)
|
||||
{
|
||||
TRACE("eeConvertModel(%d,%d)", id, version);
|
||||
|
||||
|
||||
#if STORAGE_CONVERSIONS < 220
|
||||
if (version == 219) {
|
||||
convertModelData_219_to_220(id);
|
||||
@@ -269,7 +250,7 @@ bool eeConvert(uint8_t start_version)
|
||||
#if defined(STORAGE_MODELSLIST)
|
||||
modelslist.clear();
|
||||
#endif
|
||||
|
||||
|
||||
#if LCD_W >= 212
|
||||
lcdDrawRect(60, 6*FH+4, 132, 3);
|
||||
#else
|
||||
|
||||
+1118
-380
File diff suppressed because it is too large
Load Diff
+172
-88
@@ -23,86 +23,189 @@
|
||||
#define _MODELSLIST_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "sdcard.h"
|
||||
|
||||
#if !defined(SDCARD_YAML)
|
||||
#include "sdcard_raw.h"
|
||||
#endif
|
||||
|
||||
#include "dataconstants.h"
|
||||
|
||||
// #define MODELCELL_WIDTH 172
|
||||
// #define MODELCELL_HEIGHT 59
|
||||
#include "rtc.h"
|
||||
|
||||
// modelXXXXXXX.bin F,FF F,3F,FF\r\n
|
||||
#define LEN_MODELS_IDX_LINE (LEN_MODEL_FILENAME + sizeof(" F,FF F,3F,FF\r\n")-1)
|
||||
#define LEN_MODELS_IDX_LINE \
|
||||
(LEN_MODEL_FILENAME + sizeof(" F,FF F,3F,FF\r\n") - 1)
|
||||
|
||||
#define DEFAULT_MODEL_SORT NAME_ASC
|
||||
|
||||
struct ModelData;
|
||||
struct ModuleData;
|
||||
|
||||
struct SimpleModuleData
|
||||
{
|
||||
uint8_t type;
|
||||
uint8_t subType;
|
||||
struct SimpleModuleData {
|
||||
uint8_t type = 0;
|
||||
uint8_t subType = 0;
|
||||
};
|
||||
|
||||
typedef struct PACKED {
|
||||
FSIZE_t fsize; /* File size */
|
||||
WORD fdate; /* Modified date */
|
||||
WORD ftime; /* Modified time */
|
||||
} FInfoH;
|
||||
|
||||
#define FILE_HASH_LENGTH (sizeof(FInfoH) * 2) // Hex string output
|
||||
|
||||
class ModelCell
|
||||
{
|
||||
public:
|
||||
char modelFilename[LEN_MODEL_FILENAME + 1];
|
||||
char modelName[LEN_MODEL_NAME + 1] = {};
|
||||
public:
|
||||
char modelFilename[LEN_MODEL_FILENAME + 1] = "";
|
||||
char modelName[LEN_MODEL_NAME + 1] = "";
|
||||
char modelFinfoHash[FILE_HASH_LENGTH + 1] = "";
|
||||
#if LEN_BITMAP_NAME > 0
|
||||
char modelBitmap[LEN_BITMAP_NAME] = "";
|
||||
#endif
|
||||
gtime_t lastOpened = 0;
|
||||
bool _isDirty = true;
|
||||
|
||||
bool valid_rfData;
|
||||
uint8_t modelId[NUM_MODULES];
|
||||
SimpleModuleData moduleData[NUM_MODULES];
|
||||
bool valid_rfData;
|
||||
uint8_t modelId[NUM_MODULES] = {0, 0};
|
||||
SimpleModuleData moduleData[NUM_MODULES];
|
||||
|
||||
explicit ModelCell(const char * name);
|
||||
explicit ModelCell(const char * name, uint8_t len);
|
||||
~ModelCell();
|
||||
explicit ModelCell(const char *fileName);
|
||||
explicit ModelCell(const char *fileName, uint8_t len);
|
||||
|
||||
void save(FIL * file);
|
||||
void setModelName(char *name);
|
||||
void setModelName(char *name, uint8_t len);
|
||||
void setRfData(ModelData *model);
|
||||
|
||||
void setModelName(char * name);
|
||||
void setModelName(char* name, uint8_t len);
|
||||
void setRfData(ModelData * model);
|
||||
|
||||
void setModelId(uint8_t moduleIdx, uint8_t id);
|
||||
void setRfModuleData(uint8_t moduleIdx, ModuleData* modData);
|
||||
|
||||
bool fetchRfData();
|
||||
void setModelId(uint8_t moduleIdx, uint8_t id);
|
||||
void setRfModuleData(uint8_t moduleIdx, ModuleData *modData);
|
||||
bool fetchRfData();
|
||||
};
|
||||
|
||||
class ModelsCategory: public std::list<ModelCell *>
|
||||
typedef struct {
|
||||
std::string icon;
|
||||
// Anything else?
|
||||
} SLabelDetail;
|
||||
|
||||
typedef std::vector<std::pair<uint16_t, ModelCell *>> ModelLabelsVector;
|
||||
typedef std::vector<std::string> LabelsVector;
|
||||
typedef std::vector<ModelCell *> ModelsVector;
|
||||
typedef enum {
|
||||
NO_SORT,
|
||||
NAME_ASC,
|
||||
NAME_DES,
|
||||
DATE_ASC,
|
||||
DATE_DES,
|
||||
SORT_COUNT
|
||||
} ModelsSortBy;
|
||||
|
||||
/**
|
||||
* @brief ModelMap is a multimap of all models and their cooresponding
|
||||
* labels. Lables are referenced by index, stored in var labels
|
||||
*/
|
||||
|
||||
class ModelMap : protected std::multimap<uint16_t, ModelCell *>
|
||||
{
|
||||
public:
|
||||
char name[LEN_MODEL_FILENAME + 1];
|
||||
public:
|
||||
ModelsVector getUnlabeledModels();
|
||||
ModelsVector getAllModels();
|
||||
ModelsVector getModelsByLabel(const std::string &);
|
||||
ModelsVector getModelsByLabels(const LabelsVector &);
|
||||
ModelsVector getModelsInLabels(const LabelsVector &lbls);
|
||||
LabelsVector getLabelsByModel(ModelCell *);
|
||||
std::map<std::string, bool> getSelectedLabels(ModelCell *);
|
||||
bool isLabelSelected(const std::string &, ModelCell *);
|
||||
LabelsVector getLabels();
|
||||
int addLabel(std::string lbl);
|
||||
bool addLabelToModel(const std::string &, ModelCell *, bool update = false);
|
||||
bool removeLabelFromModel(const std::string &label, ModelCell *,
|
||||
bool update = false);
|
||||
bool removeLabel(
|
||||
const std::string &,
|
||||
std::function<void(const char *file, int progress)> progress = nullptr);
|
||||
bool moveLabelTo(unsigned current, unsigned newind);
|
||||
bool renameLabel(
|
||||
const std::string &from, const std::string &to,
|
||||
std::function<void(const char *file, int progress)> progress = nullptr);
|
||||
std::string getCurrentLabel() { return currentlabel; };
|
||||
void setCurrentLabel(const std::string &lbl)
|
||||
{
|
||||
currentlabel = lbl;
|
||||
setDirty();
|
||||
}
|
||||
std::string getLabelString(ModelCell *, const char *noresults = "");
|
||||
void setDirty(bool save = false);
|
||||
bool isDirty() { return _isDirty; }
|
||||
|
||||
explicit ModelsCategory(const char * name);
|
||||
explicit ModelsCategory(const char * name, uint8_t len);
|
||||
~ModelsCategory();
|
||||
// Currently selected labels in the GUI
|
||||
void setFilteredLabels(std::set<uint32_t> filtlbls)
|
||||
{
|
||||
this->filtlbls = std::move(filtlbls);
|
||||
}
|
||||
void addFilteredLabel(const std::string &lbl);
|
||||
bool isLabelFiltered(const std::string &lbl);
|
||||
std::set<uint32_t> filteredLabels() { return filtlbls; }
|
||||
|
||||
ModelCell* addModel(const char* fileName, const char* modelName = nullptr);
|
||||
void removeModel(ModelCell * model);
|
||||
void moveModel(ModelCell * model, int8_t step);
|
||||
void setSortOrder(ModelsSortBy sortby);
|
||||
ModelsSortBy sortOrder() {return _sortOrder;}
|
||||
|
||||
int getModelIndex(const ModelCell* model);
|
||||
protected:
|
||||
ModelsSortBy _sortOrder = DEFAULT_MODEL_SORT;
|
||||
bool _isDirty = true;
|
||||
std::set<uint32_t> filtlbls;
|
||||
std::string currentlabel = "";
|
||||
|
||||
void save(FIL * file);
|
||||
void updateModelCell(ModelCell *);
|
||||
bool removeModels(
|
||||
ModelCell *); // Should only be called from ModelsList remove model
|
||||
bool updateModelFile(ModelCell *);
|
||||
void sortModelsBy(ModelsVector &mv, ModelsSortBy sortby);
|
||||
|
||||
void clear()
|
||||
{
|
||||
_isDirty = true;
|
||||
labels.clear();
|
||||
std::multimap<uint16_t, ModelCell *>::clear();
|
||||
}
|
||||
|
||||
int getIndexByLabel(const std::string &str)
|
||||
{
|
||||
auto a = std::find(labels.begin(), labels.end(), str);
|
||||
return a == labels.end() ? -1 : a - labels.begin();
|
||||
}
|
||||
|
||||
std::string getLabelByIndex(uint16_t index)
|
||||
{
|
||||
if (index < (uint16_t)labels.size())
|
||||
return labels.at(index);
|
||||
else
|
||||
return std::string();
|
||||
}
|
||||
|
||||
friend class ModelsList;
|
||||
|
||||
private:
|
||||
LabelsVector labels; // Storage space for discovered labels
|
||||
};
|
||||
|
||||
class ModelsList
|
||||
class ModelsList : public ModelsVector
|
||||
{
|
||||
bool loaded;
|
||||
std::list<ModelsCategory *> categories;
|
||||
ModelsCategory * currentCategory;
|
||||
ModelCell * currentModel;
|
||||
unsigned int modelsCount;
|
||||
|
||||
ModelCell *currentModel;
|
||||
|
||||
void init();
|
||||
|
||||
public:
|
||||
|
||||
public:
|
||||
enum class Format {
|
||||
txt,
|
||||
#if defined(SDCARD_YAML)
|
||||
@@ -113,73 +216,54 @@ public:
|
||||
load_default = txt,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
ModelsList();
|
||||
~ModelsList();
|
||||
|
||||
bool load(Format fmt = Format::load_default);
|
||||
void save();
|
||||
const char *save(LabelsVector newOrder=LabelsVector());
|
||||
void clear();
|
||||
|
||||
const std::list<ModelsCategory *> & getCategories() const
|
||||
{
|
||||
return categories;
|
||||
}
|
||||
|
||||
std::list<ModelsCategory *>& getCategories()
|
||||
{
|
||||
return categories;
|
||||
}
|
||||
void setCurrentModel(ModelCell *cell);
|
||||
void updateCurrentModelCell();
|
||||
|
||||
void setCurrentCategory(ModelsCategory * cat);
|
||||
|
||||
ModelsCategory * getCurrentCategory() const
|
||||
{
|
||||
return currentCategory;
|
||||
}
|
||||
int getCurrentCategoryIdx() const;
|
||||
|
||||
void setCurrentModel(ModelCell * cell);
|
||||
|
||||
ModelCell * getCurrentModel() const
|
||||
{
|
||||
return currentModel;
|
||||
}
|
||||
|
||||
void incModelsCount() {
|
||||
modelsCount++;
|
||||
}
|
||||
ModelCell *getCurrentModel() const { return currentModel; }
|
||||
|
||||
unsigned int getModelsCount() const
|
||||
{
|
||||
return modelsCount;
|
||||
return std::vector<ModelCell *>::size();
|
||||
}
|
||||
|
||||
bool readNextLine(char * line, int maxlen);
|
||||
bool readNextLine(char *line, int maxlen);
|
||||
|
||||
ModelsCategory * createCategory(bool save=true);
|
||||
ModelsCategory * createCategory(const char * name, bool save=true);
|
||||
void removeCategory(ModelsCategory * category);
|
||||
ModelCell *addModel(const char *name, bool save = true, ModelCell *copyCell = nullptr);
|
||||
bool removeModel(ModelCell *model);
|
||||
bool moveModelTo(unsigned curindex, unsigned toindex);
|
||||
|
||||
ModelCell * addModel(ModelsCategory * category, const char * name, const char* modelName = nullptr, bool save=true);
|
||||
void removeModel(ModelsCategory * category, ModelCell * model);
|
||||
void moveModel(ModelsCategory * category, ModelCell * model, int8_t step);
|
||||
void moveModel(ModelCell * model, ModelsCategory * previous_category, ModelsCategory * new_category);
|
||||
|
||||
bool isModelIdUnique(uint8_t moduleIdx, char* warn_buf, size_t warn_buf_len);
|
||||
bool isModelIdUnique(uint8_t moduleIdx, char *warn_buf, size_t warn_buf_len);
|
||||
uint8_t findNextUnusedModelId(uint8_t moduleIdx);
|
||||
|
||||
void onNewModelCreated(ModelCell* cell, ModelData* model);
|
||||
typedef struct {
|
||||
std::string name;
|
||||
char hash[FILE_HASH_LENGTH + 1];
|
||||
bool curmodel = false;
|
||||
bool celladded = false;
|
||||
} filedat;
|
||||
std::vector<filedat> fileHashInfo;
|
||||
|
||||
protected:
|
||||
protected:
|
||||
FIL file;
|
||||
|
||||
bool loadTxt();
|
||||
#if defined(SDCARD_YAML)
|
||||
bool loadYaml();
|
||||
bool loadYamlDirScanner();
|
||||
#endif
|
||||
};
|
||||
|
||||
extern ModelsList modelslist;
|
||||
ModelLabelsVector getUniqueLabels();
|
||||
|
||||
#endif // _MODELSLIST_H_
|
||||
extern ModelsList modelslist;
|
||||
extern ModelMap modelslabels;
|
||||
|
||||
#endif // _MODELSLIST_H_
|
||||
|
||||
@@ -26,9 +26,6 @@
|
||||
#include "conversions/conversions.h"
|
||||
#include "model_init.h"
|
||||
|
||||
// defined either in sdcard_raw.cpp or sdcard_yaml.cpp
|
||||
void storageCreateModelsList();
|
||||
|
||||
void getModelPath(char * path, const char * filename, const char* pathName)
|
||||
{
|
||||
unsigned int len = strlen(pathName);
|
||||
@@ -71,9 +68,6 @@ void storageFormat()
|
||||
sdCheckAndCreateDirectory(MODELS_PATH);
|
||||
generalDefault();
|
||||
setModelDefaults();
|
||||
#if defined(STORAGE_MODELSLIST)
|
||||
storageCreateModelsList();
|
||||
#endif
|
||||
}
|
||||
|
||||
void storageCheck(bool immediately)
|
||||
@@ -87,10 +81,24 @@ void storageCheck(bool immediately)
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(STORAGE_MODELSLIST)
|
||||
if (storageDirtyMsk & EE_LABELS) {
|
||||
TRACE("SD card write labels");
|
||||
storageDirtyMsk &= ~EE_LABELS;
|
||||
const char * error = modelslist.save();
|
||||
if (error) {
|
||||
TRACE("writeLabels error=%s", error);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (storageDirtyMsk & EE_MODEL) {
|
||||
TRACE("eeprom write model");
|
||||
storageDirtyMsk &= ~EE_MODEL;
|
||||
const char * error = writeModel();
|
||||
#if defined(STORAGE_MODELSLIST)
|
||||
modelslist.updateCurrentModelCell();
|
||||
#endif
|
||||
if (error) {
|
||||
TRACE("writeModel error=%s", error);
|
||||
}
|
||||
@@ -206,14 +214,14 @@ void storageReadAll()
|
||||
storageDirty(EE_GENERAL);
|
||||
storageCheck(true);
|
||||
}
|
||||
|
||||
|
||||
if (loadModel(g_eeGeneral.currModelFilename, false) != nullptr) {
|
||||
TRACE("No current model or SD card error");
|
||||
}
|
||||
#else
|
||||
if (loadModel(g_eeGeneral.currModel, false) != nullptr) {
|
||||
TRACE("No current model or SD card error");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,6 @@
|
||||
#include "ff.h"
|
||||
#include "translations.h"
|
||||
|
||||
#define DEFAULT_CATEGORY "Models"
|
||||
|
||||
#define MODEL_FILENAME_PREFIX "model"
|
||||
#if defined(SDCARD_RAW)
|
||||
#define MODEL_FILENAME_SUFFIX ".bin"
|
||||
|
||||
@@ -191,15 +191,3 @@ const char * writeGeneralSettingsBin()
|
||||
return writeFileBin(RADIO_SETTINGS_PATH, (uint8_t *)&g_eeGeneral, sizeof(g_eeGeneral));
|
||||
}
|
||||
|
||||
#if !defined(SDCARD_YAML)
|
||||
void storageCreateModelsList()
|
||||
{
|
||||
FIL file;
|
||||
|
||||
FRESULT result = f_open(&file, RADIO_MODELSLIST_PATH, FA_CREATE_ALWAYS | FA_WRITE);
|
||||
if (result == FR_OK) {
|
||||
f_puts("[" DEFAULT_CATEGORY "]\n" DEFAULT_MODEL_FILENAME "\n", &file);
|
||||
f_close(&file);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -122,27 +122,6 @@ const char * readYamlFile(const char* fullpath, const YamlParserCalls* calls, vo
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Generic storage interface
|
||||
//
|
||||
|
||||
#if defined(STORAGE_MODELSLIST)
|
||||
void storageCreateModelsList()
|
||||
{
|
||||
modelslist.clear();
|
||||
ModelsCategory* cat = modelslist.createCategory(DEFAULT_CATEGORY, false);
|
||||
ModelCell* model = nullptr;
|
||||
if (g_eeGeneral.currModelFilename[0] != '\0') {
|
||||
model = modelslist.addModel(cat, g_eeGeneral.currModelFilename, nullptr, false);
|
||||
} else {
|
||||
model = modelslist.addModel(cat, DEFAULT_MODEL_FILENAME, nullptr, false);
|
||||
}
|
||||
model->setModelName(g_model.header.name);
|
||||
modelslist.save();
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// SDCARD storage interface
|
||||
//
|
||||
@@ -224,12 +203,16 @@ const char * loadRadioSettings()
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(DEFAULT_INTERNAL_MODULE)
|
||||
g_eeGeneral.internalModule = DEFAULT_INTERNAL_MODULE;
|
||||
#endif
|
||||
|
||||
const char* error = loadRadioSettingsYaml();
|
||||
if (!error) {
|
||||
g_eeGeneral.chkSum = evalChkSum();
|
||||
}
|
||||
postRadioSettingsLoad();
|
||||
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -345,7 +328,7 @@ const char * writeGeneralSettings()
|
||||
return p;
|
||||
}
|
||||
f_unlink(RADIO_SETTINGS_YAML_PATH);
|
||||
|
||||
|
||||
FRESULT result = f_rename(RADIO_SETTINGS_TMPFILE_YAML_PATH, RADIO_SETTINGS_YAML_PATH);
|
||||
if(result != FR_OK)
|
||||
return SDCARD_ERROR(result);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#pragma once
|
||||
|
||||
enum class ChecksumResult {Success, Failed, None};
|
||||
#include "sdcard_common.h" // TODO CHECK REQUIRED
|
||||
|
||||
constexpr uint8_t MODELIDX_STRLEN = sizeof(MODEL_FILENAME_PREFIX "00");
|
||||
|
||||
|
||||
@@ -6,9 +6,10 @@ set(SRC ${SRC} storage/yaml/yaml_tree_walker.cpp
|
||||
storage/yaml/yaml_datastructs.cpp)
|
||||
|
||||
if(STORAGE_MODELSLIST)
|
||||
set(SRC ${SRC} storage/yaml/yaml_modelslist.cpp)
|
||||
set(SRC ${SRC} storage/yaml/yaml_labelslist.cpp)
|
||||
add_definitions(-DSTORAGE_MODELSLIST)
|
||||
endif()
|
||||
|
||||
|
||||
# Warning: this file must be kept in sync with 'yaml_datastructs.cpp'
|
||||
# in the same directory.
|
||||
|
||||
|
||||
@@ -455,6 +455,7 @@ static const struct YamlNode struct_ModelHeader[] = {
|
||||
YAML_STRING("name", 15),
|
||||
YAML_ARRAY("modelId", 8, 2, struct_unsigned_8, NULL),
|
||||
YAML_STRING("bitmap", 14),
|
||||
YAML_STRING("labels", 100),
|
||||
YAML_END
|
||||
};
|
||||
static const struct YamlNode struct_TimerData[] = {
|
||||
@@ -837,7 +838,7 @@ static const struct YamlNode struct_TopBarPersistentData[] = {
|
||||
};
|
||||
static const struct YamlNode struct_ModelData[] = {
|
||||
YAML_CUSTOM("semver",nullptr,w_semver),
|
||||
YAML_STRUCT("header", 248, struct_ModelHeader, NULL),
|
||||
YAML_STRUCT("header", 1048, struct_ModelHeader, NULL),
|
||||
YAML_ARRAY("timers", 136, 3, struct_TimerData, NULL),
|
||||
YAML_UNSIGNED( "telemetryProtocol", 3 ),
|
||||
YAML_UNSIGNED( "thrTrim", 1 ),
|
||||
@@ -889,7 +890,7 @@ static const struct YamlNode struct_ModelData[] = {
|
||||
YAML_END
|
||||
};
|
||||
static const struct YamlNode struct_PartialModel[] = {
|
||||
YAML_STRUCT("header", 248, struct_ModelHeader, NULL),
|
||||
YAML_STRUCT("header", 1048, struct_ModelHeader, NULL),
|
||||
YAML_ARRAY("timers", 136, 3, struct_TimerData, NULL),
|
||||
YAML_END
|
||||
};
|
||||
|
||||
@@ -480,6 +480,7 @@ static const struct YamlNode struct_ModelHeader[] = {
|
||||
YAML_STRING("name", 15),
|
||||
YAML_ARRAY("modelId", 8, 2, struct_unsigned_8, NULL),
|
||||
YAML_STRING("bitmap", 14),
|
||||
YAML_STRING("labels", 100),
|
||||
YAML_END
|
||||
};
|
||||
static const struct YamlNode struct_TimerData[] = {
|
||||
@@ -862,7 +863,7 @@ static const struct YamlNode struct_TopBarPersistentData[] = {
|
||||
};
|
||||
static const struct YamlNode struct_ModelData[] = {
|
||||
YAML_CUSTOM("semver",nullptr,w_semver),
|
||||
YAML_STRUCT("header", 248, struct_ModelHeader, NULL),
|
||||
YAML_STRUCT("header", 1048, struct_ModelHeader, NULL),
|
||||
YAML_ARRAY("timers", 136, 3, struct_TimerData, NULL),
|
||||
YAML_UNSIGNED( "telemetryProtocol", 3 ),
|
||||
YAML_UNSIGNED( "thrTrim", 1 ),
|
||||
@@ -914,7 +915,7 @@ static const struct YamlNode struct_ModelData[] = {
|
||||
YAML_END
|
||||
};
|
||||
static const struct YamlNode struct_PartialModel[] = {
|
||||
YAML_STRUCT("header", 248, struct_ModelHeader, NULL),
|
||||
YAML_STRUCT("header", 1048, struct_ModelHeader, NULL),
|
||||
YAML_ARRAY("timers", 136, 3, struct_TimerData, NULL),
|
||||
YAML_END
|
||||
};
|
||||
|
||||
@@ -478,6 +478,7 @@ static const struct YamlNode struct_ModelHeader[] = {
|
||||
YAML_STRING("name", 15),
|
||||
YAML_ARRAY("modelId", 8, 2, struct_unsigned_8, NULL),
|
||||
YAML_STRING("bitmap", 14),
|
||||
YAML_STRING("labels", 100),
|
||||
YAML_END
|
||||
};
|
||||
static const struct YamlNode struct_TimerData[] = {
|
||||
@@ -860,7 +861,7 @@ static const struct YamlNode struct_TopBarPersistentData[] = {
|
||||
};
|
||||
static const struct YamlNode struct_ModelData[] = {
|
||||
YAML_CUSTOM("semver",nullptr,w_semver),
|
||||
YAML_STRUCT("header", 248, struct_ModelHeader, NULL),
|
||||
YAML_STRUCT("header", 1048, struct_ModelHeader, NULL),
|
||||
YAML_ARRAY("timers", 136, 3, struct_TimerData, NULL),
|
||||
YAML_UNSIGNED( "telemetryProtocol", 3 ),
|
||||
YAML_UNSIGNED( "thrTrim", 1 ),
|
||||
@@ -912,7 +913,7 @@ static const struct YamlNode struct_ModelData[] = {
|
||||
YAML_END
|
||||
};
|
||||
static const struct YamlNode struct_PartialModel[] = {
|
||||
YAML_STRUCT("header", 248, struct_ModelHeader, NULL),
|
||||
YAML_STRUCT("header", 1048, struct_ModelHeader, NULL),
|
||||
YAML_ARRAY("timers", 136, 3, struct_TimerData, NULL),
|
||||
YAML_END
|
||||
};
|
||||
|
||||
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
* 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 "yaml_labelslist.h"
|
||||
#include "yaml_parser.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "storage/modelslist.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#ifdef DEBUG_LABELS
|
||||
#define TRACE_LABELS_YAML(...) TRACE(__VA_ARGS__)
|
||||
#else
|
||||
#define TRACE_LABELS_YAML(...)
|
||||
#endif
|
||||
|
||||
using std::list;
|
||||
|
||||
struct labelslist_iter
|
||||
{
|
||||
enum ItemData {
|
||||
Root=0,
|
||||
ModelsRoot=1,
|
||||
ModelName=2,
|
||||
ModelData=3,
|
||||
LabelsRoot=4,
|
||||
LabelName=5,
|
||||
LabelData=6,
|
||||
SortRoot=7,
|
||||
};
|
||||
|
||||
ModelCell *curmodel;
|
||||
bool modeldatavalid; // Used to determine if reading yaml values is necessary
|
||||
uint8_t level;
|
||||
char current_attr[LABEL_LENGTH+1]; // set after find_node()
|
||||
char current_label[LABEL_LENGTH+1]; // set after find_node()
|
||||
};
|
||||
|
||||
static labelslist_iter __labelslist_iter_inst;
|
||||
|
||||
void* get_labelslist_iter()
|
||||
{
|
||||
// Thoughts... record the time here.
|
||||
// If time > xxx while parsing models. pop up a window showing updating?
|
||||
|
||||
__labelslist_iter_inst.modeldatavalid = false;
|
||||
__labelslist_iter_inst.curmodel = NULL;
|
||||
__labelslist_iter_inst.level = 0;
|
||||
|
||||
TRACE_LABELS_YAML("YAML Label Reader Start %u", 0);
|
||||
|
||||
return &__labelslist_iter_inst;
|
||||
}
|
||||
|
||||
static bool to_parent(void* ctx)
|
||||
{
|
||||
labelslist_iter* mi = (labelslist_iter*)ctx;
|
||||
|
||||
if (mi->level == labelslist_iter::Root)
|
||||
return false;
|
||||
|
||||
if(mi->level == labelslist_iter::ModelName ||
|
||||
mi->level == labelslist_iter::LabelName) {
|
||||
mi->level = labelslist_iter::Root;
|
||||
}
|
||||
else
|
||||
mi->level--;
|
||||
|
||||
TRACE_LABELS_YAML("YAML To Parent %u", mi->level);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool to_child(void* ctx)
|
||||
{
|
||||
labelslist_iter* mi = (labelslist_iter*)ctx;
|
||||
|
||||
mi->level++;
|
||||
|
||||
TRACE_LABELS_YAML("YAML To Child");
|
||||
TRACE_LABELS_YAML("YAML Level %u", mi->level);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool to_next_elmt(void* ctx)
|
||||
{
|
||||
labelslist_iter* mi = (labelslist_iter*)ctx;
|
||||
if (mi->level == labelslist_iter::Root) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TRACE_LABELS_YAML("YAML To Next Element");
|
||||
TRACE_LABELS_YAML("YAML Current Level %u", mi->level);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool find_node(void* ctx, char* buf, uint8_t len)
|
||||
{
|
||||
labelslist_iter* mi = (labelslist_iter*)ctx;
|
||||
|
||||
memcpy(mi->current_attr, buf, len);
|
||||
mi->current_attr[len] = '\0';
|
||||
|
||||
TRACE_LABELS_YAML("YAML On Node %s", mi->current_attr);
|
||||
TRACE_LABELS_YAML("YAML Current Level %u", mi->level);
|
||||
|
||||
// If in the labels node, force to labelsroot enum
|
||||
if(mi->level == labelslist_iter::Root && strcasecmp(mi->current_attr,"labels") == 0) {
|
||||
TRACE_LABELS_YAML("Forced root");
|
||||
mi->level = labelslist_iter::LabelsRoot;
|
||||
TRACE_LABELS_YAML("YAML New Level %u", mi->level);
|
||||
}
|
||||
|
||||
if(mi->level == labelslist_iter::Root && strcasecmp(mi->current_attr,"models") == 0) {
|
||||
TRACE_LABELS_YAML("Forced root");
|
||||
mi->level = labelslist_iter::ModelsRoot;
|
||||
TRACE_LABELS_YAML("YAML New Level %u", mi->level);
|
||||
}
|
||||
|
||||
if(mi->level == labelslist_iter::Root && strcasecmp(mi->current_attr,"sort") == 0) {
|
||||
TRACE_LABELS_YAML("Forced root");
|
||||
mi->level = labelslist_iter::SortRoot;
|
||||
TRACE_LABELS_YAML("YAML New Level %u", mi->level);
|
||||
}
|
||||
|
||||
// Model List
|
||||
if(mi->level == labelslist_iter::ModelName) {
|
||||
bool found=false;
|
||||
for(auto &filehash : modelslist.fileHashInfo) {
|
||||
if(filehash.name == mi->current_attr) {
|
||||
TRACE_LABELS_YAML(" Model %s has a real file, creating a modelcell");
|
||||
ModelCell *model = new ModelCell(mi->current_attr);
|
||||
strcpy(model->modelFinfoHash, filehash.hash);
|
||||
modelslist.push_back(model);
|
||||
filehash.celladded = true;
|
||||
if(filehash.curmodel == true)
|
||||
modelslist.setCurrentModel(model);
|
||||
mi->curmodel = model;
|
||||
mi->modeldatavalid = false;
|
||||
mi->curmodel->_isDirty = true;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found) {
|
||||
mi->curmodel = NULL;
|
||||
TRACE_LABELS_YAML("File does not exist in /MODELS");
|
||||
}
|
||||
}
|
||||
|
||||
// Labels List
|
||||
if(mi->level == labelslist_iter::LabelName) {
|
||||
TRACE_LABELS_YAML("Label Found -- %s", mi->current_attr);
|
||||
modelslabels.addLabel(mi->current_attr);
|
||||
strncpy(mi->current_label,mi->current_attr, LABEL_LENGTH);
|
||||
mi->current_label[LABEL_LENGTH] = '\0';
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void set_attr(void* ctx, char* buf, uint8_t len)
|
||||
{
|
||||
char value[40];
|
||||
memcpy(value, buf, len);
|
||||
value[len] = '\0';
|
||||
|
||||
labelslist_iter* mi = (labelslist_iter*)ctx;
|
||||
TRACE_LABELS_YAML("YAML Attr Level %u, %s = %s", mi->level, mi->current_attr, value);
|
||||
|
||||
// Model Section
|
||||
if(mi->level == labelslist_iter::ModelData) {
|
||||
|
||||
// File Hash
|
||||
if(!strcasecmp(mi->current_attr, "hash")) {
|
||||
if(mi->curmodel != NULL) {
|
||||
if(!strcmp(mi->curmodel->modelFinfoHash, value)) {
|
||||
TRACE_LABELS_YAML("FILE HASH MATCHES, No need to scan this model, just load the settings");
|
||||
mi->modeldatavalid = true;
|
||||
mi->curmodel->valid_rfData = true;
|
||||
mi->curmodel->_isDirty = false;
|
||||
} else {
|
||||
TRACE_LABELS_YAML("FILE HASH Does not Match, Open model and rebuild modelcell");
|
||||
mi->modeldatavalid = false;
|
||||
mi->curmodel->_isDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Model Name
|
||||
} else if(mi->modeldatavalid && !strcasecmp(mi->current_attr, "name")) {
|
||||
if(mi->curmodel != NULL) {
|
||||
mi->curmodel->setModelName(value);
|
||||
TRACE_LABELS_YAML("Set the models name");
|
||||
}
|
||||
|
||||
// Last Opened
|
||||
} else if(mi->modeldatavalid && !strcasecmp(mi->current_attr, "lastopen")) {
|
||||
if(mi->curmodel != NULL) {
|
||||
mi->curmodel->lastOpened = (gtime_t)strtol(value, NULL, 0);
|
||||
TRACE_LABELS_YAML("Last Opened %lu", value);
|
||||
}
|
||||
|
||||
// Model Bitmap
|
||||
#if LEN_BITMAP_NAME > 0
|
||||
} else if(mi->modeldatavalid && !strcasecmp(mi->current_attr, "bitmap")) {
|
||||
if(mi->curmodel != NULL) {
|
||||
// TODO Check if it exists ?
|
||||
strcpy(mi->curmodel->modelBitmap, value);
|
||||
TRACE_LABELS_YAML("Set the models bitmap");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Model Labels
|
||||
} else if(mi->modeldatavalid && !strcasecmp(mi->current_attr, "labels")) {
|
||||
if(mi->curmodel != NULL) {
|
||||
char *cma;
|
||||
cma = strtok(value, ",");
|
||||
int numTokens = 0;
|
||||
while(cma != NULL) {
|
||||
modelslabels.addLabelToModel(cma,mi->curmodel);
|
||||
TRACE_LABELS_YAML(" Adding the label - %s", cma);
|
||||
cma = strtok(NULL, ",");
|
||||
numTokens++;
|
||||
}
|
||||
}
|
||||
|
||||
// RF Module Data
|
||||
} else {
|
||||
char cmp[15];
|
||||
for(int i=0; i < NUM_MODULES; i++) {
|
||||
snprintf(cmp, sizeof(cmp), MODULE_ID_STR, i);
|
||||
cmp[sizeof(cmp)-1] = '\0';
|
||||
if(mi->curmodel != NULL && mi->modeldatavalid && !strcasecmp(mi->current_attr, cmp)) {
|
||||
mi->curmodel->modelId[i] = strtol(value,NULL,10);
|
||||
TRACE_LABELS_YAML("Set the module %d rfId to %s", i, value);
|
||||
}
|
||||
snprintf(cmp, sizeof(cmp), MODULE_TYPE_STR, i);
|
||||
cmp[sizeof(cmp)-1] = '\0';
|
||||
if(mi->curmodel != NULL && mi->modeldatavalid && !strcasecmp(mi->current_attr, cmp)) {
|
||||
mi->curmodel->moduleData[i].type = strtol(value,NULL,10);
|
||||
TRACE_LABELS_YAML("Set the module %d rfType to %s", i, value);
|
||||
}
|
||||
snprintf(cmp, sizeof(cmp), MODULE_RFPROTOCOL_STR, i);
|
||||
cmp[sizeof(cmp)-1] = '\0';
|
||||
if(mi->curmodel != NULL && mi->modeldatavalid && !strcasecmp(mi->current_attr, cmp)) {
|
||||
mi->curmodel->moduleData[i].subType = strtol(value,NULL,10);
|
||||
TRACE_LABELS_YAML("Set the module %d rfProtocol to %s", i, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Label Section
|
||||
} else if(mi->level == labelslist_iter::LabelData) {
|
||||
if(!strcasecmp(mi->current_attr, "icon")) {
|
||||
TRACE_LABELS_YAML("Label Icon - %s", value);
|
||||
// TODO - Check icon exists, or ignore it.
|
||||
} else if(!strcasecmp(mi->current_attr, "selected")) {
|
||||
TRACE("FOUND %s Label is selected", mi->current_label);
|
||||
modelslabels.addFilteredLabel(mi->current_label);
|
||||
}
|
||||
|
||||
// Sort Order
|
||||
} else if (mi->level == labelslist_iter::SortRoot) {
|
||||
TRACE("Sort Order Found -- %s", mi->current_attr);
|
||||
mi->level = labelslist_iter::Root;
|
||||
modelslabels.setSortOrder((ModelsSortBy)strtol(value,NULL,10));
|
||||
}
|
||||
}
|
||||
|
||||
static const YamlParserCalls labelslistCalls = {
|
||||
to_parent,
|
||||
to_child,
|
||||
to_next_elmt,
|
||||
find_node,
|
||||
set_attr
|
||||
};
|
||||
|
||||
const YamlParserCalls* get_labelslist_parser_calls()
|
||||
{
|
||||
return &labelslistCalls;
|
||||
}
|
||||
@@ -19,12 +19,16 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef _YAML_MODELSLIST_H_
|
||||
#define _YAML_MODELSLIST_H_
|
||||
#ifndef _YAML_LABELSLIST_H_
|
||||
#define _YAML_LABELSLIST_H_
|
||||
|
||||
#define MODULE_ID_STR "mod%did"
|
||||
#define MODULE_TYPE_STR "mod%dtype"
|
||||
#define MODULE_RFPROTOCOL_STR "mod%drf"
|
||||
|
||||
struct YamlParserCalls;
|
||||
|
||||
void* get_modelslist_iter(const char* currentModel, unsigned currentModel_len);
|
||||
const YamlParserCalls* get_modelslist_parser_calls();
|
||||
void* get_labelslist_iter();
|
||||
const YamlParserCalls* get_labelslist_parser_calls();
|
||||
|
||||
#endif
|
||||
@@ -1,153 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "yaml_modelslist.h"
|
||||
#include "yaml_parser.h"
|
||||
|
||||
#include "storage/modelslist.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
using std::list;
|
||||
|
||||
struct modelslist_iter
|
||||
{
|
||||
enum Level {
|
||||
Root=0,
|
||||
Category=1,
|
||||
Model=2
|
||||
};
|
||||
|
||||
ModelsList* root;
|
||||
const char* currentModel;
|
||||
unsigned currentModel_len;
|
||||
uint8_t level;
|
||||
char current_attr[16]; // set after find_node()
|
||||
};
|
||||
|
||||
static modelslist_iter __modelslist_iter_inst;
|
||||
|
||||
void* get_modelslist_iter(const char* currentModel, unsigned currentModel_len)
|
||||
{
|
||||
__modelslist_iter_inst.root = &modelslist;
|
||||
__modelslist_iter_inst.currentModel = currentModel;
|
||||
__modelslist_iter_inst.currentModel_len = currentModel_len;
|
||||
__modelslist_iter_inst.level = 0;
|
||||
|
||||
return &__modelslist_iter_inst;
|
||||
}
|
||||
|
||||
static bool to_parent(void* ctx)
|
||||
{
|
||||
modelslist_iter* mi = (modelslist_iter*)ctx;
|
||||
if (mi->level == modelslist_iter::Root) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mi->level--;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool to_child(void* ctx)
|
||||
{
|
||||
modelslist_iter* mi = (modelslist_iter*)ctx;
|
||||
if (mi->level == modelslist_iter::Model) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mi->level++;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool to_next_elmt(void* ctx)
|
||||
{
|
||||
modelslist_iter* mi = (modelslist_iter*)ctx;
|
||||
if (mi->level == modelslist_iter::Root) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool find_node(void* ctx, char* buf, uint8_t len)
|
||||
{
|
||||
modelslist_iter* mi = (modelslist_iter*)ctx;
|
||||
|
||||
if (len > sizeof(modelslist_iter::current_attr)-1)
|
||||
len = sizeof(modelslist_iter::current_attr)-1;
|
||||
|
||||
memcpy(mi->current_attr, buf, len);
|
||||
mi->current_attr[len] = '\0';
|
||||
|
||||
list<ModelsCategory *>& cats = mi->root->getCategories();
|
||||
if (mi->level == modelslist_iter::Category) {
|
||||
ModelsCategory* cat = new ModelsCategory(buf,len);
|
||||
cats.push_back(cat);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void set_attr(void* ctx, char* buf, uint8_t len)
|
||||
{
|
||||
modelslist_iter* mi = (modelslist_iter*)ctx;
|
||||
auto ml = mi->root;
|
||||
list<ModelsCategory*>& cats = ml->getCategories();
|
||||
|
||||
switch (mi->level) {
|
||||
case modelslist_iter::Model:
|
||||
if (!strcmp(mi->current_attr, "filename")) {
|
||||
if (!cats.empty()) {
|
||||
ModelCell* model = new ModelCell(buf, len);
|
||||
auto cat = cats.back();
|
||||
cat->push_back(model);
|
||||
ml->incModelsCount();
|
||||
|
||||
if (!strncmp(model->modelFilename, mi->currentModel,
|
||||
mi->currentModel_len)) {
|
||||
ml->setCurrentCategory(cat);
|
||||
ml->setCurrentModel(model);
|
||||
}
|
||||
}
|
||||
} else if (!strcmp(mi->current_attr, "name")) {
|
||||
if (!cats.empty()) {
|
||||
auto cat = cats.back();
|
||||
if (!cat->empty()) {
|
||||
auto model = cat->back();
|
||||
model->setModelName(buf, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const YamlParserCalls modelslistCalls = {
|
||||
to_parent,
|
||||
to_child,
|
||||
to_next_elmt,
|
||||
find_node,
|
||||
set_attr
|
||||
};
|
||||
|
||||
const YamlParserCalls* get_modelslist_parser_calls()
|
||||
{
|
||||
return &modelslistCalls;
|
||||
}
|
||||
@@ -65,6 +65,7 @@ constexpr uint32_t MENU_HEADER_BACK_BUTTON_WIDTH = MENU_HEADER_HEIGHT;
|
||||
constexpr uint32_t MENU_HEADER_BACK_BUTTON_HEIGHT = MENU_HEADER_HEIGHT;
|
||||
|
||||
constexpr coord_t PAGE_PADDING = 6;
|
||||
constexpr uint32_t LABELS_LINE_HEIGHT = 30;
|
||||
constexpr uint32_t PAGE_LINE_HEIGHT = 20;
|
||||
constexpr coord_t LINE_HEIGHT = PAGE_LINE_HEIGHT;
|
||||
|
||||
|
||||
@@ -130,6 +130,7 @@ const char STR_DELETEMODEL[] = TR_DELETEMODEL;
|
||||
const char STR_COPYINGMODEL[] = TR_COPYINGMODEL;
|
||||
const char STR_MOVINGMODEL[] = TR_MOVINGMODEL;
|
||||
const char STR_LOADINGMODEL[] = TR_LOADINGMODEL;
|
||||
const char STR_UNLABELEDMODEL[] = TR_UNLABELEDMODEL;
|
||||
const char STR_NAME[] = TR_NAME;
|
||||
const char STR_BITMAP[] = TR_BITMAP;
|
||||
const char STR_TIMER[] = TR_TIMER;
|
||||
@@ -329,17 +330,17 @@ const char STR_CURRENT_CALIB[] = TR_CURRENT_CALIB;
|
||||
const char STR_UNITS_SYSTEM[] = TR_UNITS_SYSTEM;
|
||||
const char STR_VOICE_LANGUAGE[] = TR_VOICE_LANGUAGE;
|
||||
const char STR_MODELIDUSED[] = TR_MODELIDUSED;
|
||||
const char STR_MODELIDUNIQUE[] = TR_MODELIDUNIQUE;
|
||||
const char STR_BEEP_VOLUME[] = INDENT TR_BEEP_VOLUME;
|
||||
const char STR_WAV_VOLUME[] = INDENT TR_WAV_VOLUME;
|
||||
const char STR_BG_VOLUME[] = INDENT TR_BG_VOLUME;
|
||||
const char STR_SELECT_MODEL[] = TR_SELECT_MODEL;
|
||||
const char STR_MODEL_CATEGORIES[] = TR_MODEL_CATEGORIES;
|
||||
const char STR_MODELS[] = TR_MODELS;
|
||||
const char STR_SELECT_MODE[] = TR_SELECT_MODE;
|
||||
const char STR_CREATE_CATEGORY[] = TR_CREATE_CATEGORY;
|
||||
const char STR_RENAME_CATEGORY[] = TR_RENAME_CATEGORY;
|
||||
const char STR_DELETE_CATEGORY[] = TR_DELETE_CATEGORY;
|
||||
const char STR_CREATE_MODEL[] = TR_CREATE_MODEL;
|
||||
const char STR_FAVORITE_LABEL[] = TR_FAVORITE_LABEL;
|
||||
const char STR_NEW_MODEL[] = TR_NEW_MODEL;
|
||||
const char STR_EDIT_LABELS[] = TR_EDIT_LABELS;
|
||||
const char STR_DUPLICATE_MODEL[] = TR_DUPLICATE_MODEL;
|
||||
const char STR_COPY_MODEL[] = TR_COPY_MODEL;
|
||||
const char STR_MOVE_MODEL[] = TR_MOVE_MODEL;
|
||||
@@ -405,7 +406,6 @@ const char STR_WAITING_FOR_RX[] = TR_WAITING_FOR_RX;
|
||||
const char STR_WAITING_FOR_TX[] = TR_WAITING_FOR_TX;
|
||||
const char STR_WAITING_FOR_MODULE[] = TR_WAITING_FOR_MODULE;
|
||||
const char STR_WARN_5VOLTS[] = TR_WARN_5VOLTS;
|
||||
const char STR_CAT_NOT_EMPTY[] = TR_CAT_NOT_EMPTY;
|
||||
const char STR_WARNING[] = TR_WARNING;
|
||||
const char STR_THROTTLE_WARNING[] = TR_THROTTLE_WARNING;
|
||||
const char STR_CUSTOM_THROTTLE_WARNING[] = TR_CUSTOM_THROTTLE_WARNING;
|
||||
@@ -998,8 +998,17 @@ const char STR_EDIT_THEME[] = TR_EDIT_THEME;
|
||||
const char STR_DETAILS[] = TR_DETAILS;
|
||||
const char STR_THEME_EDITOR[] = TR_THEME_EDITOR;
|
||||
|
||||
|
||||
#if defined(COLORLCD)
|
||||
|
||||
const char STR_ENTER_LABEL[] = TR_ENTER_LABEL;
|
||||
const char STR_LABEL[] = TR_LABEL;
|
||||
const char STR_LABELS[] = TR_LABELS;
|
||||
const char STR_CURRENT_MODEL[] = TR_CURRENT_MODEL;
|
||||
const char STR_NEW[] = TR_NEW;
|
||||
const char STR_NEW_LABEL[] = TR_NEW_LABEL;
|
||||
const char STR_RENAME_LABEL[] = TR_RENAME_LABEL;
|
||||
const char STR_DELETE_LABEL[] = TR_DELETE_LABEL;
|
||||
const char STR_MAIN_MENU_SELECT_MODEL[] = TR_MAIN_MENU_SELECT_MODEL;
|
||||
const char STR_MAIN_MENU_MODEL_NOTES[] = TR_MAIN_MENU_MODEL_NOTES;
|
||||
const char STR_MAIN_MENU_CHANNEL_MONITOR[] = TR_MAIN_MENU_CHANNEL_MONITOR;
|
||||
|
||||
@@ -234,6 +234,7 @@ extern const char STR_DELETEMODEL[];
|
||||
extern const char STR_COPYINGMODEL[];
|
||||
extern const char STR_MOVINGMODEL[];
|
||||
extern const char STR_LOADINGMODEL[];
|
||||
extern const char STR_UNLABELEDMODEL[];
|
||||
extern const char STR_NAME[];
|
||||
extern const char STR_BITMAP[];
|
||||
extern const char STR_TIMER[];
|
||||
@@ -528,17 +529,17 @@ extern const char STR_CURRENT_CALIB[];
|
||||
extern const char STR_UNITS_SYSTEM[];
|
||||
extern const char STR_VOICE_LANGUAGE[];
|
||||
extern const char STR_MODELIDUSED[];
|
||||
extern const char STR_MODELIDUNIQUE[];
|
||||
extern const char STR_BEEP_VOLUME[];
|
||||
extern const char STR_WAV_VOLUME[];
|
||||
extern const char STR_BG_VOLUME[];
|
||||
extern const char STR_SELECT_MODEL[];
|
||||
extern const char STR_MODEL_CATEGORIES[];
|
||||
extern const char STR_MODELS[];
|
||||
extern const char STR_SELECT_MODE[];
|
||||
extern const char STR_CREATE_CATEGORY[];
|
||||
extern const char STR_RENAME_CATEGORY[];
|
||||
extern const char STR_DELETE_CATEGORY[];
|
||||
extern const char STR_CREATE_MODEL[];
|
||||
extern const char STR_FAVORITE_LABEL[];
|
||||
extern const char STR_NEW_MODEL[];
|
||||
extern const char STR_EDIT_LABELS[];
|
||||
extern const char STR_DUPLICATE_MODEL[];
|
||||
extern const char STR_COPY_MODEL[];
|
||||
extern const char STR_MOVE_MODEL[];
|
||||
@@ -631,7 +632,6 @@ extern const char STR_ASK_OVERWRITE[];
|
||||
extern const char STR_NEEDS_FILE[];
|
||||
extern const char STR_EXT_MULTI_SPEC[];
|
||||
extern const char STR_INT_MULTI_SPEC[];
|
||||
extern const char STR_CAT_NOT_EMPTY[];
|
||||
extern const char STR_WARNING[];
|
||||
extern const char STR_STORAGE_WARNING[];
|
||||
extern const char STR_EEPROM_CONVERTING[];
|
||||
@@ -1196,8 +1196,20 @@ extern const char STR_EDIT_THEME[];
|
||||
extern const char STR_DETAILS[];
|
||||
extern const char STR_THEME_EDITOR[];
|
||||
|
||||
|
||||
|
||||
#if defined(COLORLCD)
|
||||
|
||||
extern const char STR_ENTER_LABEL[];
|
||||
extern const char STR_LABEL[];
|
||||
extern const char STR_LABELS[];
|
||||
extern const char STR_SELECT_MODEL[];
|
||||
extern const char STR_NONE[];
|
||||
extern const char STR_CURRENT_MODEL[];
|
||||
extern const char STR_NEW[];
|
||||
extern const char STR_NEW_LABEL[];
|
||||
extern const char STR_RENAME_LABEL[];
|
||||
extern const char STR_DELETE_LABEL[];
|
||||
extern const char STR_MAIN_MENU_SELECT_MODEL[];
|
||||
extern const char STR_MAIN_MENU_MODEL_NOTES[];
|
||||
extern const char STR_MAIN_MENU_CHANNEL_MONITOR[];
|
||||
|
||||
+15
-11
@@ -331,6 +331,7 @@
|
||||
#define TR_COPYINGMODEL "复制模型..."
|
||||
#define TR_MOVINGMODEL "移动模型..."
|
||||
#define TR_LOADINGMODEL "载入模型..."
|
||||
#define TR_UNLABELEDMODEL "Unlabeled"
|
||||
#define TR_NAME "名称"
|
||||
#define TR_MODELNAME "模型名称"
|
||||
#define TR_PHASENAME "模式名称"
|
||||
@@ -626,13 +627,20 @@
|
||||
#define TR_VOLTAGE TR(INDENT "电压源", INDENT "电压来源")
|
||||
#define TR_CURRENT TR(INDENT "电流源", INDENT "电流来源")
|
||||
#define TR_SELECT_MODEL "选择模型"
|
||||
#define TR_MODEL_CATEGORIES "模型分类"
|
||||
#define TR_MODELS "个模型"
|
||||
#define TR_SELECT_MODE "选择模式"
|
||||
#define TR_CREATE_CATEGORY "创建分类"
|
||||
#define TR_RENAME_CATEGORY "分类改名"
|
||||
#define TR_DELETE_CATEGORY "删除分类"
|
||||
#define TR_CREATE_MODEL "创建模型"
|
||||
#define TR_FAVORITE_LABEL "Favorites"
|
||||
#define TR_NEW_MODEL "New Model"
|
||||
#define TR_EDIT_LABELS "Edit Labels"
|
||||
#define TR_ENTER_LABEL "Enter Label"
|
||||
#define TR_LABEL "Label"
|
||||
#define TR_LABELS "Labels"
|
||||
#define TR_CURRENT_MODEL "Current"
|
||||
#define TR_NEW "New"
|
||||
#define TR_NEW_LABEL "New Label"
|
||||
#define TR_RENAME_LABEL "Rename Label"
|
||||
#define TR_DELETE_LABEL "Delete Label"
|
||||
#define TR_DUPLICATE_MODEL "复制模型"
|
||||
#define TR_COPY_MODEL "复制模型"
|
||||
#define TR_MOVE_MODEL "移动模型"
|
||||
@@ -640,7 +648,6 @@
|
||||
#define TR_DELETE_MODEL "删除模型"
|
||||
#define TR_RESTORE_MODEL "恢复模型"
|
||||
#define TR_DELETE_ERROR "删除失败"
|
||||
#define TR_CAT_NOT_EMPTY "分类未清空"
|
||||
#define TR_SDCARD_ERROR TR("存储卡错误", "存储卡错误")
|
||||
#define TR_SDCARD "存储卡"
|
||||
#define TR_NO_FILES_ON_SD "存储卡中没有文件!"
|
||||
@@ -911,11 +918,8 @@
|
||||
#define TR_TRAINER "教练"
|
||||
#define TR_CHANS "通道"
|
||||
#define TR_ANTENNAPROBLEM CENTER "发射机天线故障!"
|
||||
#if defined(COLORLCD)
|
||||
#define TR_MODELIDUSED "ID已使用:"
|
||||
#else
|
||||
#define TR_MODELIDUSED TR("ID已使用:","ID已使用:")
|
||||
#endif
|
||||
#define TR_MODELIDUSED "ID已使用:"
|
||||
#define TR_MODELIDUNIQUE "ID is unique"
|
||||
#define TR_MODULE "模块"
|
||||
#define TR_RX_NAME "接收机名称"
|
||||
#define TR_TELEMETRY_TYPE TR("类型", "回传类型")
|
||||
@@ -1334,4 +1338,4 @@
|
||||
#define TR_PROTOCOL "协议"
|
||||
#define TR_YEAR "年"
|
||||
#define TR_MONTH "月"
|
||||
#define TR_DAY "日"
|
||||
#define TR_DAY "日"
|
||||
|
||||
@@ -349,6 +349,7 @@
|
||||
#define TR_COPYINGMODEL "Kopíruji model..."
|
||||
#define TR_MOVINGMODEL "Přesouvám model..."
|
||||
#define TR_LOADINGMODEL "Aktivuji model..."
|
||||
#define TR_UNLABELEDMODEL "Unlabeled"
|
||||
#define TR_NAME "Název"
|
||||
#define TR_MODELNAME TR("Model", "Název modelu")
|
||||
#define TR_PHASENAME "Název"
|
||||
@@ -642,13 +643,20 @@
|
||||
#define TR_VOLTAGE INDENT"Napětí"
|
||||
#define TR_CURRENT INDENT"Proud"
|
||||
#define TR_SELECT_MODEL "Vyber model"
|
||||
#define TR_MODEL_CATEGORIES "Kategorie modelů"
|
||||
#define TR_MODELS "Modely"
|
||||
#define TR_SELECT_MODE "Vybrat mód"
|
||||
#define TR_CREATE_CATEGORY "Nová kategorie"
|
||||
#define TR_RENAME_CATEGORY "Přejmenovat kategorii"
|
||||
#define TR_DELETE_CATEGORY "Odstranit kategorii"
|
||||
#define TR_CREATE_MODEL "Nový model"
|
||||
#define TR_FAVORITE_LABEL "Favorites"
|
||||
#define TR_NEW_MODEL "New Model"
|
||||
#define TR_EDIT_LABELS "Edit Labels"
|
||||
#define TR_ENTER_LABEL "Enter Label"
|
||||
#define TR_LABEL "Label"
|
||||
#define TR_LABELS "Labels"
|
||||
#define TR_CURRENT_MODEL "Current"
|
||||
#define TR_NEW "New"
|
||||
#define TR_NEW_LABEL "New Label"
|
||||
#define TR_RENAME_LABEL "Rename Label"
|
||||
#define TR_DELETE_LABEL "Delete Label"
|
||||
#define TR_DUPLICATE_MODEL "Duplikovat model"
|
||||
#define TR_COPY_MODEL "Kopírovat"
|
||||
#define TR_MOVE_MODEL "Přesunout model"
|
||||
@@ -656,7 +664,6 @@
|
||||
#define TR_DELETE_MODEL "Smaž model"
|
||||
#define TR_RESTORE_MODEL "Obnov model z SD"
|
||||
#define TR_DELETE_ERROR "Nelze odstranit"
|
||||
#define TR_CAT_NOT_EMPTY "Kategorie není prázdná"
|
||||
#define TR_SDCARD_ERROR "Chyba SD karty"
|
||||
#define TR_SDCARD "SD karta"
|
||||
#define TR_NO_FILES_ON_SD "Žádné soubory na SD kartě!"
|
||||
@@ -927,6 +934,7 @@
|
||||
#define TR_CHANS "Chans"
|
||||
#define TR_ANTENNAPROBLEM CENTER "Problém s TX anténou!"
|
||||
#define TR_MODELIDUSED TR("ID již použito","ID modelu je již použito")
|
||||
#define TR_MODELIDUNIQUE "ID is unique"
|
||||
#define TR_MODULE "Modul"
|
||||
#define TR_RX_NAME "Jméno RX"
|
||||
#define TR_TELEMETRY_TYPE TR("Typ", "Typ telemetrie")
|
||||
|
||||
+22
-18
@@ -332,6 +332,7 @@
|
||||
#define TR_COPYINGMODEL "Kopier model..."
|
||||
#define TR_MOVINGMODEL "Flytter model..."
|
||||
#define TR_LOADINGMODEL "Indlæser model..."
|
||||
#define TR_UNLABELEDMODEL "Typeløs"
|
||||
#define TR_NAME "Navn"
|
||||
#define TR_MODELNAME "Model navn"
|
||||
#define TR_PHASENAME "Mode navn"
|
||||
@@ -601,7 +602,7 @@
|
||||
#define TR_PROTOCOL_INVALID TR("Prot. ugyldig", "Protocol ugyldig")
|
||||
#define TR_MODULE_STATUS TR(INDENT "Status", INDENT "Module Status")
|
||||
#define TR_MODULE_SYNC TR(INDENT "Sync", INDENT "Proto Sync Status")
|
||||
#define TR_MULTI_SERVOFREQ TR("Servo rate", "Servo opdaterings rate")
|
||||
#define TR_MULTI_SERVOFREQ TR("Servo hastighed", "Servo opdaterings hastighed")
|
||||
#define TR_MULTI_MAX_THROW TR("Max. Throw", "Enable max. throw")
|
||||
#define TR_MULTI_RFCHAN TR("RF kanal", "Vælg RF kanal")
|
||||
#define TR_AFHDS3_RX_FREQ TR("RX frekv.", "RX frekvens")
|
||||
@@ -612,7 +613,7 @@
|
||||
#define TR_SYNCMENU "[Sync]"
|
||||
#define TR_LIMIT INDENT "Grænse"
|
||||
#define TR_MINRSSI "Min Rssi"
|
||||
#define TR_FLYSKY_TELEMETRY TR("FlySky RSSI #", "Brug FlySky RSSI value without rescalling")
|
||||
#define TR_FLYSKY_TELEMETRY TR("FlySky RSSI #", "Brug FlySky RSSI værdi uden reskalering")
|
||||
#define TR_LATITUDE "Breddegrad"
|
||||
#define TR_LONGITUDE "Længdegrad"
|
||||
#define TR_GPS_COORDS_FORMAT TR("GPS Coords", "Coordinate format")
|
||||
@@ -627,13 +628,20 @@
|
||||
#define TR_VOLTAGE TR(INDENT "Spænding", INDENT "Spænding kilde")
|
||||
#define TR_CURRENT TR(INDENT "Strøm", INDENT "Strøm kilde")
|
||||
#define TR_SELECT_MODEL "Vælg model"
|
||||
#define TR_MODEL_CATEGORIES "Model kategorier"
|
||||
#define TR_MODELS "Modeller"
|
||||
#define TR_SELECT_MODE "Vælg tilstand"
|
||||
#define TR_CREATE_CATEGORY "Opret category"
|
||||
#define TR_RENAME_CATEGORY "Omdøb category"
|
||||
#define TR_DELETE_CATEGORY "Slet category"
|
||||
#define TR_CREATE_MODEL "Opret model"
|
||||
#define TR_FAVORITE_LABEL "Favoritter"
|
||||
#define TR_NEW_MODEL "Ny model"
|
||||
#define TR_EDIT_LABELS "Ret type"
|
||||
#define TR_ENTER_LABEL "Vælg type"
|
||||
#define TR_LABEL "Type"
|
||||
#define TR_LABELS "Typer"
|
||||
#define TR_CURRENT_MODEL "Aktuelle"
|
||||
#define TR_NEW "Ny"
|
||||
#define TR_NEW_LABEL "Ny type"
|
||||
#define TR_RENAME_LABEL "Omdøb type"
|
||||
#define TR_DELETE_LABEL "Slet type"
|
||||
#define TR_DUPLICATE_MODEL "Dupliker model"
|
||||
#define TR_COPY_MODEL "Kopier model"
|
||||
#define TR_MOVE_MODEL "Flyt model"
|
||||
@@ -641,7 +649,6 @@
|
||||
#define TR_DELETE_MODEL "Slet model"
|
||||
#define TR_RESTORE_MODEL "Genskab model"
|
||||
#define TR_DELETE_ERROR "Slet fejl"
|
||||
#define TR_CAT_NOT_EMPTY "Kategori er ikke tom"
|
||||
#define TR_SDCARD_ERROR TR("SD fejl", "SD kort fejl")
|
||||
#define TR_SDCARD "SD kort"
|
||||
#define TR_NO_FILES_ON_SD "Ingen filer på SD!"
|
||||
@@ -887,7 +894,7 @@
|
||||
#define TR_BUZZER INDENT "Biper"
|
||||
#define TR_BYTES "bytes"
|
||||
#define TR_MODULE_BIND BUTTON(TR("Bnd", "Bind"))
|
||||
#define TR_POWERMETER_ATTN_NEEDED "Attenuator needed"
|
||||
#define TR_POWERMETER_ATTN_NEEDED "Attestering nøvendig"
|
||||
#define TR_PXX2_SELECT_RX "Vælg RX"
|
||||
#define TR_PXX2_DEFAULT "<default>"
|
||||
#define TR_BT_SELECT_DEVICE "Vælg enhed"
|
||||
@@ -906,17 +913,14 @@
|
||||
#define TR_DEBUG "Fejlfind"
|
||||
#define TR_KEYS_BTN BUTTON("Taster")
|
||||
#define TR_ANALOGS_BTN BUTTON(TR("Analo", "Analoge"))
|
||||
#define TR_TOUCH_NOTFOUND "Touch hardware not found"
|
||||
#define TR_TOUCH_EXIT "Touch screen to exit"
|
||||
#define TR_TOUCH_NOTFOUND "Tryk skærm ikke fundet"
|
||||
#define TR_TOUCH_EXIT "Tryk på skærm for at forlade"
|
||||
#define TR_SET BUTTON("Set")
|
||||
#define TR_TRAINER "Træner"
|
||||
#define TR_CHANS "Chans"
|
||||
#define TR_ANTENNAPROBLEM CENTER "TX antenne problem!"
|
||||
#if defined(COLORLCD)
|
||||
#define TR_MODELIDUSED "ID brugt i:"
|
||||
#else
|
||||
#define TR_MODELIDUSED TR("ID brugt i:","Modtager ID brugt i:")
|
||||
#endif
|
||||
#define TR_MODELIDUSED "ID brugt i:"
|
||||
#define TR_MODELIDUNIQUE "ID er entydig"
|
||||
#define TR_MODULE "Modul"
|
||||
#define TR_RX_NAME "Rx Navn"
|
||||
#define TR_TELEMETRY_TYPE TR("Type", "Telemetri type")
|
||||
@@ -980,13 +984,13 @@
|
||||
#define TR_INPUTS "Indgange"
|
||||
#define TR_OUTPUTS "Udgange"
|
||||
#define STR_EEBACKUP "EEPROM backup"
|
||||
#define STR_FACTORYRESET "Factory reset"
|
||||
#define STR_FACTORYRESET "Nulstil til fabrik indstillinger"
|
||||
#define TR_CONFIRMRESET TR("Slet ALLE", "Slet ALLE modeller og indstillinger?")
|
||||
#define TR_TOO_MANY_LUA_SCRIPTS "Too many Lua scripts!"
|
||||
#define TR_TOO_MANY_LUA_SCRIPTS "For mannge Lua scripts!"
|
||||
#define TR_SPORT_UPDATE_POWER_MODE "SP Strøm"
|
||||
#define TR_SPORT_UPDATE_POWER_MODES "AUTO","TIL"
|
||||
#define TR_NO_TELEMETRY_SCREENS "Ingen telemetri skærme"
|
||||
#define TR_TOUCH_PANEL "Touch panel:"
|
||||
#define TR_TOUCH_PANEL "Tryk panel:"
|
||||
#define TR_FILE_SIZE "Fil størrelse"
|
||||
#define TR_FILE_OPEN "Åbn alligevel?"
|
||||
|
||||
|
||||
+13
-10
@@ -336,6 +336,7 @@
|
||||
#define TR_COPYINGMODEL "Kopiere Modell"
|
||||
#define TR_MOVINGMODEL "Schiebe Modell"
|
||||
#define TR_LOADINGMODEL "Lade Modell..."
|
||||
#define TR_UNLABELEDMODEL "Unlabeled"
|
||||
#define TR_NAME "Name"
|
||||
#define TR_MODELNAME "Modellname"
|
||||
#define TR_PHASENAME "Phase-Name"
|
||||
@@ -625,13 +626,20 @@
|
||||
#define TR_VOLTAGE TR(INDENT "Spg", INDENT "Spannungsquelle") //9XR-Pro
|
||||
#define TR_CURRENT TR(INDENT "Strom", INDENT "Stromquelle")
|
||||
#define TR_SELECT_MODEL "Modell auswählen"
|
||||
#define TR_MODEL_CATEGORIES "Model Categories"
|
||||
#define TR_MODELS "Models"
|
||||
#define TR_SELECT_MODE "Select mode"
|
||||
#define TR_CREATE_CATEGORY "Modelltyp erstellen"
|
||||
#define TR_RENAME_CATEGORY "Modelltyp umbenennen"
|
||||
#define TR_DELETE_CATEGORY "Modelltyp löschen"
|
||||
#define TR_CREATE_MODEL TR("Neues Modell" , "Neues Modell erstellen")
|
||||
#define TR_FAVORITE_LABEL "Favorites"
|
||||
#define TR_NEW_MODEL "New Model"
|
||||
#define TR_EDIT_LABELS "Edit Labels"
|
||||
#define TR_ENTER_LABEL "Enter Label"
|
||||
#define TR_LABEL "Label"
|
||||
#define TR_LABELS "Labels"
|
||||
#define TR_CURRENT_MODEL "Current"
|
||||
#define TR_NEW "New"
|
||||
#define TR_NEW_LABEL "New Label"
|
||||
#define TR_RENAME_LABEL "Rename Label"
|
||||
#define TR_DELETE_LABEL "Delete Label"
|
||||
#define TR_DUPLICATE_MODEL "Kopiere Modell"
|
||||
#define TR_COPY_MODEL "Kopiere Modell"
|
||||
#define TR_MOVE_MODEL "Verschiebe Modell"
|
||||
@@ -639,7 +647,6 @@
|
||||
#define TR_DELETE_MODEL "Lösche Modell" // TODO merged into DELETEMODEL?
|
||||
#define TR_RESTORE_MODEL TR("Modell wiederher.", "Modell wiederherstellen")
|
||||
#define TR_DELETE_ERROR "Fehler beim\nLöschen"
|
||||
#define TR_CAT_NOT_EMPTY "Modelltyp nicht leer"
|
||||
#define TR_SDCARD_ERROR "SD-Kartenfehler"
|
||||
#define TR_SDCARD "SD Card"
|
||||
#define TR_NO_FILES_ON_SD "No files on SD!"
|
||||
@@ -911,11 +918,7 @@
|
||||
#define TR_TRAINER "DSC Buchse PPM In/Out"
|
||||
#define TR_CHANS "Chans"
|
||||
#define TR_ANTENNAPROBLEM CENTER "TX-Antennenproblem!"
|
||||
#if defined(COLORLCD)
|
||||
#define TR_MODELIDUSED "ID used in:"
|
||||
#else
|
||||
#define TR_MODELIDUSED TR("ID used in:","Receiver ID used in:")
|
||||
#endif
|
||||
#define TR_MODELIDUSED "ID used in:"#define TR_MODELIDUNIQUE "ID is unique"
|
||||
#define TR_MODULE "Modul-Typ"
|
||||
#define TR_RX_NAME "Rx Name"
|
||||
#define TR_TELEMETRY_TYPE TR("Typ", "Telemetrietyp")
|
||||
|
||||
+18
-14
@@ -331,6 +331,7 @@
|
||||
#define TR_COPYINGMODEL "Copying model..."
|
||||
#define TR_MOVINGMODEL "Moving model..."
|
||||
#define TR_LOADINGMODEL "Loading model..."
|
||||
#define TR_UNLABELEDMODEL "Unlabeled"
|
||||
#define TR_NAME "Name"
|
||||
#define TR_MODELNAME "Model name"
|
||||
#define TR_PHASENAME "Mode name"
|
||||
@@ -626,13 +627,20 @@
|
||||
#define TR_VOLTAGE TR(INDENT "Voltage", INDENT "Voltage source")
|
||||
#define TR_CURRENT TR(INDENT "Current", INDENT "Current source")
|
||||
#define TR_SELECT_MODEL "Select model"
|
||||
#define TR_MODEL_CATEGORIES "Model Categories"
|
||||
#define TR_MODELS "Models"
|
||||
#define TR_SELECT_MODE "Select mode"
|
||||
#define TR_CREATE_CATEGORY "Create category"
|
||||
#define TR_RENAME_CATEGORY "Rename category"
|
||||
#define TR_DELETE_CATEGORY "Delete category"
|
||||
#define TR_CREATE_MODEL "Create model"
|
||||
#define TR_FAVORITE_LABEL "Favorites"
|
||||
#define TR_NEW_MODEL "New Model"
|
||||
#define TR_EDIT_LABELS "Edit Labels"
|
||||
#define TR_ENTER_LABEL "Enter Label"
|
||||
#define TR_LABEL "Label"
|
||||
#define TR_LABELS "Labels"
|
||||
#define TR_CURRENT_MODEL "Current"
|
||||
#define TR_NEW "New"
|
||||
#define TR_NEW_LABEL "New Label"
|
||||
#define TR_RENAME_LABEL "Rename Label"
|
||||
#define TR_DELETE_LABEL "Delete Label"
|
||||
#define TR_DUPLICATE_MODEL "Duplicate model"
|
||||
#define TR_COPY_MODEL "Copy model"
|
||||
#define TR_MOVE_MODEL "Move model"
|
||||
@@ -640,7 +648,6 @@
|
||||
#define TR_DELETE_MODEL "Delete model"
|
||||
#define TR_RESTORE_MODEL "Restore model"
|
||||
#define TR_DELETE_ERROR "Delete error"
|
||||
#define TR_CAT_NOT_EMPTY "Category is not empty"
|
||||
#define TR_SDCARD_ERROR TR("SD error", "SD card error")
|
||||
#define TR_SDCARD "SD Card"
|
||||
#define TR_NO_FILES_ON_SD "No files on SD!"
|
||||
@@ -750,7 +757,7 @@
|
||||
#define TR_SAMPLE_MODE "Sample Mode"
|
||||
#define TR_SAMPLE_MODES "Normal","OneBit"
|
||||
|
||||
#define TR_SELECT_TEMPLATE_FOLDER "SELECT A TEMPLATE FOLDER:"
|
||||
#define TR_SELECT_TEMPLATE_FOLDER "Select a template folder"
|
||||
#define TR_SELECT_TEMPLATE "SELECT A MODEL TEMPLATE:"
|
||||
#define TR_NO_TEMPLATES "No model templates were found in this folder"
|
||||
#define TR_SAVE_TEMPLATE "Save as template"
|
||||
@@ -794,7 +801,7 @@
|
||||
#define TR_PATH_TOO_LONG "Path too long"
|
||||
#define TR_VIEW_TEXT "View text"
|
||||
#define TR_FLASH_BOOTLOADER "Flash bootloader"
|
||||
#define TR_FLASH_DEVICE TR("Flash device","Flash device")
|
||||
#define TR_FLASH_DEVICE TR("Flash device","Flash device")
|
||||
#define TR_FLASH_EXTERNAL_DEVICE TR("Flash S.Port", "Flash S.Port device")
|
||||
#define TR_FLASH_RECEIVER_OTA "Flash receiver OTA"
|
||||
#define TR_FLASH_RECEIVER_BY_EXTERNAL_MODULE_OTA "Flash RX by ext. OTA"
|
||||
@@ -911,11 +918,8 @@
|
||||
#define TR_TRAINER "Trainer"
|
||||
#define TR_CHANS "Chans"
|
||||
#define TR_ANTENNAPROBLEM CENTER "TX antenna problem!"
|
||||
#if defined(COLORLCD)
|
||||
#define TR_MODELIDUSED "ID used in:"
|
||||
#else
|
||||
#define TR_MODELIDUSED TR("ID used in:","Receiver ID used in:")
|
||||
#endif
|
||||
#define TR_MODELIDUSED "ID used in:"
|
||||
#define TR_MODELIDUNIQUE "ID is unique"
|
||||
#define TR_MODULE "Module"
|
||||
#define TR_RX_NAME "Rx Name"
|
||||
#define TR_TELEMETRY_TYPE TR("Type", "Telemetry type")
|
||||
@@ -1300,9 +1304,9 @@
|
||||
|
||||
#define TR_ADD_ALL_TRIMS_TO_SUBTRIMS "Add all Trims to Subtrims"
|
||||
#if LCD_W > LCD_H
|
||||
#define TR_OPEN_CHANNEL_MONITORS "Open Channel Monitor"
|
||||
#define TR_OPEN_CHANNEL_MONITORS "Open Channel Monitor"
|
||||
#else
|
||||
#define TR_OPEN_CHANNEL_MONITORS "Open Channel Mon."
|
||||
#define TR_OPEN_CHANNEL_MONITORS "Open Channel Mon."
|
||||
#endif
|
||||
#define TR_DUPLICATE "Duplicate"
|
||||
#define TR_ACTIVATE "Set Active"
|
||||
|
||||
@@ -333,6 +333,7 @@
|
||||
#define TR_COPYINGMODEL "Copiando modelo.."
|
||||
#define TR_MOVINGMODEL "Moviendo modelo..."
|
||||
#define TR_LOADINGMODEL "Cargando modelo..."
|
||||
#define TR_UNLABELEDMODEL "Unlabeled"
|
||||
#define TR_NAME "Nombre"
|
||||
#define TR_MODELNAME TR("Nom. modelo", "Nombre modelo")
|
||||
#define TR_PHASENAME "Nombre fase "
|
||||
@@ -627,13 +628,20 @@
|
||||
#define TR_VOLTAGE INDENT"Voltaje"
|
||||
#define TR_CURRENT INDENT"Actual"
|
||||
#define TR_SELECT_MODEL TR("Selec. modelo", "Seleccionar modelo")
|
||||
#define TR_MODEL_CATEGORIES "Model Categories"
|
||||
#define TR_MODELS "Models"
|
||||
#define TR_SELECT_MODE "Select mode"
|
||||
#define TR_CREATE_CATEGORY "Crear categoría"
|
||||
#define TR_RENAME_CATEGORY "Renombrar categoría"
|
||||
#define TR_DELETE_CATEGORY "Borrar categoría"
|
||||
#define TR_CREATE_MODEL "Crear modelo"
|
||||
#define TR_FAVORITE_LABEL "Favorites"
|
||||
#define TR_NEW_MODEL "New Model"
|
||||
#define TR_EDIT_LABELS "Edit Labels"
|
||||
#define TR_ENTER_LABEL "Enter Label"
|
||||
#define TR_LABEL "Label"
|
||||
#define TR_LABELS "Labels"
|
||||
#define TR_CURRENT_MODEL "Current"
|
||||
#define TR_NEW "New"
|
||||
#define TR_NEW_LABEL "New Label"
|
||||
#define TR_RENAME_LABEL "Rename Label"
|
||||
#define TR_DELETE_LABEL "Delete Label"
|
||||
#define TR_DUPLICATE_MODEL "Duplicar modelo"
|
||||
#define TR_COPY_MODEL "Copiar modelo"
|
||||
#define TR_MOVE_MODEL "Mover modelo"
|
||||
@@ -641,7 +649,6 @@
|
||||
#define TR_DELETE_MODEL "Borrar modelo"
|
||||
#define TR_RESTORE_MODEL "Restaurar modelo"
|
||||
#define TR_DELETE_ERROR "Error de borrado"
|
||||
#define TR_CAT_NOT_EMPTY "Categoría no esta vacía"
|
||||
#define TR_SDCARD_ERROR "Error SDCARD"
|
||||
#define TR_SDCARD "SD Card"
|
||||
#define TR_NO_FILES_ON_SD "No files on SD!"
|
||||
@@ -751,7 +758,7 @@
|
||||
|
||||
#define TR_SAMPLE_MODE "Modo de muestra"
|
||||
#define TR_SAMPLE_MODES "Normal","OneBit"
|
||||
#define TR_SELECT_TEMPLATE_FOLDER "SELECT A TEMPLATE FOLDER:"
|
||||
#define TR_SELECT_TEMPLATE_FOLDER "Select a template folder"
|
||||
#define TR_SELECT_TEMPLATE "SELECT A MODEL TEMPLATE:"
|
||||
#define TR_NO_TEMPLATES "No model templates were found in this folder"
|
||||
#define TR_SAVE_TEMPLATE "Save as template"
|
||||
@@ -913,6 +920,7 @@
|
||||
#define TR_CHANS "Chans"
|
||||
#define TR_ANTENNAPROBLEM CENTER "Problema antena TX!"
|
||||
#define TR_MODELIDUSED TR("ID en uso", "ID modelo en uso")
|
||||
#define TR_MODELIDUNIQUE "ID is unique"
|
||||
#define TR_MODULE "Módulo"
|
||||
#define TR_RX_NAME "Nombre Rx"
|
||||
#define TR_TELEMETRY_TYPE TR("Tipo", "Tipo telemetría")
|
||||
|
||||
@@ -343,6 +343,7 @@
|
||||
#define TR_COPYINGMODEL "Copying model..."
|
||||
#define TR_MOVINGMODEL "Moving model..."
|
||||
#define TR_LOADINGMODEL "Loading model..."
|
||||
#define TR_UNLABELEDMODEL "Unlabeled"
|
||||
#define TR_NAME "Name"
|
||||
#define TR_MODELNAME "Model Name"
|
||||
#define TR_PHASENAME "Mode Name"
|
||||
@@ -641,13 +642,20 @@
|
||||
#define TR_VOLTAGE INDENT"Jännite"
|
||||
#define TR_CURRENT INDENT"Virta"
|
||||
#define TR_SELECT_MODEL "Select Model"
|
||||
#define TR_MODEL_CATEGORIES "Model Categories"
|
||||
#define TR_MODELS "Models"
|
||||
#define TR_SELECT_MODE "Select mode"
|
||||
#define TR_CREATE_CATEGORY "Create Category"
|
||||
#define TR_RENAME_CATEGORY "Rename Category"
|
||||
#define TR_DELETE_CATEGORY "Delete Category"
|
||||
#define TR_CREATE_MODEL "Create Model"
|
||||
#define TR_FAVORITE_LABEL "Favorites"
|
||||
#define TR_NEW_MODEL "New Model"
|
||||
#define TR_EDIT_LABELS "Edit Labels"
|
||||
#define TR_ENTER_LABEL "Enter Label"
|
||||
#define TR_LABEL "Label"
|
||||
#define TR_LABELS "Labels"
|
||||
#define TR_CURRENT_MODEL "Current"
|
||||
#define TR_NEW "New"
|
||||
#define TR_NEW_LABEL "New Label"
|
||||
#define TR_RENAME_LABEL "Rename Label"
|
||||
#define TR_DELETE_LABEL "Delete Label"
|
||||
#define TR_DUPLICATE_MODEL "Duplicate Model"
|
||||
#define TR_COPY_MODEL "Copy Model"
|
||||
#define TR_MOVE_MODEL "Move Model"
|
||||
@@ -655,7 +663,6 @@
|
||||
#define TR_DELETE_MODEL "Delete Model"
|
||||
#define TR_RESTORE_MODEL "Restore Model"
|
||||
#define TR_DELETE_ERROR "Delete error"
|
||||
#define TR_CAT_NOT_EMPTY "Luokka ei ole tyhjä"
|
||||
#define TR_SDCARD_ERROR "SDCARD Error"
|
||||
#define TR_SDCARD "SD Card"
|
||||
#define TR_NO_FILES_ON_SD "No files on SD!"
|
||||
@@ -926,6 +933,7 @@
|
||||
#define TR_CHANS "Chans"
|
||||
#define TR_ANTENNAPROBLEM CENTER "Lähettimen antenniongelma!"
|
||||
#define TR_MODELIDUSED TR("ID used in:", "Receiver ID used in:")
|
||||
#define TR_MODELIDUNIQUE "ID is unique"
|
||||
#define TR_MODULE "Module"
|
||||
#define TR_RX_NAME "Rx Name"
|
||||
#define TR_TELEMETRY_TYPE TR("Type", "Telemetry Type")
|
||||
|
||||
@@ -353,6 +353,7 @@
|
||||
#define TR_COPYINGMODEL "Copie..."
|
||||
#define TR_MOVINGMODEL "Déplacement..."
|
||||
#define TR_LOADINGMODEL "Chargement..."
|
||||
#define TR_UNLABELEDMODEL "Unlabeled"
|
||||
#define TR_NAME "Nom"
|
||||
#define TR_MODELNAME TR("Nom modèle", "Nom du modèle")
|
||||
#define TR_PHASENAME "Nom phase"
|
||||
@@ -646,13 +647,20 @@
|
||||
#define TR_VOLTAGE TR(INDENT "Tension",INDENT "Source tension")
|
||||
#define TR_CURRENT TR(INDENT "Courant",INDENT "Source courant")
|
||||
#define TR_SELECT_MODEL "Sélect. modèle"
|
||||
#define TR_MODEL_CATEGORIES "Model Categories"
|
||||
#define TR_MODELS "Models"
|
||||
#define TR_SELECT_MODE "Select mode"
|
||||
#define TR_CREATE_CATEGORY "Créer une catégorie"
|
||||
#define TR_RENAME_CATEGORY "Renommer la catégorie"
|
||||
#define TR_DELETE_CATEGORY "Supprimer la catégorie"
|
||||
#define TR_CREATE_MODEL "Créer modèle"
|
||||
#define TR_FAVORITE_LABEL "Favorites"
|
||||
#define TR_NEW_MODEL "New Model"
|
||||
#define TR_EDIT_LABELS "Edit Labels"
|
||||
#define TR_ENTER_LABEL "Enter Label"
|
||||
#define TR_LABEL "Label"
|
||||
#define TR_LABELS "Labels"
|
||||
#define TR_CURRENT_MODEL "Current"
|
||||
#define TR_NEW "New"
|
||||
#define TR_NEW_LABEL "New Label"
|
||||
#define TR_RENAME_LABEL "Rename Label"
|
||||
#define TR_DELETE_LABEL "Delete Label"
|
||||
#define TR_DUPLICATE_MODEL "Dupliquer modèle"
|
||||
#define TR_COPY_MODEL "Copier modèle"
|
||||
#define TR_MOVE_MODEL "Déplacer modèle"
|
||||
@@ -660,7 +668,6 @@
|
||||
#define TR_DELETE_MODEL "Supprimer modèle"
|
||||
#define TR_RESTORE_MODEL "Restaurer modèle"
|
||||
#define TR_DELETE_ERROR "Effacement impossible"
|
||||
#define TR_CAT_NOT_EMPTY "Categorie non vide"
|
||||
#define TR_SDCARD_ERROR "Erreur carte SD"
|
||||
#define TR_SDCARD "SD Card"
|
||||
#define TR_NO_FILES_ON_SD "No files on SD!"
|
||||
@@ -769,7 +776,7 @@
|
||||
#define TR_BAUDRATE "Baudrate"
|
||||
#define TR_SAMPLE_MODE "Sample Mode"
|
||||
#define TR_SAMPLE_MODES "Normal""OneBit"
|
||||
#define TR_SELECT_TEMPLATE_FOLDER "SELECT A TEMPLATE FOLDER:"
|
||||
#define TR_SELECT_TEMPLATE_FOLDER "Select a template folder"
|
||||
#define TR_SELECT_TEMPLATE "SELECT A MODEL TEMPLATE:"
|
||||
#define TR_NO_TEMPLATES "No model templates were found in this folder"
|
||||
#define TR_SAVE_TEMPLATE "Save as template"
|
||||
@@ -930,6 +937,7 @@
|
||||
#define TR_CHANS "Chans"
|
||||
#define TR_ANTENNAPROBLEM CENTER "Antenne radio défect.!"
|
||||
#define TR_MODELIDUSED TR("ID affecté à :", "No de récepteur utilisé par :")
|
||||
#define TR_MODELIDUNIQUE "ID is unique"
|
||||
#define TR_MODULE "Module"
|
||||
#define TR_RX_NAME "Nom Rx"
|
||||
#define TR_TELEMETRY_TYPE TR("Type tél.", "Type télémétrie")
|
||||
|
||||
@@ -338,6 +338,7 @@
|
||||
#define TR_COPYINGMODEL "Copia in corso.."
|
||||
#define TR_MOVINGMODEL "Spostamento..."
|
||||
#define TR_LOADINGMODEL "Caricamento..."
|
||||
#define TR_UNLABELEDMODEL "Unlabeled"
|
||||
#define TR_NAME "Nome"
|
||||
#define TR_MODELNAME TR("Nome Mode.", "Nome Modello")
|
||||
#define TR_PHASENAME "Nome fase"
|
||||
@@ -627,13 +628,20 @@
|
||||
#define TR_VOLTAGE TR(INDENT "Voltagg.",INDENT "Voltaggio")
|
||||
#define TR_CURRENT TR(INDENT "Corrente",INDENT "Corrente")
|
||||
#define TR_SELECT_MODEL TR("Scegli mod.", "Scegli Modello")
|
||||
#define TR_MODEL_CATEGORIES "Categorie Modelli"
|
||||
#define TR_MODELS "Modelli"
|
||||
#define TR_SELECT_MODE "Seleziona modo"
|
||||
#define TR_CREATE_CATEGORY "Crea Categoria"
|
||||
#define TR_RENAME_CATEGORY "Rinomina Categoria"
|
||||
#define TR_DELETE_CATEGORY "Cancella Categoria"
|
||||
#define TR_CREATE_MODEL "Crea Modello"
|
||||
#define TR_FAVORITE_LABEL "Favorites"
|
||||
#define TR_NEW_MODEL "New Model"
|
||||
#define TR_EDIT_LABELS "Edit Labels"
|
||||
#define TR_ENTER_LABEL "Enter Label"
|
||||
#define TR_LABEL "Label"
|
||||
#define TR_LABELS "Labels"
|
||||
#define TR_CURRENT_MODEL "Current"
|
||||
#define TR_NEW "New"
|
||||
#define TR_NEW_LABEL "New Label"
|
||||
#define TR_RENAME_LABEL "Rename Label"
|
||||
#define TR_DELETE_LABEL "Delete Label"
|
||||
#define TR_DUPLICATE_MODEL "Duplica Modello"
|
||||
#define TR_COPY_MODEL "Copia Modello"
|
||||
#define TR_MOVE_MODEL "Sposta Modello"
|
||||
@@ -641,7 +649,6 @@
|
||||
#define TR_DELETE_MODEL TR("Elim. Modello", "Elimina Modello")
|
||||
#define TR_RESTORE_MODEL TR("Ripr. Modello", "Ripristina Modello")
|
||||
#define TR_DELETE_ERROR "Errore cancell."
|
||||
#define TR_CAT_NOT_EMPTY "Categoria non vuota"
|
||||
#define TR_SDCARD_ERROR TR("Errore SD", "Errore SDCard")
|
||||
#define TR_SDCARD "SD Card"
|
||||
#define TR_NO_FILES_ON_SD "SD senza file!"
|
||||
@@ -912,6 +919,7 @@
|
||||
#define TR_CHANS "Canali"
|
||||
#define TR_ANTENNAPROBLEM CENTER "Problemi antenna TX!"
|
||||
#define TR_MODELIDUSED TR("ID già usato", "ID Modello già usato")
|
||||
#define TR_MODELIDUNIQUE "ID is unique"
|
||||
#define TR_MODULE "Modulo"
|
||||
#define TR_RX_NAME "Nome RX"
|
||||
#define TR_TELEMETRY_TYPE "Tipo Telemetria"
|
||||
|
||||
@@ -336,6 +336,7 @@
|
||||
#define TR_COPYINGMODEL "Kopieer Model"
|
||||
#define TR_MOVINGMODEL "Verplaats Model"
|
||||
#define TR_LOADINGMODEL "Laad Model..."
|
||||
#define TR_UNLABELEDMODEL "Unlabeled"
|
||||
#define TR_NAME "Naam"
|
||||
#define TR_MODELNAME "Modelnaam"
|
||||
#define TR_PHASENAME "Modus"
|
||||
@@ -632,13 +633,20 @@
|
||||
#define TR_VOLTAGE TR(INDENT "Spg", INDENT "Spanningsbron") //9XR-Pro
|
||||
#define TR_CURRENT TR(INDENT "Stroom", INDENT "Stroombron")
|
||||
#define TR_SELECT_MODEL "Kies Model"
|
||||
#define TR_MODEL_CATEGORIES "Model Categories"
|
||||
#define TR_MODELS "Models"
|
||||
#define TR_SELECT_MODE "Select mode"
|
||||
#define TR_CREATE_CATEGORY "Create Category"
|
||||
#define TR_RENAME_CATEGORY "Rename Category"
|
||||
#define TR_DELETE_CATEGORY "Delete Category"
|
||||
#define TR_CREATE_MODEL "Nieuw Model"
|
||||
#define TR_FAVORITE_LABEL "Favorites"
|
||||
#define TR_NEW_MODEL "New Model"
|
||||
#define TR_EDIT_LABELS "Edit Labels"
|
||||
#define TR_ENTER_LABEL "Enter Label"
|
||||
#define TR_LABEL "Label"
|
||||
#define TR_LABELS "Labels"
|
||||
#define TR_CURRENT_MODEL "Current"
|
||||
#define TR_NEW "New"
|
||||
#define TR_NEW_LABEL "New Label"
|
||||
#define TR_RENAME_LABEL "Rename Label"
|
||||
#define TR_DELETE_LABEL "Delete Label"
|
||||
#define TR_DUPLICATE_MODEL "Dupliceer Model"
|
||||
#define TR_COPY_MODEL "Kopieer Model"
|
||||
#define TR_MOVE_MODEL "Verplaats Model"
|
||||
@@ -646,7 +654,6 @@
|
||||
#define TR_DELETE_MODEL "Wis Model"
|
||||
#define TR_RESTORE_MODEL "Model Terugzetten"
|
||||
#define TR_DELETE_ERROR "Fout bij verwijderen"
|
||||
#define TR_CAT_NOT_EMPTY "Categorie is niet leeg"
|
||||
#define TR_SDCARD_ERROR "SD-Kaart fout"
|
||||
#define TR_SDCARD "SD Card"
|
||||
#define TR_NO_FILES_ON_SD "No files on SD!"
|
||||
@@ -755,7 +762,7 @@
|
||||
#define TR_BAUDRATE "Baudrate"
|
||||
#define TR_SAMPLE_MODE "Sample Mode"
|
||||
#define TR_SAMPLE_MODES "Normal""OneBit"
|
||||
#define TR_SELECT_TEMPLATE_FOLDER "SELECT A TEMPLATE FOLDER:"
|
||||
#define TR_SELECT_TEMPLATE_FOLDER "Select a template folder"
|
||||
#define TR_SELECT_TEMPLATE "SELECT A MODEL TEMPLATE:"
|
||||
#define TR_NO_TEMPLATES "No model templates were found in this folder"
|
||||
#define TR_SAVE_TEMPLATE "Save as template"
|
||||
@@ -916,6 +923,7 @@
|
||||
#define TR_CHANS "Chans"
|
||||
#define TR_ANTENNAPROBLEM CENTER "TX-Antenneprobleem!"
|
||||
#define TR_MODELIDUSED TR("ID al gebruikt", "Model-ID al gebruikt")
|
||||
#define TR_MODELIDUNIQUE "ID is unique"
|
||||
#define TR_MODULE "Module-Type"
|
||||
#define TR_RX_NAME "Rx Name"
|
||||
#define TR_TELEMETRY_TYPE TR("Type", "Telemetrietype")
|
||||
|
||||
@@ -333,6 +333,7 @@
|
||||
#define TR_COPYINGMODEL "Kopiuj model.."
|
||||
#define TR_MOVINGMODEL "Przenieś model."
|
||||
#define TR_LOADINGMODEL "Załaduj model.."
|
||||
#define TR_UNLABELEDMODEL "Unlabeled"
|
||||
#define TR_NAME "Nazwa"
|
||||
#define TR_MODELNAME "Nazwa modelu"
|
||||
#define TR_PHASENAME "Nazwa Fazy"
|
||||
@@ -623,13 +624,20 @@
|
||||
#define TR_VOLTAGE TR (INDENT"Napięcie",INDENT"Źródło Napięcia")
|
||||
#define TR_CURRENT TR (INDENT"Prąd",INDENT"Źródło Prądu")
|
||||
#define TR_SELECT_MODEL "Wybór modelu"
|
||||
#define TR_MODEL_CATEGORIES "Model Categories"
|
||||
#define TR_MODELS "Models"
|
||||
#define TR_SELECT_MODE "Select mode"
|
||||
#define TR_CREATE_CATEGORY "Create Category"
|
||||
#define TR_RENAME_CATEGORY "Rename Category"
|
||||
#define TR_DELETE_CATEGORY "Delete Category"
|
||||
#define TR_CREATE_MODEL "Nowy model"
|
||||
#define TR_FAVORITE_LABEL "Favorites"
|
||||
#define TR_NEW_MODEL "New Model"
|
||||
#define TR_EDIT_LABELS "Edit Labels"
|
||||
#define TR_ENTER_LABEL "Enter Label"
|
||||
#define TR_LABEL "Label"
|
||||
#define TR_LABELS "Labels"
|
||||
#define TR_CURRENT_MODEL "Current"
|
||||
#define TR_NEW "New"
|
||||
#define TR_NEW_LABEL "New Label"
|
||||
#define TR_RENAME_LABEL "Rename Label"
|
||||
#define TR_DELETE_LABEL "Delete Label"
|
||||
#define TR_DUPLICATE_MODEL "Duplicate Model"
|
||||
#define TR_COPY_MODEL "Skopiuj model"
|
||||
#define TR_MOVE_MODEL "Przenieś model"
|
||||
@@ -637,7 +645,6 @@
|
||||
#define TR_DELETE_MODEL "Skasuj model"
|
||||
#define TR_RESTORE_MODEL "Odtwórz model"
|
||||
#define TR_DELETE_ERROR "Delete error"
|
||||
#define TR_CAT_NOT_EMPTY "Category is not empty"
|
||||
#define TR_SDCARD_ERROR "Błąd karty SD"
|
||||
#define TR_SDCARD "SD Card"
|
||||
#define TR_NO_FILES_ON_SD "No files on SD!"
|
||||
@@ -746,7 +753,7 @@
|
||||
#define TR_BAUDRATE "Baudrate"
|
||||
#define TR_SAMPLE_MODE "Sample Mode"
|
||||
#define TR_SAMPLE_MODES "Normal","OneBit"
|
||||
#define TR_SELECT_TEMPLATE_FOLDER "SELECT A TEMPLATE FOLDER:"
|
||||
#define TR_SELECT_TEMPLATE_FOLDER "Select a template folder"
|
||||
#define TR_SELECT_TEMPLATE "SELECT A MODEL TEMPLATE:"
|
||||
#define TR_NO_TEMPLATES "No model templates were found in this folder"
|
||||
#define TR_SAVE_TEMPLATE "Save as template"
|
||||
@@ -907,6 +914,7 @@
|
||||
#define TR_CHANS "Chans"
|
||||
#define TR_ANTENNAPROBLEM CENTER "Problem z anteną TX"
|
||||
#define TR_MODELIDUSED TR("ID zajęte","ID modelu zajęte")
|
||||
#define TR_MODELIDUNIQUE "ID is unique"
|
||||
#define TR_MODULE "Moduł "
|
||||
#define TR_RX_NAME "Rx Name"
|
||||
#define TR_TELEMETRY_TYPE TR("Typ ", "Typ Telemetrii")
|
||||
|
||||
@@ -330,6 +330,7 @@
|
||||
#define TR_COPYINGMODEL "Copiando modelo"
|
||||
#define TR_MOVINGMODEL "Movendo modelo"
|
||||
#define TR_LOADINGMODEL "Carregar modelo"
|
||||
#define TR_UNLABELEDMODEL "Unlabeled"
|
||||
#define TR_NAME "Nome"
|
||||
#define TR_MODELNAME "Model Name"
|
||||
#define TR_PHASENAME "Phase Name"
|
||||
@@ -628,13 +629,20 @@
|
||||
#define TR_VOLTAGE INDENT"Volts"
|
||||
#define TR_CURRENT INDENT"Amperes"
|
||||
#define TR_SELECT_MODEL "Selec. Modelo"
|
||||
#define TR_MODEL_CATEGORIES "Model Categories"
|
||||
#define TR_MODELS "Models"
|
||||
#define TR_SELECT_MODE "Select mode"
|
||||
#define TR_CREATE_CATEGORY "Create Category"
|
||||
#define TR_RENAME_CATEGORY "Rename Category"
|
||||
#define TR_DELETE_CATEGORY "Delete Category"
|
||||
#define TR_CREATE_MODEL "Criar Modelo"
|
||||
#define TR_FAVORITE_LABEL "Favorites"
|
||||
#define TR_NEW_MODEL "New Model"
|
||||
#define TR_EDIT_LABELS "Edit Labels"
|
||||
#define TR_ENTER_LABEL "Enter Label"
|
||||
#define TR_LABEL "Label"
|
||||
#define TR_LABELS "Labels"
|
||||
#define TR_CURRENT_MODEL "Current"
|
||||
#define TR_NEW "New"
|
||||
#define TR_NEW_LABEL "New Label"
|
||||
#define TR_RENAME_LABEL "Rename Label"
|
||||
#define TR_DELETE_LABEL "Delete Label"
|
||||
#define TR_DUPLICATE_MODEL "Duplicate Model"
|
||||
#define TR_COPY_MODEL "Copy Model"
|
||||
#define TR_MOVE_MODEL "Move Model"
|
||||
@@ -642,7 +650,6 @@
|
||||
#define TR_DELETE_MODEL "Apagar Modelo"
|
||||
#define TR_RESTORE_MODEL "Restaura Modelo"
|
||||
#define TR_DELETE_ERROR "Delete error"
|
||||
#define TR_CAT_NOT_EMPTY "Category is not empty"
|
||||
#define TR_SDCARD_ERROR "SDCARD Erro"
|
||||
#define TR_SDCARD "SD Card"
|
||||
#define TR_NO_FILES_ON_SD "No files on SD!"
|
||||
@@ -751,7 +758,7 @@
|
||||
#define TR_BAUDRATE "Baudrate"
|
||||
#define TR_SAMPLE_MODE "Sample Mode"
|
||||
#define TR_SAMPLE_MODES "Normal","OneBit"
|
||||
#define TR_SELECT_TEMPLATE_FOLDER "SELECT A TEMPLATE FOLDER:"
|
||||
#define TR_SELECT_TEMPLATE_FOLDER "Select a template folder"
|
||||
#define TR_SELECT_TEMPLATE "SELECT A MODEL TEMPLATE:"
|
||||
#define TR_NO_TEMPLATES "No model templates were found in this folder"
|
||||
#define TR_SAVE_TEMPLATE "Save as template"
|
||||
@@ -912,6 +919,7 @@
|
||||
#define TR_CHANS "Chans"
|
||||
#define TR_ANTENNAPROBLEM CENTER "TX Antenna problem!"
|
||||
#define TR_MODELIDUSED TR("ID used in:", "Receiver ID used in:")
|
||||
#define TR_MODELIDUNIQUE "ID is unique"
|
||||
#define TR_MODULE "Module"
|
||||
#define TR_RX_NAME "Rx Name"
|
||||
#define TR_TELEMETRY_TYPE TR("Type", "Telemetry Type")
|
||||
|
||||
+14
-10
@@ -354,6 +354,7 @@
|
||||
#define TR_COPYINGMODEL "Kopierar..."
|
||||
#define TR_MOVINGMODEL "Flyttar..."
|
||||
#define TR_LOADINGMODEL "Laddar..."
|
||||
#define TR_UNLABELEDMODEL "Unlabeled"
|
||||
#define TR_NAME "Namn"
|
||||
#define TR_MODELNAME "Modellnamn"
|
||||
#define TR_PHASENAME "Lägesnamn"
|
||||
@@ -653,13 +654,20 @@
|
||||
#define TR_VOLTAGE INDENT"Volt"
|
||||
#define TR_CURRENT INDENT"Ampere"
|
||||
#define TR_SELECT_MODEL "Välj modell"
|
||||
#define TR_MODEL_CATEGORIES "Modellkategori"
|
||||
#define TR_MODELS "Modeller"
|
||||
#define TR_SELECT_MODE "Select mode"
|
||||
#define TR_CREATE_CATEGORY "Skapa kategori"
|
||||
#define TR_RENAME_CATEGORY "Byt kategorinamn"
|
||||
#define TR_DELETE_CATEGORY "Ta bort kategori"
|
||||
#define TR_CREATE_MODEL "Skapa modell"
|
||||
#define TR_FAVORITE_LABEL "Favorites"
|
||||
#define TR_NEW_MODEL "New Model"
|
||||
#define TR_EDIT_LABELS "Edit Labels"
|
||||
#define TR_ENTER_LABEL "Enter Label"
|
||||
#define TR_LABEL "Label"
|
||||
#define TR_LABELS "Labels"
|
||||
#define TR_CURRENT_MODEL "Current"
|
||||
#define TR_NEW "New"
|
||||
#define TR_NEW_LABEL "New Label"
|
||||
#define TR_RENAME_LABEL "Rename Label"
|
||||
#define TR_DELETE_LABEL "Delete Label"
|
||||
#define TR_DUPLICATE_MODEL "Duplicera modell"
|
||||
#define TR_COPY_MODEL "Kopiera modell"
|
||||
#define TR_MOVE_MODEL "Flytta modell"
|
||||
@@ -667,7 +675,6 @@
|
||||
#define TR_DELETE_MODEL "Ta bort modell"
|
||||
#define TR_RESTORE_MODEL "Återställ modell"
|
||||
#define TR_DELETE_ERROR "Delete error"
|
||||
#define TR_CAT_NOT_EMPTY "Category is not empty"
|
||||
#define TR_SDCARD_ERROR "SD-kortfel"
|
||||
#define TR_SDCARD "SD-kort"
|
||||
#define TR_NO_FILES_ON_SD "Inga filer på SD!"
|
||||
@@ -941,11 +948,8 @@
|
||||
#define TR_TRAINER "Lärare"
|
||||
#define TR_CHANS "Kan"
|
||||
#define TR_ANTENNAPROBLEM CENTER "Fel på TX-antennen"
|
||||
#if defined(COLORLCD)
|
||||
#define TR_MODELIDUSED "ID använt i:"
|
||||
#else
|
||||
#define TR_MODELIDUSED TR("ID använt i:","Mottagar-ID använt i:")
|
||||
#endif
|
||||
#define TR_MODELIDUSED "ID använt i:"
|
||||
#define TR_MODELIDUNIQUE "ID is unique"
|
||||
#define TR_MODULE "Modul"
|
||||
#define TR_RX_NAME "RX namn"
|
||||
#define TR_TELEMETRY_TYPE "Telemetrityp"
|
||||
|
||||
+15
-11
@@ -331,6 +331,7 @@
|
||||
#define TR_COPYINGMODEL "複製模型..."
|
||||
#define TR_MOVINGMODEL "移動模型..."
|
||||
#define TR_LOADINGMODEL "載入模型..."
|
||||
#define TR_UNLABELEDMODEL "Unlabeled"
|
||||
#define TR_NAME "名稱"
|
||||
#define TR_MODELNAME "模型名稱"
|
||||
#define TR_PHASENAME "模式名稱"
|
||||
@@ -626,13 +627,20 @@
|
||||
#define TR_VOLTAGE TR(INDENT "電壓源", INDENT "電壓來源")
|
||||
#define TR_CURRENT TR(INDENT "電流源", INDENT "電流來源")
|
||||
#define TR_SELECT_MODEL "選擇模型"
|
||||
#define TR_MODEL_CATEGORIES "模型分類"
|
||||
#define TR_MODELS "個模型"
|
||||
#define TR_SELECT_MODE "選擇模式"
|
||||
#define TR_CREATE_CATEGORY "創建分類"
|
||||
#define TR_RENAME_CATEGORY "分類改名"
|
||||
#define TR_DELETE_CATEGORY "刪除分類"
|
||||
#define TR_CREATE_MODEL "創建模型"
|
||||
#define TR_FAVORITE_LABEL "Favorites"
|
||||
#define TR_NEW_MODEL "New Model"
|
||||
#define TR_EDIT_LABELS "Edit Labels"
|
||||
#define TR_ENTER_LABEL "Enter Label"
|
||||
#define TR_LABEL "Label"
|
||||
#define TR_LABELS "Labels"
|
||||
#define TR_CURRENT_MODEL "Current"
|
||||
#define TR_NEW "New"
|
||||
#define TR_NEW_LABEL "New Label"
|
||||
#define TR_RENAME_LABEL "Rename Label"
|
||||
#define TR_DELETE_LABEL "Delete Label"
|
||||
#define TR_DUPLICATE_MODEL "複製模型"
|
||||
#define TR_COPY_MODEL "複製模型"
|
||||
#define TR_MOVE_MODEL "移動模型"
|
||||
@@ -640,7 +648,6 @@
|
||||
#define TR_DELETE_MODEL "刪除模型"
|
||||
#define TR_RESTORE_MODEL "恢復模型"
|
||||
#define TR_DELETE_ERROR "刪除失敗"
|
||||
#define TR_CAT_NOT_EMPTY "分類未清空"
|
||||
#define TR_SDCARD_ERROR TR("存儲卡錯誤", "存儲卡錯誤")
|
||||
#define TR_SDCARD "存儲卡"
|
||||
#define TR_NO_FILES_ON_SD "存儲卡中沒有文件!"
|
||||
@@ -911,11 +918,8 @@
|
||||
#define TR_TRAINER "教練"
|
||||
#define TR_CHANS "通道"
|
||||
#define TR_ANTENNAPROBLEM CENTER "發射機天線故障!"
|
||||
#if defined(COLORLCD)
|
||||
#define TR_MODELIDUSED "ID已使用:"
|
||||
#else
|
||||
#define TR_MODELIDUSED TR("ID已使用:","ID已使用:")
|
||||
#endif
|
||||
#define TR_MODELIDUSED "ID已使用:"
|
||||
#define TR_MODELIDUNIQUE "ID is unique"
|
||||
#define TR_MODULE "模塊"
|
||||
#define TR_RX_NAME "接收機名稱"
|
||||
#define TR_TELEMETRY_TYPE TR("類型", "回傳類型")
|
||||
@@ -1334,4 +1338,4 @@
|
||||
#define TR_PROTOCOL "協議"
|
||||
#define TR_YEAR "年"
|
||||
#define TR_MONTH "月"
|
||||
#define TR_DAY "日"
|
||||
#define TR_DAY "日"
|
||||
|
||||
@@ -371,3 +371,12 @@
|
||||
#define STR_HOTT_ID_EAM_VV "EAvv"
|
||||
#define STR_HOTT_ID_EAM_RPM "EArp"
|
||||
#define STR_HOTT_ID_EAM_SPEED "EAsp"
|
||||
|
||||
#define TR_ENTER_LABEL "Enter Label"
|
||||
#define TR_LABEL "Label"
|
||||
#define TR_NONE "None"
|
||||
#define TR_CURRENT_MODEL "Current"
|
||||
#define TR_NEW "New"
|
||||
#define TR_NEW_LABEL "New Label"
|
||||
#define TR_RENAME_LABEL "Rename Label"
|
||||
#define TR_DELETE_LABEL "Delete Label"
|
||||
|
||||
Reference in New Issue
Block a user