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

commit dc99dd40d2
Author: 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

commit 4b747caff9
Author: 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.

commit e97f23a305
Author: 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:
Cliff
2022-09-03 09:01:19 +10:00
committed by GitHub
co-authored by HThuren
parent 20dc9c4d26
commit c8693569b0
85 changed files with 4006 additions and 2067 deletions
+2
View File
@@ -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
+1 -1
View File
@@ -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;
}
}
+1 -1
View File
@@ -153,7 +153,7 @@ enum Capability {
HasMixerExpo,
HasBatMeterRange,
DangerousFunctions,
HasModelCategories,
HasModelLabels,
HasSwitchableJack,
HasSportConnector,
PwrButtonPress,
+1 -1
View File
@@ -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);
+101 -7
View File
@@ -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;
}
+10 -10
View File
@@ -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();
+276
View File
@@ -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;
}
+54
View File
@@ -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
View File
@@ -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
View File
@@ -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;
};
+20 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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;
};
+1 -1
View File
@@ -10,7 +10,7 @@ set(storage_NAMES
bineeprom
eepe
hexeeprom
categorized
labeled
sdcard
etx
otx
+2 -2
View File
@@ -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;
+3 -3
View File
@@ -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)
{
}
+2 -2
View File
@@ -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;
+3 -3
View File
@@ -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)
{
}
+1 -1
View File
@@ -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)
+3 -3
View File
@@ -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)
{
}
+5 -5
View File
@@ -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;
+1 -1
View File
@@ -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
View File
@@ -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

+3
View File
@@ -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
+4 -4
View File
@@ -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
+3
View File
@@ -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)
+1
View File
@@ -389,6 +389,7 @@ enum DebugTimers {
debugTimerAudioIterval,
debugTimerAudioDuration,
debugTimerAudioConsume,
debugTimerYamlScan,
DEBUG_TIMERS_COUNT
};
+1
View File
@@ -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
+1 -1
View File
@@ -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;
+41 -2
View File
@@ -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);
+15 -1
View File
@@ -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
+93 -5
View File
@@ -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_
+25 -2
View File
@@ -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);
+2
View File
@@ -26,4 +26,6 @@ class ModelSetupPage: public PageTab {
ModelSetupPage();
void build(FormWindow * window) override;
private:
TextButton *labelTextButton = nullptr;
};
+260
View File
@@ -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();
}
+81
View File
@@ -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();
};
+39 -7
View File
@@ -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
+1 -1
View File
@@ -240,7 +240,7 @@ void ViewMain::onEvent(event_t event)
break;
case EVT_KEY_LONG(KEY_MODEL):
new ModelSelectMenu();
new ModelLabelsWindow();
break;
// TODO:
+1 -1
View File
@@ -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;
});
+1
View File
@@ -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
View File
@@ -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";
+22 -41
View File
@@ -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
File diff suppressed because it is too large Load Diff
+172 -88
View File
@@ -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_
+16 -8
View File
@@ -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
}
-2
View File
@@ -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"
-12
View File
@@ -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
+6 -23
View File
@@ -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);
+1
View File
@@ -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");
+3 -2
View File
@@ -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
};
+301
View File
@@ -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
-153
View File
@@ -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;
+14 -5
View File
@@ -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;
+17 -5
View File
@@ -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
View File
@@ -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 "日"
+13 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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"
+14 -6
View File
@@ -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")
+13 -5
View File
@@ -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")
+14 -6
View File
@@ -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")
+13 -5
View File
@@ -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"
+14 -6
View File
@@ -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")
+14 -6
View File
@@ -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")
+14 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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 "日"
+9
View File
@@ -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"