From e008b42a591b83eb1304a552fdd950287ddbc375 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 1 Mar 2020 16:26:47 +0100 Subject: core: add create_new_cylinder() function Turn the code in CylindersModel that creates a new cylinder for addition into its own function to avoid code duplication. This will be used from the undo commands. Signed-off-by: Berthold Stoeger --- qt-models/cylindermodel.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'qt-models') diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index da8b2eb1e..428d3c868 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -448,11 +448,7 @@ int CylindersModel::rowCount(const QModelIndex&) const 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; + cylinder_t cyl = create_new_cylinder(&displayed_dive); beginInsertRows(QModelIndex(), row, row); add_to_cylinder_table(&displayed_dive.cylinders, row, cyl); rows++; -- cgit v1.2.3-70-g09d2 From 30d289e4a8a0caadcf30f06872ce69f44cf0bc0d Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 23 Feb 2020 12:44:09 +0100 Subject: CylinderModel: make dive dynamic The CylinderModel always accessed the global "displayed_dive" and in some special cases also "current_dive". To implement cylinder undo, the model should work on an arbitrary dive. Therefore, in analogy to the weight model, make the dive dynamic. Signed-off-by: Berthold Stoeger --- desktop-widgets/mainwindow.cpp | 2 +- desktop-widgets/tab-widgets/TabDiveEquipment.cpp | 4 +- qt-models/cylindermodel.cpp | 133 +++++++++++++---------- qt-models/cylindermodel.h | 5 +- qt-models/diveplannermodel.cpp | 8 +- 5 files changed, 87 insertions(+), 65 deletions(-) (limited to 'qt-models') diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index 8474eee71..78877d1a5 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -918,7 +918,7 @@ void MainWindow::on_actionReplanDive_triggered() divePlannerWidget->setSalinity(current_dive->salinity); DivePlannerPointsModel::instance()->loadFromDive(current_dive); reset_cylinders(&displayed_dive, true); - DivePlannerPointsModel::instance()->cylindersModel()->updateDive(); + DivePlannerPointsModel::instance()->cylindersModel()->updateDive(&displayed_dive); } void MainWindow::on_actionDivePlanner_triggered() diff --git a/desktop-widgets/tab-widgets/TabDiveEquipment.cpp b/desktop-widgets/tab-widgets/TabDiveEquipment.cpp index 33a8e5a3c..d34b44c74 100644 --- a/desktop-widgets/tab-widgets/TabDiveEquipment.cpp +++ b/desktop-widgets/tab-widgets/TabDiveEquipment.cpp @@ -129,7 +129,7 @@ void TabDiveEquipment::toggleTriggeredColumn() void TabDiveEquipment::updateData() { - cylindersModel->updateDive(); + cylindersModel->updateDive(current_dive); weightModel->updateDive(current_dive); suitModel.updateModel(); @@ -262,7 +262,7 @@ void TabDiveEquipment::acceptChanges() void TabDiveEquipment::rejectChanges() { cylindersModel->model()->changed = false; - cylindersModel->updateDive(); + cylindersModel->updateDive(current_dive); weightModel->updateDive(current_dive); } diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index 428d3c868..5a9fe0f5e 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -10,9 +10,9 @@ #include "core/subsurface-qt/divelistnotifier.h" #include "core/subsurface-string.h" -CylindersModel::CylindersModel(QObject *parent) : - CleanerTableModel(parent), +CylindersModel::CylindersModel(QObject *parent) : CleanerTableModel(parent), changed(false), + d(nullptr), rows(0) { // enum {REMOVE, TYPE, SIZE, WORKINGPRESS, START, END, O2, HE, DEPTH, MOD, MND, USE, IS_USED}; @@ -126,13 +126,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 +147,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() >= rows) 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 = get_cylinder(d, index.row()); switch (role) { case Qt::BackgroundRole: { @@ -227,13 +226,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]); @@ -247,7 +246,7 @@ QVariant CylindersModel::data(const QModelIndex &index, int role) const case Qt::SizeHintRole: if (index.column() == REMOVE) { if ((in_planner() && DivePlannerPointsModel::instance()->tankInUse(index.row())) || - (!in_planner() && is_cylinder_prot(&displayed_dive, index.row()))) { + (!in_planner() && is_cylinder_prot(d, index.row()))) { return trashForbiddenIcon(); } return trashIcon(); @@ -257,7 +256,7 @@ QVariant CylindersModel::data(const QModelIndex &index, int role) const switch (index.column()) { case REMOVE: if ((in_planner() && DivePlannerPointsModel::instance()->tankInUse(index.row())) || - (!in_planner() && is_cylinder_prot(&displayed_dive, index.row()))) { + (!in_planner() && 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,13 +284,18 @@ 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) return false; @@ -369,12 +373,12 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in 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) + 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->depth = gas_mod(cyl->gasmix, modpO2, d, M_OR_FT(3, 10)); cyl->bestmix_o2 = false; changed = true; } @@ -400,15 +404,15 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in if (QString::compare(qPrintable(vString), "*") == 0) { 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; // 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)); + cyl->depth = gas_mod(cyl->gasmix, modpO2, d, M_OR_FT(3, 10)); changed = true; } break; @@ -417,11 +421,11 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in 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); + 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)), &displayed_dive, prefs.o2narcotic, cyl->gasmix.o2); + cyl->gasmix.he = best_he(string_to_depth(qPrintable(vString)), d, prefs.o2narcotic, cyl->gasmix.o2); } changed = true; } @@ -447,10 +451,12 @@ int CylindersModel::rowCount(const QModelIndex&) const void CylindersModel::add() { + if (!d) + return; int row = rows; - cylinder_t cyl = create_new_cylinder(&displayed_dive); + cylinder_t cyl = create_new_cylinder(d); beginInsertRows(QModelIndex(), row, row); - add_to_cylinder_table(&displayed_dive.cylinders, row, cyl); + add_to_cylinder_table(&d->cylinders, row, cyl); rows++; changed = true; endInsertRows(); @@ -465,13 +471,15 @@ void CylindersModel::clear() } } -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; + rows = d ? d->cylinders.nr : 0; endResetModel(); } @@ -484,6 +492,8 @@ Qt::ItemFlags CylindersModel::flags(const QModelIndex &index) const void CylindersModel::remove(QModelIndex index) { + if (!d) + return; if (index.column() == USE) { cylinder_t *cyl = cylinderAt(index); if (cyl->cylinder_use == OC_GAS) @@ -499,12 +509,12 @@ void CylindersModel::remove(QModelIndex index) return; if ((in_planner() && DivePlannerPointsModel::instance()->tankInUse(index.row())) || - (!in_planner() && is_cylinder_prot(&displayed_dive, index.row()))) + (!in_planner() && is_cylinder_prot(d, index.row()))) return; beginRemoveRows(QModelIndex(), index.row(), index.row()); rows--; - remove_cylinder(&displayed_dive, index.row()); + remove_cylinder(d, index.row()); changed = true; endRemoveRows(); @@ -512,12 +522,12 @@ void CylindersModel::remove(QModelIndex index) // 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 mapping(displayed_dive.cylinders.nr + 1); + std::vector mapping(d->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]); + cylinder_renumber(d, &mapping[0]); if (in_planner()) DivePlannerPointsModel::instance()->cylinderRenumber(&mapping[0]); changed = true; @@ -525,23 +535,26 @@ void CylindersModel::remove(QModelIndex index) 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 mapping(displayed_dive.cylinders.nr); + std::vector 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]); + cylinder_renumber(d, &mapping[0]); if (in_planner()) DivePlannerPointsModel::instance()->cylinderRenumber(&mapping[0]); changed = true; @@ -550,42 +563,51 @@ void CylindersModel::moveAtFirst(int cylid) 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); @@ -595,7 +617,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; } @@ -603,14 +625,13 @@ void CylindersModel::cylindersReset(const QVector &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(¤t_dive->cylinders, &displayed_dive.cylinders); - - // And update the model.. - updateDive(); + // And update the model (the actual change was already performed in the backend).. + beginResetModel(); + rows = d->cylinders.nr; + endResetModel(); } CylindersModelFiltered::CylindersModelFiltered(QObject *parent) : QSortFilterProxyModel(parent) @@ -618,9 +639,9 @@ CylindersModelFiltered::CylindersModelFiltered(QObject *parent) : QSortFilterPro setSourceModel(&source); } -void CylindersModelFiltered::updateDive() +void CylindersModelFiltered::updateDive(dive *d) { - source.updateDive(); + source.updateDive(d); } void CylindersModelFiltered::clear() diff --git a/qt-models/cylindermodel.h b/qt-models/cylindermodel.h index 5cfe72d2d..201459b72 100644 --- a/qt-models/cylindermodel.h +++ b/qt-models/cylindermodel.h @@ -41,7 +41,7 @@ public: void add(); void clear(); - void updateDive(); + void updateDive(dive *d); void updateDecoDepths(pressure_t olddecopo2); void updateTrashIcon(); void moveAtFirst(int cylid); @@ -56,6 +56,7 @@ slots: void cylindersReset(const QVector &dives); private: + dive *d; int rows; cylinder_t *cylinderAt(const QModelIndex &index); }; @@ -69,7 +70,7 @@ public: void clear(); void add(); - void updateDive(); + void updateDive(dive *d); public slots: void remove(QModelIndex index); diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index df7affe8b..4928900c4 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 @@ -908,7 +908,7 @@ void DivePlannerPointsModel::clear() { bool oldRecalc = setRecalc(false); - cylinders.updateDive(); + cylinders.updateDive(&displayed_dive); if (rowCount() > 0) { beginRemoveRows(QModelIndex(), 0, rowCount() - 1); divepoints.clear(); -- cgit v1.2.3-70-g09d2 From a37939889b1a77dd269fd7f25f97b813f733133a Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 23 Feb 2020 12:52:45 +0100 Subject: cleanup: remove CylindersModels::rows Access the number of cylinders in the dive directly instead. Signed-off-by: Berthold Stoeger --- qt-models/cylindermodel.cpp | 20 +++++++------------- qt-models/cylindermodel.h | 1 - 2 files changed, 7 insertions(+), 14 deletions(-) (limited to 'qt-models') diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index 5a9fe0f5e..4b329eafe 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -12,8 +12,7 @@ CylindersModel::CylindersModel(QObject *parent) : CleanerTableModel(parent), changed(false), - d(nullptr), - rows(0) + d(nullptr) { // 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%") @@ -147,7 +146,7 @@ bool CylindersModel::cylinderUsed(int i) const QVariant CylindersModel::data(const QModelIndex &index, int role) const { - if (!d || !index.isValid() || index.row() >= rows) + if (!d || !index.isValid() || index.row() >= d->cylinders.nr) return QVariant(); if (index.row() >= d->cylinders.nr) { @@ -446,18 +445,17 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in int CylindersModel::rowCount(const QModelIndex&) const { - return rows; + return d ? d->cylinders.nr : 0; } void CylindersModel::add() { if (!d) return; - int row = rows; + int row = d->cylinders.nr; cylinder_t cyl = create_new_cylinder(d); beginInsertRows(QModelIndex(), row, row); add_to_cylinder_table(&d->cylinders, row, cyl); - rows++; changed = true; endInsertRows(); emit dataChanged(createIndex(row, 0), createIndex(row, COLUMNS - 1)); @@ -465,10 +463,9 @@ void CylindersModel::add() void CylindersModel::clear() { - if (rows > 0) { - beginRemoveRows(QModelIndex(), 0, rows - 1); - endRemoveRows(); - } + beginResetModel(); + d = nullptr; + endResetModel(); } void CylindersModel::updateDive(dive *dIn) @@ -479,7 +476,6 @@ void CylindersModel::updateDive(dive *dIn) #endif beginResetModel(); d = dIn; - rows = d ? d->cylinders.nr : 0; endResetModel(); } @@ -513,7 +509,6 @@ void CylindersModel::remove(QModelIndex index) return; beginRemoveRows(QModelIndex(), index.row(), index.row()); - rows--; remove_cylinder(d, index.row()); changed = true; endRemoveRows(); @@ -630,7 +625,6 @@ void CylindersModel::cylindersReset(const QVector &dives) // And update the model (the actual change was already performed in the backend).. beginResetModel(); - rows = d->cylinders.nr; endResetModel(); } diff --git a/qt-models/cylindermodel.h b/qt-models/cylindermodel.h index 201459b72..40eaf98a1 100644 --- a/qt-models/cylindermodel.h +++ b/qt-models/cylindermodel.h @@ -57,7 +57,6 @@ slots: private: dive *d; - int rows; cylinder_t *cylinderAt(const QModelIndex &index); }; -- cgit v1.2.3-70-g09d2 From 5b7a3165932d9b3fced80fec17de01eb1ad89bf7 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 23 Feb 2020 19:24:06 +0100 Subject: undo: reorder cylinders on remove-cylinder undo/redo The cylinders in the events must be reordered if we remove a cylinder. To avoid duplication of code, move the reordering function into qthelper.cpp, though it might not be ideal there. Signed-off-by: Berthold Stoeger --- commands/command_edit.cpp | 3 +++ core/qthelper.cpp | 33 +++++++++++++++++++++++++++++++++ core/qthelper.h | 2 ++ qt-models/cylindermodel.cpp | 10 +--------- 4 files changed, 39 insertions(+), 9 deletions(-) (limited to 'qt-models') diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index c06ef1ffa..3ba7a2823 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -1106,6 +1106,7 @@ RemoveCylinder::RemoveCylinder(int index, bool currentDiveOnly) : void RemoveCylinder::undo() { for (size_t i = 0; i < dives.size(); ++i) { + std::vector mapping = get_cylinder_map_for_add(dives[i]->cylinders.nr, indexes[i]); add_to_cylinder_table(&dives[i]->cylinders, indexes[i], clone_cylinder(cyl)); emit diveListNotifier.cylinderAdded(dives[i], indexes[i]); } @@ -1114,7 +1115,9 @@ void RemoveCylinder::undo() void RemoveCylinder::redo() { for (size_t i = 0; i < dives.size(); ++i) { + std::vector mapping = get_cylinder_map_for_remove(dives[i]->cylinders.nr, indexes[i]); remove_cylinder(dives[i], indexes[i]); + cylinder_renumber(dives[i], &mapping[0]); emit diveListNotifier.cylinderRemoved(dives[i], indexes[i]); } } diff --git a/core/qthelper.cpp b/core/qthelper.cpp index 7b879e7d6..10e62eabe 100644 --- a/core/qthelper.cpp +++ b/core/qthelper.cpp @@ -1661,3 +1661,36 @@ extern "C" char *get_changes_made() else return nullptr; } + +// Generate a cylinder-renumber map for use when the n-th cylinder +// of a dive with count cylinders is removed. It fills an int vector +// with 0..n, -1, n..count-1. Each entry in the vector represents +// the new id of the cylinder, whereby <0 means that this particular +// cylinder does not get any new id. This should probably be moved +// to the C-core, but using std::vector is simply more convenient. +// The function assumes that n < count! +std::vector get_cylinder_map_for_remove(int count, int n) +{ + // 1) Fill mapping[0]..mapping[n-1] with 0..n-1 + // 2) Set mapping[n] to -1 + // 3) Fill mapping[n+1]..mapping[count-1] with n..count-2 + std::vector mapping(count); + std::iota(mapping.begin(), mapping.begin() + n, 0); + mapping[n] = -1; + std::iota(mapping.begin() + n + 1, mapping.end(), n); + return mapping; +} + +// Generate a cylinder-renumber map for use when a cylinder is added +// before the n-th cylinder. It fills an int vector with +// with 0..n-1, n+1..count. Each entry in the vector represents +// the new id of the cylinder. This probably should be moved +// to the C-core, but using std::vector is simply more convenient. +// This function assumes that that n <= count! +std::vector get_cylinder_map_for_add(int count, int n) +{ + std::vector mapping(count); + std::iota(mapping.begin(), mapping.begin() + n, 0); + std::iota(mapping.begin() + n, mapping.end(), n + 1); + return mapping; +} diff --git a/core/qthelper.h b/core/qthelper.h index bb8876383..848763138 100644 --- a/core/qthelper.h +++ b/core/qthelper.h @@ -83,6 +83,8 @@ QLocale getLocale(); QVector> selectedDivesGasUsed(); QString getUserAgent(); QString printGPSCoords(const location_t *loc); +std::vector get_cylinder_map_for_remove(int count, int n); +std::vector get_cylinder_map_for_add(int count, int n); extern QString (*changesCallback)(); void uiNotification(const QString &msg); diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index 4b329eafe..3a3f14605 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -513,15 +513,7 @@ void CylindersModel::remove(QModelIndex index) changed = true; 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 mapping(d->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()); - + std::vector mapping = get_cylinder_map_for_remove(d->cylinders.nr + 1, index.row()); cylinder_renumber(d, &mapping[0]); if (in_planner()) DivePlannerPointsModel::instance()->cylinderRenumber(&mapping[0]); -- cgit v1.2.3-70-g09d2 From 75f37a7d10b4c650552a344770c92a2038e9db34 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Mon, 24 Feb 2020 11:21:27 +0100 Subject: CylindersModel: don't test for planner-state in remove() This is only called from the planner. Therefore, the test is redundant. Signed-off-by: Berthold Stoeger --- qt-models/cylindermodel.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'qt-models') diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index 3a3f14605..7864da76c 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -486,6 +486,9 @@ 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) @@ -504,8 +507,7 @@ void CylindersModel::remove(QModelIndex index) if (index.column() != REMOVE) return; - if ((in_planner() && DivePlannerPointsModel::instance()->tankInUse(index.row())) || - (!in_planner() && is_cylinder_prot(d, index.row()))) + if (DivePlannerPointsModel::instance()->tankInUse(index.row())) return; beginRemoveRows(QModelIndex(), index.row(), index.row()); @@ -515,8 +517,7 @@ void CylindersModel::remove(QModelIndex index) std::vector mapping = get_cylinder_map_for_remove(d->cylinders.nr + 1, index.row()); cylinder_renumber(d, &mapping[0]); - if (in_planner()) - DivePlannerPointsModel::instance()->cylinderRenumber(&mapping[0]); + DivePlannerPointsModel::instance()->cylinderRenumber(&mapping[0]); changed = true; } -- cgit v1.2.3-70-g09d2 From 04ce908e8ed6239b8f143b3892da0dcdaa6e8262 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 26 Feb 2020 22:32:04 +0100 Subject: CylindersModel: listen and react to signals React to signals from the undo-commands and update the model accordingly. Signed-off-by: Berthold Stoeger --- qt-models/cylindermodel.cpp | 31 +++++++++++++++++++++++++++++++ qt-models/cylindermodel.h | 3 +++ 2 files changed, 34 insertions(+) (limited to 'qt-models') diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index 7864da76c..8938bfbff 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -19,6 +19,9 @@ CylindersModel::CylindersModel(QObject *parent) : CleanerTableModel(parent), << tr("Deco switch at") < &dives); + void cylinderAdded(dive *d, int pos); + void cylinderRemoved(dive *d, int pos); + void cylinderEdited(dive *d, int pos); private: dive *d; -- cgit v1.2.3-70-g09d2 From d02489f8884d4fd5282d335727eeb68105fafc80 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Thu, 27 Feb 2020 18:46:13 +0100 Subject: cylinders: remove CylindersModel::changed Nobody is testing that flag anymore. Signed-off-by: Berthold Stoeger --- qt-models/cylindermodel.cpp | 26 +++----------------------- qt-models/cylindermodel.h | 1 - 2 files changed, 3 insertions(+), 24 deletions(-) (limited to 'qt-models') diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index 8938bfbff..d69fba3ae 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -11,7 +11,6 @@ #include "core/subsurface-string.h" CylindersModel::CylindersModel(QObject *parent) : CleanerTableModel(parent), - changed(false), d(nullptr) { // enum {REMOVE, TYPE, SIZE, WORKINGPRESS, START, END, O2, HE, DEPTH, MOD, MND, USE, IS_USED}; @@ -329,7 +328,6 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in if (!same_string(qPrintable(type), cyl->type.description)) { free((void *)cyl->type.description); cyl->type.description = strdup(qPrintable(type)); - changed = true; } } break; @@ -342,7 +340,6 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in mark_divelist_changed(true); if (!matches.isEmpty()) tanks->setData(tanks->index(matches.first().row(), TankInfoModel::ML), cyl->type.size.mliter); - changed = true; } break; case WORKINGPRESS: @@ -352,21 +349,16 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in 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; } break; case START: - if (CHANGED()) { + if (CHANGED()) cyl->start = string_to_pressure(qPrintable(vString)); - changed = true; - } break; case END: - if (CHANGED()) { + if (CHANGED()) //&& (!cyl->start.mbar || string_to_pressure(qPrintable(vString)).mbar <= cyl->start.mbar)) { cyl->end = string_to_pressure(qPrintable(vString)); - changed = true; - } break; case O2: if (CHANGED()) { @@ -382,7 +374,6 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in modpO2.mbar = prefs.decopo2; cyl->depth = gas_mod(cyl->gasmix, modpO2, d, M_OR_FT(3, 10)); cyl->bestmix_o2 = false; - changed = true; } break; case HE: @@ -392,14 +383,11 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in 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; } break; case DEPTH: - if (CHANGED()) { + if (CHANGED()) cyl->depth = string_to_depth(qPrintable(vString)); - changed = true; - } break; case MOD: if (CHANGED()) { @@ -415,7 +403,6 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in pressure_t modpO2; modpO2.mbar = prefs.decopo2; cyl->depth = gas_mod(cyl->gasmix, modpO2, d, M_OR_FT(3, 10)); - changed = true; } break; case MND: @@ -429,7 +416,6 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in // Calculate fHe for input depth cyl->gasmix.he = best_he(string_to_depth(qPrintable(vString)), d, prefs.o2narcotic, cyl->gasmix.o2); } - changed = true; } break; case USE: @@ -438,7 +424,6 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in if (use > NUM_GAS_USE - 1 || use < 0) use = 0; cyl->cylinder_use = (enum cylinderuse)use; - changed = true; } break; } @@ -459,7 +444,6 @@ void CylindersModel::add() cylinder_t cyl = create_new_cylinder(d); beginInsertRows(QModelIndex(), row, row); add_to_cylinder_table(&d->cylinders, row, cyl); - changed = true; endInsertRows(); emit dataChanged(createIndex(row, 0), createIndex(row, COLUMNS - 1)); } @@ -502,7 +486,6 @@ void CylindersModel::remove(QModelIndex index) cyl->cylinder_use = NOT_USED; else if (cyl->cylinder_use == NOT_USED) cyl->cylinder_use = OC_GAS; - changed = true; dataChanged(index, index); return; } @@ -515,13 +498,11 @@ void CylindersModel::remove(QModelIndex index) beginRemoveRows(QModelIndex(), index.row(), index.row()); remove_cylinder(d, index.row()); - changed = true; endRemoveRows(); std::vector mapping = get_cylinder_map_for_remove(d->cylinders.nr + 1, index.row()); cylinder_renumber(d, &mapping[0]); DivePlannerPointsModel::instance()->cylinderRenumber(&mapping[0]); - changed = true; } void CylindersModel::cylinderAdded(struct dive *changed, int pos) @@ -576,7 +557,6 @@ void CylindersModel::moveAtFirst(int cylid) cylinder_renumber(d, &mapping[0]); if (in_planner()) DivePlannerPointsModel::instance()->cylinderRenumber(&mapping[0]); - changed = true; endMoveRows(); } diff --git a/qt-models/cylindermodel.h b/qt-models/cylindermodel.h index 85a59c9f5..ebebb46ab 100644 --- a/qt-models/cylindermodel.h +++ b/qt-models/cylindermodel.h @@ -45,7 +45,6 @@ public: 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; -- cgit v1.2.3-70-g09d2 From a4a06c48bfe2fa6f1644d68bf82e98c196aed7d0 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Thu, 27 Feb 2020 18:51:13 +0100 Subject: CylindersModel: remove mark_dive_list_changed Cylinder-editing is controlled by undo (either by saving a planned dive or by using the equipment tab). There is no point in setting the dive_list_changed flag. Signed-off-by: Berthold Stoeger --- qt-models/cylindermodel.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'qt-models') diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index d69fba3ae..9949ee98b 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -3,7 +3,6 @@ #include "tankinfomodel.h" #include "models.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" @@ -337,7 +336,6 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in 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); if (!matches.isEmpty()) tanks->setData(tanks->index(matches.first().row(), TankInfoModel::ML), cyl->type.size.mliter); } -- cgit v1.2.3-70-g09d2 From a75360f58f60d1cdbf102e58997555f490969144 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Thu, 27 Feb 2020 19:01:43 +0100 Subject: CylindersModel: test for CHANGED() outside of switch statement A small code consolidation: With one exception, all targets of the switch statement would test for CHANGED(). Instead do the test once and exit early. This changes the behavior of the function: if not changed, there will be no more dataChanged-signal. However, this appears to be the correct thing to do anyway. And it is easily changed if it matters after all. Signed-off-by: Berthold Stoeger --- qt-models/cylindermodel.cpp | 70 +++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 40 deletions(-) (limited to 'qt-models') diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index 9949ee98b..df8bd179d 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -321,17 +321,18 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in return false; } + bool changed = CHANGED(); + if (index.column() != TYPE && !changed) + return false; + 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)); - } + case TYPE: + if (!same_string(qPrintable(vString), cyl->type.description)) { + free((void *)cyl->type.description); + cyl->type.description = strdup(qPrintable(vString)); } break; - case SIZE: - if (CHANGED()) { + case SIZE: { TankInfoModel *tanks = TankInfoModel::instance(); QModelIndexList matches = tanks->match(tanks->index(0, 0), Qt::DisplayRole, cyl->type.description); @@ -340,8 +341,7 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in tanks->setData(tanks->index(matches.first().row(), TankInfoModel::ML), cyl->type.size.mliter); } 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)); @@ -350,16 +350,13 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in } break; case START: - if (CHANGED()) - cyl->start = string_to_pressure(qPrintable(vString)); + cyl->start = string_to_pressure(qPrintable(vString)); break; case END: - if (CHANGED()) - //&& (!cyl->start.mbar || string_to_pressure(qPrintable(vString)).mbar <= cyl->start.mbar)) { - cyl->end = string_to_pressure(qPrintable(vString)); + //if (!cyl->start.mbar || string_to_pressure(qPrintable(vString)).mbar <= cyl->start.mbar) { + cyl->end = string_to_pressure(qPrintable(vString)); break; - case O2: - if (CHANGED()) { + 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) @@ -375,20 +372,16 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in } 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; - } + 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; break; case DEPTH: - if (CHANGED()) - cyl->depth = string_to_depth(qPrintable(vString)); + cyl->depth = string_to_depth(qPrintable(vString)); break; - case MOD: - if (CHANGED()) { + case MOD: { if (QString::compare(qPrintable(vString), "*") == 0) { cyl->bestmix_o2 = true; // Calculate fO2 for max. depth @@ -404,20 +397,17 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in } 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(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); - } + 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); } break; - case USE: - if (CHANGED()) { + case USE: { int use = vString.toInt(); if (use > NUM_GAS_USE - 1 || use < 0) use = 0; -- cgit v1.2.3-70-g09d2 From 66fc9fcfecfd4d9a40a287b66c15d48691a24660 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Thu, 27 Feb 2020 19:07:11 +0100 Subject: CylindersModel: fold CHANGED() macro into setData() The CHANGED macro was defined in the cleanerTableModel header. Since it had only one user, expand it there. The macro was very questionably anyway, as it would set the local "vString" variable. Signed-off-by: Berthold Stoeger --- qt-models/cleanertablemodel.h | 4 ---- qt-models/cylindermodel.cpp | 5 +++-- 2 files changed, 3 insertions(+), 6 deletions(-) (limited to 'qt-models') 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 df8bd179d..62f9118f1 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -291,7 +291,6 @@ cylinder_t *CylindersModel::cylinderAt(const QModelIndex &index) bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, int role) { - QString vString; if (!d) return false; @@ -321,7 +320,9 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in return false; } - bool changed = CHANGED(); + QString vString = value.toString(); + bool changed = vString != data(index, role).toString(); + if (index.column() != TYPE && !changed) return false; -- cgit v1.2.3-70-g09d2 From d597b6dca543f1f43d3921e001f45b76975b36f2 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Thu, 27 Feb 2020 20:43:33 +0100 Subject: cleanup: remove unused CylindersModelFiltered functions add() and remove() are not used anymore since this is done using undo commands. The planner uses CylindersModel instead. Signed-off-by: Berthold Stoeger --- qt-models/cylindermodel.cpp | 10 ---------- qt-models/cylindermodel.h | 4 ---- 2 files changed, 14 deletions(-) (limited to 'qt-models') diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index 62f9118f1..d76cdb344 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -636,21 +636,11 @@ void CylindersModelFiltered::clear() source.clear(); } -void CylindersModelFiltered::add() -{ - source.add(); -} - CylindersModel *CylindersModelFiltered::model() { return &source; } -void CylindersModelFiltered::remove(QModelIndex index) -{ - source.remove(mapToSource(index)); -} - bool CylindersModelFiltered::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { return prefs.display_unused_tanks || source.cylinderUsed(source_row); diff --git a/qt-models/cylindermodel.h b/qt-models/cylindermodel.h index ebebb46ab..d3d87b900 100644 --- a/qt-models/cylindermodel.h +++ b/qt-models/cylindermodel.h @@ -70,11 +70,7 @@ public: CylindersModel *model(); // Access to unfiltered base model void clear(); - void add(); void updateDive(dive *d); -public -slots: - void remove(QModelIndex index); private: CylindersModel source; bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; -- cgit v1.2.3-70-g09d2 From 1dcc885bb257d6c8c64074f8788b468397c34aaa Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Fri, 28 Feb 2020 20:38:04 +0100 Subject: undo/cylinders: Implement editing of the type This one is tricky, as when browsing through the types-combobox, the user is presented with presets without actually changing the dive. We do not want an undo-command for every change-event in the combo-box. Therefore, implement a scheme analoguous to the weight-editing: A temporary row can be set / committed or reset. Sadly, the code is more complex because we have to consider the planner, which is not included in the undo system. Firstly, the planner uses a different model, therefore all interactions are channeled through setData() with special roles. Secondly, in the planner we shouldn't place an undo command, but simply overwrite the dive. Signed-off-by: Berthold Stoeger --- desktop-widgets/modeldelegates.cpp | 34 +++++--------- qt-models/cylindermodel.cpp | 95 ++++++++++++++++++++++++++++++++++---- qt-models/cylindermodel.h | 11 ++++- 3 files changed, 106 insertions(+), 34 deletions(-) (limited to 'qt-models') diff --git a/desktop-widgets/modeldelegates.cpp b/desktop-widgets/modeldelegates.cpp index ed7278b4c..65e8f377e 100644 --- a/desktop-widgets/modeldelegates.cpp +++ b/desktop-widgets/modeldelegates.cpp @@ -230,12 +230,6 @@ void ComboBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionV editor->setGeometry(defaultRect); } -static struct RevertCylinderData { - QString type; - int pressure; - int size; -} currCylinderData; - void TankInfoDelegate::setModelData(QWidget*, QAbstractItemModel*, const QModelIndex&) const { QAbstractItemModel *mymodel = currCombo.model; @@ -254,9 +248,9 @@ void TankInfoDelegate::setModelData(QWidget*, QAbstractItemModel*, const QModelI int tankSize = tanks->data(tanks->index(row, TankInfoModel::ML)).toInt(); int tankPressure = tanks->data(tanks->index(row, TankInfoModel::BAR)).toInt(); - mymodel->setData(IDX(CylindersModel::TYPE), cylinderName, Qt::EditRole); - mymodel->setData(IDX(CylindersModel::WORKINGPRESS), tankPressure, CylindersModel::PASS_IN_ROLE); - mymodel->setData(IDX(CylindersModel::SIZE), tankSize, CylindersModel::PASS_IN_ROLE); + mymodel->setData(IDX(CylindersModel::TYPE), cylinderName, CylindersModel::TEMP_ROLE); + mymodel->setData(IDX(CylindersModel::WORKINGPRESS), tankPressure, CylindersModel::TEMP_ROLE); + mymodel->setData(IDX(CylindersModel::SIZE), tankSize, CylindersModel::TEMP_ROLE); } TankInfoDelegate::TankInfoDelegate(QObject *parent) : ComboBoxDelegate(TankInfoModel::instance(), parent, true) @@ -275,25 +269,19 @@ void TankInfoDelegate::reenableReplot(QWidget*, QAbstractItemDelegate::EndEditHi void TankInfoDelegate::editorClosed(QWidget*, QAbstractItemDelegate::EndEditHint hint) { - if (hint == QAbstractItemDelegate::NoHint || - hint == QAbstractItemDelegate::RevertModelCache) { - QAbstractItemModel *mymodel = currCombo.model; - mymodel->setData(IDX(CylindersModel::TYPE), currCylinderData.type, Qt::EditRole); - mymodel->setData(IDX(CylindersModel::WORKINGPRESS), currCylinderData.pressure, CylindersModel::PASS_IN_ROLE); - mymodel->setData(IDX(CylindersModel::SIZE), currCylinderData.size, CylindersModel::PASS_IN_ROLE); - } + QAbstractItemModel *mymodel = currCombo.model; + // Ugly hack: We misuse setData() with COMMIT_ROLE or REVERT_ROLE to commit or + // revert the current row. We send in the type, because we may get multiple + // end events and thus can prevent multiple commits. + if (hint == QAbstractItemDelegate::RevertModelCache) + mymodel->setData(IDX(CylindersModel::TYPE), currCombo.activeText, CylindersModel::REVERT_ROLE); + else + mymodel->setData(IDX(CylindersModel::TYPE), currCombo.activeText, CylindersModel::COMMIT_ROLE); } QWidget *TankInfoDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { - // ncreate editor needs to be called before because it will populate a few - // things in the currCombo global var. QWidget *delegate = ComboBoxDelegate::createEditor(parent, option, index); - QAbstractItemModel *model = currCombo.model; - int row = index.row(); - currCylinderData.type = model->data(model->index(row, CylindersModel::TYPE)).value(); - currCylinderData.pressure = model->data(model->index(row, CylindersModel::WORKINGPRESS_INT)).value(); - currCylinderData.size = model->data(model->index(row, CylindersModel::SIZE_INT)).value(); MainWindow::instance()->graphics->setReplot(false); return delegate; } diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index d76cdb344..f2ee0c12e 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -2,6 +2,7 @@ #include "cylindermodel.h" #include "tankinfomodel.h" #include "models.h" +#include "commands/command.h" #include "core/qthelper.h" #include "core/color.h" #include "qt-models/diveplannermodel.h" @@ -10,7 +11,9 @@ #include "core/subsurface-string.h" CylindersModel::CylindersModel(QObject *parent) : CleanerTableModel(parent), - d(nullptr) + d(nullptr), + 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%") @@ -155,7 +158,7 @@ QVariant CylindersModel::data(const QModelIndex &index, int role) const return QVariant(); } - const cylinder_t *cyl = get_cylinder(d, index.row()); + const cylinder_t *cyl = index.row() == tempRow ? &tempCyl : get_cylinder(d, index.row()); switch (role) { case Qt::BackgroundRole: { @@ -299,25 +302,49 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in 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 + // 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); + } + } 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(); @@ -621,6 +648,54 @@ void CylindersModel::cylindersReset(const QVector &dives) 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; + + tempRow = row; + tempCyl = clone_cylinder(*cyl); + + dataChanged(index(row, TYPE), index(row, USE)); +} + +void CylindersModel::clearTempCyl() +{ + if (tempRow < 0) + return; + int oldRow = tempRow; + tempRow = -1; + free_cylinder(tempCyl); + dataChanged(index(oldRow, TYPE), index(oldRow, USE)); +} + +void CylindersModel::commitTempCyl(int row) +{ +#ifndef SUBSURFACE_MOBILE + 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 (in_planner()) + std::swap(*cyl, tempCyl); + else + Command::editCylinder(tempRow, tempCyl, false); + } + free_cylinder(tempCyl); + tempRow = -1; +#endif +} + CylindersModelFiltered::CylindersModelFiltered(QObject *parent) : QSortFilterProxyModel(parent) { setSourceModel(&source); diff --git a/qt-models/cylindermodel.h b/qt-models/cylindermodel.h index d3d87b900..e4585b0ac 100644 --- a/qt-models/cylindermodel.h +++ b/qt-models/cylindermodel.h @@ -31,7 +31,9 @@ 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); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; @@ -59,7 +61,14 @@ slots: private: dive *d; + // 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 -- cgit v1.2.3-70-g09d2 From 01fa983182062e00b2e661a963384289e4de1beb Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 1 Mar 2020 15:32:24 +0100 Subject: undo/cylinder: call undo command to edit cylinder fields Call an undo command when editing cylinders, but only if on the EquipmentTab. To keep code changes small, make a copy of the cylinder first, then edit the cylinder as before and then either call an undo command (EquipmentTab) or overwrite the old cylinder (Planner). The memory management here is a bit strange: Since the undo-command itself makes a deep-copy of the passed in cylinder, we only do a shallow copy. If we have to change the type, we allocate the string with an std::string, so that the memory is automatically freed at the end of the function. However, this means that in the planner we have to make a deep copy first, swap old and new cylinder and finally release the old cylinder. Certainly not ideal, but for now the pragmatic thing to do. Signed-off-by: Berthold Stoeger --- qt-models/cylindermodel.cpp | 91 +++++++++++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 36 deletions(-) (limited to 'qt-models') diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index f2ee0c12e..c2bd0198a 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -9,6 +9,7 @@ #include "core/gettextfromc.h" #include "core/subsurface-qt/divelistnotifier.h" #include "core/subsurface-string.h" +#include CylindersModel::CylindersModel(QObject *parent) : CleanerTableModel(parent), d(nullptr), @@ -298,8 +299,8 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in if (!d) return false; - cylinder_t *cyl = cylinderAt(index); - if (!cyl) + int row = index.row(); + if (row < 0 || row >= d->cylinders.nr) return false; // Here we handle a few cases that allow us to set / commit / revert @@ -350,100 +351,118 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in 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; switch (index.column()) { case TYPE: - if (!same_string(qPrintable(vString), cyl->type.description)) { - free((void *)cyl->type.description); - cyl->type.description = strdup(qPrintable(vString)); - } + newType = qPrintable(vString); + cyl.type.description = newType.c_str(); break; 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); + 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); + tanks->setData(tanks->index(matches.first().row(), TankInfoModel::ML), cyl.type.size.mliter); } break; 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); + tanks->setData(tanks->index(matches.first().row(), TankInfoModel::BAR), cyl.type.workingpressure.mbar / 1000.0); } break; case START: - cyl->start = string_to_pressure(qPrintable(vString)); + cyl.start = string_to_pressure(qPrintable(vString)); break; case END: //if (!cyl->start.mbar || string_to_pressure(qPrintable(vString)).mbar <= cyl->start.mbar) { - cyl->end = string_to_pressure(qPrintable(vString)); + cyl.end = string_to_pressure(qPrintable(vString)); break; case O2: { - cyl->gasmix.o2 = string_to_fraction(qPrintable(vString)); + 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 (d->dc.divemode == PSCR) - modpO2.mbar = prefs.decopo2 + (1000 - get_o2(cyl->gasmix)) * SURFACE_PRESSURE * + 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, d, M_OR_FT(3, 10)); - cyl->bestmix_o2 = false; + cyl.depth = gas_mod(cyl.gasmix, modpO2, d, M_OR_FT(3, 10)); + cyl.bestmix_o2 = false; } break; case HE: - cyl->gasmix.he = string_to_fraction(qPrintable(vString)); + 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; + if (get_o2(cyl.gasmix) + get_he(cyl.gasmix) > 1000) + cyl.gasmix.o2.permille = 1000 - get_he(cyl.gasmix); + cyl.bestmix_he = false; break; case DEPTH: - cyl->depth = string_to_depth(qPrintable(vString)); + cyl.depth = string_to_depth(qPrintable(vString)); break; 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(d->maxdepth, d); + 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)), d); + 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, d, M_OR_FT(3, 10)); + cyl.depth = gas_mod(cyl.gasmix, modpO2, d, M_OR_FT(3, 10)); } break; case MND: if (QString::compare(qPrintable(vString), "*") == 0) { - cyl->bestmix_he = true; + cyl.bestmix_he = true; // Calculate fO2 for max. depth - cyl->gasmix.he = best_he(d->maxdepth, d, prefs.o2narcotic, cyl->gasmix.o2); + cyl.gasmix.he = best_he(d->maxdepth, d, prefs.o2narcotic, cyl.gasmix.o2); } else { - cyl->bestmix_he = false; + 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); + cyl.gasmix.he = best_he(string_to_depth(qPrintable(vString)), d, prefs.o2narcotic, cyl.gasmix.o2); } break; case USE: { int use = vString.toInt(); if (use > NUM_GAS_USE - 1 || use < 0) use = 0; - cyl->cylinder_use = (enum cylinderuse)use; + cyl.cylinder_use = (enum cylinderuse)use; } break; } - dataChanged(index, index); + + if (in_planner()) { + // 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 { +#ifndef SUBSURFACE_MOBILE + // On the EquipmentTab - place an editCylinder command. + Command::editCylinder(index.row(), cyl, false); +#endif + } return true; } -- cgit v1.2.3-70-g09d2 From 24a7dbde16ce5f024f6137a2318e54815a495a94 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 1 Mar 2020 16:12:13 +0100 Subject: CylindersModel: use flag to decide whether we are in planner On desktop, we have two CylindersModel concurrently: One in the planner and one on the equipment-tab. They act differently, because the former modifies displayed_dive directly, the latter issues undo commands. To differentiate, we used the in_planner() function. However, that appears extremely brittle, especially when combined with undo-commands. Therefore when generating the model, pass in a parameter that says whether this is for the planner or the equipment tab and use that flag to decide how to act. Signed-off-by: Berthold Stoeger --- qt-models/cylindermodel.cpp | 22 ++++++++++++---------- qt-models/cylindermodel.h | 3 ++- qt-models/diveplannermodel.cpp | 1 + 3 files changed, 15 insertions(+), 11 deletions(-) (limited to 'qt-models') diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index c2bd0198a..8a79ff1e5 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -11,8 +11,9 @@ #include "core/subsurface-string.h" #include -CylindersModel::CylindersModel(QObject *parent) : CleanerTableModel(parent), +CylindersModel::CylindersModel(bool planner, QObject *parent) : CleanerTableModel(parent), d(nullptr), + inPlanner(planner), tempRow(-1), tempCyl(empty_cylinder) { @@ -28,7 +29,7 @@ CylindersModel::CylindersModel(QObject *parent) : CleanerTableModel(parent), 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); @@ -249,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(d, index.row()))) { + if ((inPlanner && DivePlannerPointsModel::instance()->tankInUse(index.row())) || + (!inPlanner && is_cylinder_prot(d, index.row()))) { return trashForbiddenIcon(); } return trashIcon(); @@ -259,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(d, 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."); @@ -450,7 +451,7 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in break; } - if (in_planner()) { + 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); @@ -590,7 +591,7 @@ void CylindersModel::moveAtFirst(int cylid) mapping[cylid] = 0; std::iota(mapping.begin() + (cylid + 1), mapping.end(), cylid); cylinder_renumber(d, &mapping[0]); - if (in_planner()) + if (inPlanner) DivePlannerPointsModel::instance()->cylinderRenumber(&mapping[0]); endMoveRows(); } @@ -705,7 +706,7 @@ void CylindersModel::commitTempCyl(int row) 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 (in_planner()) + if (inPlanner) std::swap(*cyl, tempCyl); else Command::editCylinder(tempRow, tempCyl, false); @@ -715,7 +716,8 @@ void CylindersModel::commitTempCyl(int row) #endif } -CylindersModelFiltered::CylindersModelFiltered(QObject *parent) : QSortFilterProxyModel(parent) +CylindersModelFiltered::CylindersModelFiltered(QObject *parent) : QSortFilterProxyModel(parent), + source(false) // Currently, only the EquipmentTab uses the filtered model. { setSourceModel(&source); } diff --git a/qt-models/cylindermodel.h b/qt-models/cylindermodel.h index e4585b0ac..7b868b5b2 100644 --- a/qt-models/cylindermodel.h +++ b/qt-models/cylindermodel.h @@ -35,7 +35,7 @@ public: 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; @@ -61,6 +61,7 @@ slots: private: dive *d; + bool inPlanner; // Used if we temporarily change a line because the user is selecting a weight type int tempRow; cylinder_t tempCyl; diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index 4928900c4..09d08c79a 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -415,6 +415,7 @@ int DivePlannerPointsModel::rowCount(const QModelIndex&) const } DivePlannerPointsModel::DivePlannerPointsModel(QObject *parent) : QAbstractTableModel(parent), + cylinders(true), mode(NOTHING), recalc(false) { -- cgit v1.2.3-70-g09d2 From 2eeb5f4fc2d6da8a8c8950f0b479b7cb2055af07 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Fri, 27 Mar 2020 21:09:59 +0100 Subject: undo: more fine-grained editing of cylinder Don't overwrite the full cylinder when editing a single field. Implement three "modes": editing of type, pressure and gasmix. Don't consider individual fields, because some of them are related. E.g. you can change the gasmix by setting the MOD. Signed-off-by: Berthold Stoeger --- commands/command.cpp | 4 ++-- commands/command.h | 7 ++++++- commands/command_edit.cpp | 38 +++++++++++++++++++++++++++++++++----- commands/command_edit.h | 9 ++++++++- core/equipment.c | 9 --------- core/equipment.h | 1 - qt-models/cylindermodel.cpp | 18 +++++++++++++++--- 7 files changed, 64 insertions(+), 22 deletions(-) (limited to 'qt-models') diff --git a/commands/command.cpp b/commands/command.cpp index c173494c0..33c866e72 100644 --- a/commands/command.cpp +++ b/commands/command.cpp @@ -304,9 +304,9 @@ int removeCylinder(int index, bool currentDiveOnly) return execute_edit(new RemoveCylinder(index, currentDiveOnly)); } -int editCylinder(int index, cylinder_t cyl, bool currentDiveOnly) +int editCylinder(int index, cylinder_t cyl, EditCylinderType type, bool currentDiveOnly) { - return execute_edit(new EditCylinder(index, cyl, currentDiveOnly)); + return execute_edit(new EditCylinder(index, cyl, type, currentDiveOnly)); } // Trip editing related commands diff --git a/commands/command.h b/commands/command.h index 66122ac99..fc1ddf582 100644 --- a/commands/command.h +++ b/commands/command.h @@ -92,7 +92,12 @@ int removeWeight(int index, bool currentDiveOnly); int editWeight(int index, weightsystem_t ws, bool currentDiveOnly); int addCylinder(bool currentDiveOnly); int removeCylinder(int index, bool currentDiveOnly); -int editCylinder(int index, cylinder_t cyl, bool currentDiveOnly); +enum class EditCylinderType { + TYPE, + PRESSURE, + GASMIX +}; +int editCylinder(int index, cylinder_t cyl, EditCylinderType type, bool currentDiveOnly); #ifdef SUBSURFACE_MOBILE // Edits a dive and creates a divesite (if createDs != NULL) or edits a divesite (if changeDs != NULL). // Takes ownership of newDive and createDs! diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index d5e2d12d0..c81e67394 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -1151,9 +1151,23 @@ void RemoveCylinder::redo() } } +static int editCylinderTypeToFlags(EditCylinderType type) +{ + switch (type) { + default: + case EditCylinderType::TYPE: + return SAME_TYPE | SAME_SIZE; + case EditCylinderType::PRESSURE: + return SAME_PRESS; + case EditCylinderType::GASMIX: + return SAME_GAS; + } +} + // ***** Edit Cylinder ***** -EditCylinder::EditCylinder(int index, cylinder_t cylIn, bool currentDiveOnly) : - EditCylinderBase(index, currentDiveOnly, false, SAME_TYPE | SAME_PRESS | SAME_GAS) +EditCylinder::EditCylinder(int index, cylinder_t cylIn, EditCylinderType typeIn, bool currentDiveOnly) : + EditCylinderBase(index, currentDiveOnly, false, editCylinderTypeToFlags(typeIn)), + type(typeIn) { if (dives.empty()) return; @@ -1184,9 +1198,23 @@ EditCylinder::EditCylinder(int index, cylinder_t cylIn, bool currentDiveOnly) : // The base class copied the cylinders for us, let's edit them for (int i = 0; i < (int)indexes.size(); ++i) { - free_cylinder(cyl[i]); - cyl[i] = cylIn; - cyl[i].type.description = copy_qstring(description); + switch (type) { + case EditCylinderType::TYPE: + free((void *)cyl[i].type.description); + cyl[i].type = cylIn.type; + cyl[i].type.description = copy_qstring(description); + cyl[i].cylinder_use = cylIn.cylinder_use; + break; + case EditCylinderType::PRESSURE: + cyl[i].start.mbar = cylIn.start.mbar; + cyl[i].end.mbar = cylIn.end.mbar; + break; + case EditCylinderType::GASMIX: + cyl[i].gasmix = cylIn.gasmix; + cyl[i].bestmix_o2 = cylIn.bestmix_o2; + cyl[i].bestmix_he = cylIn.bestmix_he; + break; + } } } diff --git a/commands/command_edit.h b/commands/command_edit.h index d7c31f7f2..ab842a13c 100644 --- a/commands/command_edit.h +++ b/commands/command_edit.h @@ -5,6 +5,7 @@ #define COMMAND_EDIT_H #include "command_base.h" +#include "command.h" // for EditCylinderType #include "core/subsurface-qt/divelistnotifier.h" #include @@ -406,10 +407,16 @@ private: void redo() override; }; +// Instead of implementing an undo command for every single field in a cylinder, +// we only have one and pass an edit "type". We either edit the type, pressure +// or gasmix fields. This has mostly historical reasons rooted in the way the +// CylindersModel code works. The model works for undo and also in the planner +// without undo. Having a single undo-command simplifies the code there. class EditCylinder : public EditCylinderBase { public: - EditCylinder(int index, cylinder_t cyl, bool currentDiveOnly); // Clones cylinder + EditCylinder(int index, cylinder_t cyl, EditCylinderType type, bool currentDiveOnly); // Clones cylinder private: + EditCylinderType type; void undo() override; void redo() override; }; diff --git a/core/equipment.c b/core/equipment.c index 27d01692e..4be46ed1e 100644 --- a/core/equipment.c +++ b/core/equipment.c @@ -282,15 +282,6 @@ void remove_cylinder(struct dive *dive, int idx) remove_from_cylinder_table(&dive->cylinders, idx); } -// cyl is cloned. -void set_cylinder(struct dive *dive, int idx, cylinder_t cyl) -{ - if (idx < 0 || idx >= dive->cylinders.nr) - return; - free_cylinder(dive->cylinders.cylinders[idx]); - dive->cylinders.cylinders[idx] = clone_cylinder(cyl); -} - void remove_weightsystem(struct dive *dive, int idx) { remove_from_weightsystem_table(&dive->weightsystems, idx); diff --git a/core/equipment.h b/core/equipment.h index af410467f..081a13835 100644 --- a/core/equipment.h +++ b/core/equipment.h @@ -85,7 +85,6 @@ extern void add_cylinder_description(const cylinder_type_t *); extern void add_weightsystem_description(const weightsystem_t *); extern bool same_weightsystem(weightsystem_t w1, weightsystem_t w2); extern void remove_cylinder(struct dive *dive, int idx); -extern void set_cylinder(struct dive *dive, int idx, cylinder_t ws); extern void remove_weightsystem(struct dive *dive, int idx); extern void set_weightsystem(struct dive *dive, int idx, weightsystem_t ws); extern void reset_cylinders(struct dive *dive, bool track_gas); diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index 8a79ff1e5..c7e1b186d 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -296,7 +296,6 @@ cylinder_t *CylindersModel::cylinderAt(const QModelIndex &index) bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, int role) { - if (!d) return false; @@ -324,6 +323,7 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in tempCyl.type.description = strdup(qPrintable(type)); dataChanged(index, index); } + return true; } case SIZE: if (tempCyl.type.size.mliter != value.toInt()) { @@ -362,10 +362,12 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in if (index.column() != TYPE && !changed) return false; + Command::EditCylinderType type = Command::EditCylinderType::TYPE; switch (index.column()) { case TYPE: newType = qPrintable(vString); cyl.type.description = newType.c_str(); + type = Command::EditCylinderType::TYPE; break; case SIZE: { TankInfoModel *tanks = TankInfoModel::instance(); @@ -375,6 +377,7 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in if (!matches.isEmpty()) tanks->setData(tanks->index(matches.first().row(), TankInfoModel::ML), cyl.type.size.mliter); } + type = Command::EditCylinderType::TYPE; break; case WORKINGPRESS: { TankInfoModel *tanks = TankInfoModel::instance(); @@ -383,13 +386,16 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in if (!matches.isEmpty()) tanks->setData(tanks->index(matches.first().row(), TankInfoModel::BAR), cyl.type.workingpressure.mbar / 1000.0); } + type = Command::EditCylinderType::TYPE; break; case START: cyl.start = string_to_pressure(qPrintable(vString)); + type = Command::EditCylinderType::PRESSURE; break; case END: //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: { cyl.gasmix.o2 = string_to_fraction(qPrintable(vString)); @@ -405,6 +411,7 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in cyl.depth = gas_mod(cyl.gasmix, modpO2, d, M_OR_FT(3, 10)); cyl.bestmix_o2 = false; } + type = Command::EditCylinderType::GASMIX; break; case HE: cyl.gasmix.he = string_to_fraction(qPrintable(vString)); @@ -412,9 +419,11 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in 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: cyl.depth = string_to_depth(qPrintable(vString)); + type = Command::EditCylinderType::GASMIX; break; case MOD: { if (QString::compare(qPrintable(vString), "*") == 0) { @@ -430,6 +439,7 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in modpO2.mbar = prefs.decopo2; cyl.depth = gas_mod(cyl.gasmix, modpO2, d, M_OR_FT(3, 10)); } + type = Command::EditCylinderType::GASMIX; break; case MND: if (QString::compare(qPrintable(vString), "*") == 0) { @@ -441,6 +451,7 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in // 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: { int use = vString.toInt(); @@ -448,6 +459,7 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in use = 0; cyl.cylinder_use = (enum cylinderuse)use; } + type = Command::EditCylinderType::TYPE; break; } @@ -461,7 +473,7 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in } else { #ifndef SUBSURFACE_MOBILE // On the EquipmentTab - place an editCylinder command. - Command::editCylinder(index.row(), cyl, false); + Command::editCylinder(index.row(), cyl, type, false); #endif } return true; @@ -709,7 +721,7 @@ void CylindersModel::commitTempCyl(int row) if (inPlanner) std::swap(*cyl, tempCyl); else - Command::editCylinder(tempRow, tempCyl, false); + Command::editCylinder(tempRow, tempCyl, Command::EditCylinderType::TYPE, false); } free_cylinder(tempCyl); tempRow = -1; -- cgit v1.2.3-70-g09d2 From 63414fc82394031a9ce9c25d635430107fde19e0 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Fri, 27 Mar 2020 21:49:19 +0100 Subject: undo: show multiple dive warning when editing equipment When editing cylinders or weights directly in the table widgets, no warning was shown if multiple dives were affected. To solve this, emit signals from the respective models and catch them in dive equipment tab. Not very nice, but it works for now. Signed-off-by: Berthold Stoeger --- desktop-widgets/tab-widgets/TabDiveEquipment.cpp | 2 ++ desktop-widgets/tab-widgets/TabDiveEquipment.h | 2 +- qt-models/cylindermodel.cpp | 11 +++++++---- qt-models/cylindermodel.h | 3 +++ qt-models/weightmodel.cpp | 9 ++++++--- qt-models/weightmodel.h | 3 +++ 6 files changed, 22 insertions(+), 8 deletions(-) (limited to 'qt-models') diff --git a/desktop-widgets/tab-widgets/TabDiveEquipment.cpp b/desktop-widgets/tab-widgets/TabDiveEquipment.cpp index 13cfe77c4..78973b6c1 100644 --- a/desktop-widgets/tab-widgets/TabDiveEquipment.cpp +++ b/desktop-widgets/tab-widgets/TabDiveEquipment.cpp @@ -35,6 +35,8 @@ TabDiveEquipment::TabDiveEquipment(QWidget *parent) : TabBase(parent), connect(&diveListNotifier, &DiveListNotifier::divesChanged, this, &TabDiveEquipment::divesChanged); connect(ui.cylinders, &TableView::itemClicked, this, &TabDiveEquipment::editCylinderWidget); connect(ui.weights, &TableView::itemClicked, this, &TabDiveEquipment::editWeightWidget); + connect(cylindersModel->model(), &CylindersModel::divesEdited, this, &TabDiveEquipment::divesEdited); + connect(weightModel, &WeightModel::divesEdited, this, &TabDiveEquipment::divesEdited); // Current display of things on Gnome3 looks like shit, so // let's fix that. diff --git a/desktop-widgets/tab-widgets/TabDiveEquipment.h b/desktop-widgets/tab-widgets/TabDiveEquipment.h index 3576b449e..55eb21d1e 100644 --- a/desktop-widgets/tab-widgets/TabDiveEquipment.h +++ b/desktop-widgets/tab-widgets/TabDiveEquipment.h @@ -21,7 +21,6 @@ public: ~TabDiveEquipment(); void updateData() override; void clear() override; - void divesEdited(int i); void closeWarning(); private slots: @@ -32,6 +31,7 @@ private slots: void editCylinderWidget(const QModelIndex &index); void editWeightWidget(const QModelIndex &index); void on_suit_editingFinished(); + void divesEdited(int count); private: Ui::TabDiveEquipment ui; diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index c7e1b186d..e519d347b 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -473,7 +473,8 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in } else { #ifndef SUBSURFACE_MOBILE // On the EquipmentTab - place an editCylinder command. - Command::editCylinder(index.row(), cyl, type, false); + int count = Command::editCylinder(index.row(), cyl, type, false); + emit divesEdited(count); #endif } return true; @@ -718,10 +719,12 @@ void CylindersModel::commitTempCyl(int row) 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) + if (inPlanner) { std::swap(*cyl, tempCyl); - else - Command::editCylinder(tempRow, tempCyl, Command::EditCylinderType::TYPE, false); + } else { + int count = Command::editCylinder(tempRow, tempCyl, Command::EditCylinderType::TYPE, false); + emit divesEdited(count); + } } free_cylinder(tempCyl); tempRow = -1; diff --git a/qt-models/cylindermodel.h b/qt-models/cylindermodel.h index 7b868b5b2..099a3beb2 100644 --- a/qt-models/cylindermodel.h +++ b/qt-models/cylindermodel.h @@ -51,6 +51,9 @@ public: bool updateBestMixes(); bool cylinderUsed(int i) const; +signals: + void divesEdited(int num); + public slots: void remove(QModelIndex index); 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 &dives); -- cgit v1.2.3-70-g09d2 From 4d5f25ccf44eb3a55def84aac43aed344afe791f Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Fri, 3 Apr 2020 21:37:52 +0200 Subject: cleanup: remove conditional compilation in cylindermodel.cpp Parts of the code were not compiled on mobile, because they used the undo-command infrastructure. However, since mobile now also compiles that, we might as well remove the conditional compilation. Signed-off-by: Berthold Stoeger --- qt-models/cylindermodel.cpp | 4 ---- 1 file changed, 4 deletions(-) (limited to 'qt-models') diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index e519d347b..04abe2fdb 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -471,11 +471,9 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in free_cylinder(copy); dataChanged(index, index); } else { -#ifndef SUBSURFACE_MOBILE // On the EquipmentTab - place an editCylinder command. int count = Command::editCylinder(index.row(), cyl, type, false); emit divesEdited(count); -#endif } return true; } @@ -709,7 +707,6 @@ void CylindersModel::clearTempCyl() void CylindersModel::commitTempCyl(int row) { -#ifndef SUBSURFACE_MOBILE if (tempRow < 0) return; if (row != tempRow) @@ -728,7 +725,6 @@ void CylindersModel::commitTempCyl(int row) } free_cylinder(tempCyl); tempRow = -1; -#endif } CylindersModelFiltered::CylindersModelFiltered(QObject *parent) : QSortFilterProxyModel(parent), -- cgit v1.2.3-70-g09d2