crash fix: Don't cast to CylindersModel or CylindersModelFiltered

The tank-info-delegate cast its model to CylindersModelFiltered,
since this is what the equipment-tab uses since implementing the
filtering of unused cylinders. However, the planner users the same
delegate and still uses the unfiltered CylindersModel. This means
that the (dynamic) cast returns a null pointer and crashes.

One possibility would be to derive CylindersModelFiltered and
CylindersModel from the same class that defines virtual functions
and cast to that class.

This is a different attempt: don't cast (i.e. stay with a
QAbstractItemModel and play it via Qt's model-view system. Firstly,
replace the passInData function by a role to setData(). Secondly,
read the working-pressure and size via new columns using data().

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
Berthold Stoeger 2020-02-27 19:46:48 +01:00 committed by Dirk Hohndel
parent 9214bdb3c5
commit cb80ff746b
3 changed files with 44 additions and 40 deletions

View file

@ -238,7 +238,7 @@ static struct RevertCylinderData {
void TankInfoDelegate::setModelData(QWidget*, QAbstractItemModel*, const QModelIndex&) const void TankInfoDelegate::setModelData(QWidget*, QAbstractItemModel*, const QModelIndex&) const
{ {
CylindersModelFiltered *mymodel = qobject_cast<CylindersModelFiltered *>(currCombo.model); QAbstractItemModel *mymodel = currCombo.model;
TankInfoModel *tanks = TankInfoModel::instance(); TankInfoModel *tanks = TankInfoModel::instance();
QModelIndexList matches = tanks->match(tanks->index(0, 0), Qt::DisplayRole, currCombo.activeText); QModelIndexList matches = tanks->match(tanks->index(0, 0), Qt::DisplayRole, currCombo.activeText);
int row; int row;
@ -255,8 +255,8 @@ void TankInfoDelegate::setModelData(QWidget*, QAbstractItemModel*, const QModelI
int tankPressure = tanks->data(tanks->index(row, TankInfoModel::BAR)).toInt(); int tankPressure = tanks->data(tanks->index(row, TankInfoModel::BAR)).toInt();
mymodel->setData(IDX(CylindersModel::TYPE), cylinderName, Qt::EditRole); mymodel->setData(IDX(CylindersModel::TYPE), cylinderName, Qt::EditRole);
mymodel->passInData(IDX(CylindersModel::WORKINGPRESS), tankPressure); mymodel->setData(IDX(CylindersModel::WORKINGPRESS), tankPressure, CylindersModel::PASS_IN_ROLE);
mymodel->passInData(IDX(CylindersModel::SIZE), tankSize); mymodel->setData(IDX(CylindersModel::SIZE), tankSize, CylindersModel::PASS_IN_ROLE);
} }
TankInfoDelegate::TankInfoDelegate(QObject *parent) : ComboBoxDelegate(TankInfoModel::instance(), parent, true) TankInfoDelegate::TankInfoDelegate(QObject *parent) : ComboBoxDelegate(TankInfoModel::instance(), parent, true)
@ -277,10 +277,10 @@ void TankInfoDelegate::editorClosed(QWidget*, QAbstractItemDelegate::EndEditHint
{ {
if (hint == QAbstractItemDelegate::NoHint || if (hint == QAbstractItemDelegate::NoHint ||
hint == QAbstractItemDelegate::RevertModelCache) { hint == QAbstractItemDelegate::RevertModelCache) {
CylindersModelFiltered *mymodel = qobject_cast<CylindersModelFiltered *>(currCombo.model); QAbstractItemModel *mymodel = currCombo.model;
mymodel->setData(IDX(CylindersModel::TYPE), currCylinderData.type, Qt::EditRole); mymodel->setData(IDX(CylindersModel::TYPE), currCylinderData.type, Qt::EditRole);
mymodel->passInData(IDX(CylindersModel::WORKINGPRESS), currCylinderData.pressure); mymodel->setData(IDX(CylindersModel::WORKINGPRESS), currCylinderData.pressure, CylindersModel::PASS_IN_ROLE);
mymodel->passInData(IDX(CylindersModel::SIZE), currCylinderData.size); mymodel->setData(IDX(CylindersModel::SIZE), currCylinderData.size, CylindersModel::PASS_IN_ROLE);
} }
} }
@ -289,11 +289,11 @@ QWidget *TankInfoDelegate::createEditor(QWidget *parent, const QStyleOptionViewI
// ncreate editor needs to be called before because it will populate a few // ncreate editor needs to be called before because it will populate a few
// things in the currCombo global var. // things in the currCombo global var.
QWidget *delegate = ComboBoxDelegate::createEditor(parent, option, index); QWidget *delegate = ComboBoxDelegate::createEditor(parent, option, index);
CylindersModelFiltered *mymodel = qobject_cast<CylindersModelFiltered *>(currCombo.model); QAbstractItemModel *model = currCombo.model;
cylinder_t *cyl = mymodel->cylinderAt(index); int row = index.row();
currCylinderData.type = cyl->type.description; currCylinderData.type = model->data(model->index(row, CylindersModel::TYPE)).value<QString>();
currCylinderData.pressure = cyl->type.workingpressure.mbar; currCylinderData.pressure = model->data(model->index(row, CylindersModel::WORKINGPRESS_INT)).value<int>();
currCylinderData.size = cyl->type.size.mliter; currCylinderData.size = model->data(model->index(row, CylindersModel::SIZE_INT)).value<int>();
MainWindow::instance()->graphics->setReplot(false); MainWindow::instance()->graphics->setReplot(false);
return delegate; return delegate;
} }

View file

@ -236,6 +236,10 @@ QVariant CylindersModel::data(const QModelIndex &index, int role) const
break; break;
case USE: case USE:
return gettextFromC::tr(cylinderuse_text[cyl->cylinder_use]); return gettextFromC::tr(cylinderuse_text[cyl->cylinder_use]);
case WORKINGPRESS_INT:
return static_cast<int>(cyl->type.workingpressure.mbar);
case SIZE_INT:
return static_cast<int>(cyl->type.size.mliter);
} }
break; break;
case Qt::DecorationRole: case Qt::DecorationRole:
@ -283,33 +287,35 @@ cylinder_t *CylindersModel::cylinderAt(const QModelIndex &index)
return get_cylinder(&displayed_dive, index.row()); return get_cylinder(&displayed_dive, index.row());
} }
// this is our magic 'pass data in' function that allows the delegate to get
// the data here without silly unit conversions;
// so we only implement the two columns we care about
void CylindersModel::passInData(const QModelIndex &index, const QVariant &value)
{
cylinder_t *cyl = cylinderAt(index);
switch (index.column()) {
case SIZE:
if (cyl->type.size.mliter != value.toInt()) {
cyl->type.size.mliter = value.toInt();
dataChanged(index, index);
}
break;
case WORKINGPRESS:
if (cyl->type.workingpressure.mbar != value.toInt()) {
cyl->type.workingpressure.mbar = value.toInt();
dataChanged(index, index);
}
break;
}
}
bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, int role) bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, int role)
{ {
QString vString; QString vString;
cylinder_t *cyl = cylinderAt(index); cylinder_t *cyl = cylinderAt(index);
if (!cyl)
return false;
if (role == PASS_IN_ROLE) {
// this is our magic 'pass data in' function that allows the delegate to get
// the data here without silly unit conversions;
// so we only implement the two columns we care about
switch (index.column()) {
case SIZE:
if (cyl->type.size.mliter != value.toInt()) {
cyl->type.size.mliter = value.toInt();
dataChanged(index, index);
}
return true;
case WORKINGPRESS:
if (cyl->type.workingpressure.mbar != value.toInt()) {
cyl->type.workingpressure.mbar = value.toInt();
dataChanged(index, index);
}
return true;
}
return false;
}
switch (index.column()) { switch (index.column()) {
case TYPE: case TYPE:
if (!value.isNull()) { if (!value.isNull()) {
@ -641,11 +647,6 @@ void CylindersModelFiltered::remove(QModelIndex index)
source.remove(mapToSource(index)); source.remove(mapToSource(index));
} }
void CylindersModelFiltered::passInData(const QModelIndex &index, const QVariant &value)
{
source.passInData(mapToSource(index), value);
}
cylinder_t *CylindersModelFiltered::cylinderAt(const QModelIndex &index) cylinder_t *CylindersModelFiltered::cylinderAt(const QModelIndex &index)
{ {
return source.cylinderAt(mapToSource(index)); return source.cylinderAt(mapToSource(index));

View file

@ -25,16 +25,20 @@ public:
MOD, MOD,
MND, MND,
USE, USE,
WORKINGPRESS_INT,
SIZE_INT,
COLUMNS COLUMNS
}; };
enum Roles {
PASS_IN_ROLE = Qt::UserRole + 1 // For setting data: don't do any conversions
};
explicit CylindersModel(QObject *parent = 0); explicit CylindersModel(QObject *parent = 0);
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override; Qt::ItemFlags flags(const QModelIndex &index) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
void passInData(const QModelIndex &index, const QVariant &value);
void add(); void add();
void clear(); void clear();
void updateDive(); void updateDive();
@ -67,7 +71,6 @@ public:
void add(); void add();
void updateDive(); void updateDive();
cylinder_t *cylinderAt(const QModelIndex &index); cylinder_t *cylinderAt(const QModelIndex &index);
void passInData(const QModelIndex &index, const QVariant &value);
public public
slots: slots:
void remove(QModelIndex index); void remove(QModelIndex index);