planner: remove displayed_dive from DivePlannerModel

To remove global state, make the dive that DivePlannerModel
works on a member variable. Pass the dive in createSimpleDive()
and loadFromDive(). Moreover, this should pave the way to more
fine-grained undo in the planner. Ultimately, the planner
should not be modal.

Attention: for now, the dive must still be displayed_dive,
because of the convoluted way in which the profile and the
planner work on the same dive.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
Berthold Stoeger 2021-01-26 07:48:22 +01:00 committed by Dirk Hohndel
parent e419ebf55a
commit 1ec0790d50
4 changed files with 72 additions and 63 deletions

View file

@ -545,7 +545,7 @@ void PlannerWidgets::planDive()
dc_number = 0; dc_number = 0;
// create a simple starting dive, using the first gas from the just copied cylinders // create a simple starting dive, using the first gas from the just copied cylinders
DivePlannerPointsModel::instance()->createSimpleDive(); DivePlannerPointsModel::instance()->createSimpleDive(&displayed_dive);
// plan the dive in the same mode as the currently selected one // plan the dive in the same mode as the currently selected one
if (current_dive) { if (current_dive) {
@ -563,17 +563,20 @@ void PlannerWidgets::planDive()
void PlannerWidgets::replanDive() void PlannerWidgets::replanDive()
{ {
if (!current_dive)
return;
copy_dive(current_dive, &displayed_dive); // Planning works on a copy of the dive (for now).
DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::PLAN); DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::PLAN);
DivePlannerPointsModel::instance()->loadFromDive(&displayed_dive);
MainWindow::instance()->graphics->setPlanState(); MainWindow::instance()->graphics->setPlanState();
plannerWidget.setReplanButton(true); plannerWidget.setReplanButton(true);
plannerWidget.setupStartTime(timestampToDateTime(current_dive->when)); plannerWidget.setupStartTime(timestampToDateTime(displayed_dive.when));
if (current_dive->surface_pressure.mbar) if (displayed_dive.surface_pressure.mbar)
plannerWidget.setSurfacePressure(current_dive->surface_pressure.mbar); plannerWidget.setSurfacePressure(displayed_dive.surface_pressure.mbar);
if (current_dive->salinity) if (displayed_dive.salinity)
plannerWidget.setSalinity(current_dive->salinity); plannerWidget.setSalinity(displayed_dive.salinity);
DivePlannerPointsModel::instance()->loadFromDive(current_dive);
reset_cylinders(&displayed_dive, true); reset_cylinders(&displayed_dive, true);
DivePlannerPointsModel::instance()->cylindersModel()->updateDive(&displayed_dive); DivePlannerPointsModel::instance()->cylindersModel()->updateDive(&displayed_dive);
} }

View file

@ -1514,10 +1514,11 @@ void MainWindow::editCurrentDive()
return; return;
disableShortcuts(); disableShortcuts();
copy_dive(current_dive, &displayed_dive); // Work on a copy of the dive
DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::ADD); DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::ADD);
graphics->setAddState(); graphics->setAddState();
setApplicationState(ApplicationState::EditDive); setApplicationState(ApplicationState::EditDive);
DivePlannerPointsModel::instance()->loadFromDive(current_dive); DivePlannerPointsModel::instance()->loadFromDive(&displayed_dive);
mainTab->enableEdition(); mainTab->enableEdition();
} }

View file

@ -43,13 +43,14 @@ void DivePlannerPointsModel::removeSelectedPoints(const QVector<int> &rows)
cylinders.updateTrashIcon(); cylinders.updateTrashIcon();
} }
void DivePlannerPointsModel::createSimpleDive() void DivePlannerPointsModel::createSimpleDive(struct dive *dIn)
{ {
// clean out the dive and give it an id and the correct dc model // clean out the dive and give it an id and the correct dc model
clear_dive(&displayed_dive); d = dIn;
displayed_dive.id = dive_getUniqID(); clear_dive(d);
displayed_dive.when = QDateTime::currentMSecsSinceEpoch() / 1000L + gettimezoneoffset() + 3600; d->id = dive_getUniqID();
displayed_dive.dc.model = strdup("planned dive"); // don't translate! this is stored in the XML file d->when = QDateTime::currentMSecsSinceEpoch() / 1000L + gettimezoneoffset() + 3600;
d->dc.model = strdup("planned dive"); // don't translate! this is stored in the XML file
clear(); clear();
setupCylinders(); setupCylinders();
@ -57,7 +58,7 @@ void DivePlannerPointsModel::createSimpleDive()
// initialize the start time in the plan // initialize the start time in the plan
diveplan.when = dateTimeToTimestamp(startTime); diveplan.when = dateTimeToTimestamp(startTime);
displayed_dive.when = diveplan.when; d->when = diveplan.when;
// Use gas from the first cylinder // Use gas from the first cylinder
int cylinderid = 0; int cylinderid = 0;
@ -92,8 +93,10 @@ void DivePlannerPointsModel::setupStartTime()
} }
} }
void DivePlannerPointsModel::loadFromDive(dive *d) void DivePlannerPointsModel::loadFromDive(dive *dIn)
{ {
d = dIn;
int depthsum = 0; int depthsum = 0;
int samplecount = 0; int samplecount = 0;
o2pressure_t last_sp; o2pressure_t last_sp;
@ -102,7 +105,7 @@ void DivePlannerPointsModel::loadFromDive(dive *d)
const struct event *evd = NULL; const struct event *evd = NULL;
enum divemode_t current_divemode = UNDEF_COMP_TYPE; enum divemode_t current_divemode = UNDEF_COMP_TYPE;
recalc = false; recalc = false;
cylinders.updateDive(&displayed_dive); cylinders.updateDive(d);
duration_t lasttime = { 0 }; duration_t lasttime = { 0 };
duration_t lastrecordedtime = {}; duration_t lastrecordedtime = {};
duration_t newtime = {}; duration_t newtime = {};
@ -175,45 +178,45 @@ void DivePlannerPointsModel::loadFromDive(dive *d)
// setup the cylinder widget accordingly // setup the cylinder widget accordingly
void DivePlannerPointsModel::setupCylinders() void DivePlannerPointsModel::setupCylinders()
{ {
clear_cylinder_table(&displayed_dive.cylinders); clear_cylinder_table(&d->cylinders);
if (mode == PLAN && current_dive) { if (mode == PLAN && current_dive) {
// take the displayed cylinders from the selected dive as starting point // take the displayed cylinders from the selected dive as starting point
copy_used_cylinders(current_dive, &displayed_dive, !prefs.display_unused_tanks); copy_used_cylinders(current_dive, d, !prefs.display_unused_tanks);
reset_cylinders(&displayed_dive, true); reset_cylinders(d, true);
if (displayed_dive.cylinders.nr > 0) { if (d->cylinders.nr > 0) {
cylinders.updateDive(&displayed_dive); cylinders.updateDive(d);
return; // We have at least one cylinder return; // We have at least one cylinder
} }
} }
if (!empty_string(prefs.default_cylinder)) { if (!empty_string(prefs.default_cylinder)) {
cylinder_t cyl = empty_cylinder; cylinder_t cyl = empty_cylinder;
fill_default_cylinder(&displayed_dive, &cyl); fill_default_cylinder(d, &cyl);
cyl.start = cyl.type.workingpressure; cyl.start = cyl.type.workingpressure;
add_cylinder(&displayed_dive.cylinders, 0, cyl); add_cylinder(&d->cylinders, 0, cyl);
} else { } else {
cylinder_t cyl = empty_cylinder; cylinder_t cyl = empty_cylinder;
// roughly an AL80 // roughly an AL80
cyl.type.description = copy_qstring(tr("unknown")); cyl.type.description = copy_qstring(tr("unknown"));
cyl.type.size.mliter = 11100; cyl.type.size.mliter = 11100;
cyl.type.workingpressure.mbar = 207000; cyl.type.workingpressure.mbar = 207000;
add_cylinder(&displayed_dive.cylinders, 0, cyl); add_cylinder(&d->cylinders, 0, cyl);
} }
reset_cylinders(&displayed_dive, false); reset_cylinders(d, false);
cylinders.updateDive(&displayed_dive); cylinders.updateDive(d);
} }
// Update the dive's maximum depth. Returns true if max. depth changed // Update the dive's maximum depth. Returns true if max. depth changed
bool DivePlannerPointsModel::updateMaxDepth() bool DivePlannerPointsModel::updateMaxDepth()
{ {
int prevMaxDepth = displayed_dive.maxdepth.mm; int prevMaxDepth = d->maxdepth.mm;
displayed_dive.maxdepth.mm = 0; d->maxdepth.mm = 0;
for (int i = 0; i < rowCount(); i++) { for (int i = 0; i < rowCount(); i++) {
divedatapoint p = at(i); divedatapoint p = at(i);
if (p.depth.mm > displayed_dive.maxdepth.mm) if (p.depth.mm > d->maxdepth.mm)
displayed_dive.maxdepth.mm = p.depth.mm; d->maxdepth.mm = p.depth.mm;
} }
return displayed_dive.maxdepth.mm != prevMaxDepth; return d->maxdepth.mm != prevMaxDepth;
} }
void DivePlannerPointsModel::removeDeco() void DivePlannerPointsModel::removeDeco()
@ -289,13 +292,13 @@ QVariant DivePlannerPointsModel::data(const QModelIndex &index, int role) const
case GAS: case GAS:
/* Check if we have the same gasmix two or more times /* Check if we have the same gasmix two or more times
* If yes return more verbose string */ * If yes return more verbose string */
int same_gas = same_gasmix_cylinder(get_cylinder(&displayed_dive, p.cylinderid), p.cylinderid, &displayed_dive, true); int same_gas = same_gasmix_cylinder(get_cylinder(d, p.cylinderid), p.cylinderid, d, true);
if (same_gas == -1) if (same_gas == -1)
return get_gas_string(get_cylinder(&displayed_dive, p.cylinderid)->gasmix); return get_gas_string(get_cylinder(d, p.cylinderid)->gasmix);
else else
return get_gas_string(get_cylinder(&displayed_dive, p.cylinderid)->gasmix) + return get_gas_string(get_cylinder(d, p.cylinderid)->gasmix) +
QString(" (%1 %2 ").arg(tr("cyl.")).arg(p.cylinderid + 1) + QString(" (%1 %2 ").arg(tr("cyl.")).arg(p.cylinderid + 1) +
get_cylinder(&displayed_dive, p.cylinderid)->type.description + ")"; get_cylinder(d, p.cylinderid)->type.description + ")";
} }
} else if (role == Qt::DecorationRole) { } else if (role == Qt::DecorationRole) {
switch (index.column()) { switch (index.column()) {
@ -388,7 +391,7 @@ bool DivePlannerPointsModel::setData(const QModelIndex &index, const QVariant &v
p.setpoint = p.divemode == CCR ? prefs.defaultsetpoint : 0; p.setpoint = p.divemode == CCR ? prefs.defaultsetpoint : 0;
} }
if (index.row() == 0) if (index.row() == 0)
displayed_dive.dc.divemode = (enum divemode_t) value.toInt(); d->dc.divemode = (enum divemode_t) value.toInt();
break; break;
} }
editStop(index.row(), p); editStop(index.row(), p);
@ -450,6 +453,7 @@ int DivePlannerPointsModel::rowCount(const QModelIndex&) const
} }
DivePlannerPointsModel::DivePlannerPointsModel(QObject *parent) : QAbstractTableModel(parent), DivePlannerPointsModel::DivePlannerPointsModel(QObject *parent) : QAbstractTableModel(parent),
d(nullptr),
cylinders(true), cylinders(true),
mode(NOTHING), mode(NOTHING),
recalc(false) recalc(false)
@ -539,7 +543,7 @@ void DivePlannerPointsModel::setGFLow(const int gflow)
void DivePlannerPointsModel::setRebreatherMode(int mode) void DivePlannerPointsModel::setRebreatherMode(int mode)
{ {
int i; int i;
displayed_dive.dc.divemode = (divemode_t) mode; d->dc.divemode = (divemode_t) mode;
for (i=0; i < rowCount(); i++) { for (i=0; i < rowCount(); i++) {
divepoints[i].setpoint = mode == CCR ? prefs.defaultsetpoint : 0; divepoints[i].setpoint = mode == CCR ? prefs.defaultsetpoint : 0;
divepoints[i].divemode = (enum divemode_t) mode; divepoints[i].divemode = (enum divemode_t) mode;
@ -724,7 +728,7 @@ void DivePlannerPointsModel::setStartDate(const QDate &date)
{ {
startTime.setDate(date); startTime.setDate(date);
diveplan.when = dateTimeToTimestamp(startTime); diveplan.when = dateTimeToTimestamp(startTime);
displayed_dive.when = diveplan.when; d->when = diveplan.when;
emitDataChanged(); emitDataChanged();
} }
@ -732,7 +736,7 @@ void DivePlannerPointsModel::setStartTime(const QTime &t)
{ {
startTime.setTime(t); startTime.setTime(t);
diveplan.when = dateTimeToTimestamp(startTime); diveplan.when = dateTimeToTimestamp(startTime);
displayed_dive.when = diveplan.when; d->when = diveplan.when;
emitDataChanged(); emitDataChanged();
} }
@ -813,7 +817,7 @@ int DivePlannerPointsModel::addStop(int milimeters, int seconds, int cylinderid_
} }
} }
if (divemode == UNDEF_COMP_TYPE) if (divemode == UNDEF_COMP_TYPE)
divemode = displayed_dive.dc.divemode; divemode = d->dc.divemode;
// add the new stop // add the new stop
beginInsertRows(QModelIndex(), row, row); beginInsertRows(QModelIndex(), row, row);
@ -1023,8 +1027,8 @@ void DivePlannerPointsModel::createTemporaryPlan()
// what does the cache do??? // what does the cache do???
struct deco_state *cache = NULL; struct deco_state *cache = NULL;
struct divedatapoint *dp = NULL; struct divedatapoint *dp = NULL;
for (int i = 0; i < displayed_dive.cylinders.nr; i++) { for (int i = 0; i < d->cylinders.nr; i++) {
cylinder_t *cyl = get_cylinder(&displayed_dive, i); cylinder_t *cyl = get_cylinder(d, i);
if (cyl->depth.mm && cyl->cylinder_use != NOT_USED) { if (cyl->depth.mm && cyl->cylinder_use != NOT_USED) {
dp = create_dp(0, cyl->depth.mm, i, 0); dp = create_dp(0, cyl->depth.mm, i, 0);
if (diveplan.dp) { if (diveplan.dp) {
@ -1045,7 +1049,7 @@ void DivePlannerPointsModel::createTemporaryPlan()
struct diveplan *plan_copy; struct diveplan *plan_copy;
memset(&plan_deco_state, 0, sizeof(struct deco_state)); memset(&plan_deco_state, 0, sizeof(struct deco_state));
plan(&plan_deco_state, &diveplan, &displayed_dive, DECOTIMESTEP, stoptable, &cache, isPlanner(), false); plan(&plan_deco_state, &diveplan, d, DECOTIMESTEP, stoptable, &cache, isPlanner(), false);
plan_copy = (struct diveplan *)malloc(sizeof(struct diveplan)); plan_copy = (struct diveplan *)malloc(sizeof(struct diveplan));
lock_planner(); lock_planner();
cloneDiveplan(&diveplan, plan_copy); cloneDiveplan(&diveplan, plan_copy);
@ -1059,12 +1063,12 @@ void DivePlannerPointsModel::createTemporaryPlan()
computeVariations(plan_copy, &plan_deco_state); computeVariations(plan_copy, &plan_deco_state);
#endif #endif
final_deco_state = plan_deco_state; final_deco_state = plan_deco_state;
emit calculatedPlanNotes(QString(displayed_dive.notes)); emit calculatedPlanNotes(QString(d->notes));
} }
// throw away the cache // throw away the cache
free(cache); free(cache);
#if DEBUG_PLAN #if DEBUG_PLAN
save_dive(stderr, &displayed_dive); save_dive(stderr, d);
dump_plan(&diveplan); dump_plan(&diveplan);
#endif #endif
} }
@ -1152,7 +1156,7 @@ void DivePlannerPointsModel::computeVariations(struct diveplan *original_plan, c
return; return;
struct dive *dive = alloc_dive(); struct dive *dive = alloc_dive();
copy_dive(&displayed_dive, dive); copy_dive(d, dive);
struct decostop original[60], deeper[60], shallower[60], shorter[60], longer[60]; struct decostop original[60], deeper[60], shallower[60], shorter[60], longer[60];
struct deco_state *cache = NULL, *save = NULL; struct deco_state *cache = NULL, *save = NULL;
struct diveplan plan_copy; struct diveplan plan_copy;
@ -1241,10 +1245,10 @@ finish:
void DivePlannerPointsModel::computeVariationsDone(QString variations) void DivePlannerPointsModel::computeVariationsDone(QString variations)
{ {
QString notes = QString(displayed_dive.notes); QString notes = QString(d->notes);
free(displayed_dive.notes); free(d->notes);
displayed_dive.notes = copy_qstring(notes.replace("VARIATIONS", variations)); d->notes = copy_qstring(notes.replace("VARIATIONS", variations));
emit calculatedPlanNotes(QString(displayed_dive.notes)); emit calculatedPlanNotes(QString(d->notes));
} }
void DivePlannerPointsModel::createPlan(bool replanCopy) void DivePlannerPointsModel::createPlan(bool replanCopy)
@ -1258,7 +1262,7 @@ void DivePlannerPointsModel::createPlan(bool replanCopy)
//TODO: C-based function here? //TODO: C-based function here?
struct decostop stoptable[60]; struct decostop stoptable[60];
plan(&ds_after_previous_dives, &diveplan, &displayed_dive, DECOTIMESTEP, stoptable, &cache, isPlanner(), true); plan(&ds_after_previous_dives, &diveplan, d, DECOTIMESTEP, stoptable, &cache, isPlanner(), true);
struct diveplan *plan_copy; struct diveplan *plan_copy;
plan_copy = (struct diveplan *)malloc(sizeof(struct diveplan)); plan_copy = (struct diveplan *)malloc(sizeof(struct diveplan));
lock_planner(); lock_planner();
@ -1269,7 +1273,7 @@ void DivePlannerPointsModel::createPlan(bool replanCopy)
free(cache); free(cache);
// Fixup planner notes. // Fixup planner notes.
if (current_dive && displayed_dive.id == current_dive->id) { if (current_dive && d->id == current_dive->id) {
// Try to identify old planner output and remove only this part // Try to identify old planner output and remove only this part
// Treat user provided text as plain text. // Treat user provided text as plain text.
QTextDocument notesDocument; QTextDocument notesDocument;
@ -1295,8 +1299,8 @@ void DivePlannerPointsModel::createPlan(bool replanCopy)
} }
// Deal with line breaks // Deal with line breaks
oldnotes.replace("\n", "<br>"); oldnotes.replace("\n", "<br>");
oldnotes.append(displayed_dive.notes); oldnotes.append(d->notes);
displayed_dive.notes = copy_qstring(oldnotes); d->notes = copy_qstring(oldnotes);
// If we save as new create a copy of the dive here // If we save as new create a copy of the dive here
} }
@ -1305,24 +1309,24 @@ void DivePlannerPointsModel::createPlan(bool replanCopy)
// so that the Undo-commands update the display accordingly (see condition in updateDiveInfo(). // so that the Undo-commands update the display accordingly (see condition in updateDiveInfo().
// Now, add or modify the dive. // Now, add or modify the dive.
if (!current_dive || displayed_dive.id != current_dive->id) { if (!current_dive || d->id != current_dive->id) {
// we were planning a new dive, not re-planning an existing one // we were planning a new dive, not re-planning an existing one
displayed_dive.divetrip = nullptr; // Should not be necessary, just in case! d->divetrip = nullptr; // Should not be necessary, just in case!
#if !defined(SUBSURFACE_TESTING) #if !defined(SUBSURFACE_TESTING)
Command::addDive(&displayed_dive, autogroup, true); Command::addDive(d, autogroup, true);
#endif // !SUBSURFACE_TESTING #endif // !SUBSURFACE_TESTING
} else { } else {
copy_events_until(current_dive, &displayed_dive, preserved_until.seconds); copy_events_until(current_dive, d, preserved_until.seconds);
if (replanCopy) { if (replanCopy) {
// we were planning an old dive and save as a new dive // we were planning an old dive and save as a new dive
displayed_dive.id = dive_getUniqID(); // Things will break horribly if we create dives with the same id. d->id = dive_getUniqID(); // Things will break horribly if we create dives with the same id.
#if !defined(SUBSURFACE_TESTING) #if !defined(SUBSURFACE_TESTING)
Command::addDive(&displayed_dive, false, false); Command::addDive(d, false, false);
#endif // !SUBSURFACE_TESTING #endif // !SUBSURFACE_TESTING
} else { } else {
// we were planning an old dive and rewrite the plan // we were planning an old dive and rewrite the plan
#if !defined(SUBSURFACE_TESTING) #if !defined(SUBSURFACE_TESTING)
Command::replanDive(&displayed_dive); Command::replanDive(d);
#endif // !SUBSURFACE_TESTING #endif // !SUBSURFACE_TESTING
} }
} }

View file

@ -39,7 +39,7 @@ public:
void removeSelectedPoints(const QVector<int> &rows); void removeSelectedPoints(const QVector<int> &rows);
void setPlanMode(Mode mode); void setPlanMode(Mode mode);
bool isPlanner() const; bool isPlanner() const;
void createSimpleDive(); void createSimpleDive(struct dive *d);
Mode currentMode() const; Mode currentMode() const;
bool setRecalc(bool recalc); bool setRecalc(bool recalc);
bool recalcQ() const; bool recalcQ() const;
@ -130,6 +130,7 @@ private:
void computeVariations(struct diveplan *diveplan, const struct deco_state *ds); void computeVariations(struct diveplan *diveplan, const struct deco_state *ds);
void computeVariationsFreeDeco(struct diveplan *diveplan, struct deco_state *ds); void computeVariationsFreeDeco(struct diveplan *diveplan, struct deco_state *ds);
int analyzeVariations(struct decostop *min, struct decostop *mid, struct decostop *max, const char *unit); int analyzeVariations(struct decostop *min, struct decostop *mid, struct decostop *max, const char *unit);
struct dive *d;
CylindersModel cylinders; CylindersModel cylinders;
Mode mode; Mode mode;
bool recalc; bool recalc;