diff --git a/companion/src/firmwares/modeldata.h b/companion/src/firmwares/modeldata.h index 8b32eb526..d13090677 100644 --- a/companion/src/firmwares/modeldata.h +++ b/companion/src/firmwares/modeldata.h @@ -46,6 +46,7 @@ class AbstractStaticItemModel; constexpr char AIM_MODELDATA_TRAINERMODE[] {"modeldata.trainermode"}; constexpr char AIM_MODELDATA_FUNCSWITCHCONFIG[] {"modeldata.funcswitchconfig"}; constexpr char AIM_MODELDATA_FUNCSWITCHSTART[] {"modeldata.funcswitchstart"}; +constexpr int LABEL_LENGTH=16; #define CHAR_FOR_NAMES " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-." #define CHAR_FOR_NAMES_REGEX "[ A-Za-z0-9_.-,]*" diff --git a/companion/src/firmwares/radiodata.cpp b/companion/src/firmwares/radiodata.cpp index 4de3c4a20..2d72339a5 100644 --- a/companion/src/firmwares/radiodata.cpp +++ b/companion/src/firmwares/radiodata.cpp @@ -106,33 +106,43 @@ void RadioData::convert(RadioDataConversionState & cstate) void RadioData::addLabel(QString label) { - label.replace("/c",","); - label.replace("//","/"); - if(labels.indexOf(label) == -1) + label = unEscapeCSV(label); + // Truncate possible UTF-8 to 16char maximum + QByteArray output = label.toUtf8(); + if (output.size() > LABEL_LENGTH) { + int truncateAt = 0; + for (int i = LABEL_LENGTH; i > 0; i--) { + if ((output[i] & 0xC0) != 0x80) { + truncateAt = i; + break; + } + } + output.truncate(truncateAt); + } + label = QString(output); + if (labels.indexOf(label) == -1) labels.append(label); } bool RadioData::deleteLabel(QString label) { bool deleted = false; - QString csvLabel = label; - csvLabel.replace("/","//"); - csvLabel.replace(",","/c"); // Remove labels in the models for(auto& model : models) { - QStringList modelLabels = QString(model.labels).split(',',QString::SkipEmptyParts); - if(modelLabels.indexOf(csvLabel) >= 0) + QStringList modelLabels = fromCSV(QString::fromUtf8(model.labels)); + if (modelLabels.indexOf(label) >= 0) { deleted = true; - modelLabels.removeAll(csvLabel); - strcpy(model.labels, QString(modelLabels.join(',')).toLocal8Bit().data()); + modelLabels.removeAll(label); + } + strcpy(model.labels, toCSV(modelLabels).toUtf8().data()); } // Remove the label from the global list labels.removeAll(label); // If no labels remain, add a Favorites one - if(!labels.size()) { + if (!labels.size()) { addLabel(tr("Favorites")); } return deleted; @@ -140,42 +150,46 @@ bool RadioData::deleteLabel(QString label) bool RadioData::deleteLabel(int index) { - if(index >= labels.size()) return false; + 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(); - QString csvFrom = from; - QString csvTo = to; - csvFrom.replace("/","//"); - csvFrom.replace(",","/c"); - csvTo.replace("/","//"); - csvTo.replace(",","/c"); + bool success = true; + QString csvFrom = escapeCSV(from); + QString csvTo = escapeCSV(to); + int lengthdiff = csvTo.size() - csvFrom.size(); - - // Check that rename is possible, not too long + // Check that rename is possible (Rename won't cause too long of a string) for(auto& model : models) { - if((int)strlen(model.labels) + lengthdiff > (int)sizeof(model.labels) - 1) { - success = false; + if (RadioData::fromCSV(model.labels).indexOf(from) != -1) { + if ((int)strlen(model.labels) + lengthdiff > (int)sizeof(model.labels) - 1) { + success = false; + throw std::length_error(model.name); + break; + } } } - if(success) { + if (success) { for(auto& model : models) { QStringList modelLabels = QString(model.labels).split(',',QString::SkipEmptyParts); int ind = modelLabels.indexOf(csvFrom); - if(ind != -1) { + if (ind != -1) { modelLabels.replace(ind, csvTo); QString outputcsv = QString(modelLabels.join(',')); - if(outputcsv.toLocal8Bit().size() < (int)sizeof(model.labels)); - strcpy(model.labels, outputcsv.toLocal8Bit().data()); + if (outputcsv.toUtf8().size() < (int)sizeof(model.labels)) { + strcpy(model.labels, outputcsv.toUtf8().data()); + } else { // Shouldn't ever get here, from check above + success = false; + throw std::length_error(model.name); + break; + } } } int ind = labels.indexOf(from); - if(ind != -1) { + if (ind != -1) { labels.replace(ind, to); } } @@ -184,42 +198,51 @@ bool RadioData::renameLabel(QString from, QString to) bool RadioData::renameLabel(int index, QString to) { - if(index >= labels.size()) return false; + if (index >= labels.size()) return false; QString from = labels.at(index); return renameLabel(from, to); } +void RadioData::swapLabel(int indFrom, int indTo) +{ + if(abs(indFrom - indTo) > 1 || + indFrom >= labels.size() || + indTo >= labels.size() || + indFrom < 0 || + indTo < 0) + return; + QString tmplbl = labels.at(indFrom); + labels.replace(indFrom, labels.at(indTo)); + labels.replace(indTo, tmplbl); +} + bool RadioData::addLabelToModel(int index, QString label) { - if(index >= models.size()) return false; - - label.replace("/","//"); - label.replace(",","/c"); + if (index >= models.size()) return false; + label = escapeCSV(label); char *modelLabelCsv = models[index].labels; // Make sure it will fit - if(strlen(modelLabelCsv) + label.size() + 1 < sizeof(models[index].labels)-1) { - QStringList modelLabels = QString(modelLabelCsv).split(',',QString::SkipEmptyParts); - if(modelLabels.indexOf(label) == -1) { + if (strlen(modelLabelCsv) + label.size() + 1 < sizeof(models[index].labels)-1) { + QStringList modelLabels = QString::fromUtf8(modelLabelCsv).split(',',QString::SkipEmptyParts); + if (modelLabels.indexOf(label) == -1) { modelLabels.append(label); - strcpy(models[index].labels, QString(modelLabels.join(',')).toLocal8Bit().data()); + strcpy(models[index].labels, QString(modelLabels.join(',')).toUtf8().data()); return true; } } + throw std::length_error(models[index].name); return false; } bool RadioData::removeLabelFromModel(int index, QString label) { - if(index >= models.size()) return false; - label.replace("/","//"); - label.replace(",","/c"); + if (index >= models.size()) return false; - char *modelLabelCsv = models[index].labels; - QStringList modelLabels = QString(modelLabelCsv).split(',',QString::SkipEmptyParts); - if(modelLabels.indexOf(label) >= 0) { - modelLabels.removeAll(label); - strcpy(models[index].labels, QString(modelLabels.join(',')).toLocal8Bit().data()); + QStringList lbls = fromCSV(QString::fromUtf8(models[index].labels)); + if (lbls.indexOf(label) >= 0) { + lbls.removeAll(label); + strcpy(models[index].labels, toCSV(lbls).toUtf8().data()); return true; } return false; @@ -234,3 +257,34 @@ void RadioData::addLabelsFromModels() } } } + +QStringList RadioData::fromCSV(const QString &csv) +{ + QStringList lbls = QString(csv).split(',',QString::SkipEmptyParts); + for(QString &label: lbls) { + label = unEscapeCSV(label); + } + return lbls; +} + +QString RadioData::toCSV(QStringList lbls) +{ + for(QString &label: lbls) { + label = escapeCSV(label); + } + return lbls.join(','); +} + +QString RadioData::escapeCSV(QString str) +{ + str.replace("/","//"); + str.replace(",","/c"); + return str; +} + +QString RadioData::unEscapeCSV(QString str) +{ + str.replace("/c",","); + str.replace("//","/"); + return str; +} diff --git a/companion/src/firmwares/radiodata.h b/companion/src/firmwares/radiodata.h index ff672d1f4..26d291113 100644 --- a/companion/src/firmwares/radiodata.h +++ b/companion/src/firmwares/radiodata.h @@ -27,10 +27,16 @@ class RadioData { bool deleteLabel(int index); bool renameLabel(QString from, QString to); bool renameLabel(int index, QString to); + void swapLabel(int indFrom, int indTo); bool addLabelToModel(int index, QString label); bool removeLabelFromModel(int index, QString label); void addLabelsFromModels(); + static QStringList fromCSV(const QString &csv); + static QString toCSV(QStringList lbls); + static QString escapeCSV(QString str); + static QString unEscapeCSV(QString str); + void setCurrentModel(unsigned int index); void fixModelFilenames(); QString getNextModelFilename(); @@ -41,4 +47,5 @@ class RadioData { protected: void fixModelFilename(unsigned int index); + }; diff --git a/companion/src/labels.cpp b/companion/src/labels.cpp index 97880d8fa..9980d38e4 100644 --- a/companion/src/labels.cpp +++ b/companion/src/labels.cpp @@ -22,12 +22,12 @@ Qt::ItemFlags LabelsModel::flags(const QModelIndex &index) const { Qt::ItemFlags flags = Qt::ItemIsEnabled; - if(index.column() == 0) { + if (index.column() == 0) { flags |= Qt::ItemIsUserCheckable; flags |= Qt::ItemIsSelectable; flags |= Qt::ItemIsDropEnabled; flags |= Qt::ItemIsEditable; - if(index.isValid()) + if (index.isValid()) flags |= Qt::ItemIsDragEnabled; } return flags; @@ -35,33 +35,48 @@ Qt::ItemFlags LabelsModel::flags(const QModelIndex &index) const bool LabelsModel::setData(const QModelIndex &index, const QVariant &value, int role) { - if(!index.isValid() || index.column() != 0 || + if (!index.isValid() || index.column() != 0 || index.row() >= radioData->labels.size()) return false; - if(role == Qt::CheckStateRole) { - if(value==Qt::Unchecked && selectedModel != -1) { - if(radioData->removeLabelFromModel(selectedModel, radioData->labels.at(index.row()))) + if (role == Qt::CheckStateRole) { + if (value==Qt::Unchecked && selectedModel != -1) { + if (radioData->removeLabelFromModel(selectedModel, radioData->labels.at(index.row()))) emit modelChanged(selectedModel); } else { - if(radioData->addLabelToModel(selectedModel, radioData->labels.at(index.row()))) + try{ + if (radioData->addLabelToModel(selectedModel, radioData->labels.at(index.row()))) emit modelChanged(selectedModel); + } catch(const std::length_error& le) { + emit labelsFault(tr("Unable to add label \"%1\" to model \"%2\" not enough room")\ + .arg(radioData->labels.at(index.row())).arg(le.what())); + } } emit dataChanged(this->index(index.row(), 0), this->index(index.row(), 0)); return true; - } else if(role == Qt::EditRole) { + } else if (role == Qt::EditRole) { QString replFrom = labels[index.row()].label; QString replTo = value.toString(); - if(radioData->labels.indexOf(replTo) == -1) { // Don't allow duplicates - bool modelsChanged = radioData->renameLabel(replFrom,replTo); + if (replFrom == replTo) // User exits edit without changing + return true; + if (radioData->labels.indexOf(replTo) == -1) { // Don't allow duplicates + bool modelsChanged = false; + try{ + modelsChanged = radioData->renameLabel(replFrom,replTo); + } catch(const std::length_error& le) { + emit labelsFault(tr("Unable to rename \"%1\" to \"%2\" not enough room in model %3")\ + .arg(replFrom).arg(replTo).arg(le.what())); + return false; + } labels[index.row()].label = replTo; emit dataChanged(this->index(index.row(), 0), this->index(index.row(), 0)); - if(selectedModel != -1 && modelsChanged) + if (selectedModel != -1 && modelsChanged) emit modelChanged(selectedModel); } else { - emit renameFault(tr("Unable to rename \"%1\" to \"%2\" the label already exists").arg(replFrom).arg(replTo)); + emit labelsFault(tr("Unable to rename \"%1\" to \"%2\" the label already exists")\ + .arg(replFrom).arg(replTo)); } return true; } @@ -70,22 +85,21 @@ bool LabelsModel::setData(const QModelIndex &index, const QVariant &value, int r QVariant LabelsModel::data(const QModelIndex &index, int role) const { - if(index.row() >= 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) { + 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 && + if (index.column() == 0 && selectedModel >= 0 && selectedModel < (int)radioData->models.size()) { QStringList modelLabels = QString(radioData->models.at(selectedModel).labels).split(',',QString::SkipEmptyParts); - label.replace("/","//"); - label.replace(",","/c"); + label = RadioData::escapeCSV(label); return modelLabels.indexOf(label)==-1?Qt::Unchecked:Qt::Checked; } else if (index.column() == 0 && selectedModel == -1) { return Qt::Unchecked; @@ -98,7 +112,7 @@ QModelIndex LabelsModel::index(int row, int column, const QModelIndex &parent) c { Q_UNUSED(parent) - if(row < labels.size()) + if (row < labels.size()) return createIndex(row,0); return QModelIndex(); @@ -123,94 +137,13 @@ int LabelsModel::columnCount(const QModelIndex &parent) const QVariant LabelsModel::headerData(int section, Qt::Orientation orientation, int role) const { - if(role != Qt::DisplayRole || + 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()) @@ -218,16 +151,16 @@ bool LabelsModel::insertRows(int row, int count, const QModelIndex &parent) // Find a New Number for (int i = 0; i < count; i++) { - int newno=0; - QString newstr; + int newNo=0; + QString newStr; do { - newstr = QString(tr("New%1").arg(newno)); - if(newno == 0) - newstr = QString(tr("New")); - newno++; - } while(radioData->labels.indexOf(newstr) >= 0); + newStr = QString(tr("New%1").arg(newNo)); + if (newNo == 0) + newStr = QString(tr("New")); + newNo++; + } while(radioData->labels.indexOf(newStr) >= 0); // Add it to radioData - radioData->labels.insert(row+i, newstr); + radioData->labels.insert(row+i, newStr); } buildLabelsList(); return true; @@ -240,12 +173,12 @@ bool LabelsModel::removeRows(int row, int count, const QModelIndex &parent) bool deleted=false; beginRemoveRows(parent, row, row + count - 1); for (int i = 0; i != count; ++i) - if(radioData->deleteLabel(row+i)) { + if (radioData->deleteLabel(row+i)) { deleted = true; } endRemoveRows(); // Refresh all - if(deleted) + if (deleted) emit modelChanged(-1); buildLabelsList(); return true; @@ -273,7 +206,7 @@ void LabelsModel::modelsSelectionChanged() QModelIndex index = modelsSelection->currentIndex(); if (index.isValid()) { int mi = modelsSelection->currentIndex().row(); - if(mi < (int)radioData->models.size()) { + if (mi < (int)radioData->models.size()) { selectedModel = mi; buildLabelsList(); } @@ -281,3 +214,41 @@ void LabelsModel::modelsSelectionChanged() else selectedModel = -1; } + +QValidator::State LabelValidator::validate(QString &label, int &pos) const +{ + Q_UNUSED(pos) + QString lbl = RadioData::escapeCSV(label); + if (lbl.toUtf8().size() > LABEL_LENGTH) + return QValidator::Invalid; + if(lbl.contains('\\') || // TODO: Fix me to allow all, requires FW changes + lbl.contains('\"') || + lbl.contains(':') || + lbl.contains('-') || + lbl.contains('\'')) + return QValidator::Invalid; + if(lbl.size() == 0) + return QValidator::Intermediate; + return QValidator::Acceptable; +} + +void LabelValidator::fixup(QString &input) const +{ + QByteArray output = input.toUtf8(); + if (output.size() > LABEL_LENGTH) { + int truncateAt = 0; + for (int i = LABEL_LENGTH; i > 0; i--) { + if ((output[i] & 0xC0) != 0x80) { + truncateAt = i; + break; + } + } + output.truncate(truncateAt); + } + input = QString(output); + input.remove('\\'); // TODO: Fix me to allow all, requires FW changes + input.remove('\"'); + input.remove(':'); + input.remove('-'); + input.remove('\''); +} diff --git a/companion/src/labels.h b/companion/src/labels.h index 3d836ba8d..44377b478 100644 --- a/companion/src/labels.h +++ b/companion/src/labels.h @@ -4,6 +4,9 @@ #include #include #include +#include +#include +#include #include "radiodata.h" @@ -29,11 +32,6 @@ public: 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; @@ -45,7 +43,7 @@ private slots: signals: void modelChanged(int index); - void renameFault(QString msg); + void labelsFault(QString msg); private: QItemSelectionModel *modelsSelection; @@ -54,3 +52,27 @@ private: QList modelIndices; QList labels; }; + +class LabelValidator : public QValidator +{ + Q_OBJECT +public: + QValidator::State validate(QString &label, int &pos) const; + void fixup(QString &input) const; +}; + +class LabelEditTextDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + LabelEditTextDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) {} + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const + { + QLineEdit *editor = new QLineEdit(parent); + editor->setValidator(new LabelValidator); + return editor; + } +}; diff --git a/companion/src/mdichild.cpp b/companion/src/mdichild.cpp index 1718314ae..7f13d6e4a 100644 --- a/companion/src/mdichild.cpp +++ b/companion/src/mdichild.cpp @@ -221,7 +221,10 @@ void MdiChild::setupNavigation() 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_LBL_DEL, "clear.png", SLOT(labelDelete()), tr("Alt-L")); + addAct(ACT_LBL_REN, "edit.png", SLOT(labelRename()), tr("Alt-R")); + addAct(ACT_LBL_MVU, "moveup.png", SLOT(labelMoveUp()), tr("Alt-+")); + addAct(ACT_LBL_MVD, "movedown.png", SLOT(labelMoveDown()), tr("Alt--")); addAct(ACT_MDL_DFT, "currentmodel.png", SLOT(setDefault()), tr("Alt+U")); addAct(ACT_MDL_PRT, "print.png", SLOT(print()), QKeySequence::Print); @@ -247,7 +250,7 @@ void MdiChild::setupNavigation() // Add labels Label to bottom layout if(lblLabels) lblLabels->deleteLater(); - lblLabels = new QLabel(tr("Labels")); + lblLabels = new QLabel(tr("Labels Management")); lblLabels->setStyleSheet("font-weight: bold"); ui->bottomLayout->addWidget(lblLabels); @@ -261,6 +264,9 @@ void MdiChild::setupNavigation() labelsToolbar->setStyleSheet(tbCss); labelsToolbar->addAction(getAction(ACT_LBL_ADD)); labelsToolbar->addAction(getAction(ACT_LBL_DEL)); + labelsToolbar->addAction(getAction(ACT_LBL_REN)); + labelsToolbar->addAction(getAction(ACT_LBL_MVU)); + labelsToolbar->addAction(getAction(ACT_LBL_MVD)); ui->bottomLayout->addWidget(labelsToolbar); if (radioToolbar) @@ -353,8 +359,11 @@ void MdiChild::retranslateUi() 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_LBL_ADD]->setText(tr("Add")); + action[ACT_LBL_DEL]->setText(tr("Delete")); + action[ACT_LBL_REN]->setText(tr("Rename")); + action[ACT_LBL_MVU]->setText(tr("Move Up")); + action[ACT_LBL_MVD]->setText(tr("Move Down")); action[ACT_MDL_ADD]->setText(tr("Add Model")); action[ACT_MDL_ADD]->setIconText(tr("Model")); @@ -369,6 +378,7 @@ void MdiChild::retranslateUi() radioToolbar->setWindowTitle(tr("Show Radio Actions Toolbar")); modelsToolbar->setWindowTitle(tr("Show Model Actions Toolbar")); + labelsToolbar->setWindowTitle(tr("Show Labels Actions Toolbar")); } QList MdiChild::getGeneralActions() @@ -416,6 +426,9 @@ QList MdiChild::getLabelsActions() QList actGrp; actGrp.append(getAction(ACT_LBL_ADD)); actGrp.append(getAction(ACT_LBL_DEL)); + actGrp.append(getAction(ACT_LBL_REN)); + actGrp.append(getAction(ACT_LBL_MVU)); + actGrp.append(getAction(ACT_LBL_MVD)); return actGrp; } @@ -457,6 +470,9 @@ void MdiChild::showLabelsContextMenu(const QPoint &pos) contextMenu.addAction(action[ACT_LBL_ADD]); contextMenu.addAction(action[ACT_LBL_DEL]); + contextMenu.addAction(action[ACT_LBL_REN]); + contextMenu.addAction(action[ACT_LBL_MVU]); + contextMenu.addAction(action[ACT_LBL_MVD]); if (!contextMenu.isEmpty()) contextMenu.exec(ui->lstLabels->mapToGlobal(pos)); @@ -467,6 +483,8 @@ void MdiChild::showContextMenu(const QPoint & pos) QMenu contextMenu; contextMenu.addAction(modelsToolbar->toggleViewAction()); contextMenu.addAction(radioToolbar->toggleViewAction()); + if(firmware->getCapability(Capability::HasModelLabels)) + contextMenu.addAction(labelsToolbar->toggleViewAction()); if (!contextMenu.isEmpty()) contextMenu.exec(mapToGlobal(pos)); } @@ -509,15 +527,11 @@ void MdiChild::initModelsList() labelsListModel = new LabelsModel(ui->modelsList->selectionModel(), &radioData, this); connect(labelsListModel, &LabelsModel::modelChanged, this, &MdiChild::modelLabelsChanged); - connect(labelsListModel, &LabelsModel::renameFault, this, &MdiChild::labelRenameFault); + connect(labelsListModel, &LabelsModel::labelsFault, this, &MdiChild::labelsFault); 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->lstLabels->setItemDelegate(new LabelEditTextDelegate); ui->modelsList->setIndentation(0); @@ -1634,23 +1648,56 @@ void MdiChild::modelExport() void MdiChild::labelAdd() { labelsListModel->insertRow(0); + QModelIndex newind = labelsListModel->index(0,0); + ui->lstLabels->setCurrentIndex(newind); + ui->lstLabels->edit(newind); setWindowModified(true); } void MdiChild::labelDelete() { int row = ui->lstLabels->selectionModel()->currentIndex().row(); + if(row < 0) return; labelsListModel->removeRow(row); setWindowModified(true); } -void MdiChild::modelLabelsChanged() +void MdiChild::labelRename() +{ + int row = ui->lstLabels->selectionModel()->currentIndex().row(); + if(row < 0) return; + QModelIndex newind = labelsListModel->index(row,0); + ui->lstLabels->setCurrentIndex(newind); + ui->lstLabels->edit(newind); +} + +void MdiChild::labelMoveUp() +{ + int row = ui->lstLabels->selectionModel()->currentIndex().row(); + if(row == 0) return; + radioData.swapLabel(row, row-1); + labelsListModel->buildLabelsList(); + ui->lstLabels->selectionModel()->setCurrentIndex(labelsListModel->index(row-1,0), QItemSelectionModel::ClearAndSelect); +} + +void MdiChild::labelMoveDown() +{ + int row = ui->lstLabels->selectionModel()->currentIndex().row(); + if(row == labelsListModel->rowCount() -1) return; + radioData.swapLabel(row, row+1); + labelsListModel->buildLabelsList(); + ui->lstLabels->selectionModel()->setCurrentIndex(labelsListModel->index(row+1,0), QItemSelectionModel::ClearAndSelect); +} + +void MdiChild::modelLabelsChanged(int row) { setWindowModified(true); refresh(); + ui->modelsList->selectionModel()->select(modelsListModel->index(row,0), QItemSelectionModel::ClearAndSelect | + QItemSelectionModel::Rows); } -void MdiChild::labelRenameFault(QString msg) +void MdiChild::labelsFault(QString msg) { QMessageBox::warning(this, CPN_STR_TTL_WARNING, msg); } diff --git a/companion/src/mdichild.h b/companion/src/mdichild.h index 5e67b1939..4a5293b32 100644 --- a/companion/src/mdichild.h +++ b/companion/src/mdichild.h @@ -56,6 +56,9 @@ class MdiChild : public QWidget ACT_ITM_DEL, // delete model or cat ACT_LBL_ADD, ACT_LBL_DEL, + ACT_LBL_MVU, // Move up + ACT_LBL_MVD, // Move down + ACT_LBL_REN, // Move down ACT_MDL_ADD, // model actions... ACT_MDL_CPY, ACT_MDL_CUT, @@ -140,8 +143,11 @@ class MdiChild : public QWidget void modelExport(); void labelAdd(); void labelDelete(); - void modelLabelsChanged(); - void labelRenameFault(QString msg); + void labelRename(); + void labelMoveUp(); + void labelMoveDown(); + void modelLabelsChanged(int row); + void labelsFault(QString msg); void wizardEdit(); void modelDuplicate(); diff --git a/companion/src/modelslist.cpp b/companion/src/modelslist.cpp index 82f4ac6f1..d9c1a65bc 100644 --- a/companion/src/modelslist.cpp +++ b/companion/src/modelslist.cpp @@ -682,7 +682,8 @@ void ModelsListModel::refresh() current->setData(currentColumn++, rxs); } if (hasLabels) { - current->setData(currentColumn++, QString(model.labels)); + QStringList labels = RadioData::fromCSV(QString::fromUtf8(model.labels)); + current->setData(currentColumn++, labels.join(QChar(0x2022))); } } } diff --git a/radio/src/dataconstants.h b/radio/src/dataconstants.h index 7eb4a7041..7f7f54a6b 100644 --- a/radio/src/dataconstants.h +++ b/radio/src/dataconstants.h @@ -38,7 +38,7 @@ #endif #define LABELS_LENGTH 100 // Maximum length of the label string -#define LABEL_LENGTH 20 // Maximum length of a single label +#define LABEL_LENGTH 16 #if defined(PCBHORUS) || defined(PCBNV14) #define MAX_MODELS 60 diff --git a/radio/src/gui/colorlcd/model_select.cpp b/radio/src/gui/colorlcd/model_select.cpp index ab11242ce..1c5d82956 100644 --- a/radio/src/gui/colorlcd/model_select.cpp +++ b/radio/src/gui/colorlcd/model_select.cpp @@ -383,7 +383,6 @@ void ModelsPageBody::selectModel(ModelCell *model) storageCheck(true); modelslist.setCurrentModel(model); - checkAll(); // Exit to main view auto w = Layer::back(); @@ -496,8 +495,8 @@ class LabelDialog : public Dialog Dialog(parent, STR_ENTER_LABEL, rect_t{}), saveHandler(std::move(_saveHandler)) { - strncpy(this->label, label, MAX_LABEL_SIZE); - this->label[MAX_LABEL_SIZE] = '\0'; + strncpy(this->label, label, LABEL_LENGTH); + this->label[LABEL_LENGTH] = '\0'; auto form = &content->form; form->padRow(lv_dpx(8)); @@ -505,7 +504,7 @@ class LabelDialog : public Dialog auto form_obj = form->getLvObj(); lv_obj_set_style_flex_cross_place(form_obj, LV_FLEX_ALIGN_CENTER, 0); - new TextEdit(form, rect_t{}, label, MAX_LABEL_SIZE); + new TextEdit(form, rect_t{}, label, LABEL_LENGTH); auto box = new FormGroup(form, rect_t{}); box->setFlexLayout(LV_FLEX_FLOW_ROW); @@ -538,7 +537,7 @@ class LabelDialog : public Dialog protected: std::function saveHandler; - char label[MAX_LABEL_SIZE + 1]; + char label[LABEL_LENGTH + 1]; }; //----------------------------------------------------------------------------- @@ -625,11 +624,9 @@ void ModelLabelsWindow::newModel() // On complete update the current cell's data modelslist.updateCurrentModelCell(); - auto labels = getLabels(); - lblselector->setNames(labels); - lblselector->setSelected(modelslabels.getLabels().size()); - mdlselector->update(0); - setTitle(); + // Close Window + auto w = Layer::back(); + if (w) w->onCancel(); }); } @@ -778,8 +775,8 @@ void ModelLabelsWindow::buildBody(FormWindow *window) menu->setTitle(selectedLabel); menu->addLine(STR_RENAME_LABEL, [=]() { auto oldLabel = labels[selected]; - strncpy(tmpLabel, oldLabel.c_str(), MAX_LABEL_SIZE); - tmpLabel[MAX_LABEL_SIZE] = '\0'; + strncpy(tmpLabel, oldLabel.c_str(), LABEL_LENGTH); + tmpLabel[LABEL_LENGTH] = '\0'; new LabelDialog(this, tmpLabel, [=](std::string newLabel) { auto rndialog = new ProgressDialog(this, STR_RENAME_LABEL, [=]() {}); diff --git a/radio/src/gui/colorlcd/model_select.h b/radio/src/gui/colorlcd/model_select.h index 619a1424d..ab1e0e01b 100644 --- a/radio/src/gui/colorlcd/model_select.h +++ b/radio/src/gui/colorlcd/model_select.h @@ -30,8 +30,6 @@ #include "storage/modelslist.h" #include "tabsgroup.h" -constexpr int MAX_LABEL_SIZE = 20; - class ModelButton; class ModelsPageBody : public FormWindow @@ -78,7 +76,7 @@ class ModelLabelsWindow : public Page protected: ModelsSortBy sort = DEFAULT_MODEL_SORT; - char tmpLabel[MAX_LABEL_SIZE + 1] = "\0"; + char tmpLabel[LABEL_LENGTH + 1] = "\0"; ListBox *lblselector; ModelsPageBody *mdlselector; std::string currentLabel; diff --git a/radio/src/gui/colorlcd/model_setup.cpp b/radio/src/gui/colorlcd/model_setup.cpp index 49d0238d1..fd8ae2fc1 100644 --- a/radio/src/gui/colorlcd/model_setup.cpp +++ b/radio/src/gui/colorlcd/model_setup.cpp @@ -127,7 +127,7 @@ void SubScreenButton::event_cb(lv_event_t* e) auto btn = (SubScreenButton*)lv_obj_get_user_data(obj); if (!btn) return; - lv_event_code_t code = lv_event_get_code(e); + lv_event_code_t code = lv_event_get_code(e); if (code == LV_EVENT_DRAW_PART_BEGIN) { lv_obj_draw_part_dsc_t* dsc = lv_event_get_draw_part_dsc(e); @@ -142,7 +142,7 @@ void SubScreenButton::event_cb(lv_event_t* e) } else if (code == LV_EVENT_DRAW_PART_END) { if (btn->text.empty()) return; - + lv_obj_draw_part_dsc_t* dsc = lv_event_get_draw_part_dsc(e); if (dsc->part != LV_PART_MAIN) return; @@ -243,7 +243,7 @@ void ModelSetupPage::build(FormWindow * window) 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), [=] () { + new TextButton(line, rect_t{}, modelslabels.getBulletLabelString(curmod ,STR_UNLABELEDMODEL), [=] () { Menu *menu = new Menu(window, true); menu->setTitle(STR_LABELS); for (auto &label: modelslabels.getLabels()) { @@ -253,8 +253,9 @@ void ModelSetupPage::build(FormWindow * window) 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()); + labelTextButton->setText(modelslabels.getBulletLabelString(curmod,STR_UNLABELEDMODEL)); + strncpy(g_model.header.labels, ModelMap::toCSV(modelslabels.getLabelsByModel(curmod)).c_str(),sizeof(g_model.header.labels)); + g_model.header.labels[sizeof(g_model.header.labels)-1] = '\0'; SET_DIRTY(); }, [=] () { return modelslabels.isLabelSelected(label, curmod); @@ -285,7 +286,7 @@ void ModelSetupPage::build(FormWindow * window) form->setFlexLayout(LV_FLEX_FLOW_ROW_WRAP, lv_dpx(8)); lv_obj_set_style_flex_main_place(form->getLvObj(), LV_FLEX_ALIGN_SPACE_EVENLY, 0); form->padAll(lv_dpx(8)); - + Window* btn = new IntmoduleButton(form); lv_obj_set_style_min_width(btn->getLvObj(), LV_DPI_DEF, 0); @@ -342,11 +343,11 @@ TimerBtnMatrix::TimerBtnMatrix(Window* parent, const rect_t& r) : setText(1, TR_TIMER "2"); setText(2, TR_TIMER "3"); update(); - + lv_btnmatrix_set_btn_width(lvobj, 3, 2); lv_obj_set_width(lvobj, lv_pct(100)); lv_obj_set_height(lvobj, LV_DPI_DEF / 2); - + lv_obj_set_style_bg_opa(lvobj, LV_OPA_0, 0); lv_obj_set_style_pad_all(lvobj, lv_dpx(8), 0); diff --git a/radio/src/storage/modelslist.cpp b/radio/src/storage/modelslist.cpp index ce7f94e3d..697d4c7d9 100644 --- a/radio/src/storage/modelslist.cpp +++ b/radio/src/storage/modelslist.cpp @@ -330,10 +330,14 @@ LabelsVector ModelMap::getLabels() int ModelMap::addLabel(std::string lbl) { - if (lbl.size() == 0) return -1; if (lbl == STR_UNLABELEDMODEL) return -1; - // Add a new label if if doesn't already exist in the list + // Limit maximum label length, TODO... Truncate UTF8 Properly + lbl = lbl.substr(0, LABEL_LENGTH); + removeYAMLChars(lbl); + if (lbl.size() == 0) return -1; + + // Add a new label if it doesn't already exist in the list // Returns the index to the label int ind = getIndexByLabel(lbl); if (ind < 0) { @@ -419,8 +423,7 @@ std::string ModelMap::toCSV(const LabelsVector &labels) csv += ','; } // Escape out commas - replace_all(lbl, "/", "//"); - replace_all(lbl, ",", "/c"); + escapeCSV(lbl); csv += lbl; comma = true; } @@ -440,14 +443,36 @@ LabelsVector ModelMap::fromCSV(const char *str) std::istringstream f(str); std::string lbl; while (std::getline(f, lbl, ',')) { - // Un-escape commas - replace_all(lbl, "/c", ","); - replace_all(lbl, "//", "/"); + unEscapeCSV(lbl); lbls.push_back(lbl); } return lbls; } +// TODO - Fix me, ideally there should be no limitations +void ModelMap::escapeCSV(std::string &str) +{ + replace_all(str, "/", "//"); + replace_all(str, ",", "/c"); +} + +// TODO - Fix me, ideally there should be no limitations +void ModelMap::unEscapeCSV(std::string &str) +{ + replace_all(str, "//", "/"); + replace_all(str, "/c", ","); +} + +// TODO - Fix me, ideally there should be no limitations +void ModelMap::removeYAMLChars(std::string &str) +{ + replace_all(str, "\\", ""); + replace_all(str, "\"", ""); + replace_all(str, ":", ""); + replace_all(str, "\'", ""); + replace_all(str, "-", ""); +} + /** * @brief Replace all occurances of to in * @@ -585,13 +610,17 @@ bool ModelMap::moveLabelTo(unsigned curind, unsigned newind) * @return false success */ -bool ModelMap::renameLabel( - const std::string &from, const std::string &to, +bool ModelMap::renameLabel(const std::string &from, std::string to, std::function progress) { + if (from == "") return true; DEBUG_TIMER_START(debugTimerYamlScan); - if (from == "") return true; + // Limit max label name. TODO: Warn user they entered too long of a string + to = to.substr(0, LABEL_LENGTH); + removeYAMLChars(to); + if(to.size() == 0) return true; + ModelData *modeldata = (ModelData *)malloc(sizeof(ModelData)); if (!modeldata) { @@ -604,6 +633,23 @@ bool ModelMap::renameLabel( bool fault = false; ModelsVector mods = getModelsByLabel(from); // Find all models to be renamed + + // Scan all these models first, recombine their labels to a csv, + // make sure re-size is going to fit before starting. Otherwise new partial + // labels would be created on next scan. + for(const auto &model: mods) { + int curlen = toCSV(getLabelsByModel(model)).size(); + std::string csvto = to; + escapeCSV(csvto); + std::string csvfrom = from; + escapeCSV(csvfrom); + if(curlen + csvto.size() - csvfrom.size() > LABELS_LENGTH - 1) { + TRACE("Labels: Rename Error! Labels too long on %s", model->modelName); + if (progress != nullptr) progress("", 100); // Kill progress dialog + return true; + } + } + int i = 0; for (const auto &modcell : mods) { if (progress != nullptr) { @@ -613,25 +659,9 @@ bool ModelMap::renameLabel( readModelYaml(modcell->modelFilename, (uint8_t *)modeldata, sizeof(ModelData)); - // Make sure there is room to rename, use toCSV to capture escape chars - LabelsVector tmpvect; - tmpvect.push_back(to); - int newlen = ModelMap::toCSV(tmpvect).size(); - tmpvect.clear(); - tmpvect.push_back(from); - int oldlen = ModelMap::toCSV(tmpvect).size(); - // Separate Curent CSV LabelsVector lbls = ModelMap::fromCSV(modeldata->header.labels); - int nlen = lbls.size() + newlen - oldlen; - if (nlen > LABELS_LENGTH - 1) { - fault = true; - TRACE("Labels: Rename Error! Labels too long on %s - %s", - modeldata->header.name, modcell->modelFilename); - continue; - } - // Replace from->to strings for (auto &lbl : lbls) { if (lbl == from) lbl = to; @@ -691,19 +721,26 @@ bool ModelMap::renameLabel( } /** - * @brief Returns a comma separated list of the labels, used in model_setup + * @brief Returns a bullet separated list of the labels, used in model_setup * * @param curmod Model Cell * @param noresults String to return when no labels found * @return std::string of all Labels, if no results return */ -std::string ModelMap::getLabelString(ModelCell *curmod, const char *noresults) +std::string ModelMap::getBulletLabelString(ModelCell *curmod, const char *noresults) { std::string lbls = ModelMap::toCSV(getLabelsByModel(curmod)); + replace_all(lbls, ",", "\u2022"); + unEscapeCSV(lbls); + if(lbls.size() == 0) { return noresults; } + if(lbls.size() > LABEL_TRUNCATE_LENGTH) { + lbls = lbls.substr(0, LABEL_TRUNCATE_LENGTH); + lbls += "..."; + } return lbls; } @@ -745,7 +782,7 @@ bool ModelMap::updateModelFile(ModelCell *cell) { // Update memory copy if on current model if (cell == modelslist.getCurrentModel()) { - strncpy(g_model.header.labels, getLabelString(cell).c_str(), + strncpy(g_model.header.labels, ModelMap::toCSV(getLabelsByModel(cell)).c_str(), LABELS_LENGTH - 1); g_model.header.labels[LABELS_LENGTH - 1] = '\0'; storageDirty(EE_MODEL); @@ -761,7 +798,7 @@ bool ModelMap::updateModelFile(ModelCell *cell) bool fault = false; readModelYaml(cell->modelFilename, (uint8_t *)modeldata, sizeof(ModelData)); - strncpy(modeldata->header.labels, getLabelString(cell).c_str(), + strncpy(modeldata->header.labels, ModelMap::toCSV(getLabelsByModel(cell)).c_str(), LABELS_LENGTH - 1); modeldata->header.labels[LABELS_LENGTH - 1] = '\0'; @@ -1087,6 +1124,9 @@ bool ModelsList::load(Format fmt) TRACE("ERROR no Current Model Found"); if (modelslist.size()) { modelslist.setCurrentModel(modelslist.at(0)); + strncpy(g_eeGeneral.currModelFilename, modelslist.at(0)->modelFilename, + sizeof(g_eeGeneral.currModelFilename)); + g_eeGeneral.currModelFilename[sizeof(g_eeGeneral.currModelFilename) - 1] = '\0'; TRACE(" - Set current model to first available"); } else { TRACE(" - No Models Found, making a new one"); @@ -1127,7 +1167,7 @@ const char *ModelsList::save(LabelsVector newOrder) if(newOrder.empty()) newOrder = modelslabels.getLabels(); for (auto &lbl : newOrder) { - f_printf(&file, " %s:\r\n", lbl.c_str()); + f_printf(&file, " \"%s\":\r\n", lbl.c_str()); if (modelslabels.isLabelFiltered(lbl)) f_printf(&file, " selected: true\r\n", lbl.c_str()); } diff --git a/radio/src/storage/modelslist.h b/radio/src/storage/modelslist.h index ca7328e60..14ddb30c7 100644 --- a/radio/src/storage/modelslist.h +++ b/radio/src/storage/modelslist.h @@ -48,6 +48,12 @@ #define DEFAULT_MODEL_SORT NAME_ASC +#if LCD_W > LCD_H // Landscape +#define LABEL_TRUNCATE_LENGTH 21 +#else +#define LABEL_TRUNCATE_LENGTH 16 +#endif + struct ModelData; struct ModuleData; @@ -134,8 +140,7 @@ class ModelMap : protected std::multimap const std::string &, std::function progress = nullptr); bool moveLabelTo(unsigned current, unsigned newind); - bool renameLabel( - const std::string &from, const std::string &to, + bool renameLabel(const std::string &from, std::string to, std::function progress = nullptr); std::string getCurrentLabel() { return currentlabel; }; void setCurrentLabel(const std::string &lbl) @@ -143,7 +148,7 @@ class ModelMap : protected std::multimap currentlabel = lbl; setDirty(); } - std::string getLabelString(ModelCell *, const char *noresults = ""); + std::string getBulletLabelString(ModelCell *, const char *noresults = ""); void setDirty(bool save = false); bool isDirty() { return _isDirty; } @@ -161,6 +166,9 @@ class ModelMap : protected std::multimap static std::string toCSV(const LabelsVector &labels); static LabelsVector fromCSV(const char *str); + static void escapeCSV(std::string &str); + static void unEscapeCSV(std::string &str); + static void removeYAMLChars(std::string &str); static void replace_all(std::string &str, const std::string &from, const std::string &to); diff --git a/radio/src/storage/yaml/yaml_labelslist.cpp b/radio/src/storage/yaml/yaml_labelslist.cpp index 2f9302483..bbcdf256e 100644 --- a/radio/src/storage/yaml/yaml_labelslist.cpp +++ b/radio/src/storage/yaml/yaml_labelslist.cpp @@ -49,8 +49,8 @@ struct labelslist_iter bool modeldatavalid; // Used to determine if reading yaml values is necessary uint8_t level; uint8_t section; - char current_attr[LABEL_LENGTH+1]; // set after find_node() - char current_label[LABEL_LENGTH+1]; // set after find_node() + char current_attr[LABELS_LENGTH+1]; // set after find_node() + char current_label[LABELS_LENGTH+1]; // set after find_node() }; static labelslist_iter __labelslist_iter_inst; @@ -130,9 +130,9 @@ static bool find_node(void* ctx, char* buf, uint8_t len) 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", filehash.name); + TRACE_LABELS_YAML(" Model %s has a real file, creating a modelcell", mi->current_attr); if(filehash.celladded) { - TRACE_LABELS_YAML(" Duplicate found labels.yml model cell %s already added", filehash.name); + TRACE_LABELS_YAML(" Duplicate found labels.yml model cell %s already added", mi->current_attr); break; } ModelCell *model = new ModelCell(mi->current_attr); @@ -158,8 +158,8 @@ static bool find_node(void* ctx, char* buf, uint8_t len) if(mi->level == 1 && mi->section == labelslist_iter::SEC_Labels) { 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'; + strncpy(mi->current_label,mi->current_attr, LABELS_LENGTH); + mi->current_label[LABELS_LENGTH] = '\0'; } return true; @@ -167,7 +167,11 @@ static bool find_node(void* ctx, char* buf, uint8_t len) static void set_attr(void* ctx, char* buf, uint8_t len) { - char value[40]; + char value[LABELS_LENGTH + 1]; + if(len > LABELS_LENGTH) { + TRACE("ERROR: YAML too long for buffer"); + return; + } memcpy(value, buf, len); value[len] = '\0'; @@ -256,7 +260,7 @@ static void set_attr(void* ctx, char* buf, uint8_t len) // Sort Order } else if (mi->level == 0 && mi->section == labelslist_iter::SEC_Sort) { - TRACE_LABELS_YAML(" Sort Order Found -- %s", mi->current_attr); + TRACE_LABELS_YAML(" Sort Order Found -- %s", value); modelslabels.setSortOrder((ModelsSortBy)strtol(value,NULL,10)); } } diff --git a/radio/src/storage/yaml/yaml_parser.h b/radio/src/storage/yaml/yaml_parser.h index 15b4b4907..d5964b198 100644 --- a/radio/src/storage/yaml/yaml_parser.h +++ b/radio/src/storage/yaml/yaml_parser.h @@ -24,7 +24,7 @@ #include -#define MAX_STR 40 +#define MAX_STR 100 #define MAX_DEPTH 16 // 12 real + 4 virtual struct YamlParserCalls @@ -78,7 +78,7 @@ class YamlParser // tree iterator state const YamlParserCalls* calls; void* ctx; - + // Reset parser state for next line void reset(); @@ -97,7 +97,7 @@ public: YamlParser(); void init(const YamlParserCalls* parser_calls, void* parser_ctx); - + YamlResult parse(const char* buffer, unsigned int size); void set_eof() { eof = true; }