Include unused tanks in merges of multiple logs into a single dive if
the 'Show unused cylinders' preference is enabled.
Also rename the preference (in code) to `include_unused_tanks` to
reflect the fact that it is already used in more places than just the
display (exporting, cloning dives).
Simplified the cylinder model to make forced inclusion of unused tanks
dependent on use of the model in planner.
Leaving the persisted name of the preference as `display_unused_tanks`
to avoid resetting this for all users - is there a good way to migrate
preference names?
Signed-off-by: Michael Keller <github@ike.ch>
There was this completely weird loop that the planner-widget would
call the planner-model to get the current rebreather mode, which
would then access the dive in the planner widget. Just keep those
things in the planner widgets.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
The gas and dive-type models were repopulated in the
diveplanner model. The former are used in the planner.
However, the latter is also used outside of the planner,
when editing non-planned dives. Thus the former shouldn't
be repopulated by the latter, but by the code that needs
it.
Side note: repopulating the dive-type model seems to
make no sense whatsoever since the values never change,
but let's keep it for now.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
The mode was accessed via the global `displayed_dive`. In an effort
to remove globals, access it via the DivePlannerPointsModel instead.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Instead of accessing the global dc_number from the
DivePlannerPointsModel and the CylinderModel, pass them
in the respective initialization functions.
The dc_number global might not make sense on mobile.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This tries to encapsulate the management of the current dive and
divecomputer in the selection code. The current dive is alreay
set by setSelection(). Add a new parameter to also set the
current divecomputer. If -1 is passed, then the current
computer number is remained. This will allow us to audit the code.
Because for now, the whole "current dive computer" thing seems
to be ill-defined.
This fixes a bug: the dive-computer number wasn't validated
when making a new dive the current dive. The new code has some
drawbacks though: when selecting a whole trip, the validation
will be called for all dives in the trip and thus the dive computer
number will depend on the dive with the lowest amount of dive
computers in the trip. This will need to be fixed.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
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>
Fix a bug that results in dive plans outside of the configured risk
profile being produced when planning a CCR dive with the first segment
set to open circuit.
`d->dc.divemode` is already set in `setRebreatherMode`, which is
sufficient, and congruent with the setting of other dive parameters,
like `diveplan.gflow`.
Signed-off-by: Michael Keller <github@ike.ch>
Changed the way dive data points for OC cylinders to be added to the
dive plan are created in `createTemporaryPlan()` in
`diveplannermodel.cpp`. This now uses `plan_add_segment()` like all
other places where dive data points are added, in particular the planner
tests.
This also allowed for `create_dp()` to be made static.
Signed-off-by: Michael Keller <github@ike.ch>
Use the drop down for editing the tank use in the gas list in both the
equipment tab and the dive planner.
The tank use column is now available in the equipment tab for all dives
and not just CCR dives, as 'not used' is a valid entry in both cases.
However, if the current dive is an OC dive, only 'OC-gas' and 'not used' are
shown.
There still seems to be a problem that in some cases, when opening the
planner after selecting an existing CCR dive the drop down in the
planner does not list CCR gas uses - for some reason `displayed_dive`
does not seem to be updated correctly on opening of the planner. But I have not been able to
reproduce this consistently, and changing 'Dive mode' fixes this.
Signed-off-by: Michael Keller <github@ike.ch>
For dives with many samples (i.e. logged dives), samples are merged.
I'm not exactly sure how this code works, but it does an
out-of-bound access in some cases. Avoid that by a simple
check.
That said, I wonder if this downsampling is a good idea. A user
reports that they have logged dives marked as manually added dives.
We now load them into edit mode, which means a significant loss
of information.
Perhaps we should consider dives with more than 100 samples as
non-manual dives?
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
The moveInVector() function was defined in qthelper.h, even
though it has nothing to do with Qt. Therefore, move it into
its own header.
Morover, since it is a very low-level function, use snake_case.
And rename it to move_in_range(), because it does not only
work on vectors, but any range with random-access iterators.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
One would think that calling free() on a dive structure, as the code
did in some places, would lead to a memory leak.
(Insert rant about C memory management.)
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
When computing plan variations, deco can get shorter when
staying longer when the last step is actually already at
off gasing depth. FRACTION forces unsiged, so this introduces
a sign aware version of FRACTION that returns a sign character
in addition.
Reported-by: Patrick Naujoks <p.naujoks@me.com>
Signed-off-by: Robert C. Helling <helling@atdotde.de>
The UI was updated before storing the dive. This had a nasty
effect: the current dive was shown in the profile and if that
was a manually added dive, the DivePlannerPointsModel was
overwritten. Thus the planned dive couldn't be saved anymore.
There is a comment why the UI switch was done beforehand.
But in my tests, this didn't seem to be valid anymore.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Place undo commands for every change of the profile, not
only on "saving". Move the edit-mode from the mainwindow
and the maintab to the profile widget.
This is still very rough. For example, the only way to exit
the edit mode is changing the current dive.
The undo-commands are placed by the desktop-profile widget.
We might think about moving that down to the profile-view so
that this will be useable on mobile.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Coverity warning: divedatapoint::minimum_gas was not initialized
in DivePlannerPointsModel::addStop.
I don't know the meaning of that member variable and therefore
cannot tell if this was a real issue.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
On the equipment tab, unused cylinders (automatically added,
no pressure data) could be hidden. This was implemented using
a QSortFilterProxyModel.
Apparently, it causes confusion if cylinders in the middle of
the list are hidden. Therefore, only hide cylinders at the end
of the list.
QSortFilterProxyModel seems the wrong tool for that job, so
remove it and add a flag "hideUnused" to the base model. Calculate
the number of cylinders when changing the dive.
This is rather complex, because the same model is used for
the planner (which doesn't hide cylinders) and the equipment
tab (which does). Of course, syncing core and model now becomes
harder. For instance, the caching of the number of rows was removed
in a37939889b and now has to be
readded.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This makes sure that the dive plan is updated (including the
planner notes) when parameters of the dive or the planner
change.
This fixes a bug reported by Jay Anchor.
There is a chance that by partly undoing 77a6bc6d62, this
introduces too many recalculations of the plan. But without
this patch, there are definitely not enough recalculations.
Reported-by: Jay Anchor <jay.anchor-subsurface@e257.fi>
Signed-off-by: Robert C. Helling <helling@atdotde.de>
A change not to compute plan variations when not needed
was too aggressive and eliminated also the signal to update
the notes. Bug fixed.
Reported-by: Jay Anchor <jay.anchor-subsurface@e257.fi>
Signed-off-by: Robert C. Helling <helling@atdotde.de>
There are two cases in this function: with and without holding
the control-key. The former deletes one point, the latter all
points starting with the selected point to the end.
The code was interlaced making it very hard to reason about.
Notably, it was buggy: with control, all points could be
deleted, leading to a crash.
Split the function in two versions, with their own bound
checking. This produces a bit of duplicate code, which
might be broken out later.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
When updating the dive profile, a thread is started to calculate
plan-variations. This is done even when only editing the profile
or when variation calculation is disabled by the user. The thread
then exits if it shouldn't calculate the variations.
Turn this around: test whether variations should be calculated
before starting the thread.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
removeDeco() was called by addStop() if the recalc flag was
set. If the caller didn't want to call removeDeco() it had
to clear and restore the flag.
Instead, call removeDeco() explicitly when needed.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
There are no more external users of this flag, therefore clearing
that flag is a no-op.
Moreover, clear the cylinders array and the preserved_until
flag befor emitting the model-reset signal.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Split the function in one external version, that updates the
dive profile and cylinders and one internal version, that
does no recalculations. In the latter case, the caller is
responsible for updating the dive.
Thus, the recalculation flag-clearing can be removed from
removeDeco().
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
In planner or profile-edit mode, the plotDive() function takes
the current plan and turns it into a dive profile. Not only
is this a layering violation (the display layer modifying the
dive), it is also fundamentally flawed. The control-flow is
out of control, if you wish. There are numerous reasons why
the profile needs to be replot, many of which do not need
a recalculated dive profile.
Move the code that updates the dive-profile to the
DivePlannerPointsModel. Thus, the profile recalculations
and replots can be pooled. This will break the planner, since
there now might be missing calls to the profile recalculation.
But it already has some positive effects: when removing
multiple points, the profile is only recalculated once.
This will need much more work, but it is a start.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
The DivePlannerPointsModel::addStop() function is called by
the profile to add a planner-stop. It is also used internally
to create profiles.
If we ever want to include this in the undo system, we have
to split these into to versions. One will ultimately place
an undo command and update the profile, the other one doesn't.
For now, this makes the external interface simpler, as some
parameters are redundant.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
The DivePlannerPointsModel::createTemporaryPlan() function had
two distinct and independent parts:
1) create the data points.
2) create the dive sample and calculate variations.
The second part was only exectuted if the recalc flag was set.
Out of the two callers, one was explicitly disabling and setting
the recalc flag to avoid the second part.
The much more logical thing is to simply split the function in
two and only call the first part.
To avoid any functional change, the second caller (the profile)
still tests for the recalc flag. However, if it shouldn't replot
a new plan, why calculate it in the first place!? And why does
the display function change the plan at all? This appears all
very ill-thought out and should be changed in due course.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
The way the blocks in DivePlannerPointsModel::setData()'s
switch statement were demarked messed with my mind.
There were at least three variants. Let's try to be consistent.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
The only external user of setRecalc() was turning recalculation
on. In fact, this happened when constructing the planner-widget.
However, for example editing of the profile only works when
the recalc flag is on.
This is all very confusing, let's just turn the flag on by
default and remove the accessor. Internally, the planner can
simply use the std::exchange function to set and reset the
recalc flag.
Perhaps the setting/resetting can be replaced by simple
recalc = true;
...
recalc = false;
pairs. It is unclear whether there is need for recursion.
Something to be investigated.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
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>
Both loadFromDive() callers were clearing the model before
calling loadFromDive(). Move the clearing into that function
since it makes no sense to load into a non-cleared model.
Apparently this changes the way that no-cylinder dives are
treated and the code in ProfileWidget2::repositionDiveHandlers()
must now explicitly check for that condition.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
In DivePlannerPointsModel::clear(), the cylinder model is
updated before it is cleared. This must be an artifact.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
There must not be two dive planner points at the same time
stamp, as this violates the laws of physics (and internal
assumptions).
The corresponding test was done in the profile code at
two different places with floating point arithmetics.
This is a bad idea, because
1) code duplication
2) danger of rounding issues
Instead, do this in one central point in the planner model
and use integer arithmetics. Simply add a few seconds until
a unique timestamp is obtained.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
When reordering the points, the DivePlannerPointsModel would
not emit the appropriate move signals, but simply a data-changed
signal over all elements. This obviously violates Qt's
model/view API, though it is probably harmless. Let's do
the right thing so that the frontend knows that the selected
item changed place.
Also, emit dataChanged only on the actually changed element,
not all elements.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
The beginRemoveRows() function was fed erroneous values. It
is a mystery why this didn't crash. In any case, deletion
of multiple points did not work properly. Instead of trying
to be fancy, remove each point one-by-one.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Instead of inserting the point at the calculated
position, the DivePlannerPointsModel would append it
at the end and then resort the vector. That's just
silly.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
When clearing the model, use "beginResetModel/endResetModel"
instead of "beginRemoveRows/endRemoveRows".
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
When clicking on "+" in the planner, a default stop point was
added using a signal/slot connection. This used the archaic
string-based connect syntax, because it was realized with
default parameters passed to "addStop()". Instead, add a
"addDefaultStop()" slot, which passes the default parameters.
Since all other callers do not use callbacks, unslotify
"addStop()". The slot was the only user of the default parameters,
so they can be removed alltogether.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
To remove reliance on global state, pass an "in_planner" argument
to decoMode(). Thus, calls to in_planner() can be removed.
This is a more-or-less automated change. Ultimately it would
probably be better to pass the current deco-mode to the affected
functions instead of calling decoMode() with an in_planner
parameter.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
It simplifies reasoning about control flow a lot if it is known
that functions can't be invoked from a different part of the code
base.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
The way the starting time of a new plan was set was bonkers:
1) PlannerWidgets::planDive() invokes DivePlannerPointsModel::
createSimpleDive().
2) createSimpleDive() calls DivePlannerPointsModel::
setupStartTime()
3) setupStartTime() emits a signal startTimeChanged()
4) startTimeChanged is caught by PlannerWidget and sets
the UI field
5) change of the UI field emits a timeChanged() signal which
is connected to DivePlannerPointsModel::setStartTime()
6) setStartTime() sets the time of the plan and displayed_dive
and emits dataChanged()
7) dataChanged() replots the dive()
8) Back in DivePlannerPointsModel::createSimpleDive() the diveplan
start time is overwritten with displayed_dive (the value are
equal owing to 6)
Wow!
But it gets worse:
9) The initial dive plan is set up in createSimpleDive().
Since the profile is drawn in 7) after clearing the displayed_dive
and before constructing the initial plan, the profile is shown
on a dive without samples. It therefore generates a dummy profile.
To make this somewhat less insane, remove the startTimeChanged()
signal in 3), explicitly set the start time of plan and dive to
the one calculated by setupStartTime() and explicitly set the UI
filed in the plannerWidget.
This still indirectly draws the profile via signals in a convoluted
way, but at it straightens out things somewhat. Most importantly,
the profile doesn't have to generate a fake DC.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>