When the dive data is cleared, all pointers in the undo-stack become stale.
Desktop explicitly called Command::clear() in that case, but mobile doesn't.
Thus, move the clear() call into DiveTripModelBase::clear()
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
To act as a drop-in replacement (at least as much as possible),
move the roles from the old DiveListModel to the common base
model of mobile and desktop.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Create a model which represents all top-level items and, potentially, one
expanded trip as a flat list.
Pass down roles to the source model and let the source model handle that. We'll
have to do some ifdef-ery, but so be it.
Additionally, compile the base model on mobile as well.
This contains a couple of hacks to make things compile at all.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Once the the mobile app uses the new models, this will be necessary
to get the correct number of filtered dives after startup.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
If compiled on mobile, on change of the current dive, don't send
a signal, but send changed-event with the CURRENT_ROLE for both
dives that changed status (previously selected and newly selected).
Mobile does not use this yet, but will do so with the new flattened
models.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This role returns true if the given trip contains the current
dive. This will be needed by the mobile list model to decide
if a newly added trip should be expanded right away.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Use this role to test whether a dive is the currently displayed dive.
This will be needed to transport changes of the current dive to
the mobile list models.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
The DiveFilter class defined the showDive() function to test
whether a dive should be filtered or not. This was used in
DiveTripModel to loop over all dives or all dives affected by
an editing action.
This restricts us in how we do filtering: We can't use indexes
that give us directly the result. To make the filtering more
flexible, move the actual loops that do the filtering to
the DiveFilter class.
The undo-commands likewise called directly the showDive()
function to check whether newly added dives are shown.
Use the new interface here as well.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
When changing the filter-criterion and the current dive is
hidden, a new dive is made current. However, when a dive is
hidden because it was edited, it is still shown.
Make this consistent by also selecing a new current dive
in the latter case. Do this by comparing the current_dive
before and after calculating the filter. Since this is now
done in multiple places move this code to the ShownChange
class.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Since we now filter at the model level, items are by definition
shown. No need for a flag.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This role was used by the filter-proxy model as filter criterion.
Since we don't use that model to filter anymore, we can remove
the role.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
We use a QFilterProxyModel to filter out dives that are hidden
according to the current filter-criterion. Instead, filter the
dives already at the DiveTripModel level. Filter out hidden
dives immediately when receiving them. The only difficult case
is when dives are changed, because then visibility can change.
This means that we have three cases to consider:
1) Visibility unchanged -> send change signal
2) Change from hidden to unhidden -> add dives to model
3) Change from unhidden to hidden -> remove dives from model
Things get complicated by the fact that the tree-version of
the model might have to add/remove full trips!
Suggested-by: Dirk Hohndel <dirk@hohndel.org>
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Use std::equal_to instead of lambdas that compare two dive pointers.
One could argue over which version is more readable. For whatever
it's worth, std::equal_to is more compact and expressive.
This removes an old erroneous comment that stated that std::equal_to
is only available since C++14.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
On desktop, resetting the model is realized by generating a new
model object. This is due to the fact that we have two different
models (tree and list) and for switching between those, we have
to create a new object.
On mobile, currently there are no plans to support the list-mode.
Therefore, there is no reason the recreate the object. Instead,
implement a reset() function that reloads the core data.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
The DiveTripModelList forgot to collect the changed dives
when resetting the filter. Fix that.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Updating the filter can lead to changes of the current dive.
Keep the UI in the know by re-initializing the selection.
This is not optimal, because the whole selection is reset,
but the pragmatic thing to do for now.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This part of the code had that horrible pattern, where reseting
the model would invalidate all pointers to the DiveTrip model.
Internalize these complexities in the MultiFilterSortModel.
All accesses are now performed via that proxy model.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
The old code called directly into the DiveListModel. Instead,
send a signal and hook into the signal from the model. This
will allow us to remove the DiveListModel::instance() function.
This, in turn, is a step towards supporting multiple models
at the same time. However, currently the model manually
sets the hidden_by_filter flag in the core and therefore
only one active model is supported at a time.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
The goal here is to unify desktop and mobile by moving
selection code from the desktop-only view.
Currently, initialization of the selection still has to be
called from the view after connecting the appropriate signals.
This is due to the weird way in which create completely new
models when resetting them.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
In analogy to the tree-model send signals when dives change
their shown status in the list-view. Do this in two passes
(collect changes; send changes) to be able to reuse the
already existing functions.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Send signals if the shown-status of top level items changed.
Do this in two passes to be able to use the previously created
function.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
To avoid having to do full filter reloads, send dive-changed signals
for dives in trips when the shown-status changed. But only for trips
where not all dives are hidden. Because for those, the plan is to
hide the trip as a whole.
Implement the signal sending in its own function so that it can be
reused for top-level items and the list-view.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Implementing proper model semantics (only sending a changed
signal for items that actually changed) will be somewhat
complicated. Therefore, move the filtering of trip-items
to its own function to make the nesting a little bit less
deep.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
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>
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>
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>
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>
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>
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>
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>
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>
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>
When editing the dive site, for certain fields a divesChanged signal
was emitted so that the dive-list can be updated.
Arguably it is wrong to decide which fields are relevant to the
dive list in the undo-command code. Therefore, let the list
catch the dive-site-edited signal and decide itself.
But the actual reason for this commit is that if the dive-site
field of a dive changes, we might have to reload the dive-location-model
because suddenly a new dive site appears. Now if this is done
in QML context on some Qt version (notably 5.9) we get crashes
later on. But that can happen if the user moves a flag. So in that
case only send a diveSiteChanged signal.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
When pressing F2 in the dive list, the number can be edited.
Make this action undoable by implementing a EditNumber command.
This command is differs from the other undo commands, as not the
currently selected dives are changed. This means that the EditCommand
needs an alternative constructor taking a single dive. This constructor
was implemented in the base class so that all edit commands can now
be called with a single dive.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
For historic reasons, there where three distinct signals concerning
dive-selection from the undo-machinery:
1) divesSelected: sent newly selected dives
2) currentDiveChanged: sent if the current dive changed
3) selectionChanged: sent at the end of a command if either the selection
or the current dive changed
Since now the undo-commands do a full reset of the selection, merge these
three signals into a single signal.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Some commands tried to retain the current selection on undo/redo,
others set the selection to the modified dives.
The latter was introduced because it was easier in some cases, but
it is probably more user-friendly because the user gets feedback
on the change.
Therefore, unify to always select the affected dives on undo()/redo().
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Since the default view is batched by trips, signals were sent trip-wise.
This seemed like a good idea at first, but when more and more parts used
these signals, it became a burden. Therefore push the batching to the
part of the code where it is needed: the trip view.
The divesAdded and divesDeleted are not yet converted, because these
are combined with trip addition/deletion. This should also be detangled,
but not now.
Since the dive-lists were sorted in the processByTrip function, the
dive-list model now does its own sorting. This will have to be
audited.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
We have a very fundamental problem with data-duplication in
core and qt-models. In a particular case, this led to an easily
reproducible crash:
1) An undo command moved the last dive of a trip to another.
2) When an undo-command removed the last dive of
a trip to a different trip, the dive was removed from the
trip in the core. Then, the model was updated.
3) That lead at first to a rearrangement of the trips, because
the trip with the added dive is moved before the trip with
the removed dive.
4) In such a case, the filter-model checks the visibility of
the trip.
5) Since the trip with the removed dive has no dives in the core,
visibility was determined as false.
6) From this point on the mappings of the QSortFilterProxyModel
were messed up. Accesses led to crashes. It is unclear
whether this is a Qt bug or only a QOI issue.
As a quick-fix, cache the visibility flag of trips directly
in the Qt-models. Don't set the visibility directly in the
core, but go via the Qt-models. Thus, a more clear layering
is achieved.
In the long run, we can hopefully get rid of the data-duplication
in the models.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
These functions were spread out over dive.c and divelist.c.
Move them into their own file to make all this a bit less monolithic.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Make dive.h a bit slimmer. It's only a drop in the bucket - but at
least when modifying tag functions not the *whole* application is
rebuilt anymore.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This is just minor fixes that are not user-visible:
Fix a few erroneous comments and a debug message. These are
copy & paste mistakes and mistakes introduced during code-
refactoring.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
At some places we use UTF8 string literals. Therefore, we effectively
only support UTF8 build systems. We might just as well remove all
the other UTF_* macros and use direct string literals.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>