cylinders: only hide cylinders at the end of the list

On the equipment tab, unused cylinders (automatically added,
no pressure data) could be hidden. This was implemented using
a QSortFilterProxyModel.

Apparently, it causes confusion if cylinders in the middle of
the list are hidden. Therefore, only hide cylinders at the end
of the list.

QSortFilterProxyModel seems the wrong tool for that job, so
remove it and add a flag "hideUnused" to the base model. Calculate
the number of cylinders when changing the dive.

This is rather complex, because the same model is used for
the planner (which doesn't hide cylinders) and the equipment
tab (which does). Of course, syncing core and model now becomes
harder. For instance, the caching of the number of rows was removed
in a37939889b and now has to be
readded.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
Berthold Stoeger 2021-11-20 09:37:28 +01:00 committed by Dirk Hohndel
parent 3466d0c44d
commit a40b40ae7a
5 changed files with 75 additions and 65 deletions

View file

@ -21,7 +21,7 @@ static bool ignoreHiddenFlag(int i)
} }
TabDiveEquipment::TabDiveEquipment(QWidget *parent) : TabBase(parent), TabDiveEquipment::TabDiveEquipment(QWidget *parent) : TabBase(parent),
cylindersModel(new CylindersModelFiltered(this)), cylindersModel(new CylindersModel(false, true, this)),
weightModel(new WeightModel(this)) weightModel(new WeightModel(this))
{ {
QCompleter *suitCompleter; QCompleter *suitCompleter;
@ -39,7 +39,7 @@ TabDiveEquipment::TabDiveEquipment(QWidget *parent) : TabBase(parent),
connect(&diveListNotifier, &DiveListNotifier::divesChanged, this, &TabDiveEquipment::divesChanged); connect(&diveListNotifier, &DiveListNotifier::divesChanged, this, &TabDiveEquipment::divesChanged);
connect(ui.cylinders, &TableView::itemClicked, this, &TabDiveEquipment::editCylinderWidget); connect(ui.cylinders, &TableView::itemClicked, this, &TabDiveEquipment::editCylinderWidget);
connect(ui.weights, &TableView::itemClicked, this, &TabDiveEquipment::editWeightWidget); connect(ui.weights, &TableView::itemClicked, this, &TabDiveEquipment::editWeightWidget);
connect(cylindersModel->model(), &CylindersModel::divesEdited, this, &TabDiveEquipment::divesEdited); connect(cylindersModel, &CylindersModel::divesEdited, this, &TabDiveEquipment::divesEdited);
connect(weightModel, &WeightModel::divesEdited, this, &TabDiveEquipment::divesEdited); connect(weightModel, &WeightModel::divesEdited, this, &TabDiveEquipment::divesEdited);
// Current display of things on Gnome3 looks like shit, so // Current display of things on Gnome3 looks like shit, so
@ -174,9 +174,8 @@ void TabDiveEquipment::editCylinderWidget(const QModelIndex &index)
return; return;
if (index.column() == CylindersModel::REMOVE) { if (index.column() == CylindersModel::REMOVE) {
int cylinder_id = cylindersModel->mapToSource(index).row();
for (dive *d: getDiveSelection()) { for (dive *d: getDiveSelection()) {
if (cylinder_with_sensor_sample(d, cylinder_id)) { if (cylinder_with_sensor_sample(d, index.row())) {
if (QMessageBox::warning(this, tr("Remove cylinder?"), if (QMessageBox::warning(this, tr("Remove cylinder?"),
tr("The deleted cylinder has sensor readings, which will be lost.\n" tr("The deleted cylinder has sensor readings, which will be lost.\n"
"Do you want to continue?"), "Do you want to continue?"),
@ -184,7 +183,7 @@ void TabDiveEquipment::editCylinderWidget(const QModelIndex &index)
return; return;
} }
} }
divesEdited(Command::removeCylinder(cylindersModel->mapToSource(index).row(), false)); divesEdited(Command::removeCylinder(index.row(), false));
} else { } else {
ui.cylinders->edit(index); ui.cylinders->edit(index);
} }

View file

@ -12,7 +12,7 @@ namespace Ui {
}; };
class WeightModel; class WeightModel;
class CylindersModelFiltered; class CylindersModel;
class TabDiveEquipment : public TabBase { class TabDiveEquipment : public TabBase {
Q_OBJECT Q_OBJECT
@ -36,7 +36,7 @@ private slots:
private: private:
Ui::TabDiveEquipment ui; Ui::TabDiveEquipment ui;
SuitCompletionModel suitModel; SuitCompletionModel suitModel;
CylindersModelFiltered *cylindersModel; CylindersModel *cylindersModel;
WeightModel *weightModel; WeightModel *weightModel;
}; };

View file

@ -11,9 +11,11 @@
#include "core/subsurface-string.h" #include "core/subsurface-string.h"
#include <string> #include <string>
CylindersModel::CylindersModel(bool planner, QObject *parent) : CleanerTableModel(parent), CylindersModel::CylindersModel(bool planner, bool hideUnused, QObject *parent) : CleanerTableModel(parent),
d(nullptr), d(nullptr),
inPlanner(planner), inPlanner(planner),
hideUnused(hideUnused),
numRows(0),
tempRow(-1), tempRow(-1),
tempCyl(empty_cylinder) tempCyl(empty_cylinder)
{ {
@ -150,12 +152,26 @@ bool CylindersModel::cylinderUsed(int i) const
return false; return false;
} }
// Calculate the number of displayed cylinders: If hideUnused
// is set, we don't show unused cylinders at the end of the list.
int CylindersModel::calcNumRows() const
{
if (!d)
return 0;
if (!hideUnused || prefs.display_unused_tanks)
return d->cylinders.nr;
int res = d->cylinders.nr;
while (res > 0 && !cylinderUsed(res - 1))
--res;
return res;
}
QVariant CylindersModel::data(const QModelIndex &index, int role) const QVariant CylindersModel::data(const QModelIndex &index, int role) const
{ {
if (!d || !index.isValid()) if (!d || !index.isValid())
return QVariant(); return QVariant();
if (index.row() >= d->cylinders.nr) { if (index.row() >= numRows) {
qWarning("CylindersModel and dive are out of sync!"); qWarning("CylindersModel and dive are out of sync!");
return QVariant(); return QVariant();
} }
@ -300,7 +316,7 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in
return false; return false;
int row = index.row(); int row = index.row();
if (row < 0 || row >= d->cylinders.nr) if (row < 0 || row >= numRows)
return false; return false;
// Here we handle a few cases that allow us to set / commit / revert // Here we handle a few cases that allow us to set / commit / revert
@ -480,9 +496,10 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in
int CylindersModel::rowCount(const QModelIndex&) const int CylindersModel::rowCount(const QModelIndex&) const
{ {
return d ? d->cylinders.nr : 0; return numRows;
} }
// Only invoked from planner.
void CylindersModel::add() void CylindersModel::add()
{ {
if (!d) if (!d)
@ -510,6 +527,7 @@ void CylindersModel::updateDive(dive *dIn)
#endif #endif
beginResetModel(); beginResetModel();
d = dIn; d = dIn;
numRows = calcNumRows();
endResetModel(); endResetModel();
} }
@ -558,8 +576,12 @@ void CylindersModel::cylinderAdded(struct dive *changed, int pos)
return; return;
// The row was already inserted by the undo command. Just inform the model. // The row was already inserted by the undo command. Just inform the model.
beginInsertRows(QModelIndex(), pos, pos); if (pos < numRows) {
endInsertRows(); beginInsertRows(QModelIndex(), pos, pos);
++numRows;
endInsertRows();
}
updateNumRows();
} }
void CylindersModel::cylinderRemoved(struct dive *changed, int pos) void CylindersModel::cylinderRemoved(struct dive *changed, int pos)
@ -568,8 +590,12 @@ void CylindersModel::cylinderRemoved(struct dive *changed, int pos)
return; return;
// The row was already deleted by the undo command. Just inform the model. // The row was already deleted by the undo command. Just inform the model.
beginRemoveRows(QModelIndex(), pos, pos); if (pos < numRows) {
endRemoveRows(); beginRemoveRows(QModelIndex(), pos, pos);
--numRows;
endRemoveRows();
}
updateNumRows();
} }
void CylindersModel::cylinderEdited(struct dive *changed, int pos) void CylindersModel::cylinderEdited(struct dive *changed, int pos)
@ -577,9 +603,26 @@ void CylindersModel::cylinderEdited(struct dive *changed, int pos)
if (d != changed) if (d != changed)
return; return;
dataChanged(index(pos, TYPE), index(pos, USE)); if (pos < numRows)
dataChanged(index(pos, TYPE), index(pos, USE));
updateNumRows();
} }
void CylindersModel::updateNumRows()
{
int numRowsNew = calcNumRows();
if (numRowsNew < numRows) {
beginRemoveRows(QModelIndex(), numRowsNew, numRows - 1);
numRows = numRowsNew;
endRemoveRows();
} else if (numRowsNew > numRows) {
beginInsertRows(QModelIndex(), numRows, numRowsNew - 1);
numRows = numRowsNew;
endInsertRows();
}
}
// Only invoked from planner.
void CylindersModel::moveAtFirst(int cylid) void CylindersModel::moveAtFirst(int cylid)
{ {
if (!d) if (!d)
@ -607,9 +650,10 @@ void CylindersModel::moveAtFirst(int cylid)
endMoveRows(); endMoveRows();
} }
// Only invoked from planner.
void CylindersModel::updateDecoDepths(pressure_t olddecopo2) void CylindersModel::updateDecoDepths(pressure_t olddecopo2)
{ {
if (!d) if (!d || numRows <= 0)
return; return;
pressure_t decopo2; pressure_t decopo2;
@ -622,17 +666,18 @@ void CylindersModel::updateDecoDepths(pressure_t olddecopo2)
cyl->depth = gas_mod(cyl->gasmix, decopo2, d, M_OR_FT(3, 10)); cyl->depth = gas_mod(cyl->gasmix, decopo2, d, M_OR_FT(3, 10));
} }
} }
emit dataChanged(createIndex(0, 0), createIndex(d->cylinders.nr - 1, COLUMNS - 1)); emit dataChanged(createIndex(0, 0), createIndex(numRows - 1, COLUMNS - 1));
} }
void CylindersModel::updateTrashIcon() void CylindersModel::updateTrashIcon()
{ {
if (!d) if (!d || numRows <= 0)
return; return;
emit dataChanged(createIndex(0, 0), createIndex(d->cylinders.nr - 1, 0)); emit dataChanged(createIndex(0, 0), createIndex(numRows - 1, 0));
} }
// Only invoked from planner.
bool CylindersModel::updateBestMixes() bool CylindersModel::updateBestMixes()
{ {
if (!d) if (!d)
@ -667,8 +712,9 @@ bool CylindersModel::updateBestMixes()
return gasUpdated; return gasUpdated;
} }
void CylindersModel::emitDataChanged() { void CylindersModel::emitDataChanged()
emit dataChanged(createIndex(0, 0), createIndex(d->cylinders.nr - 1, COLUMNS - 1)); {
emit dataChanged(createIndex(0, 0), createIndex(numRows - 1, COLUMNS - 1));
} }
void CylindersModel::cylindersReset(const QVector<dive *> &dives) void CylindersModel::cylindersReset(const QVector<dive *> &dives)
@ -680,6 +726,7 @@ void CylindersModel::cylindersReset(const QVector<dive *> &dives)
// And update the model (the actual change was already performed in the backend).. // And update the model (the actual change was already performed in the backend)..
beginResetModel(); beginResetModel();
numRows = calcNumRows();
endResetModel(); endResetModel();
} }
@ -730,29 +777,3 @@ void CylindersModel::commitTempCyl(int row)
free_cylinder(tempCyl); free_cylinder(tempCyl);
tempRow = -1; tempRow = -1;
} }
CylindersModelFiltered::CylindersModelFiltered(QObject *parent) : QSortFilterProxyModel(parent),
source(false) // Currently, only the EquipmentTab uses the filtered model.
{
setSourceModel(&source);
}
void CylindersModelFiltered::updateDive(dive *d)
{
source.updateDive(d);
}
void CylindersModelFiltered::clear()
{
source.clear();
}
CylindersModel *CylindersModelFiltered::model()
{
return &source;
}
bool CylindersModelFiltered::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
return prefs.display_unused_tanks || source.cylinderUsed(source_row);
}

View file

@ -35,7 +35,7 @@ public:
COMMIT_ROLE, // Save the temporary data to the dive. Must be set with Column == TYPE. COMMIT_ROLE, // Save the temporary data to the dive. Must be set with Column == TYPE.
REVERT_ROLE // Revert to original data from dive. Must be set with Column == TYPE. REVERT_ROLE // Revert to original data from dive. Must be set with Column == TYPE.
}; };
explicit CylindersModel(bool planner, QObject *parent = 0); // First argument: true if this model is used for the planner explicit CylindersModel(bool planner, bool hideUnused, QObject *parent = 0); // First argument: true if this model is used for the planner
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;
@ -50,7 +50,6 @@ public:
QVariant headerData(int section, Qt::Orientation orientation, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
bool updateBestMixes(); bool updateBestMixes();
void emitDataChanged(); void emitDataChanged();
bool cylinderUsed(int i) const;
signals: signals:
void divesEdited(int num); void divesEdited(int num);
@ -66,6 +65,8 @@ slots:
private: private:
dive *d; dive *d;
bool inPlanner; bool inPlanner;
bool hideUnused;
int numRows; // Does not include unused cylinders at the end
// Used if we temporarily change a line because the user is selecting a weight type // Used if we temporarily change a line because the user is selecting a weight type
int tempRow; int tempRow;
cylinder_t tempCyl; cylinder_t tempCyl;
@ -74,20 +75,9 @@ private:
void initTempCyl(int row); void initTempCyl(int row);
void clearTempCyl(); void clearTempCyl();
void commitTempCyl(int row); void commitTempCyl(int row);
}; bool cylinderUsed(int i) const;
int calcNumRows() const;
// Cylinder model that hides unused cylinders if the pref.show_unused_cylinders flag is not set void updateNumRows();
class CylindersModelFiltered : public QSortFilterProxyModel {
Q_OBJECT
public:
CylindersModelFiltered(QObject *parent = 0);
CylindersModel *model(); // Access to unfiltered base model
void clear();
void updateDive(dive *d);
private:
CylindersModel source;
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
}; };
#endif #endif

View file

@ -446,7 +446,7 @@ int DivePlannerPointsModel::rowCount(const QModelIndex&) const
DivePlannerPointsModel::DivePlannerPointsModel(QObject *parent) : QAbstractTableModel(parent), DivePlannerPointsModel::DivePlannerPointsModel(QObject *parent) : QAbstractTableModel(parent),
d(nullptr), d(nullptr),
cylinders(true), cylinders(true, false),
mode(NOTHING) mode(NOTHING)
{ {
memset(&diveplan, 0, sizeof(diveplan)); memset(&diveplan, 0, sizeof(diveplan));