2017-04-27 18:25:32 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2015-05-28 19:23:49 +00:00
|
|
|
#include "diveplannermodel.h"
|
2022-11-12 07:40:04 +00:00
|
|
|
#include "core/dive.h"
|
2020-05-01 11:43:52 +00:00
|
|
|
#include "core/divelist.h"
|
core: introduce divelog structure
The parser API was very annoying, as a number of tables
to-be-filled were passed in as pointers. The goal of this
commit is to collect all these tables in a single struct.
This should make it (more or less) clear what is actually
written into the divelog files.
Moreover, it should now be rather easy to search for
instances, where the global logfile is accessed (and it
turns out that there are many!).
The divelog struct does not contain the tables as substructs,
but only collects pointers. The idea is that the "divelog.h"
file can be included without all the other files describing
the numerous tables.
To make it easier to use from C++ parts of the code, the
struct implements a constructor and a destructor. Sadly,
we can't use smart pointers, since the pointers are accessed
from C code. Therfore the constructor and destructor are
quite complex.
The whole commit is large, but was mostly an automatic
conversion.
One oddity of note: the divelog structure also contains
the "autogroup" flag, since that is saved in the divelog.
This actually fixes a bug: Before, when importing dives
from a different log, the autogroup flag was overwritten.
This was probably not intended and does not happen anymore.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2022-11-08 20:31:08 +00:00
|
|
|
#include "core/divelog.h"
|
2018-05-11 15:25:41 +00:00
|
|
|
#include "core/subsurface-string.h"
|
2016-04-05 05:02:03 +00:00
|
|
|
#include "qt-models/cylindermodel.h"
|
2022-11-07 17:45:30 +00:00
|
|
|
#include "qt-models/models.h" // For defaultModelFont().
|
2016-04-05 05:02:03 +00:00
|
|
|
#include "core/planner.h"
|
2016-11-08 20:17:35 +00:00
|
|
|
#include "core/device.h"
|
2017-11-22 19:42:33 +00:00
|
|
|
#include "core/qthelper.h"
|
2022-09-24 12:06:56 +00:00
|
|
|
#include "core/range.h"
|
2020-10-25 12:28:55 +00:00
|
|
|
#include "core/sample.h"
|
2022-04-04 16:57:28 +00:00
|
|
|
#include "core/selection.h"
|
2018-08-15 09:52:05 +00:00
|
|
|
#include "core/settings/qPrefDivePlanner.h"
|
2020-01-23 15:27:41 +00:00
|
|
|
#include "core/settings/qPrefUnit.h"
|
2020-04-13 16:07:17 +00:00
|
|
|
#if !defined(SUBSURFACE_TESTING)
|
2019-11-13 14:08:40 +00:00
|
|
|
#include "commands/command.h"
|
2020-04-13 16:07:17 +00:00
|
|
|
#endif // !SUBSURFACE_TESTING
|
2018-06-17 06:48:54 +00:00
|
|
|
#include "core/gettextfromc.h"
|
2019-07-15 21:36:14 +00:00
|
|
|
#include "core/deco.h"
|
2017-01-23 17:11:52 +00:00
|
|
|
#include <QApplication>
|
2017-04-19 13:40:59 +00:00
|
|
|
#include <QTextDocument>
|
2017-08-29 09:41:30 +00:00
|
|
|
#include <QtConcurrent>
|
2015-05-28 19:23:49 +00:00
|
|
|
|
2017-11-27 21:07:09 +00:00
|
|
|
#define VARIATIONS_IN_BACKGROUND 1
|
|
|
|
|
2024-03-08 10:21:02 +00:00
|
|
|
static double unit_factor()
|
|
|
|
{
|
|
|
|
return prefs.units.length == units::METERS ? 1000.0 / 60.0
|
|
|
|
: feet_to_mm(1.0) / 60.0;
|
|
|
|
}
|
2017-10-03 08:06:15 +00:00
|
|
|
|
2024-03-08 10:18:17 +00:00
|
|
|
static constexpr int decotimestep = 60; // seconds
|
|
|
|
|
2020-02-03 17:59:12 +00:00
|
|
|
CylindersModel *DivePlannerPointsModel::cylindersModel()
|
2020-02-03 17:52:17 +00:00
|
|
|
{
|
|
|
|
return &cylinders;
|
|
|
|
}
|
|
|
|
|
2022-02-19 10:58:36 +00:00
|
|
|
void DivePlannerPointsModel::removePoints(const std::vector<int> &rows)
|
2015-05-28 19:23:49 +00:00
|
|
|
{
|
2022-02-19 10:58:36 +00:00
|
|
|
if (rows.empty())
|
2015-05-28 19:23:49 +00:00
|
|
|
return;
|
2022-02-19 10:58:36 +00:00
|
|
|
std::vector<int> v2 = rows;
|
2015-05-28 19:23:49 +00:00
|
|
|
std::sort(v2.begin(), v2.end());
|
|
|
|
|
2022-02-19 10:58:36 +00:00
|
|
|
for (int i = (int)v2.size() - 1; i >= 0; i--) {
|
2021-01-24 07:50:10 +00:00
|
|
|
beginRemoveRows(QModelIndex(), v2[i], v2[i]);
|
|
|
|
divepoints.erase(divepoints.begin() + v2[i]);
|
|
|
|
endRemoveRows();
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
2021-02-27 21:24:33 +00:00
|
|
|
}
|
|
|
|
|
2022-02-19 10:58:36 +00:00
|
|
|
void DivePlannerPointsModel::removeSelectedPoints(const std::vector<int> &rows)
|
2021-02-27 21:24:33 +00:00
|
|
|
{
|
|
|
|
removePoints(rows);
|
|
|
|
|
2021-02-27 21:06:05 +00:00
|
|
|
updateDiveProfile();
|
|
|
|
emitDataChanged();
|
2020-02-03 17:59:12 +00:00
|
|
|
cylinders.updateTrashIcon();
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
2021-01-26 06:48:22 +00:00
|
|
|
void DivePlannerPointsModel::createSimpleDive(struct dive *dIn)
|
2015-05-28 19:23:49 +00:00
|
|
|
{
|
2020-04-13 13:35:27 +00:00
|
|
|
// clean out the dive and give it an id and the correct dc model
|
2021-01-26 06:48:22 +00:00
|
|
|
d = dIn;
|
2024-04-25 20:16:08 +00:00
|
|
|
dcNr = 0;
|
2021-01-26 06:48:22 +00:00
|
|
|
clear_dive(d);
|
|
|
|
d->id = dive_getUniqID();
|
|
|
|
d->when = QDateTime::currentMSecsSinceEpoch() / 1000L + gettimezoneoffset() + 3600;
|
2024-05-23 04:06:23 +00:00
|
|
|
make_planner_dc(&d->dc);
|
2020-04-13 13:35:27 +00:00
|
|
|
|
|
|
|
clear();
|
2021-02-27 21:34:08 +00:00
|
|
|
removeDeco();
|
2020-04-13 13:35:27 +00:00
|
|
|
setupCylinders();
|
|
|
|
setupStartTime();
|
|
|
|
|
2015-05-28 19:23:49 +00:00
|
|
|
// initialize the start time in the plan
|
2021-01-20 08:41:21 +00:00
|
|
|
diveplan.when = dateTimeToTimestamp(startTime);
|
2021-01-26 06:48:22 +00:00
|
|
|
d->when = diveplan.when;
|
2015-05-28 19:23:49 +00:00
|
|
|
|
2016-07-06 12:40:28 +00:00
|
|
|
// Use gas from the first cylinder
|
|
|
|
int cylinderid = 0;
|
2015-05-28 19:23:49 +00:00
|
|
|
|
|
|
|
// If we're in drop_stone_mode, don't add a first point.
|
2021-01-20 08:41:21 +00:00
|
|
|
// It will be added implicitly.
|
2015-05-28 19:23:49 +00:00
|
|
|
if (!prefs.drop_stone_mode)
|
2018-06-17 11:41:06 +00:00
|
|
|
addStop(M_OR_FT(15, 45), 1 * 60, cylinderid, 0, true, UNDEF_COMP_TYPE);
|
2015-05-28 19:23:49 +00:00
|
|
|
|
2018-06-17 11:41:06 +00:00
|
|
|
addStop(M_OR_FT(15, 45), 20 * 60, 0, 0, true, UNDEF_COMP_TYPE);
|
2015-05-28 19:23:49 +00:00
|
|
|
if (!isPlanner()) {
|
2018-06-17 11:41:06 +00:00
|
|
|
addStop(M_OR_FT(5, 15), 42 * 60, 0, cylinderid, true, UNDEF_COMP_TYPE);
|
|
|
|
addStop(M_OR_FT(5, 15), 45 * 60, 0, cylinderid, true, UNDEF_COMP_TYPE);
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
2021-02-27 21:06:05 +00:00
|
|
|
updateDiveProfile();
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DivePlannerPointsModel::setupStartTime()
|
|
|
|
{
|
|
|
|
// if the latest dive is in the future, then start an hour after it ends
|
|
|
|
// otherwise start an hour from now
|
|
|
|
startTime = QDateTime::currentDateTimeUtc().addSecs(3600 + gettimezoneoffset());
|
core: introduce divelog structure
The parser API was very annoying, as a number of tables
to-be-filled were passed in as pointers. The goal of this
commit is to collect all these tables in a single struct.
This should make it (more or less) clear what is actually
written into the divelog files.
Moreover, it should now be rather easy to search for
instances, where the global logfile is accessed (and it
turns out that there are many!).
The divelog struct does not contain the tables as substructs,
but only collects pointers. The idea is that the "divelog.h"
file can be included without all the other files describing
the numerous tables.
To make it easier to use from C++ parts of the code, the
struct implements a constructor and a destructor. Sadly,
we can't use smart pointers, since the pointers are accessed
from C code. Therfore the constructor and destructor are
quite complex.
The whole commit is large, but was mostly an automatic
conversion.
One oddity of note: the divelog structure also contains
the "autogroup" flag, since that is saved in the divelog.
This actually fixes a bug: Before, when importing dives
from a different log, the autogroup flag was overwritten.
This was probably not intended and does not happen anymore.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2022-11-08 20:31:08 +00:00
|
|
|
if (divelog.dives->nr > 0) {
|
|
|
|
struct dive *d = get_dive(divelog.dives->nr - 1);
|
2017-09-30 16:00:34 +00:00
|
|
|
time_t ends = dive_endtime(d);
|
2020-05-22 16:53:25 +00:00
|
|
|
time_t diff = ends - dateTimeToTimestamp(startTime);
|
core: introduce divelog structure
The parser API was very annoying, as a number of tables
to-be-filled were passed in as pointers. The goal of this
commit is to collect all these tables in a single struct.
This should make it (more or less) clear what is actually
written into the divelog files.
Moreover, it should now be rather easy to search for
instances, where the global logfile is accessed (and it
turns out that there are many!).
The divelog struct does not contain the tables as substructs,
but only collects pointers. The idea is that the "divelog.h"
file can be included without all the other files describing
the numerous tables.
To make it easier to use from C++ parts of the code, the
struct implements a constructor and a destructor. Sadly,
we can't use smart pointers, since the pointers are accessed
from C code. Therfore the constructor and destructor are
quite complex.
The whole commit is large, but was mostly an automatic
conversion.
One oddity of note: the divelog structure also contains
the "autogroup" flag, since that is saved in the divelog.
This actually fixes a bug: Before, when importing dives
from a different log, the autogroup flag was overwritten.
This was probably not intended and does not happen anymore.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2022-11-08 20:31:08 +00:00
|
|
|
if (diff > 0)
|
2015-05-28 19:23:49 +00:00
|
|
|
startTime = startTime.addSecs(diff + 3600);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-21 20:08:46 +00:00
|
|
|
void DivePlannerPointsModel::loadFromDive(dive *dIn, int dcNrIn)
|
2015-05-28 19:23:49 +00:00
|
|
|
{
|
2021-01-26 06:48:22 +00:00
|
|
|
d = dIn;
|
2022-05-21 20:08:46 +00:00
|
|
|
dcNr = dcNrIn;
|
2021-01-26 06:48:22 +00:00
|
|
|
|
2015-05-28 19:23:49 +00:00
|
|
|
int depthsum = 0;
|
|
|
|
int samplecount = 0;
|
2017-04-20 06:43:48 +00:00
|
|
|
o2pressure_t last_sp;
|
2024-04-25 20:16:08 +00:00
|
|
|
struct divecomputer *dc = get_dive_dc(d, dcNr);
|
2018-08-16 22:58:30 +00:00
|
|
|
const struct event *evd = NULL;
|
2018-06-17 11:41:06 +00:00
|
|
|
enum divemode_t current_divemode = UNDEF_COMP_TYPE;
|
2022-05-21 20:08:46 +00:00
|
|
|
cylinders.updateDive(d, dcNr);
|
2024-05-04 17:15:47 +00:00
|
|
|
duration_t lasttime;
|
|
|
|
duration_t lastrecordedtime;
|
|
|
|
duration_t newtime;
|
2021-01-25 20:45:29 +00:00
|
|
|
|
|
|
|
clear();
|
2021-02-27 21:34:08 +00:00
|
|
|
removeDeco();
|
2015-05-28 19:23:49 +00:00
|
|
|
free_dps(&diveplan);
|
2021-01-25 20:45:29 +00:00
|
|
|
|
2015-05-28 19:23:49 +00:00
|
|
|
diveplan.when = d->when;
|
|
|
|
// is this a "new" dive where we marked manually entered samples?
|
|
|
|
// if yes then the first sample should be marked
|
|
|
|
// if it is we only add the manually entered samples as waypoints to the diveplan
|
|
|
|
// otherwise we have to add all of them
|
2015-10-08 15:59:37 +00:00
|
|
|
|
|
|
|
bool hasMarkedSamples = false;
|
|
|
|
|
2016-11-08 20:17:35 +00:00
|
|
|
if (dc->samples)
|
|
|
|
hasMarkedSamples = dc->sample[0].manually_entered;
|
|
|
|
else
|
2018-05-05 17:26:48 +00:00
|
|
|
fake_dc(dc);
|
2015-10-08 15:59:37 +00:00
|
|
|
|
2015-05-28 19:23:49 +00:00
|
|
|
// if this dive has more than 100 samples (so it is probably a logged dive),
|
|
|
|
// average samples so we end up with a total of 100 samples.
|
2016-11-08 20:17:35 +00:00
|
|
|
int plansamples = dc->samples <= 100 ? dc->samples : 100;
|
2015-05-28 19:23:49 +00:00
|
|
|
int j = 0;
|
2017-03-05 01:14:22 +00:00
|
|
|
int cylinderid = 0;
|
2020-08-17 20:04:53 +00:00
|
|
|
|
2017-04-20 06:43:48 +00:00
|
|
|
last_sp.mbar = 0;
|
2015-05-28 19:23:49 +00:00
|
|
|
for (int i = 0; i < plansamples - 1; i++) {
|
2018-02-04 22:17:58 +00:00
|
|
|
if (dc->last_manual_time.seconds && dc->last_manual_time.seconds > 120 && lasttime.seconds >= dc->last_manual_time.seconds)
|
Improve profile display in planner
This patch allows the planner to save the last manually-entered
dive planner point of a dive plan. When the plan has been saved
and re-opened for edit, the time of the last-entered dive planner
point is used to ensure that dive planning continues from the same
point in the profile as was when the original dive plan was saved.
Mechanism:
1) In dive.h, create a new dc attribute dc->last_manual_time
with data type of duration_t.
2) In diveplanner.c, ensure that the last manually-entered
dive planner point is saved in dc->last_manual_time.
3) In save-xml.c, create a new XML attribute for the <divecomputer>
element, named last-manual-time. For dive plans, the element would
now look like:
<divecomputer model='planned dive' last-manual-time='31:17 min'>
4) In parse-xml.c, insert code that recognises the last-manual-time
XML attribute, reads the time value and assigns this time to
dc->last_manual_time.
5) In diveplannermodel.cpp, method DiveplannerPointModel::loadfromdive,
insert code that sets the appropriate boolean value to dp->entered
by comparing newtime (i.e. time of dp) with dc->last_manual_time.
6) Diveplannermodel.cpp also accepts profile data from normal dives in
the dive log, whether hand-entered or loaded from dive computer. It
looks like the reduction of dive points for dives with >100 points
continues to work ok.
The result is that when a dive plan is saved with manually entered
points up to e.g. 10 minutes into the dive, it can be re-opened for edit
in the dive planner and the planner re-creates the plan with manually
entered points up to 10 minutes. The rest of the points are "soft"
points, shaped by the deco calculations of the planner.
Improvements: Improve code for profile display in dive planner
This responds to #1052.
Change load-git.c and save-git.c so that the last-manual-time is
also saved in the git-format dive log.
Several stylistic changes in text for consistent C source code.
Improvement of dive planner profile display:
Do some simplification of my alterations to diveplannermodel.cpp
Two small style changes in planner.c and diveplannermodel.cpp
as requested ny @neolit123
Signed-off-by: Willem Ferguson <willemferguson@zoology.up.ac.za>
2018-01-15 12:51:47 +00:00
|
|
|
break;
|
2016-11-08 20:17:35 +00:00
|
|
|
while (j * plansamples <= i * dc->samples) {
|
|
|
|
const sample &s = dc->sample[j];
|
2015-05-28 19:23:49 +00:00
|
|
|
if (s.time.seconds != 0 && (!hasMarkedSamples || s.manually_entered)) {
|
|
|
|
depthsum += s.depth.mm;
|
2022-10-20 22:06:08 +00:00
|
|
|
if (j > 0)
|
|
|
|
last_sp = dc->sample[j-1].setpoint;
|
2015-05-28 19:23:49 +00:00
|
|
|
++samplecount;
|
|
|
|
newtime = s.time;
|
|
|
|
}
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
if (samplecount) {
|
2017-02-26 04:51:13 +00:00
|
|
|
cylinderid = get_cylinderid_at_time(d, dc, lasttime);
|
2020-08-17 20:04:53 +00:00
|
|
|
duration_t nexttime = newtime;
|
|
|
|
++nexttime.seconds;
|
|
|
|
if (newtime.seconds - lastrecordedtime.seconds > 10 || cylinderid == get_cylinderid_at_time(d, dc, nexttime)) {
|
|
|
|
if (newtime.seconds == lastrecordedtime.seconds)
|
|
|
|
newtime.seconds += 10;
|
2018-06-17 21:21:53 +00:00
|
|
|
current_divemode = get_current_divemode(dc, newtime.seconds - 1, &evd, ¤t_divemode);
|
2018-06-17 11:41:06 +00:00
|
|
|
addStop(depthsum / samplecount, newtime.seconds, cylinderid, last_sp.mbar, true, current_divemode);
|
2017-01-23 15:37:04 +00:00
|
|
|
lastrecordedtime = newtime;
|
|
|
|
}
|
2015-05-28 19:23:49 +00:00
|
|
|
lasttime = newtime;
|
|
|
|
depthsum = 0;
|
|
|
|
samplecount = 0;
|
|
|
|
}
|
|
|
|
}
|
2017-02-26 04:51:13 +00:00
|
|
|
// make sure we get the last point right so the duration is correct
|
2024-04-25 20:16:08 +00:00
|
|
|
current_divemode = get_current_divemode(dc, dc->duration.seconds, &evd, ¤t_divemode);
|
Improve profile display in planner
This patch allows the planner to save the last manually-entered
dive planner point of a dive plan. When the plan has been saved
and re-opened for edit, the time of the last-entered dive planner
point is used to ensure that dive planning continues from the same
point in the profile as was when the original dive plan was saved.
Mechanism:
1) In dive.h, create a new dc attribute dc->last_manual_time
with data type of duration_t.
2) In diveplanner.c, ensure that the last manually-entered
dive planner point is saved in dc->last_manual_time.
3) In save-xml.c, create a new XML attribute for the <divecomputer>
element, named last-manual-time. For dive plans, the element would
now look like:
<divecomputer model='planned dive' last-manual-time='31:17 min'>
4) In parse-xml.c, insert code that recognises the last-manual-time
XML attribute, reads the time value and assigns this time to
dc->last_manual_time.
5) In diveplannermodel.cpp, method DiveplannerPointModel::loadfromdive,
insert code that sets the appropriate boolean value to dp->entered
by comparing newtime (i.e. time of dp) with dc->last_manual_time.
6) Diveplannermodel.cpp also accepts profile data from normal dives in
the dive log, whether hand-entered or loaded from dive computer. It
looks like the reduction of dive points for dives with >100 points
continues to work ok.
The result is that when a dive plan is saved with manually entered
points up to e.g. 10 minutes into the dive, it can be re-opened for edit
in the dive planner and the planner re-creates the plan with manually
entered points up to 10 minutes. The rest of the points are "soft"
points, shaped by the deco calculations of the planner.
Improvements: Improve code for profile display in dive planner
This responds to #1052.
Change load-git.c and save-git.c so that the last-manual-time is
also saved in the git-format dive log.
Several stylistic changes in text for consistent C source code.
Improvement of dive planner profile display:
Do some simplification of my alterations to diveplannermodel.cpp
Two small style changes in planner.c and diveplannermodel.cpp
as requested ny @neolit123
Signed-off-by: Willem Ferguson <willemferguson@zoology.up.ac.za>
2018-01-15 12:51:47 +00:00
|
|
|
if (!hasMarkedSamples && !dc->last_manual_time.seconds)
|
2024-04-25 20:16:08 +00:00
|
|
|
addStop(0, dc->duration.seconds,cylinderid, last_sp.mbar, true, current_divemode);
|
2020-04-12 11:39:01 +00:00
|
|
|
preserved_until = d->duration;
|
2021-02-27 21:06:05 +00:00
|
|
|
|
|
|
|
updateDiveProfile();
|
2015-05-28 19:23:49 +00:00
|
|
|
emitDataChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
// copy the tanks from the current dive, or the default cylinder
|
|
|
|
// or an unknown cylinder
|
|
|
|
// setup the cylinder widget accordingly
|
|
|
|
void DivePlannerPointsModel::setupCylinders()
|
|
|
|
{
|
2021-01-26 06:48:22 +00:00
|
|
|
clear_cylinder_table(&d->cylinders);
|
2015-05-28 19:23:49 +00:00
|
|
|
if (mode == PLAN && current_dive) {
|
|
|
|
// take the displayed cylinders from the selected dive as starting point
|
2023-07-09 00:14:27 +00:00
|
|
|
copy_used_cylinders(current_dive, d, !prefs.include_unused_tanks);
|
2021-01-26 06:48:22 +00:00
|
|
|
reset_cylinders(d, true);
|
2015-10-20 07:56:01 +00:00
|
|
|
|
2021-01-26 06:48:22 +00:00
|
|
|
if (d->cylinders.nr > 0) {
|
2022-05-21 20:08:46 +00:00
|
|
|
cylinders.updateDive(d, dcNr);
|
2019-08-04 16:44:57 +00:00
|
|
|
return; // We have at least one cylinder
|
|
|
|
}
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
2024-05-25 07:03:39 +00:00
|
|
|
|
|
|
|
add_default_cylinder(d);
|
2022-05-21 20:08:46 +00:00
|
|
|
cylinders.updateDive(d, dcNr);
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
2017-03-06 12:27:39 +00:00
|
|
|
// Update the dive's maximum depth. Returns true if max. depth changed
|
2016-07-06 12:40:35 +00:00
|
|
|
bool DivePlannerPointsModel::updateMaxDepth()
|
|
|
|
{
|
2021-01-26 06:48:22 +00:00
|
|
|
int prevMaxDepth = d->maxdepth.mm;
|
|
|
|
d->maxdepth.mm = 0;
|
2016-07-06 12:40:35 +00:00
|
|
|
for (int i = 0; i < rowCount(); i++) {
|
|
|
|
divedatapoint p = at(i);
|
2021-01-26 06:48:22 +00:00
|
|
|
if (p.depth.mm > d->maxdepth.mm)
|
|
|
|
d->maxdepth.mm = p.depth.mm;
|
2016-07-06 12:40:35 +00:00
|
|
|
}
|
2021-01-26 06:48:22 +00:00
|
|
|
return d->maxdepth.mm != prevMaxDepth;
|
2016-07-06 12:40:35 +00:00
|
|
|
}
|
|
|
|
|
2015-05-28 19:23:49 +00:00
|
|
|
void DivePlannerPointsModel::removeDeco()
|
|
|
|
{
|
2022-02-19 10:58:36 +00:00
|
|
|
std::vector<int> computedPoints;
|
2021-02-27 21:24:33 +00:00
|
|
|
for (int i = 0; i < rowCount(); i++) {
|
2015-05-28 19:23:49 +00:00
|
|
|
if (!at(i).entered)
|
|
|
|
computedPoints.push_back(i);
|
2021-02-27 21:24:33 +00:00
|
|
|
}
|
|
|
|
removePoints(computedPoints);
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DivePlannerPointsModel::addCylinder_clicked()
|
|
|
|
{
|
2020-02-03 17:52:17 +00:00
|
|
|
cylinders.add();
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DivePlannerPointsModel::setPlanMode(Mode m)
|
|
|
|
{
|
|
|
|
mode = m;
|
|
|
|
// the planner may reset our GF settings that are used to show deco
|
|
|
|
// reset them to what's in the preferences
|
2016-09-24 08:02:08 +00:00
|
|
|
if (m != PLAN) {
|
2017-09-19 12:38:38 +00:00
|
|
|
set_gf(prefs.gflow, prefs.gfhigh);
|
2016-09-24 08:02:08 +00:00
|
|
|
set_vpmb_conservatism(prefs.vpmb_conservatism);
|
|
|
|
}
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
2021-01-23 10:28:19 +00:00
|
|
|
bool DivePlannerPointsModel::isPlanner() const
|
2015-05-28 19:23:49 +00:00
|
|
|
{
|
|
|
|
return mode == PLAN;
|
|
|
|
}
|
|
|
|
|
2018-05-21 15:53:42 +00:00
|
|
|
int DivePlannerPointsModel::columnCount(const QModelIndex&) const
|
2015-05-28 19:23:49 +00:00
|
|
|
{
|
|
|
|
return COLUMNS; // to disable CCSETPOINT subtract one
|
|
|
|
}
|
|
|
|
|
|
|
|
QVariant DivePlannerPointsModel::data(const QModelIndex &index, int role) const
|
|
|
|
{
|
|
|
|
divedatapoint p = divepoints.at(index.row());
|
|
|
|
if (role == Qt::DisplayRole || role == Qt::EditRole) {
|
|
|
|
switch (index.column()) {
|
|
|
|
case CCSETPOINT:
|
|
|
|
return (double)p.setpoint / 1000;
|
|
|
|
case DEPTH:
|
2017-03-11 16:41:41 +00:00
|
|
|
return (int) lrint(get_depth_units(p.depth.mm, NULL, NULL));
|
2015-05-28 19:23:49 +00:00
|
|
|
case RUNTIME:
|
|
|
|
return p.time / 60;
|
|
|
|
case DURATION:
|
|
|
|
if (index.row())
|
|
|
|
return (p.time - divepoints.at(index.row() - 1).time) / 60;
|
|
|
|
else
|
|
|
|
return p.time / 60;
|
2018-03-28 20:50:28 +00:00
|
|
|
case DIVEMODE:
|
2018-06-17 15:55:47 +00:00
|
|
|
return gettextFromC::tr(divemode_text_ui[p.divemode]);
|
2015-05-28 19:23:49 +00:00
|
|
|
case GAS:
|
2017-10-08 03:14:57 +00:00
|
|
|
/* Check if we have the same gasmix two or more times
|
|
|
|
* If yes return more verbose string */
|
2021-01-26 06:48:22 +00:00
|
|
|
int same_gas = same_gasmix_cylinder(get_cylinder(d, p.cylinderid), p.cylinderid, d, true);
|
2017-10-08 03:14:57 +00:00
|
|
|
if (same_gas == -1)
|
2021-01-26 06:48:22 +00:00
|
|
|
return get_gas_string(get_cylinder(d, p.cylinderid)->gasmix);
|
2017-10-08 03:14:57 +00:00
|
|
|
else
|
2021-01-26 06:48:22 +00:00
|
|
|
return get_gas_string(get_cylinder(d, p.cylinderid)->gasmix) +
|
2017-10-08 03:14:57 +00:00
|
|
|
QString(" (%1 %2 ").arg(tr("cyl.")).arg(p.cylinderid + 1) +
|
2021-01-26 06:48:22 +00:00
|
|
|
get_cylinder(d, p.cylinderid)->type.description + ")";
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
} else if (role == Qt::DecorationRole) {
|
|
|
|
switch (index.column()) {
|
|
|
|
case REMOVE:
|
|
|
|
if (rowCount() > 1)
|
|
|
|
return p.entered ? trashIcon() : QVariant();
|
2015-09-17 22:27:49 +00:00
|
|
|
else
|
|
|
|
return trashForbiddenIcon();
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
} else if (role == Qt::SizeHintRole) {
|
|
|
|
switch (index.column()) {
|
|
|
|
case REMOVE:
|
|
|
|
if (rowCount() > 1)
|
|
|
|
return p.entered ? trashIcon().size() : QVariant();
|
2015-09-17 22:27:49 +00:00
|
|
|
else
|
|
|
|
return trashForbiddenIcon().size();
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
} else if (role == Qt::FontRole) {
|
|
|
|
if (divepoints.at(index.row()).entered) {
|
|
|
|
return defaultModelFont();
|
|
|
|
} else {
|
|
|
|
QFont font = defaultModelFont();
|
|
|
|
font.setBold(true);
|
|
|
|
return font;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DivePlannerPointsModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
|
|
|
{
|
|
|
|
int i, shift;
|
|
|
|
if (role == Qt::EditRole) {
|
|
|
|
divedatapoint &p = divepoints[index.row()];
|
|
|
|
switch (index.column()) {
|
|
|
|
case DEPTH:
|
2016-07-06 12:40:35 +00:00
|
|
|
if (value.toInt() >= 0) {
|
2017-03-10 12:37:54 +00:00
|
|
|
p.depth = units_to_depth(value.toInt());
|
2016-07-06 12:40:35 +00:00
|
|
|
if (updateMaxDepth())
|
2020-02-03 17:59:12 +00:00
|
|
|
cylinders.updateBestMixes();
|
2016-07-06 12:40:35 +00:00
|
|
|
}
|
2015-05-28 19:23:49 +00:00
|
|
|
break;
|
2021-02-23 19:43:17 +00:00
|
|
|
case RUNTIME: {
|
2020-08-22 11:03:51 +00:00
|
|
|
int secs = value.toInt() * 60;
|
2015-05-28 19:23:49 +00:00
|
|
|
i = index.row();
|
2020-08-22 11:03:51 +00:00
|
|
|
int duration = secs;
|
2015-05-28 19:23:49 +00:00
|
|
|
if (i)
|
2020-08-22 11:03:51 +00:00
|
|
|
duration -= divepoints[i-1].time;
|
|
|
|
// Make sure segments have a minimal duration
|
|
|
|
if (duration <= 0)
|
|
|
|
secs += 10 - duration;
|
|
|
|
p.time = secs;
|
|
|
|
while (++i < divepoints.size())
|
|
|
|
if (divepoints[i].time < divepoints[i - 1].time + 10)
|
|
|
|
divepoints[i].time = divepoints[i - 1].time + 10;
|
2015-05-28 19:23:49 +00:00
|
|
|
break;
|
2021-02-23 19:43:17 +00:00
|
|
|
}
|
|
|
|
case DURATION: {
|
2020-08-22 11:03:51 +00:00
|
|
|
int secs = value.toInt() * 60;
|
|
|
|
if (!secs)
|
|
|
|
secs = 10;
|
|
|
|
i = index.row();
|
|
|
|
if (i)
|
|
|
|
shift = divepoints[i].time - divepoints[i - 1].time - secs;
|
|
|
|
else
|
|
|
|
shift = divepoints[i].time - secs;
|
|
|
|
while (i < divepoints.size())
|
|
|
|
divepoints[i++].time -= shift;
|
|
|
|
break;
|
2021-02-23 19:43:17 +00:00
|
|
|
}
|
2015-05-28 19:23:49 +00:00
|
|
|
case CCSETPOINT: {
|
|
|
|
int po2 = 0;
|
|
|
|
QByteArray gasv = value.toByteArray();
|
|
|
|
if (validate_po2(gasv.data(), &po2))
|
|
|
|
p.setpoint = po2;
|
2021-02-23 19:43:17 +00:00
|
|
|
break;
|
|
|
|
}
|
2015-05-28 19:23:49 +00:00
|
|
|
case GAS:
|
2019-08-04 16:44:57 +00:00
|
|
|
if (value.toInt() >= 0)
|
2016-07-06 12:40:28 +00:00
|
|
|
p.cylinderid = value.toInt();
|
2017-11-18 22:59:22 +00:00
|
|
|
/* Did we change the start (dp 0) cylinder to another cylinderid than 0? */
|
2024-05-12 06:43:08 +00:00
|
|
|
if (value.toInt() > 0 && index.row() == 0)
|
2020-02-03 17:59:12 +00:00
|
|
|
cylinders.moveAtFirst(value.toInt());
|
|
|
|
cylinders.updateTrashIcon();
|
2015-05-28 19:23:49 +00:00
|
|
|
break;
|
2018-03-28 20:50:28 +00:00
|
|
|
case DIVEMODE:
|
2018-06-17 19:30:51 +00:00
|
|
|
if (value.toInt() < FREEDIVE) {
|
2018-05-08 14:24:51 +00:00
|
|
|
p.divemode = (enum divemode_t) value.toInt();
|
2018-06-17 19:30:51 +00:00
|
|
|
p.setpoint = p.divemode == CCR ? prefs.defaultsetpoint : 0;
|
|
|
|
}
|
2018-03-28 20:50:28 +00:00
|
|
|
break;
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
editStop(index.row(), p);
|
|
|
|
}
|
|
|
|
return QAbstractItemModel::setData(index, value, role);
|
|
|
|
}
|
|
|
|
|
2017-10-11 20:03:03 +00:00
|
|
|
void DivePlannerPointsModel::gasChange(const QModelIndex &index, int newcylinderid)
|
2015-05-28 19:23:49 +00:00
|
|
|
{
|
2016-07-06 12:40:28 +00:00
|
|
|
int i = index.row(), oldcylinderid = divepoints[i].cylinderid;
|
|
|
|
while (i < rowCount() && oldcylinderid == divepoints[i].cylinderid)
|
|
|
|
divepoints[i++].cylinderid = newcylinderid;
|
2017-02-15 22:15:08 +00:00
|
|
|
emitDataChanged();
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
2017-10-11 19:27:06 +00:00
|
|
|
void DivePlannerPointsModel::cylinderRenumber(int mapping[])
|
|
|
|
{
|
2017-11-29 09:16:00 +00:00
|
|
|
for (int i = 0; i < rowCount(); i++) {
|
|
|
|
if (mapping[divepoints[i].cylinderid] >= 0)
|
|
|
|
divepoints[i].cylinderid = mapping[divepoints[i].cylinderid];
|
|
|
|
}
|
2017-10-11 19:27:06 +00:00
|
|
|
emitDataChanged();
|
|
|
|
}
|
|
|
|
|
2015-05-28 19:23:49 +00:00
|
|
|
QVariant DivePlannerPointsModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
|
|
{
|
|
|
|
if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
|
|
|
|
switch (section) {
|
|
|
|
case DEPTH:
|
|
|
|
return tr("Final depth");
|
|
|
|
case RUNTIME:
|
|
|
|
return tr("Run time");
|
|
|
|
case DURATION:
|
|
|
|
return tr("Duration");
|
|
|
|
case GAS:
|
|
|
|
return tr("Used gas");
|
|
|
|
case CCSETPOINT:
|
2015-09-18 14:28:01 +00:00
|
|
|
return tr("CC setpoint");
|
2018-03-28 20:50:28 +00:00
|
|
|
case DIVEMODE:
|
|
|
|
return tr("Dive mode");
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
} else if (role == Qt::FontRole) {
|
|
|
|
return defaultModelFont();
|
|
|
|
}
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
|
|
|
|
Qt::ItemFlags DivePlannerPointsModel::flags(const QModelIndex &index) const
|
|
|
|
{
|
|
|
|
if (index.column() != REMOVE)
|
|
|
|
return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
|
|
|
|
else
|
|
|
|
return QAbstractItemModel::flags(index);
|
|
|
|
}
|
|
|
|
|
2018-05-21 15:53:42 +00:00
|
|
|
int DivePlannerPointsModel::rowCount(const QModelIndex&) const
|
2015-05-28 19:23:49 +00:00
|
|
|
{
|
|
|
|
return divepoints.count();
|
|
|
|
}
|
|
|
|
|
|
|
|
DivePlannerPointsModel::DivePlannerPointsModel(QObject *parent) : QAbstractTableModel(parent),
|
2021-01-26 06:48:22 +00:00
|
|
|
d(nullptr),
|
2023-07-09 00:14:27 +00:00
|
|
|
cylinders(true),
|
2021-02-27 21:39:01 +00:00
|
|
|
mode(NOTHING)
|
2015-05-28 19:23:49 +00:00
|
|
|
{
|
|
|
|
memset(&diveplan, 0, sizeof(diveplan));
|
2016-06-22 20:46:22 +00:00
|
|
|
startTime.setTimeSpec(Qt::UTC);
|
2020-04-13 12:46:15 +00:00
|
|
|
// use a Qt-connection to send the variations text across thread boundary (in case we
|
|
|
|
// are calculating the variations in a background thread).
|
|
|
|
connect(this, &DivePlannerPointsModel::variationsComputed, this, &DivePlannerPointsModel::computeVariationsDone);
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DivePlannerPointsModel *DivePlannerPointsModel::instance()
|
|
|
|
{
|
2017-12-20 19:00:26 +00:00
|
|
|
static DivePlannerPointsModel self;
|
|
|
|
return &self;
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DivePlannerPointsModel::emitDataChanged()
|
|
|
|
{
|
2021-05-08 20:14:54 +00:00
|
|
|
updateDiveProfile();
|
2015-05-28 19:23:49 +00:00
|
|
|
emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, COLUMNS - 1));
|
|
|
|
}
|
|
|
|
|
|
|
|
void DivePlannerPointsModel::setBottomSac(double sac)
|
|
|
|
{
|
2020-01-25 12:02:59 +00:00
|
|
|
// mobile delivers the same value as desktop when using
|
|
|
|
// units:METERS
|
|
|
|
// however when using units:CUFT mobile deliver 0-300 which
|
|
|
|
// are really 0.00 - 3.00 so start be correcting that
|
2020-01-23 15:27:41 +00:00
|
|
|
#ifdef SUBSURFACE_MOBILE
|
|
|
|
if (qPrefUnits::volume() == units::CUFT)
|
|
|
|
sac /= 100; // cuft without decimals (0 - 300)
|
|
|
|
#endif
|
2015-05-28 19:23:49 +00:00
|
|
|
diveplan.bottomsac = units_to_sac(sac);
|
2018-08-15 09:52:05 +00:00
|
|
|
qPrefDivePlanner::set_bottomsac(diveplan.bottomsac);
|
2017-02-15 22:15:08 +00:00
|
|
|
emitDataChanged();
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DivePlannerPointsModel::setDecoSac(double sac)
|
|
|
|
{
|
2020-01-25 12:02:59 +00:00
|
|
|
// mobile delivers the same value as desktop when using
|
|
|
|
// units:METERS
|
|
|
|
// however when using units:CUFT mobile deliver 0-300 which
|
|
|
|
// are really 0.00 - 3.00 so start be correcting that
|
2020-01-23 15:27:41 +00:00
|
|
|
#ifdef SUBSURFACE_MOBILE
|
|
|
|
if (qPrefUnits::volume() == units::CUFT)
|
|
|
|
sac /= 100; // cuft without decimals (0 - 300)
|
|
|
|
#endif
|
2015-05-28 19:23:49 +00:00
|
|
|
diveplan.decosac = units_to_sac(sac);
|
2018-08-15 09:52:05 +00:00
|
|
|
qPrefDivePlanner::set_decosac(diveplan.decosac);
|
2017-02-15 22:15:08 +00:00
|
|
|
emitDataChanged();
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
2017-02-11 19:24:18 +00:00
|
|
|
void DivePlannerPointsModel::setSacFactor(double factor)
|
|
|
|
{
|
2020-01-25 12:02:59 +00:00
|
|
|
// sacfactor is normal x.y (one decimal), however mobile
|
|
|
|
// delivers 0 - 100 so adjust that to 0.0 - 10.0, to have
|
|
|
|
// the same value as desktop
|
2020-01-23 15:27:41 +00:00
|
|
|
#ifdef SUBSURFACE_MOBILE
|
|
|
|
factor /= 10.0;
|
|
|
|
#endif
|
2018-08-15 09:52:05 +00:00
|
|
|
qPrefDivePlanner::set_sacfactor((int) round(factor * 100));
|
2017-02-11 19:24:18 +00:00
|
|
|
emitDataChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DivePlannerPointsModel::setProblemSolvingTime(int minutes)
|
|
|
|
{
|
2018-08-15 09:52:05 +00:00
|
|
|
qPrefDivePlanner::set_problemsolvingtime(minutes);
|
2017-02-11 19:24:18 +00:00
|
|
|
emitDataChanged();
|
|
|
|
}
|
|
|
|
|
2015-05-28 19:23:49 +00:00
|
|
|
void DivePlannerPointsModel::setGFHigh(const int gfhigh)
|
|
|
|
{
|
2017-12-19 19:55:17 +00:00
|
|
|
if (diveplan.gfhigh != gfhigh) {
|
|
|
|
diveplan.gfhigh = gfhigh;
|
2015-05-28 19:23:49 +00:00
|
|
|
emitDataChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-25 12:18:26 +00:00
|
|
|
int DivePlannerPointsModel::gfHigh() const
|
|
|
|
{
|
|
|
|
return diveplan.gfhigh;
|
|
|
|
}
|
|
|
|
|
2017-12-19 18:15:53 +00:00
|
|
|
void DivePlannerPointsModel::setGFLow(const int gflow)
|
2015-05-28 19:23:49 +00:00
|
|
|
{
|
2017-12-19 19:55:17 +00:00
|
|
|
if (diveplan.gflow != gflow) {
|
|
|
|
diveplan.gflow = gflow;
|
|
|
|
emitDataChanged();
|
|
|
|
}
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
2024-01-25 12:18:26 +00:00
|
|
|
int DivePlannerPointsModel::gfLow() const
|
|
|
|
{
|
|
|
|
return diveplan.gflow;
|
|
|
|
}
|
|
|
|
|
2015-05-28 19:23:49 +00:00
|
|
|
void DivePlannerPointsModel::setRebreatherMode(int mode)
|
|
|
|
{
|
2024-04-25 20:16:08 +00:00
|
|
|
get_dive_dc(d, dcNr)->divemode = (divemode_t) mode;
|
|
|
|
for (int i = 0; i < rowCount(); i++) {
|
2015-05-28 19:23:49 +00:00
|
|
|
divepoints[i].setpoint = mode == CCR ? prefs.defaultsetpoint : 0;
|
2018-05-08 14:24:51 +00:00
|
|
|
divepoints[i].divemode = (enum divemode_t) mode;
|
2018-03-28 20:50:28 +00:00
|
|
|
}
|
2015-05-28 19:23:49 +00:00
|
|
|
emitDataChanged();
|
|
|
|
}
|
|
|
|
|
2016-09-24 08:02:07 +00:00
|
|
|
void DivePlannerPointsModel::setVpmbConservatism(int level)
|
2015-08-15 13:16:51 +00:00
|
|
|
{
|
2016-09-24 08:02:08 +00:00
|
|
|
if (diveplan.vpmb_conservatism != level) {
|
|
|
|
diveplan.vpmb_conservatism = level;
|
|
|
|
emitDataChanged();
|
|
|
|
}
|
2015-08-15 13:16:51 +00:00
|
|
|
}
|
|
|
|
|
2015-05-28 19:23:49 +00:00
|
|
|
void DivePlannerPointsModel::setSurfacePressure(int pressure)
|
|
|
|
{
|
|
|
|
diveplan.surface_pressure = pressure;
|
2017-02-15 22:15:08 +00:00
|
|
|
emitDataChanged();
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DivePlannerPointsModel::setSalinity(int salinity)
|
|
|
|
{
|
|
|
|
diveplan.salinity = salinity;
|
2017-02-15 22:15:08 +00:00
|
|
|
emitDataChanged();
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
2021-01-23 10:28:19 +00:00
|
|
|
int DivePlannerPointsModel::getSurfacePressure() const
|
2015-05-28 19:23:49 +00:00
|
|
|
{
|
|
|
|
return diveplan.surface_pressure;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DivePlannerPointsModel::setLastStop6m(bool value)
|
|
|
|
{
|
2018-08-15 09:52:05 +00:00
|
|
|
qPrefDivePlanner::set_last_stop(value);
|
2017-02-15 22:15:08 +00:00
|
|
|
emitDataChanged();
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
2020-01-20 09:48:31 +00:00
|
|
|
void DivePlannerPointsModel::setAscrate75Display(int rate)
|
2017-10-03 08:06:15 +00:00
|
|
|
{
|
2024-03-08 10:21:02 +00:00
|
|
|
qPrefDivePlanner::set_ascrate75(lrint(rate * unit_factor()));
|
2017-10-03 08:06:15 +00:00
|
|
|
emitDataChanged();
|
|
|
|
}
|
2021-01-23 10:28:19 +00:00
|
|
|
int DivePlannerPointsModel::ascrate75Display() const
|
2020-01-19 17:19:14 +00:00
|
|
|
{
|
2024-03-08 10:21:02 +00:00
|
|
|
return lrint((float)prefs.ascrate75 / unit_factor());
|
2020-01-19 17:19:14 +00:00
|
|
|
}
|
2017-10-03 08:06:15 +00:00
|
|
|
|
2020-01-20 09:48:31 +00:00
|
|
|
void DivePlannerPointsModel::setAscrate50Display(int rate)
|
2017-10-03 08:06:15 +00:00
|
|
|
{
|
2024-03-08 10:21:02 +00:00
|
|
|
qPrefDivePlanner::set_ascrate50(lrint(rate * unit_factor()));
|
2017-10-03 08:06:15 +00:00
|
|
|
emitDataChanged();
|
|
|
|
}
|
2021-01-23 10:28:19 +00:00
|
|
|
int DivePlannerPointsModel::ascrate50Display() const
|
2020-01-19 17:19:14 +00:00
|
|
|
{
|
2024-03-08 10:21:02 +00:00
|
|
|
return lrint((float)prefs.ascrate50 / unit_factor());
|
2020-01-19 17:19:14 +00:00
|
|
|
}
|
2017-10-03 08:06:15 +00:00
|
|
|
|
2020-01-20 09:48:31 +00:00
|
|
|
void DivePlannerPointsModel::setAscratestopsDisplay(int rate)
|
2017-10-03 08:06:15 +00:00
|
|
|
{
|
2024-03-08 10:21:02 +00:00
|
|
|
qPrefDivePlanner::set_ascratestops(lrint(rate * unit_factor()));
|
2017-10-03 08:06:15 +00:00
|
|
|
emitDataChanged();
|
|
|
|
}
|
2021-01-23 10:28:19 +00:00
|
|
|
int DivePlannerPointsModel::ascratestopsDisplay() const
|
2020-01-19 17:19:14 +00:00
|
|
|
{
|
2024-03-08 10:21:02 +00:00
|
|
|
return lrint((float)prefs.ascratestops / unit_factor());
|
2020-01-19 17:19:14 +00:00
|
|
|
}
|
2017-10-03 08:06:15 +00:00
|
|
|
|
2020-01-20 09:48:31 +00:00
|
|
|
void DivePlannerPointsModel::setAscratelast6mDisplay(int rate)
|
2017-10-03 08:06:15 +00:00
|
|
|
{
|
2024-03-08 10:21:02 +00:00
|
|
|
qPrefDivePlanner::set_ascratelast6m(lrint(rate * unit_factor()));
|
2017-10-03 08:06:15 +00:00
|
|
|
emitDataChanged();
|
|
|
|
}
|
2021-01-23 10:28:19 +00:00
|
|
|
int DivePlannerPointsModel::ascratelast6mDisplay() const
|
2020-01-19 17:19:14 +00:00
|
|
|
{
|
2024-03-08 10:21:02 +00:00
|
|
|
return lrint((float)prefs.ascratelast6m / unit_factor());
|
2020-01-19 17:19:14 +00:00
|
|
|
}
|
2017-10-03 08:06:15 +00:00
|
|
|
|
2020-01-20 09:48:31 +00:00
|
|
|
void DivePlannerPointsModel::setDescrateDisplay(int rate)
|
2017-10-03 08:06:15 +00:00
|
|
|
{
|
2024-03-08 10:21:02 +00:00
|
|
|
qPrefDivePlanner::set_descrate(lrint(rate * unit_factor()));
|
2017-10-03 08:06:15 +00:00
|
|
|
emitDataChanged();
|
|
|
|
}
|
2021-01-23 10:28:19 +00:00
|
|
|
int DivePlannerPointsModel::descrateDisplay() const
|
2020-01-19 17:19:14 +00:00
|
|
|
{
|
2024-03-08 10:21:02 +00:00
|
|
|
return lrint((float)prefs.descrate / unit_factor());
|
2020-01-19 17:19:14 +00:00
|
|
|
}
|
2017-10-03 08:06:15 +00:00
|
|
|
|
2015-05-28 19:23:49 +00:00
|
|
|
void DivePlannerPointsModel::setVerbatim(bool value)
|
|
|
|
{
|
2018-08-15 09:52:05 +00:00
|
|
|
qPrefDivePlanner::set_verbatim_plan(value);
|
2017-02-15 22:15:08 +00:00
|
|
|
emitDataChanged();
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DivePlannerPointsModel::setDisplayRuntime(bool value)
|
|
|
|
{
|
2018-08-15 09:52:05 +00:00
|
|
|
qPrefDivePlanner::set_display_runtime(value);
|
2017-02-15 22:15:08 +00:00
|
|
|
emitDataChanged();
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DivePlannerPointsModel::setDisplayDuration(bool value)
|
|
|
|
{
|
2018-08-15 09:52:05 +00:00
|
|
|
qPrefDivePlanner::set_display_duration(value);
|
2017-02-15 22:15:08 +00:00
|
|
|
emitDataChanged();
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DivePlannerPointsModel::setDisplayTransitions(bool value)
|
|
|
|
{
|
2018-08-15 09:52:05 +00:00
|
|
|
qPrefDivePlanner::set_display_transitions(value);
|
2017-02-15 22:15:08 +00:00
|
|
|
emitDataChanged();
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
2017-09-18 14:10:47 +00:00
|
|
|
void DivePlannerPointsModel::setDisplayVariations(bool value)
|
|
|
|
{
|
2018-08-15 09:52:05 +00:00
|
|
|
qPrefDivePlanner::set_display_variations(value);
|
2017-09-18 14:10:47 +00:00
|
|
|
emitDataChanged();
|
|
|
|
}
|
|
|
|
|
2015-07-03 21:07:58 +00:00
|
|
|
void DivePlannerPointsModel::setDecoMode(int mode)
|
2015-05-28 19:23:49 +00:00
|
|
|
{
|
2018-08-15 09:52:05 +00:00
|
|
|
qPrefDivePlanner::set_planner_deco_mode(deco_mode(mode));
|
2017-01-07 02:11:19 +00:00
|
|
|
emit recreationChanged(mode == int(prefs.planner_deco_mode));
|
2017-12-19 20:08:23 +00:00
|
|
|
emitDataChanged();
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DivePlannerPointsModel::setSafetyStop(bool value)
|
|
|
|
{
|
2018-08-15 09:52:05 +00:00
|
|
|
qPrefDivePlanner::set_safetystop(value);
|
2017-12-19 20:08:23 +00:00
|
|
|
emitDataChanged();
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DivePlannerPointsModel::setReserveGas(int reserve)
|
|
|
|
{
|
2016-03-22 22:44:59 +00:00
|
|
|
if (prefs.units.pressure == units::BAR)
|
2018-08-15 09:52:05 +00:00
|
|
|
qPrefDivePlanner::set_reserve_gas(reserve * 1000);
|
2016-03-22 22:44:59 +00:00
|
|
|
else
|
2018-08-15 09:52:05 +00:00
|
|
|
qPrefDivePlanner::set_reserve_gas(psi_to_mbar(reserve));
|
2017-02-15 22:15:08 +00:00
|
|
|
emitDataChanged();
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DivePlannerPointsModel::setDropStoneMode(bool value)
|
|
|
|
{
|
2018-08-15 09:52:05 +00:00
|
|
|
qPrefDivePlanner::set_drop_stone_mode(value);
|
2015-05-28 19:23:49 +00:00
|
|
|
if (prefs.drop_stone_mode) {
|
|
|
|
/* Remove the first entry if we enable drop_stone_mode */
|
|
|
|
if (rowCount() >= 2) {
|
|
|
|
beginRemoveRows(QModelIndex(), 0, 0);
|
|
|
|
divepoints.remove(0);
|
|
|
|
endRemoveRows();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Add a first entry if we disable drop_stone_mode */
|
|
|
|
beginInsertRows(QModelIndex(), 0, 0);
|
|
|
|
/* Copy the first current point */
|
|
|
|
divedatapoint p = divepoints.at(0);
|
2017-03-10 12:37:54 +00:00
|
|
|
p.time = p.depth.mm / prefs.descrate;
|
2015-05-28 19:23:49 +00:00
|
|
|
divepoints.push_front(p);
|
|
|
|
endInsertRows();
|
|
|
|
}
|
2017-02-15 22:15:08 +00:00
|
|
|
emitDataChanged();
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
2015-06-22 11:48:42 +00:00
|
|
|
void DivePlannerPointsModel::setSwitchAtReqStop(bool value)
|
|
|
|
{
|
2018-08-15 09:52:05 +00:00
|
|
|
qPrefDivePlanner::set_switch_at_req_stop(value);
|
2017-02-15 22:15:08 +00:00
|
|
|
emitDataChanged();
|
2015-06-22 11:48:42 +00:00
|
|
|
}
|
|
|
|
|
2015-06-19 10:25:03 +00:00
|
|
|
void DivePlannerPointsModel::setMinSwitchDuration(int duration)
|
|
|
|
{
|
2018-08-15 09:52:05 +00:00
|
|
|
qPrefDivePlanner::set_min_switch_duration(duration * 60);
|
2017-02-15 22:15:08 +00:00
|
|
|
emitDataChanged();
|
2015-06-19 10:25:03 +00:00
|
|
|
}
|
|
|
|
|
2019-03-25 21:40:59 +00:00
|
|
|
void DivePlannerPointsModel::setSurfaceSegment(int duration)
|
|
|
|
{
|
|
|
|
qPrefDivePlanner::set_surface_segment(duration * 60);
|
|
|
|
emitDataChanged();
|
|
|
|
}
|
|
|
|
|
2015-05-28 19:23:49 +00:00
|
|
|
void DivePlannerPointsModel::setStartDate(const QDate &date)
|
|
|
|
{
|
|
|
|
startTime.setDate(date);
|
2020-05-22 16:53:25 +00:00
|
|
|
diveplan.when = dateTimeToTimestamp(startTime);
|
2021-01-26 06:48:22 +00:00
|
|
|
d->when = diveplan.when;
|
2017-02-15 22:15:08 +00:00
|
|
|
emitDataChanged();
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DivePlannerPointsModel::setStartTime(const QTime &t)
|
|
|
|
{
|
|
|
|
startTime.setTime(t);
|
2021-01-19 20:20:01 +00:00
|
|
|
diveplan.when = dateTimeToTimestamp(startTime);
|
2021-01-26 06:48:22 +00:00
|
|
|
d->when = diveplan.when;
|
2017-02-15 22:15:08 +00:00
|
|
|
emitDataChanged();
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool divePointsLessThan(const divedatapoint &p1, const divedatapoint &p2)
|
|
|
|
{
|
2017-12-29 18:29:23 +00:00
|
|
|
return p1.time < p2.time;
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
2021-01-23 10:28:19 +00:00
|
|
|
int DivePlannerPointsModel::lastEnteredPoint() const
|
2015-05-28 19:23:49 +00:00
|
|
|
{
|
|
|
|
for (int i = divepoints.count() - 1; i >= 0; i--)
|
|
|
|
if (divepoints.at(i).entered)
|
|
|
|
return i;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2021-01-23 10:41:42 +00:00
|
|
|
void DivePlannerPointsModel::addDefaultStop()
|
|
|
|
{
|
2021-02-27 21:34:08 +00:00
|
|
|
removeDeco();
|
2021-01-23 10:41:42 +00:00
|
|
|
addStop(0, 0, -1, 0, true, UNDEF_COMP_TYPE);
|
|
|
|
}
|
|
|
|
|
2021-02-27 18:42:42 +00:00
|
|
|
void DivePlannerPointsModel::addStop(int milimeters, int seconds)
|
|
|
|
{
|
2021-02-27 21:34:08 +00:00
|
|
|
removeDeco();
|
2021-02-27 18:42:42 +00:00
|
|
|
addStop(milimeters, seconds, -1, 0, true, UNDEF_COMP_TYPE);
|
2021-02-27 21:06:05 +00:00
|
|
|
updateDiveProfile();
|
2021-02-27 18:42:42 +00:00
|
|
|
}
|
|
|
|
|
2017-02-04 13:12:43 +00:00
|
|
|
// cylinderid_in == -1 means same gas as before.
|
2018-06-17 11:41:06 +00:00
|
|
|
// divemode == UNDEF_COMP_TYPE means determine from previous point.
|
|
|
|
int DivePlannerPointsModel::addStop(int milimeters, int seconds, int cylinderid_in, int ccpoint, bool entered, enum divemode_t divemode)
|
2015-05-28 19:23:49 +00:00
|
|
|
{
|
2016-07-09 19:37:05 +00:00
|
|
|
int cylinderid = 0;
|
2015-05-28 19:23:49 +00:00
|
|
|
bool usePrevious = false;
|
2017-02-04 13:12:43 +00:00
|
|
|
if (cylinderid_in >= 0)
|
2016-07-06 12:40:28 +00:00
|
|
|
cylinderid = cylinderid_in;
|
2015-05-28 19:23:49 +00:00
|
|
|
else
|
|
|
|
usePrevious = true;
|
|
|
|
|
|
|
|
int row = divepoints.count();
|
|
|
|
if (seconds == 0 && milimeters == 0 && row != 0) {
|
|
|
|
/* this is only possible if the user clicked on the 'plus' sign on the DivePoints Table */
|
|
|
|
const divedatapoint t = divepoints.at(lastEnteredPoint());
|
2017-03-10 12:37:54 +00:00
|
|
|
milimeters = t.depth.mm;
|
2015-05-28 19:23:49 +00:00
|
|
|
seconds = t.time + 600; // 10 minutes.
|
2016-07-06 12:40:28 +00:00
|
|
|
cylinderid = t.cylinderid;
|
2015-05-28 19:23:49 +00:00
|
|
|
ccpoint = t.setpoint;
|
|
|
|
} else if (seconds == 0 && milimeters == 0 && row == 0) {
|
|
|
|
milimeters = M_OR_FT(5, 15); // 5m / 15ft
|
|
|
|
seconds = 600; // 10 min
|
2016-07-06 12:40:28 +00:00
|
|
|
// Default to the first cylinder
|
|
|
|
cylinderid = 0;
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// check if there's already a new stop before this one:
|
|
|
|
for (int i = 0; i < row; i++) {
|
|
|
|
const divedatapoint &dp = divepoints.at(i);
|
|
|
|
if (dp.time == seconds) {
|
|
|
|
row = i;
|
|
|
|
beginRemoveRows(QModelIndex(), row, row);
|
|
|
|
divepoints.remove(row);
|
|
|
|
endRemoveRows();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (dp.time > seconds) {
|
|
|
|
row = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Previous, actually means next as we are typically subdiving a segment and the gas for
|
|
|
|
// the segment is determined by the waypoint at the end.
|
|
|
|
if (usePrevious) {
|
|
|
|
if (row < divepoints.count()) {
|
2016-07-06 12:40:28 +00:00
|
|
|
cylinderid = divepoints.at(row).cylinderid;
|
2018-06-17 11:41:06 +00:00
|
|
|
if (divemode == UNDEF_COMP_TYPE)
|
|
|
|
divemode = divepoints.at(row).divemode;
|
2018-06-17 21:20:02 +00:00
|
|
|
ccpoint = divepoints.at(row).setpoint;
|
2015-05-28 19:23:49 +00:00
|
|
|
} else if (row > 0) {
|
2016-07-06 12:40:28 +00:00
|
|
|
cylinderid = divepoints.at(row - 1).cylinderid;
|
2018-06-17 11:41:06 +00:00
|
|
|
if (divemode == UNDEF_COMP_TYPE)
|
|
|
|
divemode = divepoints.at(row - 1).divemode;
|
2018-06-17 21:20:02 +00:00
|
|
|
ccpoint = divepoints.at(row -1).setpoint;
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
}
|
2018-06-17 11:41:06 +00:00
|
|
|
if (divemode == UNDEF_COMP_TYPE)
|
2024-04-25 20:16:08 +00:00
|
|
|
divemode = get_dive_dc_const(d, dcNr)->divemode;
|
2015-05-28 19:23:49 +00:00
|
|
|
|
|
|
|
// add the new stop
|
|
|
|
beginInsertRows(QModelIndex(), row, row);
|
|
|
|
divedatapoint point;
|
2017-03-10 12:37:54 +00:00
|
|
|
point.depth.mm = milimeters;
|
2015-05-28 19:23:49 +00:00
|
|
|
point.time = seconds;
|
2016-07-06 12:40:28 +00:00
|
|
|
point.cylinderid = cylinderid;
|
2015-05-28 19:23:49 +00:00
|
|
|
point.setpoint = ccpoint;
|
2022-02-15 06:29:13 +00:00
|
|
|
point.minimum_gas.mbar = 0;
|
2015-05-28 19:23:49 +00:00
|
|
|
point.entered = entered;
|
2018-03-28 20:50:28 +00:00
|
|
|
point.divemode = divemode;
|
2015-05-28 19:23:49 +00:00
|
|
|
point.next = NULL;
|
2021-01-23 12:51:39 +00:00
|
|
|
divepoints.insert(divepoints.begin() + row, point);
|
2015-05-28 19:23:49 +00:00
|
|
|
endInsertRows();
|
|
|
|
return row;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DivePlannerPointsModel::editStop(int row, divedatapoint newData)
|
|
|
|
{
|
2021-01-25 13:29:47 +00:00
|
|
|
if (row < 0 || row >= divepoints.count())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Refuse to move to 0, since that has special meaning.
|
|
|
|
if (newData.time <= 0)
|
|
|
|
return;
|
|
|
|
|
2015-05-28 19:23:49 +00:00
|
|
|
/*
|
|
|
|
* When moving divepoints rigorously, we might end up with index
|
|
|
|
* out of range, thus returning the last one instead.
|
|
|
|
*/
|
2017-10-18 07:02:28 +00:00
|
|
|
int old_first_cylid = divepoints[0].cylinderid;
|
2021-01-25 13:29:47 +00:00
|
|
|
|
2021-01-25 15:03:03 +00:00
|
|
|
// Refuse creation of two points with the same time stamp.
|
|
|
|
// Note: "time" is moved in the positive direction to avoid
|
|
|
|
// time becoming zero or, worse, negative.
|
|
|
|
while (std::any_of(divepoints.begin(), divepoints.begin() + row,
|
|
|
|
[t = newData.time] (const divedatapoint &data)
|
|
|
|
{ return data.time == t; }))
|
|
|
|
newData.time += 10;
|
|
|
|
while (std::any_of(divepoints.begin() + row + 1, divepoints.end(),
|
|
|
|
[t = newData.time] (const divedatapoint &data)
|
|
|
|
{ return data.time == t; }))
|
|
|
|
newData.time += 10;
|
|
|
|
|
2021-01-25 13:29:47 +00:00
|
|
|
// Is it ok to change data first and then move the rows?
|
2015-05-28 19:23:49 +00:00
|
|
|
divepoints[row] = newData;
|
2021-01-25 13:29:47 +00:00
|
|
|
|
|
|
|
// If the time changed, the item might have to be moved. Oh joy.
|
|
|
|
int newRow = row;
|
|
|
|
while (newRow + 1 < divepoints.count() && divepoints[newRow + 1].time < divepoints[row].time)
|
|
|
|
++newRow;
|
|
|
|
if (newRow != row) {
|
|
|
|
++newRow; // Move one past item with smaller time stamp
|
|
|
|
} else {
|
|
|
|
// If we didn't move forward, try moving backwards
|
|
|
|
while (newRow > 0 && divepoints[newRow - 1].time > divepoints[row].time)
|
|
|
|
--newRow;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newRow != row && newRow != row + 1) {
|
|
|
|
beginMoveRows(QModelIndex(), row, row, QModelIndex(), newRow);
|
2022-09-24 12:06:56 +00:00
|
|
|
move_in_range(divepoints, row, row + 1, newRow);
|
2021-01-25 13:29:47 +00:00
|
|
|
endMoveRows();
|
|
|
|
|
|
|
|
// Account for moving the row backwards in the array.
|
|
|
|
row = newRow > row ? newRow - 1 : newRow;
|
|
|
|
}
|
|
|
|
|
2016-07-06 12:40:38 +00:00
|
|
|
if (updateMaxDepth())
|
2020-02-03 17:59:12 +00:00
|
|
|
cylinders.updateBestMixes();
|
2017-10-18 07:02:28 +00:00
|
|
|
if (divepoints[0].cylinderid != old_first_cylid)
|
2020-02-03 17:59:12 +00:00
|
|
|
cylinders.moveAtFirst(divepoints[0].cylinderid);
|
2021-01-25 13:29:47 +00:00
|
|
|
|
2021-02-27 21:06:05 +00:00
|
|
|
updateDiveProfile();
|
2021-01-25 13:29:47 +00:00
|
|
|
emit dataChanged(createIndex(row, 0), createIndex(row, COLUMNS - 1));
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
2021-01-23 10:28:19 +00:00
|
|
|
divedatapoint DivePlannerPointsModel::at(int row) const
|
2015-05-28 19:23:49 +00:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* When moving divepoints rigorously, we might end up with index
|
|
|
|
* out of range, thus returning the last one instead.
|
|
|
|
*/
|
|
|
|
if (row >= divepoints.count())
|
|
|
|
return divepoints.at(divepoints.count() - 1);
|
|
|
|
return divepoints.at(row);
|
|
|
|
}
|
|
|
|
|
2021-03-02 09:54:36 +00:00
|
|
|
void DivePlannerPointsModel::removeControlPressed(const QModelIndex &index)
|
2015-05-28 19:23:49 +00:00
|
|
|
{
|
2021-03-02 09:54:36 +00:00
|
|
|
// Never delete all points.
|
|
|
|
int rows = rowCount();
|
|
|
|
if (index.column() != REMOVE || index.row() <= 0 || index.row() >= rows)
|
2015-05-28 19:23:49 +00:00
|
|
|
return;
|
|
|
|
|
2017-10-18 07:02:28 +00:00
|
|
|
int old_first_cylid = divepoints[0].cylinderid;
|
|
|
|
|
2021-03-02 09:54:36 +00:00
|
|
|
preserved_until.seconds = divepoints.at(index.row()).time;
|
|
|
|
beginRemoveRows(QModelIndex(), index.row(), rows - 1);
|
|
|
|
divepoints.erase(divepoints.begin() + index.row(), divepoints.end());
|
|
|
|
endRemoveRows();
|
|
|
|
|
|
|
|
cylinders.updateTrashIcon();
|
|
|
|
if (divepoints[0].cylinderid != old_first_cylid)
|
|
|
|
cylinders.moveAtFirst(divepoints[0].cylinderid);
|
|
|
|
|
|
|
|
updateDiveProfile();
|
|
|
|
emitDataChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DivePlannerPointsModel::remove(const QModelIndex &index)
|
|
|
|
{
|
2015-05-28 19:23:49 +00:00
|
|
|
/* TODO: this seems so wrong.
|
|
|
|
* We can't do this here if we plan to use QML on mobile
|
|
|
|
* as mobile has no ControlModifier.
|
|
|
|
* The correct thing to do is to create a new method
|
|
|
|
* remove method that will pass the first and last index of the
|
|
|
|
* removed rows, and remove those in a go.
|
|
|
|
*/
|
2021-03-02 09:54:36 +00:00
|
|
|
if (QApplication::keyboardModifiers() & Qt::ControlModifier)
|
|
|
|
return removeControlPressed(index);
|
|
|
|
|
|
|
|
// Refuse deleting the last point.
|
2017-01-23 17:11:52 +00:00
|
|
|
int rows = rowCount();
|
2021-03-02 09:54:36 +00:00
|
|
|
if (index.column() != REMOVE || index.row() < 0 || index.row() >= rows || rows <= 1)
|
|
|
|
return;
|
2021-02-27 21:06:05 +00:00
|
|
|
|
2021-03-02 09:54:36 +00:00
|
|
|
divedatapoint dp = at(index.row());
|
|
|
|
if (!dp.entered)
|
|
|
|
return;
|
|
|
|
|
|
|
|
int old_first_cylid = divepoints[0].cylinderid;
|
|
|
|
|
|
|
|
if (index.row() == rows)
|
|
|
|
preserved_until.seconds = divepoints.at(rows - 1).time;
|
|
|
|
beginRemoveRows(QModelIndex(), index.row(), index.row());
|
|
|
|
divepoints.remove(index.row());
|
2015-05-28 19:23:49 +00:00
|
|
|
endRemoveRows();
|
2021-03-02 09:54:36 +00:00
|
|
|
|
2020-02-03 17:59:12 +00:00
|
|
|
cylinders.updateTrashIcon();
|
2017-10-18 07:02:28 +00:00
|
|
|
if (divepoints[0].cylinderid != old_first_cylid)
|
2020-02-03 17:59:12 +00:00
|
|
|
cylinders.moveAtFirst(divepoints[0].cylinderid);
|
2021-02-27 21:06:05 +00:00
|
|
|
|
|
|
|
updateDiveProfile();
|
|
|
|
emitDataChanged();
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct diveplan &DivePlannerPointsModel::getDiveplan()
|
|
|
|
{
|
|
|
|
return diveplan;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DivePlannerPointsModel::cancelPlan()
|
|
|
|
{
|
|
|
|
/* TODO:
|
|
|
|
* This check shouldn't be here - this is the interface responsability.
|
|
|
|
* as soon as the interface thinks that it could cancel the plan, this should be
|
|
|
|
* called.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
if (mode == PLAN && rowCount()) {
|
|
|
|
if (QMessageBox::warning(MainWindow::instance(), TITLE_OR_TEXT(tr("Discard the plan?"),
|
|
|
|
tr("You are about to discard your plan.")),
|
|
|
|
QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Discard) != QMessageBox::Discard) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
setPlanMode(NOTHING);
|
|
|
|
free_dps(&diveplan);
|
|
|
|
|
|
|
|
emit planCanceled();
|
|
|
|
}
|
|
|
|
|
|
|
|
DivePlannerPointsModel::Mode DivePlannerPointsModel::currentMode() const
|
|
|
|
{
|
|
|
|
return mode;
|
|
|
|
}
|
|
|
|
|
2021-01-23 10:28:19 +00:00
|
|
|
bool DivePlannerPointsModel::tankInUse(int cylinderid) const
|
2015-05-28 19:23:49 +00:00
|
|
|
{
|
|
|
|
for (int j = 0; j < rowCount(); j++) {
|
2021-01-23 10:28:19 +00:00
|
|
|
const divedatapoint &p = divepoints[j];
|
2015-05-28 19:23:49 +00:00
|
|
|
if (p.time == 0) // special entries that hold the available gases
|
|
|
|
continue;
|
|
|
|
if (!p.entered) // removing deco gases is ok
|
|
|
|
continue;
|
2016-07-06 12:40:28 +00:00
|
|
|
if (p.cylinderid == cylinderid) // tank is in use
|
2015-05-28 19:23:49 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DivePlannerPointsModel::clear()
|
|
|
|
{
|
2021-02-27 21:27:47 +00:00
|
|
|
cylinders.clear();
|
|
|
|
preserved_until.seconds = 0;
|
2021-01-23 10:46:04 +00:00
|
|
|
beginResetModel();
|
|
|
|
divepoints.clear();
|
|
|
|
endResetModel();
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DivePlannerPointsModel::createTemporaryPlan()
|
|
|
|
{
|
|
|
|
// Get the user-input and calculate the dive info
|
|
|
|
free_dps(&diveplan);
|
2023-02-17 03:24:53 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < d->cylinders.nr; i++) {
|
|
|
|
cylinder_t *cyl = get_cylinder(d, i);
|
|
|
|
if (cyl->depth.mm && cyl->cylinder_use == OC_GAS) {
|
|
|
|
plan_add_segment(&diveplan, 0, cyl->depth.mm, i, 0, false, OC);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-28 19:23:49 +00:00
|
|
|
int lastIndex = -1;
|
|
|
|
for (int i = 0; i < rowCount(); i++) {
|
|
|
|
divedatapoint p = at(i);
|
|
|
|
int deltaT = lastIndex != -1 ? p.time - at(lastIndex).time : p.time;
|
|
|
|
lastIndex = i;
|
2017-02-26 04:07:43 +00:00
|
|
|
if (i == 0 && mode == PLAN && prefs.drop_stone_mode) {
|
2016-07-06 12:40:28 +00:00
|
|
|
/* Okay, we add a first segment where we go down to depth */
|
2018-03-28 20:50:28 +00:00
|
|
|
plan_add_segment(&diveplan, p.depth.mm / prefs.descrate, p.depth.mm, p.cylinderid, p.setpoint, true, p.divemode);
|
2017-03-10 12:37:54 +00:00
|
|
|
deltaT -= p.depth.mm / prefs.descrate;
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
if (p.entered)
|
2018-03-28 20:50:28 +00:00
|
|
|
plan_add_segment(&diveplan, deltaT, p.depth.mm, p.cylinderid, p.setpoint, true, p.divemode);
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#if DEBUG_PLAN
|
|
|
|
dump_plan(&diveplan);
|
|
|
|
#endif
|
2021-02-25 21:20:26 +00:00
|
|
|
}
|
|
|
|
|
2021-02-28 16:28:09 +00:00
|
|
|
static bool shouldComputeVariations()
|
|
|
|
{
|
|
|
|
return prefs.display_variations && decoMode(true) != RECREATIONAL;
|
|
|
|
}
|
|
|
|
|
2021-02-27 21:06:05 +00:00
|
|
|
void DivePlannerPointsModel::updateDiveProfile()
|
2021-02-25 21:20:26 +00:00
|
|
|
{
|
2021-05-08 20:14:54 +00:00
|
|
|
if (!d)
|
|
|
|
return;
|
2021-02-27 21:06:05 +00:00
|
|
|
createTemporaryPlan();
|
2021-02-25 21:20:26 +00:00
|
|
|
if (diveplan_empty(&diveplan))
|
|
|
|
return;
|
|
|
|
|
2024-03-07 18:08:22 +00:00
|
|
|
deco_state_cache cache;
|
2021-02-25 21:20:26 +00:00
|
|
|
struct decostop stoptable[60];
|
|
|
|
struct deco_state plan_deco_state;
|
|
|
|
|
|
|
|
memset(&plan_deco_state, 0, sizeof(struct deco_state));
|
2024-04-25 20:16:08 +00:00
|
|
|
plan(&plan_deco_state, &diveplan, d, dcNr, decotimestep, stoptable, cache, isPlanner(), false);
|
2021-02-27 21:06:05 +00:00
|
|
|
updateMaxDepth();
|
2021-02-28 16:28:09 +00:00
|
|
|
|
|
|
|
if (isPlanner() && shouldComputeVariations()) {
|
|
|
|
struct diveplan *plan_copy = (struct diveplan *)malloc(sizeof(struct diveplan));
|
|
|
|
lock_planner();
|
|
|
|
cloneDiveplan(&diveplan, plan_copy);
|
|
|
|
unlock_planner();
|
2017-11-27 21:07:09 +00:00
|
|
|
#ifdef VARIATIONS_IN_BACKGROUND
|
2021-02-28 16:28:09 +00:00
|
|
|
// Since we're calling computeVariations asynchronously and plan_deco_state is allocated
|
|
|
|
// on the stack, it must be copied and freed by the worker-thread.
|
|
|
|
struct deco_state *plan_deco_state_copy = new deco_state(plan_deco_state);
|
2022-02-10 01:08:09 +00:00
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
|
|
|
QtConcurrent::run(&DivePlannerPointsModel::computeVariationsFreeDeco, this, plan_copy, plan_deco_state_copy);
|
|
|
|
#else
|
2021-02-28 16:28:09 +00:00
|
|
|
QtConcurrent::run(this, &DivePlannerPointsModel::computeVariationsFreeDeco, plan_copy, plan_deco_state_copy);
|
2022-02-10 01:08:09 +00:00
|
|
|
#endif
|
2017-11-27 21:07:09 +00:00
|
|
|
#else
|
2021-02-28 16:28:09 +00:00
|
|
|
computeVariations(plan_copy, &plan_deco_state);
|
2017-11-27 21:07:09 +00:00
|
|
|
#endif
|
2021-02-28 16:28:09 +00:00
|
|
|
final_deco_state = plan_deco_state;
|
|
|
|
}
|
2021-05-07 15:52:30 +00:00
|
|
|
emit calculatedPlanNotes(QString(d->notes));
|
|
|
|
|
2021-02-25 21:20:26 +00:00
|
|
|
|
2015-05-28 19:23:49 +00:00
|
|
|
#if DEBUG_PLAN
|
2021-01-26 06:48:22 +00:00
|
|
|
save_dive(stderr, d);
|
2015-05-28 19:23:49 +00:00
|
|
|
dump_plan(&diveplan);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void DivePlannerPointsModel::deleteTemporaryPlan()
|
|
|
|
{
|
|
|
|
free_dps(&diveplan);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DivePlannerPointsModel::savePlan()
|
|
|
|
{
|
|
|
|
createPlan(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DivePlannerPointsModel::saveDuplicatePlan()
|
|
|
|
{
|
|
|
|
createPlan(true);
|
|
|
|
}
|
|
|
|
|
2021-01-26 06:48:22 +00:00
|
|
|
struct divedatapoint *DivePlannerPointsModel::cloneDiveplan(struct diveplan *plan_src, struct diveplan *plan_copy)
|
2017-08-28 21:59:58 +00:00
|
|
|
{
|
|
|
|
divedatapoint *src, *last_segment;
|
|
|
|
divedatapoint **dp;
|
|
|
|
|
2017-11-27 16:36:21 +00:00
|
|
|
src = plan_src->dp;
|
|
|
|
*plan_copy = *plan_src;
|
2017-08-28 21:59:58 +00:00
|
|
|
dp = &plan_copy->dp;
|
|
|
|
while (src && (!src->time || src->entered)) {
|
|
|
|
*dp = (struct divedatapoint *)malloc(sizeof(struct divedatapoint));
|
|
|
|
**dp = *src;
|
|
|
|
dp = &(*dp)->next;
|
|
|
|
src = src->next;
|
|
|
|
}
|
|
|
|
(*dp) = NULL;
|
|
|
|
|
|
|
|
last_segment = plan_copy->dp;
|
2017-11-22 19:42:33 +00:00
|
|
|
while (last_segment && last_segment->next && last_segment->next->next)
|
2017-08-28 21:59:58 +00:00
|
|
|
last_segment = last_segment->next;
|
|
|
|
return last_segment;
|
|
|
|
}
|
|
|
|
|
2017-08-29 10:26:00 +00:00
|
|
|
int DivePlannerPointsModel::analyzeVariations(struct decostop *min, struct decostop *mid, struct decostop *max, const char *unit)
|
2017-08-28 21:59:58 +00:00
|
|
|
{
|
2017-12-02 07:45:41 +00:00
|
|
|
int minsum = 0;
|
|
|
|
int midsum = 0;
|
|
|
|
int maxsum = 0;
|
2017-08-28 21:59:58 +00:00
|
|
|
int leftsum = 0;
|
|
|
|
int rightsum = 0;
|
|
|
|
|
2017-12-02 07:45:41 +00:00
|
|
|
while (min->depth) {
|
|
|
|
minsum += min->time;
|
2017-08-28 21:59:58 +00:00
|
|
|
++min;
|
2017-12-02 07:45:41 +00:00
|
|
|
}
|
|
|
|
while (mid->depth) {
|
|
|
|
midsum += mid->time;
|
2017-08-28 21:59:58 +00:00
|
|
|
++mid;
|
2017-12-02 07:45:41 +00:00
|
|
|
}
|
|
|
|
while (max->depth) {
|
|
|
|
maxsum += max->time;
|
2017-08-28 21:59:58 +00:00
|
|
|
++max;
|
|
|
|
}
|
2017-12-02 07:45:41 +00:00
|
|
|
|
|
|
|
leftsum = midsum - minsum;
|
|
|
|
rightsum = maxsum - midsum;
|
|
|
|
|
|
|
|
#ifdef DEBUG_STOPVAR
|
2024-05-01 09:01:06 +00:00
|
|
|
printf("Total + %d:%02d/%s +- %d s/%s\n\n", FRACTION_TUPLE((leftsum + rightsum) / 2, 60), unit,
|
2017-08-29 10:26:00 +00:00
|
|
|
(rightsum - leftsum) / 2, unit);
|
2017-12-08 13:51:47 +00:00
|
|
|
#else
|
|
|
|
Q_UNUSED(unit)
|
2017-08-29 10:26:00 +00:00
|
|
|
#endif
|
|
|
|
return (leftsum + rightsum) / 2;
|
2017-08-28 21:59:58 +00:00
|
|
|
}
|
|
|
|
|
2019-10-17 20:56:40 +00:00
|
|
|
void DivePlannerPointsModel::computeVariationsFreeDeco(struct diveplan *original_plan, struct deco_state *previous_ds)
|
|
|
|
{
|
|
|
|
computeVariations(original_plan, previous_ds);
|
|
|
|
delete previous_ds;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DivePlannerPointsModel::computeVariations(struct diveplan *original_plan, const struct deco_state *previous_ds)
|
2017-08-28 21:59:58 +00:00
|
|
|
{
|
2019-10-27 01:11:39 +00:00
|
|
|
// nothing to do unless there's an original plan
|
|
|
|
if (!original_plan)
|
|
|
|
return;
|
2017-11-27 16:36:21 +00:00
|
|
|
|
2017-08-28 21:59:58 +00:00
|
|
|
struct dive *dive = alloc_dive();
|
2021-01-26 06:48:22 +00:00
|
|
|
copy_dive(d, dive);
|
2017-08-28 21:59:58 +00:00
|
|
|
struct decostop original[60], deeper[60], shallower[60], shorter[60], longer[60];
|
2024-03-07 18:08:22 +00:00
|
|
|
deco_state_cache cache, save;
|
2017-08-28 21:59:58 +00:00
|
|
|
struct diveplan plan_copy;
|
|
|
|
struct divedatapoint *last_segment;
|
2019-10-17 20:56:40 +00:00
|
|
|
struct deco_state ds = *previous_ds;
|
2017-11-27 16:36:21 +00:00
|
|
|
|
2021-02-28 16:28:09 +00:00
|
|
|
int my_instance = ++instanceCounter;
|
2024-03-07 18:08:22 +00:00
|
|
|
save.cache(&ds);
|
2017-12-26 21:14:40 +00:00
|
|
|
|
2021-02-28 16:28:09 +00:00
|
|
|
duration_t delta_time = { .seconds = 60 };
|
|
|
|
QString time_units = tr("min");
|
|
|
|
depth_t delta_depth;
|
|
|
|
QString depth_units;
|
2017-12-02 10:29:35 +00:00
|
|
|
|
2021-02-28 16:28:09 +00:00
|
|
|
if (prefs.units.length == units::METERS) {
|
|
|
|
delta_depth.mm = 1000; // 1m
|
|
|
|
depth_units = tr("m");
|
|
|
|
} else {
|
|
|
|
delta_depth.mm = feet_to_mm(1.0); // 1ft
|
|
|
|
depth_units = tr("ft");
|
|
|
|
}
|
2017-11-27 16:36:21 +00:00
|
|
|
|
2021-02-28 16:28:09 +00:00
|
|
|
last_segment = cloneDiveplan(original_plan, &plan_copy);
|
|
|
|
if (!last_segment)
|
|
|
|
goto finish;
|
|
|
|
if (my_instance != instanceCounter)
|
|
|
|
goto finish;
|
2024-04-25 20:16:08 +00:00
|
|
|
plan(&ds, &plan_copy, dive, dcNr, 1, original, cache, true, false);
|
2021-02-28 16:28:09 +00:00
|
|
|
free_dps(&plan_copy);
|
2024-03-07 18:08:22 +00:00
|
|
|
save.restore(&ds, false);
|
2021-02-28 16:28:09 +00:00
|
|
|
|
|
|
|
last_segment = cloneDiveplan(original_plan, &plan_copy);
|
|
|
|
last_segment->depth.mm += delta_depth.mm;
|
|
|
|
last_segment->next->depth.mm += delta_depth.mm;
|
|
|
|
if (my_instance != instanceCounter)
|
|
|
|
goto finish;
|
2024-04-25 20:16:08 +00:00
|
|
|
plan(&ds, &plan_copy, dive, dcNr, 1, deeper, cache, true, false);
|
2021-02-28 16:28:09 +00:00
|
|
|
free_dps(&plan_copy);
|
2024-03-07 18:08:22 +00:00
|
|
|
save.restore(&ds, false);
|
2021-02-28 16:28:09 +00:00
|
|
|
|
|
|
|
last_segment = cloneDiveplan(original_plan, &plan_copy);
|
|
|
|
last_segment->depth.mm -= delta_depth.mm;
|
|
|
|
last_segment->next->depth.mm -= delta_depth.mm;
|
|
|
|
if (my_instance != instanceCounter)
|
|
|
|
goto finish;
|
2024-04-25 20:16:08 +00:00
|
|
|
plan(&ds, &plan_copy, dive, dcNr, 1, shallower, cache, true, false);
|
2021-02-28 16:28:09 +00:00
|
|
|
free_dps(&plan_copy);
|
2024-03-07 18:08:22 +00:00
|
|
|
save.restore(&ds, false);
|
2021-02-28 16:28:09 +00:00
|
|
|
|
|
|
|
last_segment = cloneDiveplan(original_plan, &plan_copy);
|
|
|
|
last_segment->next->time += delta_time.seconds;
|
|
|
|
if (my_instance != instanceCounter)
|
|
|
|
goto finish;
|
2024-04-25 20:16:08 +00:00
|
|
|
plan(&ds, &plan_copy, dive, dcNr, 1, longer, cache, true, false);
|
2021-02-28 16:28:09 +00:00
|
|
|
free_dps(&plan_copy);
|
2024-03-07 18:08:22 +00:00
|
|
|
save.restore(&ds, false);
|
2021-02-28 16:28:09 +00:00
|
|
|
|
|
|
|
last_segment = cloneDiveplan(original_plan, &plan_copy);
|
|
|
|
last_segment->next->time -= delta_time.seconds;
|
|
|
|
if (my_instance != instanceCounter)
|
|
|
|
goto finish;
|
2024-04-25 20:16:08 +00:00
|
|
|
plan(&ds, &plan_copy, dive, dcNr, 1, shorter, cache, true, false);
|
2021-02-28 16:28:09 +00:00
|
|
|
free_dps(&plan_copy);
|
2024-03-07 18:08:22 +00:00
|
|
|
save.restore(&ds, false);
|
2021-02-28 16:28:09 +00:00
|
|
|
|
|
|
|
char buf[200];
|
2022-06-04 11:58:33 +00:00
|
|
|
sprintf(buf, ", %s: %c %d:%02d /%s %c %d:%02d /min", qPrintable(tr("Stop times")),
|
2024-05-01 09:01:06 +00:00
|
|
|
SIGNED_FRAC_TRIPLET(analyzeVariations(shallower, original, deeper, qPrintable(depth_units)), 60), qPrintable(depth_units),
|
|
|
|
SIGNED_FRAC_TRIPLET(analyzeVariations(shorter, original, longer, qPrintable(time_units)), 60));
|
2021-02-28 16:28:09 +00:00
|
|
|
|
|
|
|
// By using a signal, we can transport the variations to the main thread.
|
|
|
|
emit variationsComputed(QString(buf));
|
2017-12-02 07:45:41 +00:00
|
|
|
#ifdef DEBUG_STOPVAR
|
2021-02-28 16:28:09 +00:00
|
|
|
printf("\n\n");
|
2017-11-28 19:23:47 +00:00
|
|
|
#endif
|
|
|
|
finish:
|
|
|
|
free_dps(original_plan);
|
|
|
|
free(original_plan);
|
2022-08-20 15:24:45 +00:00
|
|
|
free_dive(dive);
|
2017-08-28 21:59:58 +00:00
|
|
|
}
|
|
|
|
|
2020-04-13 12:46:15 +00:00
|
|
|
void DivePlannerPointsModel::computeVariationsDone(QString variations)
|
|
|
|
{
|
2021-01-26 06:48:22 +00:00
|
|
|
QString notes = QString(d->notes);
|
|
|
|
free(d->notes);
|
|
|
|
d->notes = copy_qstring(notes.replace("VARIATIONS", variations));
|
|
|
|
emit calculatedPlanNotes(QString(d->notes));
|
2020-04-13 12:46:15 +00:00
|
|
|
}
|
|
|
|
|
2024-05-25 07:03:39 +00:00
|
|
|
void DivePlannerPointsModel::createPlan(bool saveAsNew)
|
2015-05-28 19:23:49 +00:00
|
|
|
{
|
|
|
|
// Ok, so, here the diveplan creates a dive
|
2024-03-07 18:08:22 +00:00
|
|
|
deco_state_cache cache;
|
2015-05-28 19:23:49 +00:00
|
|
|
removeDeco();
|
|
|
|
createTemporaryPlan();
|
|
|
|
|
2017-08-27 20:49:41 +00:00
|
|
|
struct decostop stoptable[60];
|
2024-04-25 20:16:08 +00:00
|
|
|
plan(&ds_after_previous_dives, &diveplan, d, dcNr, decotimestep, stoptable, cache, isPlanner(), true);
|
2021-02-28 16:28:09 +00:00
|
|
|
|
|
|
|
if (shouldComputeVariations()) {
|
|
|
|
struct diveplan *plan_copy;
|
|
|
|
plan_copy = (struct diveplan *)malloc(sizeof(struct diveplan));
|
|
|
|
lock_planner();
|
|
|
|
cloneDiveplan(&diveplan, plan_copy);
|
|
|
|
unlock_planner();
|
|
|
|
computeVariations(plan_copy, &ds_after_previous_dives);
|
|
|
|
}
|
2017-11-27 16:36:21 +00:00
|
|
|
|
Undo: make adding of planned dive undo-able
Planned dives were still added by directly calling core code.
This could confuse the undo-machinery, leading to crashes.
Instead, use the proper undo-command. The problem is that as
opposed to the other AddDive-commands, planned dives may
belong to a trip. Thus, the interface to the AddDive command
was changed to respect the divetrip field. Make sure that
the other callers reset that field (actually, it should never
be set). Add a comment describing the perhaps surprising
interface (the passed-in dive, usually displayed dive, is
reset).
Moreover, a dive cloned in the planner is not assigned a
new number. Thus, add an argument to the AddDive-command,
which expresses whether a new number should be generated
for the to-be-added dive.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2018-09-08 17:58:11 +00:00
|
|
|
// Fixup planner notes.
|
2021-01-26 06:48:22 +00:00
|
|
|
if (current_dive && d->id == current_dive->id) {
|
2017-03-29 19:25:30 +00:00
|
|
|
// Try to identify old planner output and remove only this part
|
2017-04-19 13:40:59 +00:00
|
|
|
// Treat user provided text as plain text.
|
|
|
|
QTextDocument notesDocument;
|
|
|
|
notesDocument.setHtml(current_dive->notes);
|
|
|
|
QString oldnotes(notesDocument.toPlainText());
|
2019-09-01 15:52:25 +00:00
|
|
|
QString disclaimer = get_planner_disclaimer();
|
|
|
|
int disclaimerMid = disclaimer.indexOf("%s");
|
|
|
|
QString disclaimerBegin, disclaimerEnd;
|
|
|
|
if (disclaimerMid >= 0) {
|
|
|
|
disclaimerBegin = disclaimer.left(disclaimerMid);
|
|
|
|
disclaimerEnd = disclaimer.mid(disclaimerMid + 2);
|
|
|
|
} else {
|
2024-01-16 16:39:19 +00:00
|
|
|
disclaimerBegin = std::move(disclaimer);
|
2019-09-01 15:52:25 +00:00
|
|
|
}
|
|
|
|
int disclaimerPositionStart = oldnotes.indexOf(disclaimerBegin);
|
|
|
|
if (disclaimerPositionStart >= 0) {
|
|
|
|
if (oldnotes.indexOf(disclaimerEnd, disclaimerPositionStart) >= 0) {
|
|
|
|
// We found a disclaimer according to the current locale.
|
|
|
|
// Remove the disclaimer and anything after the disclaimer, because
|
|
|
|
// that's supposedly the old planner notes.
|
|
|
|
oldnotes = oldnotes.left(disclaimerPositionStart);
|
|
|
|
}
|
|
|
|
}
|
2017-04-19 13:40:59 +00:00
|
|
|
// Deal with line breaks
|
2017-04-23 11:48:47 +00:00
|
|
|
oldnotes.replace("\n", "<br>");
|
2021-01-26 06:48:22 +00:00
|
|
|
oldnotes.append(d->notes);
|
|
|
|
d->notes = copy_qstring(oldnotes);
|
2017-03-29 19:25:30 +00:00
|
|
|
// If we save as new create a copy of the dive here
|
Undo: make adding of planned dive undo-able
Planned dives were still added by directly calling core code.
This could confuse the undo-machinery, leading to crashes.
Instead, use the proper undo-command. The problem is that as
opposed to the other AddDive-commands, planned dives may
belong to a trip. Thus, the interface to the AddDive command
was changed to respect the divetrip field. Make sure that
the other callers reset that field (actually, it should never
be set). Add a comment describing the perhaps surprising
interface (the passed-in dive, usually displayed dive, is
reset).
Moreover, a dive cloned in the planner is not assigned a
new number. Thus, add an argument to the AddDive-command,
which expresses whether a new number should be generated
for the to-be-added dive.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2018-09-08 17:58:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
setPlanMode(NOTHING);
|
|
|
|
|
|
|
|
// Now, add or modify the dive.
|
2021-01-26 06:48:22 +00:00
|
|
|
if (!current_dive || d->id != current_dive->id) {
|
Undo: make adding of planned dive undo-able
Planned dives were still added by directly calling core code.
This could confuse the undo-machinery, leading to crashes.
Instead, use the proper undo-command. The problem is that as
opposed to the other AddDive-commands, planned dives may
belong to a trip. Thus, the interface to the AddDive command
was changed to respect the divetrip field. Make sure that
the other callers reset that field (actually, it should never
be set). Add a comment describing the perhaps surprising
interface (the passed-in dive, usually displayed dive, is
reset).
Moreover, a dive cloned in the planner is not assigned a
new number. Thus, add an argument to the AddDive-command,
which expresses whether a new number should be generated
for the to-be-added dive.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2018-09-08 17:58:11 +00:00
|
|
|
// we were planning a new dive, not re-planning an existing one
|
2021-01-26 06:48:22 +00:00
|
|
|
d->divetrip = nullptr; // Should not be necessary, just in case!
|
2020-04-13 16:07:17 +00:00
|
|
|
#if !defined(SUBSURFACE_TESTING)
|
2022-11-12 08:14:00 +00:00
|
|
|
Command::addDive(d, divelog.autogroup, true);
|
2020-04-13 16:07:17 +00:00
|
|
|
#endif // !SUBSURFACE_TESTING
|
2020-04-12 11:39:01 +00:00
|
|
|
} else {
|
2024-04-25 20:16:08 +00:00
|
|
|
copy_events_until(current_dive, d, dcNr, preserved_until.seconds);
|
2024-05-25 07:03:39 +00:00
|
|
|
if (saveAsNew) {
|
2020-04-12 11:39:01 +00:00
|
|
|
// we were planning an old dive and save as a new dive
|
2021-01-26 06:48:22 +00:00
|
|
|
d->id = dive_getUniqID(); // Things will break horribly if we create dives with the same id.
|
2020-04-13 16:07:17 +00:00
|
|
|
#if !defined(SUBSURFACE_TESTING)
|
2021-01-26 06:48:22 +00:00
|
|
|
Command::addDive(d, false, false);
|
2020-04-13 16:07:17 +00:00
|
|
|
#endif // !SUBSURFACE_TESTING
|
2020-04-12 11:39:01 +00:00
|
|
|
} else {
|
|
|
|
// we were planning an old dive and rewrite the plan
|
2020-04-13 16:07:17 +00:00
|
|
|
#if !defined(SUBSURFACE_TESTING)
|
2021-01-26 06:48:22 +00:00
|
|
|
Command::replanDive(d);
|
2020-04-13 16:07:17 +00:00
|
|
|
#endif // !SUBSURFACE_TESTING
|
2020-04-12 11:39:01 +00:00
|
|
|
}
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Remove and clean the diveplan, so we don't delete
|
|
|
|
// the dive by mistake.
|
|
|
|
free_dps(&diveplan);
|
2022-03-12 14:02:34 +00:00
|
|
|
|
|
|
|
planCreated(); // This signal will exit the UI from planner state.
|
2015-05-28 19:23:49 +00:00
|
|
|
}
|