Commit graph

734 commits

Author SHA1 Message Date
Berthold Stoeger
635d67ee7f Dive list: clear internal data when clearing model
The DiveListModelBase::clear() implementation was flawed: It cleared
the data in the core, but left the data in the model untouched.
The code was relying on the fact that the caller would reset the
model manually. Not a good idea.

Therefore, clear the internal data to keep the model consistent at
all times.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-11-19 21:13:40 -08:00
Berthold Stoeger
9f573df971 Dive list: update selection after clearing model
When clearing the model the selection is cleared. Send the according
signal.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-11-19 21:13:40 -08:00
Berthold Stoeger
3003c6e1ee Filter: move recalculation of filter from FilterModel to TripModel
The way this was accessed via Qt's model semantics was horrible.
This gives arguably more readable code, since we don't have to
shoehorn things through QVariants.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-11-19 21:13:40 -08:00
Berthold Stoeger
9ffafbc326 Filter: move num_shown logic from model to core
Since the number of shown dives is stored in the core, let's also
keep it updated there.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-11-19 21:13:40 -08:00
Berthold Stoeger
b76f207158 Filter: split out filter from model
Split out the actual filtering from the MultiFilterSortModel.
Create a DiveFilter class that does the actual filtering.
Currently, mobile and desktop have their own version of this
class, though ultimately we may want to merge them.

The idea here is that the trip-model and undo-commands have
direct access to the filter-function and thus can take care
of keeping track of the number of shown dives, etc.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-11-19 21:13:40 -08:00
Berthold Stoeger
6d6d10f03a Filter: move calculation of shown dives to undo command
The filter-model was catching dives-added / dives-deleted signals
from the models to keep track of the number of shown dives.

To simplify the data flow, do this directly in the undo-command.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-11-19 21:13:40 -08:00
Berthold Stoeger
2d09819ddf Filter: move number of shown dives to core
We mark hidden/shown dives in the core but store the number
of shown dives in the MultiFilterSortModel. Move this datum
to the core for improved locality.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-11-19 21:13:40 -08:00
Berthold Stoeger
cbd98edb73 Cleanup: remove MultiFilterSortModel::filterChanged()
Nobody was calling this function.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-11-19 21:13:40 -08:00
Berthold Stoeger
669ca76b08 Cleanup: remove MultiFilterSortModel::clearFilter
Nobody was using that function.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-11-19 21:13:40 -08:00
Berthold Stoeger
8a33c04894 Cleanup: use DiveTripModelBase::clear() to reset the log
Introduce a DiveTripModelBase::clear() function that cleanly
clears all dive data inside a beginResetModel()/endResetModel()
pair. Thus, the UI will be cleanly reset and we can remove
explicit calls to
 - graphics->setEmptyState()
 - mainTab->clearTabs()
 - mainTab->clearTabs()
 - diveList->reload()
from MainWindow::closeCurrentFile().

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-11-16 08:29:53 -08:00
Berthold Stoeger
36094e4a18 Partially revert 3025e0630d
This commit did the "right" thing by implementing Qt mode semantics
as intended, but for unknown reasons the profile is not properly
cleared on close-file anymore. This code is so convoluted that there
is not point in fighting it at the moment. Revert to remove-rows
instead of reset-model.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-11-16 08:29:53 -08:00
Berthold Stoeger
2f77716e8f Dive list: signal correct trip in DiveTripModelTree::topLevelChanged
DiveTripModelTree::topLevelChanged() has pretty complex code, as
it has to handle the fact that when adding/removing a dive from
a trip, the trip can change its position.

The code did not account for the fact that when moving an object
back in the top level list, one has to subtract one from the new
index, because the object was removed somewhere in the front of
the list.

To make matters worse, when an entry stayed where it was, this
was realized by moving the entry right behind itself, which of
course means that it stays where it is. But this meant that in
the by far most common case (no moving) the wrong entry was
updated.

Fix this by subtracting 1 from the new index when moving an
entry to the back.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-11-15 13:50:35 -08:00
Berthold Stoeger
5e29245e68 Refactoring: move undo commands to top level
In the future we might want to use undo-commands for mobile as
well (even if not implementing undo).

Therefore, move the undo-command source from desktop-widgets
to their own commands top-level folder.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-11-14 21:02:07 +01:00
Berthold Stoeger
e434b5aa40 Cleanup: introduce DiveTripModelBase::IS_TRIP_ROLE
To test whether an entry is a trip, we passed a pointer to the
trip through a QVariant and tested that for null-ity.

Passing pointers through QVariants has given us myriads of
problems in QML, therefore introduce a bool IS_TRIP_ROLE

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-11-11 22:44:58 +01:00
Berthold Stoeger
a474264cc8 Cleanup: fix typos in comment
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-11-11 22:44:58 +01:00
Berthold Stoeger
794066b236 Cylinders: access cylinders with get_cylinder()
Instead of accessing the cylinder table directly, use the get_cylinder()
function. This gives less unwieldy expressions. But more importantly,
the function does bound checking. This is crucial for now as the code
hasn't be properly audited since the change to arbitrarily sized
cylinder tables. Accesses of invalid cylinder indexes may lead to
silent data-corruption that is sometimes not even noticed by
valgrind. Returning NULL instead of an invalid pointer will make
debugging much easier.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-11-09 19:19:04 +01:00
Berthold Stoeger
7c9f46acd2 Core: remove MAX_CYLINDERS restriction
Instead of using fixed size arrays, use a new cylinder_table structure.
The code copies the weightsystem code, but is significantly more complex
because cylinders are such an integral part of the core.

Two functions to access the cylinders were added:
get_cylinder() and get_or_create_cylinder()
The former does a simple array access and supposes that the cylinder
exists. The latter is used by the parser(s) and if a cylinder with
the given id does not exist, cylinders up to that id are generated.

One point will make C programmers cringe: the cylinder structure is
passed by value. This is due to the way the table-macros work. A
refactoring of the table macros is planned. It has to be noted that
the size of a cylinder_t is 64 bytes, i.e. 8 long words on a 64-bit
architecture, so passing on the stack is probably not even significantly
slower than passing as reference.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-11-09 19:19:04 +01:00
Berthold Stoeger
cd4f66014f Cleanup: Make cylinder formatting functions const-clean
In qt-models/cylindermodel.cpp the various formatting functions
can take a pointer-to-const cylinder. Thus, the data() function
can likewise treat the cylinder as const - as it should.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-11-09 19:19:04 +01:00
Berthold Stoeger
4e86d99714 Cleanup: free plot data on exit
Some widgets copy the full plot info. Free these data on exit to
prevent monstrous valgrind reports.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-11-09 19:19:04 +01:00
Berthold Stoeger
00289cd222 Profile: dynamically allocate plot pressure data
All accesses to the pressure data were converted to use functions.
Therefore it is now rather trivial to dynamically allocate the
pressure array and just change the functions.

The only thing to take care of is the idiosyncratic memory
management. Make sure to free and copy the buffer in the
appropriate places.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-11-09 19:19:04 +01:00
Berthold Stoeger
bef1eac7fa Profile: use pressure data functions in DivePlotDataModel
The model was accessing the pressure data directly. Instead,
use the accessor functions so that the core structure can
be changed more easily.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-11-09 19:19:04 +01:00
Berthold Stoeger
3025e0630d Cleanup: implement proper Qt-model semantics in DivePlotData model
User beginResetModel()/endResetModel() pairs to reset the model.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-11-09 19:19:04 +01:00
Berthold Stoeger
ff653f721c Cylinders: dynamically allocate cylinder arrays
When keeping track of cylinder related data, the code was using
static arrays of MAX_CYLINDERS length. If we want to use dynamically
sized cylinder arrays, these have to be dynamically allocated.
In C++ code, this is trivial: simply replace the C-style arrays
by std::vector<>. Don't use QVector, as no reference counting or
COW semantics are needed here. These are purely local and unshared
arrays.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-11-09 19:19:04 +01:00
Berthold Stoeger
11467fa326 Core: dynamically allocate the result of get_gas_used()
get_gas_used() returns the volume of used gases. Currently,
an array with MAX_CYLINDERS is passed in. If we want to make the
number of cylinders dynamic, the function must use an arbitrarilly
sized array.

Therefore, return a dynamically allocated array and free it
in the caller.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-11-09 19:19:04 +01:00
Dirk Hohndel
2c11544d93 Mobile: correctly update filter text and update all three models
This is even harder because setActiveTrip is called from an action slot from
QML. If the C++ code called from that slot causes the object to which this slot
belongs to be destroyed, we get very strange crashes. The only workaround I
could come up with was to update the filter asynchronously.

This all seems very ugly and fragile.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2019-11-08 20:50:05 +01:00
Dirk Hohndel
2c9bf775b7 Mobile: ensure the sort order is always set
This shouldn't be necessary every time we replace the sort model,
but it can't hurt, either (famous last words?).

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2019-11-08 20:50:05 +01:00
Dirk Hohndel
6095a5f04f Mobile: add second filter proxy model for dive list
This one significantly reduces the number of dives that are handed to the
ListView in QML. For every trip that isn't expanded (only zero or one trips are
expanded at any time, so almost all the others are collapsed), send only first
dive to the View to allow creation of the section. Hide the rest so we don't
have all these invisible, zero height entries for the vertical dive list.

A big part of this commit is moving a few functions from the DiveListSortModel
to the CollapsedDiveListSortModel. Those are the ones that are needed for the
trip header.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2019-11-08 20:50:05 +01:00
Robert C. Helling
9c8fbe494d Planner: Add option to treat O2 as narcotic
When computing the best mix for a target depth, for helium, one
can either require that the partial pressure of N2 is the same
as at the target depth or the partial pressure of N2 plus O2.

Signed-off-by: Robert C. Helling <helling@atdotde.de>
2019-10-31 00:30:38 -07:00
Dirk Hohndel
130946fcf4 Cleanup: NULL check pointer before dereferencing
I we have no dive, whatever this is shouldn't be shown.

Found by Coverity. Fixes CID 350093

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2019-10-27 07:35:46 -07:00
Dirk Hohndel
08fcda60ac Cleanup: add missing break statements
The current code of course works just fine. But there's a risk someone might
add something to one of these cases and not realize that there is an implicit
fall through going on. This is cleaner.

Found by Coverity. Fixes CID 350079

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2019-10-27 07:35:46 -07:00
Dirk Hohndel
c9a39bb08e Cleanup: avoid resource leak in dive planner model
Found by Coverity. Fixes CID 350121

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2019-10-26 19:17:53 -07:00
Dirk Hohndel
4f9783ff60 Cleanup: avoid out of bounds access
This is extremely unlikely to ever happen since we reserve space for a
hundred weight models, but hey, doing this right is quite easy, so let's
fix it.

Found by Coverity. Fixes CID #350117

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2019-10-26 19:17:53 -07:00
Berthold Stoeger
c479c5ca36 Desktop: use undo command when replanning dive
Instead of copying the displayed dive, generate an undo command.
This makes the replanning an undoable action and fixes a bug
where the dive details have not been updated correctly.

Fixes #2280

Reported-by: Stefan Fuchs <sfuchs@gmx.de>
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-10-26 11:36:23 -07:00
Dirk Hohndel
158a2ec159 Mobile: remove remaining accesses to DiveObjectHelper from QML
Add a couple more roles and remove the dive role that allows accesss to
the DiveObjectHelper in the first place.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2019-10-20 16:08:55 -04:00
Dirk Hohndel
613a3112d2 Mobile: get dive details directly from the model
By getting a DiveObjectHelper and then dereferencing that we ended up
creating hundres and hundreds of these objects, only to immediately
destroy them after using a tiny part of the data.

Instead make those data available directly from the model, without
having to create a DiveObjectHelper forst.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2019-10-20 16:08:55 -04:00
Berthold Stoeger
769403a4b2 Planner: copy deco state before passing it to worker thread
The planner has a computeVariations() function that can be run
in a worker thread. The code was not thread safe: a deco_state
object allocated on the stack of the caller was passed down to
the worker thread. It's well possible that the object would go
out of scope before the thread run.

Therefore, when running in the background, copy the object first
and free it in the worker thread.

Side note: Qt makes proper memory management again as difficult
as possible: You can't pass a std::unique_ptr<> to QtConcurrent::run,
because move-only objects are not supported. Not very friendly!

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-10-20 16:20:58 +02:00
Berthold Stoeger
1e1f33c0f5 Cleanup: Make constructor of singletons private
This guarantees that they are actually singletons: there can
only be one application-wide instantiation of these objects.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-10-04 09:19:10 -07:00
Berthold Stoeger
3db50aedea Cleanup: Turn DiveListSortModel into classical singleton
To make this class available from QMLManager, the run_ui()
function would create the object and then set a pointer in
QMLManager. It works, but is inconsistent with the rest of
the code. Therefore, make it a classical singleton class,
which is generated on demand.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-10-04 09:19:10 -07:00
Berthold Stoeger
2a9a3dda20 Cleanup: turn GpsListModel into standard singleton
GpsListModel was one of those "special" singletons that could
be created explicitly with new. This would make sense if a
parameter were passed to the constructor. We only passed null,
so one might as well turn that into a classical singleton with
default constructor.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-10-04 09:19:10 -07:00
Berthold Stoeger
36d42a6a57 Cleanup: turn DiveListModel into standard singleton
DiveListModel was one of those "special" singletons that could
be created explicitly with new. This would make sense if a
parameter were passed to the constructor. We only passed null,
so one might as well turn that into a classical singleton with
default constructor.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-10-04 09:19:10 -07:00
Berthold Stoeger
2560734624 Cleanup: initialize DiveListSortModel in constructor
The model was initialized in the global run_ui() function.
Move that into the constructor of the class.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-10-04 09:19:10 -07:00
Berthold Stoeger
4d5acc8461 Import: remove DiveImportedModel::lastIndex
This is redundant, as the actual size is stored in the dive table.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-10-02 08:04:49 -07:00
Berthold Stoeger
a33f381dc6 Cleanup: remove DiveImportedModel::firstIndex
This index was never set to anything else than 0. Might as
well remove it.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-10-02 08:04:49 -07:00
Berthold Stoeger
5037fcdbdc Import: avoid model inconsistency in DiveImportedModel::recordDives()
DiveImportedModel::recordDives() called add_imported_dives(). But that
actually consumes the dive and dive-site tables. Which in turn will
lead to an inconsistent model.

Properly reset the model by using the consumeTables() function.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-10-02 08:04:49 -07:00
Berthold Stoeger
087a80194a Import: keep dive and dive site tables in DiveImportedModel
The DiveImportedModel and DownloadThread used the same table
of dives and dive sites. This made it very hard to keep the
model consistent: Every modification of the download thread
would make the model inconsistent and could lead to memory
corruption owing to dangling pointers.

Therefore, keep a copy in the model. When updating the model,
use move-semantics, i.e. move the data and reset the tables
of the thread to zero elements.

Since the DiveImportedModel and the DownloadThread are very
tightly integrated, remove the accessor-functions of the
dive and dive-site tables. They fulfilled no purpose
whatsoever as they gave the same access-rights as a public
field.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-10-02 08:04:49 -07:00
Berthold Stoeger
81268adfd3 Import: extract number of dives from model not from thread
The plan is to make the model the authoritative source of
the imported dives. Therefore, access the number of
downloaded dives from there.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-10-02 08:04:49 -07:00
Berthold Stoeger
8f3c85f58d Import: get tables from DiveImportedModel not DownloadThread
When importing dives, consume the tables from DiveImportedModel
and not the DownloadThread. This appears more logical and avoids
an inconsistent state of the DiveImportedModel: On import the
tables would be reset, but the DiveImportedModel wasn't
informed of that.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-10-02 08:04:49 -07:00
Berthold Stoeger
30b384cebd Import: keep model state consistent when deleting unselected dives
In DiveImportedModel::deleteDeselected(), unselected dives were
deleted from the dive-table. But this left the model in an
inconsistent state and the frontend was not informed of the
missing dives.

Fix this by invoking the appropriate beginRemoveRows()/
endRemoveRows() pairs. Move the functionality into its
own function so that it can be reused by the desktop version.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-10-02 08:04:49 -07:00
Berthold Stoeger
ad7ffa0af0 Import: Make DownloadThread a subobject of DiveImportedModel
Currently, desktop and mobile are accessing the DownloadThread
and the DiveImportedModel concurrently. This makes a big data
flow mess. To achieve a more hierarchical data flow, start
by making the DownloadThread a subobject of DiveImportedModel.

Start the download by calling a function in DiveImportedModel.

Route the finished signal through DiveImportedModel. Thus,
the model can reload itself with the new data.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-10-02 08:04:49 -07:00
Berthold Stoeger
eecca6aab0 Mobile: replace model-reset by row-addition in DiveListModel::reload()
Owing to apparent QML breakage, a model-reset leads to the DiveDetail
page being reloaded for every dive in the list(!). Therefore, add
rows instead.

This leads to extremely subtle code, as it is now imperative that
the model has been properly cleared beforehand. Nevertheless, for
now we have to do this to fix a severe performance regression.

Fixes #2295

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-09-27 16:33:37 -07:00