From 64f4968e899e2e08d8a5c1daec2e4d050f6c5fca Mon Sep 17 00:00:00 2001 From: Neil Horne Date: Sat, 26 Aug 2023 20:06:10 +1000 Subject: [PATCH] fix(cpn): Model and labels lists sync (#3967) * labels use proxy model mapping to source * cosmetics * Fix model list focus * fix model focus on change to labels --- companion/src/labels.cpp | 83 +++++++++++++++++++++++++----------- companion/src/labels.h | 82 ++++++++++++++++++----------------- companion/src/mdichild.cpp | 16 +++---- companion/src/mdichild.h | 2 +- companion/src/modelslist.cpp | 2 +- companion/src/modelslist.h | 2 +- 6 files changed, 113 insertions(+), 74 deletions(-) diff --git a/companion/src/labels.cpp b/companion/src/labels.cpp index 76a0757f5..95a7542d5 100644 --- a/companion/src/labels.cpp +++ b/companion/src/labels.cpp @@ -1,14 +1,15 @@ #include "labels.h" -LabelsModel::LabelsModel(QItemSelectionModel *selectionModel, +LabelsModel::LabelsModel(QSortFilterProxyModel * modelsListProxyModel, + QItemSelectionModel *selectionModel, RadioData *radioData, QObject *parent) : QAbstractItemModel(parent), + modelsListProxyModel(modelsListProxyModel), modelsSelection(selectionModel), radioData(radioData), selectedModel(-1) { - connect(modelsSelection, &QItemSelectionModel::currentChanged, - this, &LabelsModel::modelsSelectionChanged ); + connect(modelsSelection, &QItemSelectionModel::currentChanged, this, &LabelsModel::modelsSelectionChanged ); buildLabelsList(); } @@ -44,7 +45,7 @@ bool LabelsModel::setData(const QModelIndex &index, const QVariant &value, int r if (radioData->removeLabelFromModel(selectedModel, radioData->labels.at(index.row()).name)) emit modelChanged(selectedModel); } else { - try{ + try { if (radioData->addLabelToModel(selectedModel, radioData->labels.at(index.row()).name)) emit modelChanged(selectedModel); } catch(const std::length_error& le) { @@ -52,34 +53,42 @@ bool LabelsModel::setData(const QModelIndex &index, const QVariant &value, int r .arg(radioData->labels.at(index.row()).name).arg(le.what())); } } - emit dataChanged(this->index(index.row(), 0), - this->index(index.row(), 0)); + + emit dataChanged(this->index(index.row(), 0), this->index(index.row(), 0)); return true; + } else if (role == Qt::EditRole) { QString replFrom = labels[index.row()].label; QString replTo = value.toString(); + if (replFrom == replTo) // User exits edit without changing return true; + if (radioData->indexOfLabel(replTo) == -1) { // Don't allow duplicates bool modelsChanged = false; - try{ + + 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)); + emit dataChanged(this->index(index.row(), 0), this->index(index.row(), 0)); + if (selectedModel != -1 && modelsChanged) emit modelChanged(selectedModel); + } else { emit labelsFault(tr("Unable to rename \"%1\" to \"%2\" the label already exists")\ .arg(replFrom).arg(replTo)); } + return true; } + return false; } @@ -88,7 +97,7 @@ QVariant LabelsModel::data(const QModelIndex &index, int role) const if (index.row() >= labels.size() || !index.isValid() ) return QVariant(); - QString label = radioData->labels.at(index.row()).name; + QString label = radioData->labels.at(index.row()).name; if (role == Qt::DisplayRole || role == Qt::EditRole) { if (index.column() == 0) { @@ -138,9 +147,10 @@ int LabelsModel::columnCount(const QModelIndex &parent) const QVariant LabelsModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole || - orientation != Qt::Horizontal || - section != 0) + orientation != Qt::Horizontal || + section != 0) return QVariant(); + return tr("Labels"); } @@ -153,16 +163,19 @@ bool LabelsModel::insertRows(int row, int count, const QModelIndex &parent) 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->indexOfLabel(newStr) >= 0); + // Add it to radioData RadioData::LabelData ld = { newStr , false }; radioData->labels.insert(row + i, ld); } + buildLabelsList(); return true; } @@ -170,19 +183,23 @@ bool LabelsModel::insertRows(int row, int count, const QModelIndex &parent) bool LabelsModel::removeRows(int row, int count, const QModelIndex &parent) { if (parent.isValid() || row < 0) - return false; + 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; - } + + 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; + return true; } void LabelsModel::buildLabelsList() @@ -190,52 +207,66 @@ void LabelsModel::buildLabelsList() labels.clear(); int i = 0; + foreach(RadioData::LabelData ld, radioData->labels) { LabelItem itm; itm.label = ld.name; itm.radioLabelIndex = i++; labels.append(itm); } + if (i) { - emit dataChanged(index(0,0), - index(i-1,0)); + emit dataChanged(index(0, 0), index(i - 1, 0)); } } void LabelsModel::modelsSelectionChanged() { - QModelIndex index = modelsSelection->currentIndex(); + QModelIndex index = getDataIndex(modelsSelection->currentIndex()); + if (index.isValid()) { - int mi = modelsSelection->currentIndex().row(); - if (mi < (int)radioData->models.size()) { - selectedModel = mi; - buildLabelsList(); + int mi = index.row(); + + if (mi < (int)radioData->models.size()) { + selectedModel = mi; + buildLabelsList(); } } else selectedModel = -1; } +QModelIndex LabelsModel::getDataIndex(QModelIndex viewIndex) const +{ + return modelsListProxyModel->mapToSource(viewIndex); +} + + 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--) { @@ -244,8 +275,10 @@ void LabelValidator::fixup(QString &input) const break; } } + output.truncate(truncateAt); } + input = QString(output); input.remove('\\'); // TODO: Fix me to allow all, requires FW changes input.remove('\"'); diff --git a/companion/src/labels.h b/companion/src/labels.h index 44377b478..e5be7b012 100644 --- a/companion/src/labels.h +++ b/companion/src/labels.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "radiodata.h" @@ -21,58 +22,63 @@ 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; - bool insertRows(int row, int count, const QModelIndex &parent) override; - bool removeRows(int row, int count, const QModelIndex &parent) override; + public: + LabelsModel(QSortFilterProxyModel * modelsListProxyModel, QItemSelectionModel *selectionModel, RadioData *radioData, QObject *parent = nullptr); + virtual ~LabelsModel(); -public slots: - void buildLabelsList(); + 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; + bool insertRows(int row, int count, const QModelIndex &parent) override; + bool removeRows(int row, int count, const QModelIndex &parent) override; -private slots: - void modelsSelectionChanged(); + public slots: + void buildLabelsList(); -signals: - void modelChanged(int index); - void labelsFault(QString msg); + private slots: + void modelsSelectionChanged(); -private: - QItemSelectionModel *modelsSelection; - RadioData *radioData; - int selectedModel; - QList modelIndices; - QList labels; + signals: + void modelChanged(int index); + void labelsFault(QString msg); + + private: + QSortFilterProxyModel * modelsListProxyModel; + QItemSelectionModel *modelsSelection; + RadioData *radioData; + int selectedModel; + QList modelIndices; + QList labels; + + QModelIndex getDataIndex(QModelIndex viewIndex) const; }; class LabelValidator : public QValidator { Q_OBJECT -public: - QValidator::State validate(QString &label, int &pos) const; - void fixup(QString &input) const; + + 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) {} + 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; - } + 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 2a10e32c2..1df017be3 100644 --- a/companion/src/mdichild.cpp +++ b/companion/src/mdichild.cpp @@ -553,8 +553,7 @@ void MdiChild::initModelsList() if (labelsListModel) delete labelsListModel; - labelsListModel = new LabelsModel(ui->modelsList->selectionModel(), - &radioData, this); + labelsListModel = new LabelsModel(modelsListProxyModel, ui->modelsList->selectionModel(), &radioData, this); connect(labelsListModel, &LabelsModel::modelChanged, this, &MdiChild::modelLabelsChanged); connect(labelsListModel, &LabelsModel::labelsFault, this, &MdiChild::labelsFault); ui->lstLabels->setModel(labelsListModel); @@ -1096,7 +1095,7 @@ bool MdiChild::hasClipboardData(const quint8 type) const return modelsListModel->hasModelsMimeData(QApplication::clipboard()->mimeData()); } else { - return modelsListModel->hasGenralMimeData(QApplication::clipboard()->mimeData()); + return modelsListModel->hasGeneralMimeData(QApplication::clipboard()->mimeData()); } } @@ -1742,7 +1741,7 @@ void MdiChild::labelMoveUp() if(row == 0) return; radioData.swapLabel(row, row-1); labelsListModel->buildLabelsList(); - ui->lstLabels->selectionModel()->setCurrentIndex(labelsListModel->index(row-1,0), QItemSelectionModel::ClearAndSelect); + ui->lstLabels->selectionModel()->setCurrentIndex(labelsListModel->index(row - 1,0), QItemSelectionModel::ClearAndSelect); } void MdiChild::labelMoveDown() @@ -1751,15 +1750,16 @@ void MdiChild::labelMoveDown() if(row == labelsListModel->rowCount() -1) return; radioData.swapLabel(row, row+1); labelsListModel->buildLabelsList(); - ui->lstLabels->selectionModel()->setCurrentIndex(labelsListModel->index(row+1,0), QItemSelectionModel::ClearAndSelect); + ui->lstLabels->selectionModel()->setCurrentIndex(labelsListModel->index(row + 1,0), QItemSelectionModel::ClearAndSelect); } -void MdiChild::modelLabelsChanged(int row) +void MdiChild::modelLabelsChanged(int index) { setWindowModified(true); refresh(); - ui->modelsList->selectionModel()->select(modelsListModel->index(row,0), QItemSelectionModel::ClearAndSelect | - QItemSelectionModel::Rows); + const QModelIndex idx = modelsListProxyModel->mapFromSource(modelsListModel->getIndexForModel(index)); + ui->modelsList->selectionModel()->select(idx, QItemSelectionModel::ClearAndSelect | + QItemSelectionModel::Rows); } void MdiChild::labelsFault(QString msg) diff --git a/companion/src/mdichild.h b/companion/src/mdichild.h index e700a0d3a..5d9d8933b 100644 --- a/companion/src/mdichild.h +++ b/companion/src/mdichild.h @@ -147,7 +147,7 @@ class MdiChild : public QWidget void labelRename(); void labelMoveUp(); void labelMoveDown(); - void modelLabelsChanged(int row); + void modelLabelsChanged(int index); void labelsFault(QString msg); void wizardEdit(); void modelDuplicate(); diff --git a/companion/src/modelslist.cpp b/companion/src/modelslist.cpp index 2d9de5cad..85eb18ece 100644 --- a/companion/src/modelslist.cpp +++ b/companion/src/modelslist.cpp @@ -416,7 +416,7 @@ bool ModelsListModel::hasModelsMimeData(const QMimeData * mimeData) const return mimeData->hasFormat("application/x-companion-modeldata"); } -bool ModelsListModel::hasGenralMimeData(const QMimeData * mimeData) const +bool ModelsListModel::hasGeneralMimeData(const QMimeData * mimeData) const { return mimeData->hasFormat("application/x-companion-generaldata"); } diff --git a/companion/src/modelslist.h b/companion/src/modelslist.h index dfce4319a..1cef6e582 100644 --- a/companion/src/modelslist.h +++ b/companion/src/modelslist.h @@ -114,7 +114,7 @@ class ModelsListModel : public QAbstractItemModel QUuid getMimeDataSourceId(const QMimeData * mimeData) const; bool hasSupportedMimeData(const QMimeData * mimeData) const; bool hasModelsMimeData(const QMimeData * mimeData) const; - bool hasGenralMimeData(const QMimeData * mimeData) const; + bool hasGeneralMimeData(const QMimeData * mimeData) const; bool hasHeaderMimeData(const QMimeData * mimeData) const; bool hasOwnMimeData(const QMimeData * mimeData) const;