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
This commit is contained in:
Neil Horne
2023-08-26 20:06:10 +10:00
committed by GitHub
parent 543171884a
commit 64f4968e89
6 changed files with 113 additions and 74 deletions
+58 -25
View File
@@ -1,14 +1,15 @@
#include "labels.h" #include "labels.h"
LabelsModel::LabelsModel(QItemSelectionModel *selectionModel, LabelsModel::LabelsModel(QSortFilterProxyModel * modelsListProxyModel,
QItemSelectionModel *selectionModel,
RadioData *radioData, QObject *parent) : RadioData *radioData, QObject *parent) :
QAbstractItemModel(parent), QAbstractItemModel(parent),
modelsListProxyModel(modelsListProxyModel),
modelsSelection(selectionModel), modelsSelection(selectionModel),
radioData(radioData), radioData(radioData),
selectedModel(-1) selectedModel(-1)
{ {
connect(modelsSelection, &QItemSelectionModel::currentChanged, connect(modelsSelection, &QItemSelectionModel::currentChanged, this, &LabelsModel::modelsSelectionChanged );
this, &LabelsModel::modelsSelectionChanged );
buildLabelsList(); 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)) if (radioData->removeLabelFromModel(selectedModel, radioData->labels.at(index.row()).name))
emit modelChanged(selectedModel); emit modelChanged(selectedModel);
} else { } else {
try{ try {
if (radioData->addLabelToModel(selectedModel, radioData->labels.at(index.row()).name)) if (radioData->addLabelToModel(selectedModel, radioData->labels.at(index.row()).name))
emit modelChanged(selectedModel); emit modelChanged(selectedModel);
} catch(const std::length_error& le) { } 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())); .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; return true;
} else if (role == Qt::EditRole) { } else if (role == Qt::EditRole) {
QString replFrom = labels[index.row()].label; QString replFrom = labels[index.row()].label;
QString replTo = value.toString(); QString replTo = value.toString();
if (replFrom == replTo) // User exits edit without changing if (replFrom == replTo) // User exits edit without changing
return true; return true;
if (radioData->indexOfLabel(replTo) == -1) { // Don't allow duplicates if (radioData->indexOfLabel(replTo) == -1) { // Don't allow duplicates
bool modelsChanged = false; bool modelsChanged = false;
try{
try {
modelsChanged = radioData->renameLabel(replFrom, replTo); modelsChanged = radioData->renameLabel(replFrom, replTo);
} catch(const std::length_error& le) { } catch(const std::length_error& le) {
emit labelsFault(tr("Unable to rename \"%1\" to \"%2\" not enough room in model %3")\ emit labelsFault(tr("Unable to rename \"%1\" to \"%2\" not enough room in model %3")\
.arg(replFrom).arg(replTo).arg(le.what())); .arg(replFrom).arg(replTo).arg(le.what()));
return false; return false;
} }
labels[index.row()].label = replTo; labels[index.row()].label = replTo;
emit dataChanged(this->index(index.row(), 0), emit dataChanged(this->index(index.row(), 0), this->index(index.row(), 0));
this->index(index.row(), 0));
if (selectedModel != -1 && modelsChanged) if (selectedModel != -1 && modelsChanged)
emit modelChanged(selectedModel); emit modelChanged(selectedModel);
} else { } else {
emit labelsFault(tr("Unable to rename \"%1\" to \"%2\" the label already exists")\ emit labelsFault(tr("Unable to rename \"%1\" to \"%2\" the label already exists")\
.arg(replFrom).arg(replTo)); .arg(replFrom).arg(replTo));
} }
return true; return true;
} }
return false; return false;
} }
@@ -88,7 +97,7 @@ 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(); 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 (role == Qt::DisplayRole || role == Qt::EditRole) {
if (index.column() == 0) { 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 QVariant LabelsModel::headerData(int section, Qt::Orientation orientation, int role) const
{ {
if (role != Qt::DisplayRole || if (role != Qt::DisplayRole ||
orientation != Qt::Horizontal || orientation != Qt::Horizontal ||
section != 0) section != 0)
return QVariant(); return QVariant();
return tr("Labels"); return tr("Labels");
} }
@@ -153,16 +163,19 @@ bool LabelsModel::insertRows(int row, int count, const QModelIndex &parent)
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
int newNo=0; int newNo=0;
QString newStr; QString newStr;
do { do {
newStr = QString(tr("New%1").arg(newNo)); newStr = QString(tr("New%1").arg(newNo));
if (newNo == 0) if (newNo == 0)
newStr = QString(tr("New")); newStr = QString(tr("New"));
newNo++; newNo++;
} while(radioData->indexOfLabel(newStr) >= 0); } while(radioData->indexOfLabel(newStr) >= 0);
// Add it to radioData // Add it to radioData
RadioData::LabelData ld = { newStr , false }; RadioData::LabelData ld = { newStr , false };
radioData->labels.insert(row + i, ld); radioData->labels.insert(row + i, ld);
} }
buildLabelsList(); buildLabelsList();
return true; 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) bool LabelsModel::removeRows(int row, int count, const QModelIndex &parent)
{ {
if (parent.isValid() || row < 0) if (parent.isValid() || row < 0)
return false; return false;
bool deleted=false; bool deleted=false;
beginRemoveRows(parent, row, row + count - 1); beginRemoveRows(parent, row, row + count - 1);
for (int i = 0; i != count; ++i)
if (radioData->deleteLabel(row+i)) { for (int i = 0; i != count; ++i) {
deleted = true; if (radioData->deleteLabel(row+i)) {
} deleted = true;
}
}
endRemoveRows(); endRemoveRows();
// Refresh all // Refresh all
if (deleted) if (deleted)
emit modelChanged(-1); emit modelChanged(-1);
buildLabelsList(); buildLabelsList();
return true; return true;
} }
void LabelsModel::buildLabelsList() void LabelsModel::buildLabelsList()
@@ -190,52 +207,66 @@ void LabelsModel::buildLabelsList()
labels.clear(); labels.clear();
int i = 0; int i = 0;
foreach(RadioData::LabelData ld, radioData->labels) { foreach(RadioData::LabelData ld, radioData->labels) {
LabelItem itm; LabelItem itm;
itm.label = ld.name; itm.label = ld.name;
itm.radioLabelIndex = i++; itm.radioLabelIndex = i++;
labels.append(itm); labels.append(itm);
} }
if (i) { if (i) {
emit dataChanged(index(0,0), emit dataChanged(index(0, 0), index(i - 1, 0));
index(i-1,0));
} }
} }
void LabelsModel::modelsSelectionChanged() void LabelsModel::modelsSelectionChanged()
{ {
QModelIndex index = modelsSelection->currentIndex(); QModelIndex index = getDataIndex(modelsSelection->currentIndex());
if (index.isValid()) { if (index.isValid()) {
int mi = modelsSelection->currentIndex().row(); int mi = index.row();
if (mi < (int)radioData->models.size()) {
selectedModel = mi; if (mi < (int)radioData->models.size()) {
buildLabelsList(); selectedModel = mi;
buildLabelsList();
} }
} }
else else
selectedModel = -1; selectedModel = -1;
} }
QModelIndex LabelsModel::getDataIndex(QModelIndex viewIndex) const
{
return modelsListProxyModel->mapToSource(viewIndex);
}
QValidator::State LabelValidator::validate(QString &label, int &pos) const QValidator::State LabelValidator::validate(QString &label, int &pos) const
{ {
Q_UNUSED(pos) Q_UNUSED(pos)
QString lbl = RadioData::escapeCSV(label); QString lbl = RadioData::escapeCSV(label);
if (lbl.toUtf8().size() > LABEL_LENGTH) if (lbl.toUtf8().size() > LABEL_LENGTH)
return QValidator::Invalid; return QValidator::Invalid;
if(lbl.contains('\\') || // TODO: Fix me to allow all, requires FW changes if(lbl.contains('\\') || // TODO: Fix me to allow all, requires FW changes
lbl.contains('\"') || lbl.contains('\"') ||
lbl.contains(':') || lbl.contains(':') ||
lbl.contains('-') || lbl.contains('-') ||
lbl.contains('\'')) lbl.contains('\''))
return QValidator::Invalid; return QValidator::Invalid;
if(lbl.size() == 0) if(lbl.size() == 0)
return QValidator::Intermediate; return QValidator::Intermediate;
return QValidator::Acceptable; return QValidator::Acceptable;
} }
void LabelValidator::fixup(QString &input) const void LabelValidator::fixup(QString &input) const
{ {
QByteArray output = input.toUtf8(); QByteArray output = input.toUtf8();
if (output.size() > LABEL_LENGTH) { if (output.size() > LABEL_LENGTH) {
int truncateAt = 0; int truncateAt = 0;
for (int i = LABEL_LENGTH; i > 0; i--) { for (int i = LABEL_LENGTH; i > 0; i--) {
@@ -244,8 +275,10 @@ void LabelValidator::fixup(QString &input) const
break; break;
} }
} }
output.truncate(truncateAt); output.truncate(truncateAt);
} }
input = QString(output); input = QString(output);
input.remove('\\'); // TODO: Fix me to allow all, requires FW changes input.remove('\\'); // TODO: Fix me to allow all, requires FW changes
input.remove('\"'); input.remove('\"');
+44 -38
View File
@@ -7,6 +7,7 @@
#include <QValidator> #include <QValidator>
#include <QLineEdit> #include <QLineEdit>
#include <QStyledItemDelegate> #include <QStyledItemDelegate>
#include <QSortFilterProxyModel>
#include "radiodata.h" #include "radiodata.h"
@@ -21,58 +22,63 @@ class LabelsModel : public QAbstractItemModel
{ {
Q_OBJECT Q_OBJECT
public: public:
LabelsModel(QItemSelectionModel *selectionModel, RadioData *radioData, QObject *parent = nullptr); LabelsModel(QSortFilterProxyModel * modelsListProxyModel, QItemSelectionModel *selectionModel, RadioData *radioData, QObject *parent = nullptr);
~LabelsModel(); virtual ~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 slots: Qt::ItemFlags flags(const QModelIndex &index) const override;
void buildLabelsList(); 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: public slots:
void modelsSelectionChanged(); void buildLabelsList();
signals: private slots:
void modelChanged(int index); void modelsSelectionChanged();
void labelsFault(QString msg);
private: signals:
QItemSelectionModel *modelsSelection; void modelChanged(int index);
RadioData *radioData; void labelsFault(QString msg);
int selectedModel;
QList<QModelIndex> modelIndices; private:
QList<LabelItem> labels; QSortFilterProxyModel * modelsListProxyModel;
QItemSelectionModel *modelsSelection;
RadioData *radioData;
int selectedModel;
QList<QModelIndex> modelIndices;
QList<LabelItem> labels;
QModelIndex getDataIndex(QModelIndex viewIndex) const;
}; };
class LabelValidator : public QValidator class LabelValidator : public QValidator
{ {
Q_OBJECT Q_OBJECT
public:
QValidator::State validate(QString &label, int &pos) const; public:
void fixup(QString &input) const; QValidator::State validate(QString &label, int &pos) const;
void fixup(QString &input) const;
}; };
class LabelEditTextDelegate : public QStyledItemDelegate class LabelEditTextDelegate : public QStyledItemDelegate
{ {
Q_OBJECT Q_OBJECT
public: public:
LabelEditTextDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) {} LabelEditTextDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) {}
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const const QModelIndex &index) const
{ {
QLineEdit *editor = new QLineEdit(parent); QLineEdit *editor = new QLineEdit(parent);
editor->setValidator(new LabelValidator); editor->setValidator(new LabelValidator);
return editor; return editor;
} }
}; };
+8 -8
View File
@@ -553,8 +553,7 @@ void MdiChild::initModelsList()
if (labelsListModel) if (labelsListModel)
delete labelsListModel; delete labelsListModel;
labelsListModel = new LabelsModel(ui->modelsList->selectionModel(), labelsListModel = new LabelsModel(modelsListProxyModel, ui->modelsList->selectionModel(), &radioData, this);
&radioData, this);
connect(labelsListModel, &LabelsModel::modelChanged, this, &MdiChild::modelLabelsChanged); connect(labelsListModel, &LabelsModel::modelChanged, this, &MdiChild::modelLabelsChanged);
connect(labelsListModel, &LabelsModel::labelsFault, this, &MdiChild::labelsFault); connect(labelsListModel, &LabelsModel::labelsFault, this, &MdiChild::labelsFault);
ui->lstLabels->setModel(labelsListModel); ui->lstLabels->setModel(labelsListModel);
@@ -1096,7 +1095,7 @@ bool MdiChild::hasClipboardData(const quint8 type) const
return modelsListModel->hasModelsMimeData(QApplication::clipboard()->mimeData()); return modelsListModel->hasModelsMimeData(QApplication::clipboard()->mimeData());
} }
else { else {
return modelsListModel->hasGenralMimeData(QApplication::clipboard()->mimeData()); return modelsListModel->hasGeneralMimeData(QApplication::clipboard()->mimeData());
} }
} }
@@ -1742,7 +1741,7 @@ void MdiChild::labelMoveUp()
if(row == 0) return; if(row == 0) return;
radioData.swapLabel(row, row-1); radioData.swapLabel(row, row-1);
labelsListModel->buildLabelsList(); 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() void MdiChild::labelMoveDown()
@@ -1751,15 +1750,16 @@ void MdiChild::labelMoveDown()
if(row == labelsListModel->rowCount() -1) return; if(row == labelsListModel->rowCount() -1) return;
radioData.swapLabel(row, row+1); radioData.swapLabel(row, row+1);
labelsListModel->buildLabelsList(); 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); setWindowModified(true);
refresh(); refresh();
ui->modelsList->selectionModel()->select(modelsListModel->index(row,0), QItemSelectionModel::ClearAndSelect | const QModelIndex idx = modelsListProxyModel->mapFromSource(modelsListModel->getIndexForModel(index));
QItemSelectionModel::Rows); ui->modelsList->selectionModel()->select(idx, QItemSelectionModel::ClearAndSelect |
QItemSelectionModel::Rows);
} }
void MdiChild::labelsFault(QString msg) void MdiChild::labelsFault(QString msg)
+1 -1
View File
@@ -147,7 +147,7 @@ class MdiChild : public QWidget
void labelRename(); void labelRename();
void labelMoveUp(); void labelMoveUp();
void labelMoveDown(); void labelMoveDown();
void modelLabelsChanged(int row); void modelLabelsChanged(int index);
void labelsFault(QString msg); void labelsFault(QString msg);
void wizardEdit(); void wizardEdit();
void modelDuplicate(); void modelDuplicate();
+1 -1
View File
@@ -416,7 +416,7 @@ bool ModelsListModel::hasModelsMimeData(const QMimeData * mimeData) const
return mimeData->hasFormat("application/x-companion-modeldata"); 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"); return mimeData->hasFormat("application/x-companion-generaldata");
} }
+1 -1
View File
@@ -114,7 +114,7 @@ class ModelsListModel : public QAbstractItemModel
QUuid getMimeDataSourceId(const QMimeData * mimeData) const; QUuid getMimeDataSourceId(const QMimeData * mimeData) const;
bool hasSupportedMimeData(const QMimeData * mimeData) const; bool hasSupportedMimeData(const QMimeData * mimeData) const;
bool hasModelsMimeData(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 hasHeaderMimeData(const QMimeData * mimeData) const;
bool hasOwnMimeData(const QMimeData * mimeData) const; bool hasOwnMimeData(const QMimeData * mimeData) const;