summaryrefslogtreecommitdiffstats
path: root/qt-models
diff options
context:
space:
mode:
authorGravatar Dirk Hohndel <dirk@hohndel.org>2020-04-11 11:03:05 -0700
committerGravatar GitHub <noreply@github.com>2020-04-11 11:03:05 -0700
commit6d187b5f4a3b51043fa3b53b6c73a7e0ec7f0b53 (patch)
tree93b26b7381e565ecc0dc6d93e2d35b61a1cdaca9 /qt-models
parent2990010ccde56acec980e76ef5671137e87af488 (diff)
parent7dc04b4437c7aa1788f7d85a513a246ce502dea4 (diff)
downloadsubsurface-6d187b5f4a3b51043fa3b53b6c73a7e0ec7f0b53.tar.gz
Merge pull request #2643 from bstoeger/cylinder4
First steps of cylinder-editing undo
Diffstat (limited to 'qt-models')
-rw-r--r--qt-models/cleanertablemodel.h4
-rw-r--r--qt-models/cylindermodel.cpp461
-rw-r--r--qt-models/cylindermodel.h31
-rw-r--r--qt-models/diveplannermodel.cpp9
-rw-r--r--qt-models/weightmodel.cpp9
-rw-r--r--qt-models/weightmodel.h3
6 files changed, 316 insertions, 201 deletions
diff --git a/qt-models/cleanertablemodel.h b/qt-models/cleanertablemodel.h
index 4299e9002..d54deb248 100644
--- a/qt-models/cleanertablemodel.h
+++ b/qt-models/cleanertablemodel.h
@@ -31,8 +31,4 @@ private:
QStringList headers;
};
-/* Has the string value changed */
-#define CHANGED() \
- (vString = value.toString()) != data(index, role).toString()
-
#endif
diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp
index da8b2eb1e..04abe2fdb 100644
--- a/qt-models/cylindermodel.cpp
+++ b/qt-models/cylindermodel.cpp
@@ -2,29 +2,34 @@
#include "cylindermodel.h"
#include "tankinfomodel.h"
#include "models.h"
+#include "commands/command.h"
#include "core/qthelper.h"
-#include "core/divelist.h" // for mark_divelist_changed()
#include "core/color.h"
#include "qt-models/diveplannermodel.h"
#include "core/gettextfromc.h"
#include "core/subsurface-qt/divelistnotifier.h"
#include "core/subsurface-string.h"
+#include <string>
-CylindersModel::CylindersModel(QObject *parent) :
- CleanerTableModel(parent),
- changed(false),
- rows(0)
+CylindersModel::CylindersModel(bool planner, QObject *parent) : CleanerTableModel(parent),
+ d(nullptr),
+ inPlanner(planner),
+ tempRow(-1),
+ tempCyl(empty_cylinder)
{
// enum {REMOVE, TYPE, SIZE, WORKINGPRESS, START, END, O2, HE, DEPTH, MOD, MND, USE, IS_USED};
setHeaderDataStrings(QStringList() << "" << tr("Type") << tr("Size") << tr("Work press.") << tr("Start press.") << tr("End press.") << tr("O₂%") << tr("He%")
<< tr("Deco switch at") <<tr("Bot. MOD") <<tr("MND") << tr("Use"));
connect(&diveListNotifier, &DiveListNotifier::cylindersReset, this, &CylindersModel::cylindersReset);
+ connect(&diveListNotifier, &DiveListNotifier::cylinderAdded, this, &CylindersModel::cylinderAdded);
+ connect(&diveListNotifier, &DiveListNotifier::cylinderRemoved, this, &CylindersModel::cylinderRemoved);
+ connect(&diveListNotifier, &DiveListNotifier::cylinderEdited, this, &CylindersModel::cylinderEdited);
}
QVariant CylindersModel::headerData(int section, Qt::Orientation orientation, int role) const
{
- if (role == Qt::DisplayRole && orientation == Qt::Horizontal && in_planner() && section == WORKINGPRESS)
+ if (role == Qt::DisplayRole && orientation == Qt::Horizontal && inPlanner && section == WORKINGPRESS)
return tr("Start press.");
else
return CleanerTableModel::headerData(section, orientation, role);
@@ -126,13 +131,12 @@ static QVariant percent_string(fraction_t fraction)
bool CylindersModel::cylinderUsed(int i) const
{
- const struct dive *dive = &displayed_dive;
- if (i < 0 || i >= dive->cylinders.nr)
+ if (i < 0 || i >= d->cylinders.nr)
return false;
- if (is_cylinder_used(dive, i))
+ if (is_cylinder_used(d, i))
return true;
- cylinder_t *cyl = get_cylinder(dive, i);
+ cylinder_t *cyl = get_cylinder(d, i);
if (cyl->start.mbar || cyl->sample_start.mbar ||
cyl->end.mbar || cyl->sample_end.mbar)
return true;
@@ -148,15 +152,15 @@ bool CylindersModel::cylinderUsed(int i) const
QVariant CylindersModel::data(const QModelIndex &index, int role) const
{
- if (!index.isValid() || index.row() >= rows)
+ if (!d || !index.isValid() || index.row() >= d->cylinders.nr)
return QVariant();
- if (index.row() >= displayed_dive.cylinders.nr) {
- qWarning("CylindersModel and displayed_dive are out of sync!");
+ if (index.row() >= d->cylinders.nr) {
+ qWarning("CylindersModel and dive are out of sync!");
return QVariant();
}
- const cylinder_t *cyl = get_cylinder(&displayed_dive, index.row());
+ const cylinder_t *cyl = index.row() == tempRow ? &tempCyl : get_cylinder(d, index.row());
switch (role) {
case Qt::BackgroundRole: {
@@ -227,13 +231,13 @@ QVariant CylindersModel::data(const QModelIndex &index, int role) const
} else {
pressure_t modpO2;
modpO2.mbar = prefs.bottompo2;
- return get_depth_string(gas_mod(cyl->gasmix, modpO2, &displayed_dive, M_OR_FT(1,1)), true);
+ return get_depth_string(gas_mod(cyl->gasmix, modpO2, d, M_OR_FT(1,1)), true);
}
case MND:
if (cyl->bestmix_he)
return QStringLiteral("*");
else
- return get_depth_string(gas_mnd(cyl->gasmix, prefs.bestmixend, &displayed_dive, M_OR_FT(1,1)), true);
+ return get_depth_string(gas_mnd(cyl->gasmix, prefs.bestmixend, d, M_OR_FT(1,1)), true);
break;
case USE:
return gettextFromC::tr(cylinderuse_text[cyl->cylinder_use]);
@@ -246,8 +250,8 @@ QVariant CylindersModel::data(const QModelIndex &index, int role) const
case Qt::DecorationRole:
case Qt::SizeHintRole:
if (index.column() == REMOVE) {
- if ((in_planner() && DivePlannerPointsModel::instance()->tankInUse(index.row())) ||
- (!in_planner() && is_cylinder_prot(&displayed_dive, index.row()))) {
+ if ((inPlanner && DivePlannerPointsModel::instance()->tankInUse(index.row())) ||
+ (!inPlanner && is_cylinder_prot(d, index.row()))) {
return trashForbiddenIcon();
}
return trashIcon();
@@ -256,8 +260,8 @@ QVariant CylindersModel::data(const QModelIndex &index, int role) const
case Qt::ToolTipRole:
switch (index.column()) {
case REMOVE:
- if ((in_planner() && DivePlannerPointsModel::instance()->tankInUse(index.row())) ||
- (!in_planner() && is_cylinder_prot(&displayed_dive, index.row()))) {
+ if ((inPlanner && DivePlannerPointsModel::instance()->tankInUse(index.row())) ||
+ (!inPlanner && is_cylinder_prot(d, index.row()))) {
return tr("This gas is in use. Only cylinders that are not used in the dive can be removed.");
}
return tr("Clicking here will remove this cylinder.");
@@ -285,197 +289,227 @@ QVariant CylindersModel::data(const QModelIndex &index, int role) const
cylinder_t *CylindersModel::cylinderAt(const QModelIndex &index)
{
- return get_cylinder(&displayed_dive, index.row());
+ if (!d)
+ return nullptr;
+ return get_cylinder(d, index.row());
}
bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
- QString vString;
+ if (!d)
+ return false;
- cylinder_t *cyl = cylinderAt(index);
- if (!cyl)
+ int row = index.row();
+ if (row < 0 || row >= d->cylinders.nr)
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
+ // Here we handle a few cases that allow us to set / commit / revert
+ // a temporary row. This is a horribly misuse of the model/view system.
+ // The reason it is done this way is that the planner and the equipment
+ // tab use different model-classes which are not in a superclass / subclass
+ // relationship.
+ switch (role) {
+ case TEMP_ROLE:
+ // TEMP_ROLE means that we are not supposed to write through to the
+ // actual dive, but a temporary cylinder that is displayed while the
+ // user browses throught the cylinder types.
+ initTempCyl(index.row());
+
switch (index.column()) {
+ case TYPE: {
+ QString type = value.toString();
+ if (!same_string(qPrintable(type), tempCyl.type.description)) {
+ free((void *)tempCyl.type.description);
+ tempCyl.type.description = strdup(qPrintable(type));
+ dataChanged(index, index);
+ }
+ return true;
+ }
case SIZE:
- if (cyl->type.size.mliter != value.toInt()) {
- cyl->type.size.mliter = value.toInt();
+ if (tempCyl.type.size.mliter != value.toInt()) {
+ tempCyl.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();
+ if (tempCyl.type.workingpressure.mbar != value.toInt()) {
+ tempCyl.type.workingpressure.mbar = value.toInt();
dataChanged(index, index);
}
return true;
}
return false;
+ case COMMIT_ROLE:
+ commitTempCyl(index.row());
+ return true;
+ case REVERT_ROLE:
+ clearTempCyl();
+ return true;
+ default:
+ break;
}
+ QString vString = value.toString();
+ bool changed = vString != data(index, role).toString();
+
+ std::string newType; // If we allocate a new type string, this makes sure that it is freed at the end of the function
+
+ // First, we make a shallow copy of the old cylinder. Then we modify the fields inside that copy.
+ // At the end, we either place an EditCylinder undo command (EquipmentTab) or copy the cylinder back (planner).
+ // Yes, this is not ideal, but the pragmatic thing to do for now.
+ cylinder_t cyl = d->cylinders.cylinders[row];
+
+ if (index.column() != TYPE && !changed)
+ return false;
+
+ Command::EditCylinderType type = Command::EditCylinderType::TYPE;
switch (index.column()) {
- case TYPE: {
- QString type = value.toString();
- if (!same_string(qPrintable(type), cyl->type.description)) {
- free((void *)cyl->type.description);
- cyl->type.description = strdup(qPrintable(type));
- changed = true;
- }
- }
+ case TYPE:
+ newType = qPrintable(vString);
+ cyl.type.description = newType.c_str();
+ type = Command::EditCylinderType::TYPE;
break;
- case SIZE:
- if (CHANGED()) {
+ case SIZE: {
TankInfoModel *tanks = TankInfoModel::instance();
- QModelIndexList matches = tanks->match(tanks->index(0, 0), Qt::DisplayRole, cyl->type.description);
+ QModelIndexList matches = tanks->match(tanks->index(0, 0), Qt::DisplayRole, cyl.type.description);
- cyl->type.size = string_to_volume(qPrintable(vString), cyl->type.workingpressure);
- mark_divelist_changed(true);
+ cyl.type.size = string_to_volume(qPrintable(vString), cyl.type.workingpressure);
if (!matches.isEmpty())
- tanks->setData(tanks->index(matches.first().row(), TankInfoModel::ML), cyl->type.size.mliter);
- changed = true;
+ tanks->setData(tanks->index(matches.first().row(), TankInfoModel::ML), cyl.type.size.mliter);
}
+ type = Command::EditCylinderType::TYPE;
break;
- case WORKINGPRESS:
- if (CHANGED()) {
+ case WORKINGPRESS: {
TankInfoModel *tanks = TankInfoModel::instance();
- QModelIndexList matches = tanks->match(tanks->index(0, 0), Qt::DisplayRole, cyl->type.description);
- cyl->type.workingpressure = string_to_pressure(qPrintable(vString));
+ QModelIndexList matches = tanks->match(tanks->index(0, 0), Qt::DisplayRole, cyl.type.description);
+ cyl.type.workingpressure = string_to_pressure(qPrintable(vString));
if (!matches.isEmpty())
- tanks->setData(tanks->index(matches.first().row(), TankInfoModel::BAR), cyl->type.workingpressure.mbar / 1000.0);
- changed = true;
+ tanks->setData(tanks->index(matches.first().row(), TankInfoModel::BAR), cyl.type.workingpressure.mbar / 1000.0);
}
+ type = Command::EditCylinderType::TYPE;
break;
case START:
- if (CHANGED()) {
- cyl->start = string_to_pressure(qPrintable(vString));
- changed = true;
- }
+ cyl.start = string_to_pressure(qPrintable(vString));
+ type = Command::EditCylinderType::PRESSURE;
break;
case END:
- if (CHANGED()) {
- //&& (!cyl->start.mbar || string_to_pressure(qPrintable(vString)).mbar <= cyl->start.mbar)) {
- cyl->end = string_to_pressure(qPrintable(vString));
- changed = true;
- }
+ //if (!cyl->start.mbar || string_to_pressure(qPrintable(vString)).mbar <= cyl->start.mbar) {
+ cyl.end = string_to_pressure(qPrintable(vString));
+ type = Command::EditCylinderType::PRESSURE;
break;
- case O2:
- if (CHANGED()) {
- cyl->gasmix.o2 = string_to_fraction(qPrintable(vString));
+ case O2: {
+ cyl.gasmix.o2 = string_to_fraction(qPrintable(vString));
// fO2 + fHe must not be greater than 1
- if (get_o2(cyl->gasmix) + get_he(cyl->gasmix) > 1000)
- cyl->gasmix.he.permille = 1000 - get_o2(cyl->gasmix);
+ if (get_o2(cyl.gasmix) + get_he(cyl.gasmix) > 1000)
+ cyl.gasmix.he.permille = 1000 - get_o2(cyl.gasmix);
pressure_t modpO2;
- if (displayed_dive.dc.divemode == PSCR)
- modpO2.mbar = prefs.decopo2 + (1000 - get_o2(cyl->gasmix)) * SURFACE_PRESSURE *
+ if (d->dc.divemode == PSCR)
+ modpO2.mbar = prefs.decopo2 + (1000 - get_o2(cyl.gasmix)) * SURFACE_PRESSURE *
prefs.o2consumption / prefs.decosac / prefs.pscr_ratio;
else
modpO2.mbar = prefs.decopo2;
- cyl->depth = gas_mod(cyl->gasmix, modpO2, &displayed_dive, M_OR_FT(3, 10));
- cyl->bestmix_o2 = false;
- changed = true;
+ cyl.depth = gas_mod(cyl.gasmix, modpO2, d, M_OR_FT(3, 10));
+ cyl.bestmix_o2 = false;
}
+ type = Command::EditCylinderType::GASMIX;
break;
case HE:
- if (CHANGED()) {
- cyl->gasmix.he = string_to_fraction(qPrintable(vString));
- // fO2 + fHe must not be greater than 1
- if (get_o2(cyl->gasmix) + get_he(cyl->gasmix) > 1000)
- cyl->gasmix.o2.permille = 1000 - get_he(cyl->gasmix);
- cyl->bestmix_he = false;
- changed = true;
- }
+ cyl.gasmix.he = string_to_fraction(qPrintable(vString));
+ // fO2 + fHe must not be greater than 1
+ if (get_o2(cyl.gasmix) + get_he(cyl.gasmix) > 1000)
+ cyl.gasmix.o2.permille = 1000 - get_he(cyl.gasmix);
+ cyl.bestmix_he = false;
+ type = Command::EditCylinderType::GASMIX;
break;
case DEPTH:
- if (CHANGED()) {
- cyl->depth = string_to_depth(qPrintable(vString));
- changed = true;
- }
+ cyl.depth = string_to_depth(qPrintable(vString));
+ type = Command::EditCylinderType::GASMIX;
break;
- case MOD:
- if (CHANGED()) {
+ case MOD: {
if (QString::compare(qPrintable(vString), "*") == 0) {
- cyl->bestmix_o2 = true;
+ cyl.bestmix_o2 = true;
// Calculate fO2 for max. depth
- cyl->gasmix.o2 = best_o2(displayed_dive.maxdepth, &displayed_dive);
+ cyl.gasmix.o2 = best_o2(d->maxdepth, d);
} else {
- cyl->bestmix_o2 = false;
+ cyl.bestmix_o2 = false;
// Calculate fO2 for input depth
- cyl->gasmix.o2 = best_o2(string_to_depth(qPrintable(vString)), &displayed_dive);
+ cyl.gasmix.o2 = best_o2(string_to_depth(qPrintable(vString)), d);
}
pressure_t modpO2;
modpO2.mbar = prefs.decopo2;
- cyl->depth = gas_mod(cyl->gasmix, modpO2, &displayed_dive, M_OR_FT(3, 10));
- changed = true;
+ cyl.depth = gas_mod(cyl.gasmix, modpO2, d, M_OR_FT(3, 10));
}
+ type = Command::EditCylinderType::GASMIX;
break;
case MND:
- if (CHANGED()) {
- if (QString::compare(qPrintable(vString), "*") == 0) {
- cyl->bestmix_he = true;
- // Calculate fO2 for max. depth
- cyl->gasmix.he = best_he(displayed_dive.maxdepth, &displayed_dive, prefs.o2narcotic, cyl->gasmix.o2);
- } else {
- cyl->bestmix_he = false;
- // Calculate fHe for input depth
- cyl->gasmix.he = best_he(string_to_depth(qPrintable(vString)), &displayed_dive, prefs.o2narcotic, cyl->gasmix.o2);
- }
- changed = true;
+ if (QString::compare(qPrintable(vString), "*") == 0) {
+ cyl.bestmix_he = true;
+ // Calculate fO2 for max. depth
+ cyl.gasmix.he = best_he(d->maxdepth, d, prefs.o2narcotic, cyl.gasmix.o2);
+ } else {
+ cyl.bestmix_he = false;
+ // Calculate fHe for input depth
+ cyl.gasmix.he = best_he(string_to_depth(qPrintable(vString)), d, prefs.o2narcotic, cyl.gasmix.o2);
}
+ type = Command::EditCylinderType::GASMIX;
break;
- case USE:
- if (CHANGED()) {
+ case USE: {
int use = vString.toInt();
if (use > NUM_GAS_USE - 1 || use < 0)
use = 0;
- cyl->cylinder_use = (enum cylinderuse)use;
- changed = true;
+ cyl.cylinder_use = (enum cylinderuse)use;
}
+ type = Command::EditCylinderType::TYPE;
break;
}
- dataChanged(index, index);
+
+ if (inPlanner) {
+ // In the planner - simply overwrite the cylinder in the dive with the modified cylinder.
+ // We have only made a shallow copy, therefore copy the new cylinder first.
+ cylinder_t copy = clone_cylinder(cyl);
+ std::swap(copy, d->cylinders.cylinders[row]);
+ free_cylinder(copy);
+ dataChanged(index, index);
+ } else {
+ // On the EquipmentTab - place an editCylinder command.
+ int count = Command::editCylinder(index.row(), cyl, type, false);
+ emit divesEdited(count);
+ }
return true;
}
int CylindersModel::rowCount(const QModelIndex&) const
{
- return rows;
+ return d ? d->cylinders.nr : 0;
}
void CylindersModel::add()
{
- int row = rows;
- cylinder_t cyl = empty_cylinder;
- fill_default_cylinder(&displayed_dive, &cyl);
- cyl.start = cyl.type.workingpressure;
- cyl.manually_added = true;
- cyl.cylinder_use = OC_GAS;
+ if (!d)
+ return;
+ int row = d->cylinders.nr;
+ cylinder_t cyl = create_new_cylinder(d);
beginInsertRows(QModelIndex(), row, row);
- add_to_cylinder_table(&displayed_dive.cylinders, row, cyl);
- rows++;
- changed = true;
+ add_to_cylinder_table(&d->cylinders, row, cyl);
endInsertRows();
emit dataChanged(createIndex(row, 0), createIndex(row, COLUMNS - 1));
}
void CylindersModel::clear()
{
- if (rows > 0) {
- beginRemoveRows(QModelIndex(), 0, rows - 1);
- endRemoveRows();
- }
+ beginResetModel();
+ d = nullptr;
+ endResetModel();
}
-void CylindersModel::updateDive()
+void CylindersModel::updateDive(dive *dIn)
{
#ifdef DEBUG_CYL
- dump_cylinders(&displayed_dive, true);
+ if (d)
+ dump_cylinders(dIn, true);
#endif
beginResetModel();
- rows = displayed_dive.cylinders.nr;
+ d = dIn;
endResetModel();
}
@@ -486,15 +520,19 @@ Qt::ItemFlags CylindersModel::flags(const QModelIndex &index) const
return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
}
+// This function is only invoked from the planner! Therefore, there is
+// no need to check whether we are in the planner. Perhaps move some
+// of this functionality to the planner itself.
void CylindersModel::remove(QModelIndex index)
{
+ if (!d)
+ return;
if (index.column() == USE) {
cylinder_t *cyl = cylinderAt(index);
if (cyl->cylinder_use == OC_GAS)
cyl->cylinder_use = NOT_USED;
else if (cyl->cylinder_use == NOT_USED)
cyl->cylinder_use = OC_GAS;
- changed = true;
dataChanged(index, index);
return;
}
@@ -502,94 +540,120 @@ void CylindersModel::remove(QModelIndex index)
if (index.column() != REMOVE)
return;
- if ((in_planner() && DivePlannerPointsModel::instance()->tankInUse(index.row())) ||
- (!in_planner() && is_cylinder_prot(&displayed_dive, index.row())))
+ if (DivePlannerPointsModel::instance()->tankInUse(index.row()))
return;
beginRemoveRows(QModelIndex(), index.row(), index.row());
- rows--;
- remove_cylinder(&displayed_dive, index.row());
- changed = true;
+ remove_cylinder(d, index.row());
endRemoveRows();
- // Create a mapping of cylinder indices:
- // 1) Fill mapping[0]..mapping[index-1] with 0..index
- // 2) Set mapping[index] to -1
- // 3) Fill mapping[index+1]..mapping[end] with index..
- std::vector<int> mapping(displayed_dive.cylinders.nr + 1);
- std::iota(mapping.begin(), mapping.begin() + index.row(), 0);
- mapping[index.row()] = -1;
- std::iota(mapping.begin() + index.row() + 1, mapping.end(), index.row());
-
- cylinder_renumber(&displayed_dive, &mapping[0]);
- if (in_planner())
- DivePlannerPointsModel::instance()->cylinderRenumber(&mapping[0]);
- changed = true;
+ std::vector<int> mapping = get_cylinder_map_for_remove(d->cylinders.nr + 1, index.row());
+ cylinder_renumber(d, &mapping[0]);
+ DivePlannerPointsModel::instance()->cylinderRenumber(&mapping[0]);
+}
+
+void CylindersModel::cylinderAdded(struct dive *changed, int pos)
+{
+ if (d != changed)
+ return;
+
+ // The row was already inserted by the undo command. Just inform the model.
+ beginInsertRows(QModelIndex(), pos, pos);
+ endInsertRows();
+}
+
+void CylindersModel::cylinderRemoved(struct dive *changed, int pos)
+{
+ if (d != changed)
+ return;
+
+ // The row was already deleted by the undo command. Just inform the model.
+ beginRemoveRows(QModelIndex(), pos, pos);
+ endRemoveRows();
+}
+
+void CylindersModel::cylinderEdited(struct dive *changed, int pos)
+{
+ if (d != changed)
+ return;
+
+ dataChanged(index(pos, TYPE), index(pos, USE));
}
void CylindersModel::moveAtFirst(int cylid)
{
+ if (!d)
+ return;
+
cylinder_t temp_cyl;
beginMoveRows(QModelIndex(), cylid, cylid, QModelIndex(), 0);
- memmove(&temp_cyl, get_cylinder(&displayed_dive, cylid), sizeof(temp_cyl));
+ memmove(&temp_cyl, get_cylinder(d, cylid), sizeof(temp_cyl));
for (int i = cylid - 1; i >= 0; i--)
- memmove(get_cylinder(&displayed_dive, i + 1), get_cylinder(&displayed_dive, i), sizeof(temp_cyl));
- memmove(get_cylinder(&displayed_dive, 0), &temp_cyl, sizeof(temp_cyl));
+ memmove(get_cylinder(d, i + 1), get_cylinder(d, i), sizeof(temp_cyl));
+ memmove(get_cylinder(d, 0), &temp_cyl, sizeof(temp_cyl));
// Create a mapping of cylinder indices:
// 1) Fill mapping[0]..mapping[cyl] with 0..index
// 2) Set mapping[cyl] to 0
// 3) Fill mapping[cyl+1]..mapping[end] with cyl..
- std::vector<int> mapping(displayed_dive.cylinders.nr);
+ std::vector<int> mapping(d->cylinders.nr);
std::iota(mapping.begin(), mapping.begin() + cylid, 1);
mapping[cylid] = 0;
std::iota(mapping.begin() + (cylid + 1), mapping.end(), cylid);
- cylinder_renumber(&displayed_dive, &mapping[0]);
- if (in_planner())
+ cylinder_renumber(d, &mapping[0]);
+ if (inPlanner)
DivePlannerPointsModel::instance()->cylinderRenumber(&mapping[0]);
- changed = true;
endMoveRows();
}
void CylindersModel::updateDecoDepths(pressure_t olddecopo2)
{
+ if (!d)
+ return;
+
pressure_t decopo2;
decopo2.mbar = prefs.decopo2;
- for (int i = 0; i < displayed_dive.cylinders.nr; i++) {
- cylinder_t *cyl = get_cylinder(&displayed_dive, i);
+ for (int i = 0; i < d->cylinders.nr; i++) {
+ cylinder_t *cyl = get_cylinder(d, i);
/* If the gas's deco MOD matches the old pO2, it will have been automatically calculated and should be updated.
* If they don't match, we should leave the user entered depth as it is */
- if (cyl->depth.mm == gas_mod(cyl->gasmix, olddecopo2, &displayed_dive, M_OR_FT(3, 10)).mm) {
- cyl->depth = gas_mod(cyl->gasmix, decopo2, &displayed_dive, M_OR_FT(3, 10));
+ if (cyl->depth.mm == gas_mod(cyl->gasmix, olddecopo2, d, M_OR_FT(3, 10)).mm) {
+ cyl->depth = gas_mod(cyl->gasmix, decopo2, d, M_OR_FT(3, 10));
}
}
- emit dataChanged(createIndex(0, 0), createIndex(displayed_dive.cylinders.nr - 1, COLUMNS - 1));
+ emit dataChanged(createIndex(0, 0), createIndex(d->cylinders.nr - 1, COLUMNS - 1));
}
void CylindersModel::updateTrashIcon()
{
- emit dataChanged(createIndex(0, 0), createIndex(displayed_dive.cylinders.nr - 1, 0));
+ if (!d)
+ return;
+
+ emit dataChanged(createIndex(0, 0), createIndex(d->cylinders.nr - 1, 0));
}
bool CylindersModel::updateBestMixes()
{
+ if (!d)
+ return false;
+
// Check if any of the cylinders are best mixes, update if needed
bool gasUpdated = false;
- for (int i = 0; i < displayed_dive.cylinders.nr; i++) {
- cylinder_t *cyl = get_cylinder(&displayed_dive, i);
+ for (int i = 0; i < d->cylinders.nr; i++) {
+ cylinder_t *cyl = get_cylinder(d, i);
if (cyl->bestmix_o2) {
- cyl->gasmix.o2 = best_o2(displayed_dive.maxdepth, &displayed_dive);
+ cyl->gasmix.o2 = best_o2(d->maxdepth, d);
// fO2 + fHe must not be greater than 1
if (get_o2(cyl->gasmix) + get_he(cyl->gasmix) > 1000)
cyl->gasmix.he.permille = 1000 - get_o2(cyl->gasmix);
pressure_t modpO2;
modpO2.mbar = prefs.decopo2;
- cyl->depth = gas_mod(cyl->gasmix, modpO2, &displayed_dive, M_OR_FT(3, 10));
+ cyl->depth = gas_mod(cyl->gasmix, modpO2, d, M_OR_FT(3, 10));
gasUpdated = true;
}
if (cyl->bestmix_he) {
- cyl->gasmix.he = best_he(displayed_dive.maxdepth, &displayed_dive, prefs.o2narcotic, cyl->gasmix.o2);
+ cyl->gasmix.he = best_he(d->maxdepth, d, prefs.o2narcotic, cyl->gasmix.o2);
// fO2 + fHe must not be greater than 1
if (get_o2(cyl->gasmix) + get_he(cyl->gasmix) > 1000)
cyl->gasmix.o2.permille = 1000 - get_he(cyl->gasmix);
@@ -599,7 +663,7 @@ bool CylindersModel::updateBestMixes()
/* This slot is called when the bottom pO2 and END preferences are updated, we want to
* emit dataChanged so MOD and MND are refreshed, even if the gas mix hasn't been changed */
if (gasUpdated)
- emit dataChanged(createIndex(0, 0), createIndex(displayed_dive.cylinders.nr - 1, COLUMNS - 1));
+ emit dataChanged(createIndex(0, 0), createIndex(d->cylinders.nr - 1, COLUMNS - 1));
return gasUpdated;
}
@@ -607,44 +671,81 @@ void CylindersModel::cylindersReset(const QVector<dive *> &dives)
{
// This model only concerns the currently displayed dive. If this is not among the
// dives that had their cylinders reset, exit.
- if (!current_dive || std::find(dives.begin(), dives.end(), current_dive) == dives.end())
+ if (!d || std::find(dives.begin(), dives.end(), d) == dives.end())
return;
- // Copy the cylinders from the current dive to the displayed dive.
- copy_cylinders(&current_dive->cylinders, &displayed_dive.cylinders);
+ // And update the model (the actual change was already performed in the backend)..
+ beginResetModel();
+ endResetModel();
+}
+
+// Save the cylinder in the given row so that we can revert if the user cancels a type-editing action.
+void CylindersModel::initTempCyl(int row)
+{
+ if (!d || tempRow == row)
+ return;
+ clearTempCyl();
+ const cylinder_t *cyl = get_cylinder(d, row);
+ if (!cyl)
+ return;
- // And update the model..
- updateDive();
+ tempRow = row;
+ tempCyl = clone_cylinder(*cyl);
+
+ dataChanged(index(row, TYPE), index(row, USE));
}
-CylindersModelFiltered::CylindersModelFiltered(QObject *parent) : QSortFilterProxyModel(parent)
+void CylindersModel::clearTempCyl()
{
- setSourceModel(&source);
+ if (tempRow < 0)
+ return;
+ int oldRow = tempRow;
+ tempRow = -1;
+ free_cylinder(tempCyl);
+ dataChanged(index(oldRow, TYPE), index(oldRow, USE));
}
-void CylindersModelFiltered::updateDive()
+void CylindersModel::commitTempCyl(int row)
{
- source.updateDive();
+ if (tempRow < 0)
+ return;
+ if (row != tempRow)
+ return clearTempCyl(); // Huh? We are supposed to commit a different row than the one we stored?
+ cylinder_t *cyl = get_cylinder(d, tempRow);
+ if (!cyl)
+ return;
+ // Only submit a command if the type changed
+ if (!same_string(cyl->type.description, tempCyl.type.description) || gettextFromC::tr(cyl->type.description) != QString(tempCyl.type.description)) {
+ if (inPlanner) {
+ std::swap(*cyl, tempCyl);
+ } else {
+ int count = Command::editCylinder(tempRow, tempCyl, Command::EditCylinderType::TYPE, false);
+ emit divesEdited(count);
+ }
+ }
+ free_cylinder(tempCyl);
+ tempRow = -1;
}
-void CylindersModelFiltered::clear()
+CylindersModelFiltered::CylindersModelFiltered(QObject *parent) : QSortFilterProxyModel(parent),
+ source(false) // Currently, only the EquipmentTab uses the filtered model.
{
- source.clear();
+ setSourceModel(&source);
}
-void CylindersModelFiltered::add()
+void CylindersModelFiltered::updateDive(dive *d)
{
- source.add();
+ source.updateDive(d);
}
-CylindersModel *CylindersModelFiltered::model()
+void CylindersModelFiltered::clear()
{
- return &source;
+ source.clear();
}
-void CylindersModelFiltered::remove(QModelIndex index)
+CylindersModel *CylindersModelFiltered::model()
{
- source.remove(mapToSource(index));
+ return &source;
}
bool CylindersModelFiltered::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
diff --git a/qt-models/cylindermodel.h b/qt-models/cylindermodel.h
index 5cfe72d2d..099a3beb2 100644
--- a/qt-models/cylindermodel.h
+++ b/qt-models/cylindermodel.h
@@ -31,9 +31,11 @@ public:
};
enum Roles {
- PASS_IN_ROLE = Qt::UserRole + 1 // For setting data: don't do any conversions
+ TEMP_ROLE = Qt::UserRole + 1, // Temporarily set data, but don't store in dive
+ 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.
};
- explicit CylindersModel(QObject *parent = 0);
+ explicit CylindersModel(bool planner, 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;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
@@ -41,23 +43,36 @@ public:
void add();
void clear();
- void updateDive();
+ void updateDive(dive *d);
void updateDecoDepths(pressure_t olddecopo2);
void updateTrashIcon();
void moveAtFirst(int cylid);
- bool changed;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
bool updateBestMixes();
bool cylinderUsed(int i) const;
+signals:
+ void divesEdited(int num);
+
public
slots:
void remove(QModelIndex index);
void cylindersReset(const QVector<dive *> &dives);
+ void cylinderAdded(dive *d, int pos);
+ void cylinderRemoved(dive *d, int pos);
+ void cylinderEdited(dive *d, int pos);
private:
- int rows;
+ dive *d;
+ bool inPlanner;
+ // Used if we temporarily change a line because the user is selecting a weight type
+ int tempRow;
+ cylinder_t tempCyl;
+
cylinder_t *cylinderAt(const QModelIndex &index);
+ void initTempCyl(int row);
+ void clearTempCyl();
+ void commitTempCyl(int row);
};
// Cylinder model that hides unused cylinders if the pref.show_unused_cylinders flag is not set
@@ -68,11 +83,7 @@ public:
CylindersModel *model(); // Access to unfiltered base model
void clear();
- void add();
- void updateDive();
-public
-slots:
- void remove(QModelIndex index);
+ void updateDive(dive *d);
private:
CylindersModel source;
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp
index df7affe8b..09d08c79a 100644
--- a/qt-models/diveplannermodel.cpp
+++ b/qt-models/diveplannermodel.cpp
@@ -92,7 +92,7 @@ void DivePlannerPointsModel::loadFromDive(dive *d)
const struct event *evd = NULL;
enum divemode_t current_divemode = UNDEF_COMP_TYPE;
recalc = false;
- cylinders.updateDive();
+ cylinders.updateDive(&displayed_dive);
duration_t lasttime = { 0 };
duration_t lastrecordedtime = {};
duration_t newtime = {};
@@ -165,7 +165,7 @@ void DivePlannerPointsModel::setupCylinders()
reset_cylinders(&displayed_dive, true);
if (displayed_dive.cylinders.nr > 0) {
- cylinders.updateDive();
+ cylinders.updateDive(&displayed_dive);
return; // We have at least one cylinder
}
}
@@ -183,7 +183,7 @@ void DivePlannerPointsModel::setupCylinders()
add_to_cylinder_table(&displayed_dive.cylinders, 0, cyl);
}
reset_cylinders(&displayed_dive, false);
- cylinders.updateDive();
+ cylinders.updateDive(&displayed_dive);
}
// Update the dive's maximum depth. Returns true if max. depth changed
@@ -415,6 +415,7 @@ int DivePlannerPointsModel::rowCount(const QModelIndex&) const
}
DivePlannerPointsModel::DivePlannerPointsModel(QObject *parent) : QAbstractTableModel(parent),
+ cylinders(true),
mode(NOTHING),
recalc(false)
{
@@ -908,7 +909,7 @@ void DivePlannerPointsModel::clear()
{
bool oldRecalc = setRecalc(false);
- cylinders.updateDive();
+ cylinders.updateDive(&displayed_dive);
if (rowCount() > 0) {
beginRemoveRows(QModelIndex(), 0, rowCount() - 1);
divepoints.clear();
diff --git a/qt-models/weightmodel.cpp b/qt-models/weightmodel.cpp
index 6e56071e2..6b5f9e70d 100644
--- a/qt-models/weightmodel.cpp
+++ b/qt-models/weightmodel.cpp
@@ -112,8 +112,10 @@ void WeightModel::commitTempWS()
return;
// Only submit a command if the type changed
weightsystem_t ws = d->weightsystems.weightsystems[tempRow];
- if (!same_string(ws.description, tempWS.description) || gettextFromC::tr(ws.description) != QString(tempWS.description))
- Command::editWeight(tempRow, tempWS, false);
+ if (!same_string(ws.description, tempWS.description) || gettextFromC::tr(ws.description) != QString(tempWS.description)) {
+ int count = Command::editWeight(tempRow, tempWS, false);
+ emit divesEdited(count);
+ }
tempRow = -1;
#endif
}
@@ -126,7 +128,8 @@ bool WeightModel::setData(const QModelIndex &index, const QVariant &value, int r
switch (index.column()) {
case WEIGHT:
ws.weight = string_to_weight(qPrintable(vString));
- Command::editWeight(index.row(), ws, false);
+ int count = Command::editWeight(index.row(), ws, false);
+ emit divesEdited(count);
return true;
}
return false;
diff --git a/qt-models/weightmodel.h b/qt-models/weightmodel.h
index 950e96d2b..b1df4fc8e 100644
--- a/qt-models/weightmodel.h
+++ b/qt-models/weightmodel.h
@@ -29,6 +29,9 @@ public:
void updateDive(dive *d);
weightsystem_t weightSystemAt(const QModelIndex &index) const;
+signals:
+ void divesEdited(int num);
+
public
slots:
void weightsystemsReset(const QVector<dive *> &dives);