Add a column to the equipment table that shows if a sensor is attached to a
tank, or which sensors would be available to attach to a tank that currently
doesn't have a pressure sensor associated with it.
Changing the sensor assignement can be undone.
This column is hidden by default as this is a somewhat unusual activity.
Signed-off-by: Michael Andreen <michael@andreen.dev>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
In a40b40a the cylinder-hiding functionality was changed,
which made it necessary to keep track of the number of
cylinders. Ironically, that code was removed previously,
as it was redundant. The count was not readded to the
functions called by the planner, making editing of cylinders
in the planner impossible.
I wonder more and more if the models for planner and the
equipment tab should be changed. They are too different.
Fixes#3375
Reported-by: Anton Lundin <glance@acc.umu.se>
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
When adding a cylinder, it was added at the end of the list.
This would make hidden cylinders visible as the new rule is
to only hide unused cylinders at the end of the list.
Therefore, add the cylinder after the last used cylinder,
i.e. before the first hidden cylinder.
This means that the position where the cylinder is added has
to be hidden in the undo command.
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>
There is a warning when the code tries to access a non-existing
cylinder, since that indicates that something went out of sync.
However, this warning can never trigger because the bounds are
checked before. Remove the first of the two redundant checks.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
The logic did not consider the WORKINGPRESS_INT and SIZE_INT
columns added in cb80ff746b.
By some unknown magic this worked by routing everything
through the CylindersModelFiltered model.
Let's fix it and explicitly ignore these columns. Put
the test whether a column should be ignored in a function
to avoid inconsistencies should new columns be added.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Fix a pair of warnings, which annoyed me for a long time:
For some reasons prefs.bottompo2 is an integer (mbar)
whereas prefs.modpO2 is a float (bar). This results
in mixed integer/floating point arithmetics when
conditionally using either of them. And ultimately
a warning, when storing a mbar value as an integer.
Fix this by an explicit cast to int after converting
modpO2 to mbar.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Air is a special gas that does not contain oxygen according
to gasmix.o2.fraction. If you want to use the fo2, you
need to use get_o2() to treat this special case correctly.
This fixes a bug when setting the MND of a gas containing
21% oxygen when o2 is considered not narcotic.
Reported-by: Christoph Gruen <gruen.christoph@gmail.com>
Signed-off-by: Robert C. Helling <helling@atdotde.de>
in_planner() is problematic, since it is uses desktop-only
application state. Since the cylinder-model already has
an appropriate inPlanner flag, use this instead.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
The cylinder model is used both in the planner and the
equipment tab. We have three preferences for the pO2 that
is used to compute MOD: In the planner, there is one for
the bottom part of the dive and another one for deco.
Those are set in the planenr UI. There is another value,
controlled in the Tec Prefernces. That one should be
used in the equipment tab rather than the one from
the planner.
Fixes#2984
Signed-off-by: Robert C. Helling <helling@atdotde.de>
get_cylinder(d, i) is more readable than d->cylinders.cylinders[i].
Moreover, it does bound checking and is more flexible with respect to
changing the core data structures. Most places already used this accessor,
but some still accessed the cylinders directly.
This patch unifies the accesses by consistently switching to get_cylinder().
The affected code is in C++ and accesses the cylinder as reference or
object, whereas the get_cylinder() function is C and returns a pointer.
This results in funky looking "*get_cylinder(d, i)" expressions.
Arguably still better than the original.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
A while ago, we introduced a preference whether O2 should
be considered narcotic. We used this when computing
best mix or when entering the He content via MND. But
we forgot to make the displayed MND depend on this
preference. This patch add this.
Fixes#2895
Signed-off-by: Robert C. Helling <helling@atdotde.de>
This will be temporarilly used by the planner to mark consumption of
air at the surface. Do this by creating a new function add_cylinder,
which replaces add_to_cylinder_table() and takes care of always adding
a dummy cylinder at the end of the table. Make the original
add_to_cylinder_table() local, so that it cannot be accessed anymore.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
It appears that some misguided compiler / library combinations crash
on &vector[0] for empty vectors. Even though very unfriendly, they are
technically correct, so let's remove these constructs.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Parts of the code were not compiled on mobile, because they used
the undo-command infrastructure. However, since mobile now also
compiles that, we might as well remove the conditional compilation.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
When editing cylinders or weights directly in the table widgets,
no warning was shown if multiple dives were affected. To solve this,
emit signals from the respective models and catch them in dive
equipment tab. Not very nice, but it works for now.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Don't overwrite the full cylinder when editing a single field.
Implement three "modes": editing of type, pressure and gasmix.
Don't consider individual fields, because some of them are
related. E.g. you can change the gasmix by setting the MOD.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
On desktop, we have two CylindersModel concurrently: One in the
planner and one on the equipment-tab. They act differently, because
the former modifies displayed_dive directly, the latter issues
undo commands.
To differentiate, we used the in_planner() function. However, that
appears extremely brittle, especially when combined with undo-commands.
Therefore when generating the model, pass in a parameter that says
whether this is for the planner or the equipment tab and use
that flag to decide how to act.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Call an undo command when editing cylinders, but only if on
the EquipmentTab.
To keep code changes small, make a copy of the cylinder first,
then edit the cylinder as before and then either call an undo
command (EquipmentTab) or overwrite the old cylinder (Planner).
The memory management here is a bit strange: Since the undo-command
itself makes a deep-copy of the passed in cylinder, we only do
a shallow copy. If we have to change the type, we allocate the
string with an std::string, so that the memory is automatically
freed at the end of the function. However, this means that in
the planner we have to make a deep copy first, swap old and
new cylinder and finally release the old cylinder. Certainly
not ideal, but for now the pragmatic thing to do.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This one is tricky, as when browsing through the types-combobox,
the user is presented with presets without actually changing the
dive. We do not want an undo-command for every change-event in
the combo-box.
Therefore, implement a scheme analoguous to the weight-editing:
A temporary row can be set / committed or reset. Sadly, the
code is more complex because we have to consider the planner,
which is not included in the undo system.
Firstly, the planner uses a different model, therefore all
interactions are channeled through setData() with special roles.
Secondly, in the planner we shouldn't place an undo command,
but simply overwrite the dive.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
add() and remove() are not used anymore since this is done using
undo commands. The planner uses CylindersModel instead.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
The CHANGED macro was defined in the cleanerTableModel header.
Since it had only one user, expand it there. The macro was very
questionably anyway, as it would set the local "vString" variable.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
A small code consolidation:
With one exception, all targets of the switch statement would
test for CHANGED(). Instead do the test once and exit early.
This changes the behavior of the function: if not changed, there
will be no more dataChanged-signal. However, this appears to be
the correct thing to do anyway. And it is easily changed if
it matters after all.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Cylinder-editing is controlled by undo (either by saving a planned
dive or by using the equipment tab). There is no point in setting
the dive_list_changed flag.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
The cylinders in the events must be reordered if we remove
a cylinder. To avoid duplication of code, move the reordering
function into qthelper.cpp, though it might not be ideal
there.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
The CylinderModel always accessed the global "displayed_dive" and in
some special cases also "current_dive". To implement cylinder undo,
the model should work on an arbitrary dive. Therefore, in analogy
to the weight model, make the dive dynamic.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Turn the code in CylindersModel that creates a new cylinder for
addition into its own function to avoid code duplication. This
will be used from the undo commands.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Grammar-nazi ran
git grep -l 'indexes' | xargs sed -i '' -e 's/indexes/indices/g'
to prevent future wincing when reading the source code.
Unfortunatly, Qt itself is infected as in
QModelIndexList QItemSelection::indexes() const
Signed-off-by: Robert C. Helling <helling@atdotde.de>
Convert to QString instead of a QByteArray. Use qPrintable()
instead of data(). This might do one more UTF16->UTF8 conversion.
However, this is completely irrelevant, since we don't change
the type of a cylinder in a tight loop.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
The C-string cyl->type.description was set without the old data
being freed. Free the old string before overwriting the pointer.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
The tank-info-delegate cast its model to CylindersModelFiltered,
since this is what the equipment-tab uses since implementing the
filtering of unused cylinders. However, the planner users the same
delegate and still uses the unfiltered CylindersModel. This means
that the (dynamic) cast returns a null pointer and crashes.
One possibility would be to derive CylindersModelFiltered and
CylindersModel from the same class that defines virtual functions
and cast to that class.
This is a different attempt: don't cast (i.e. stay with a
QAbstractItemModel and play it via Qt's model-view system. Firstly,
replace the passInData function by a role to setData(). Secondly,
read the working-pressure and size via new columns using data().
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This header is a rebase artifact and introduces a pointless
column in the cylinder tables. It was erroneously introduced
in 6622f42aab.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
The cylinder-model had an instance() function, but actually
there were two cylinder models: one used by the equipment tab,
one used by the planner.
This is misleading. Therefore, remove the instance() function
and make the cylinder-model a subobject of the planner-model.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
When the show_unused_cylinders flag is not set, the cylinder tables
in the equipment tab and the planner should not show unused cylinders.
However, the code in CylindersModel is fundamentally broken if the
unused cylinders are not at the end of the list: The correct number
of cylinders is shown, but not the correct cylinders.
Therefore, add a higher-level CylindersModelFiltered model on top
of CylindersModel that does the actual filtering. Some calls are
routed through to the base model (notably those that take indexes,
as these have to be mapped), for some calls the caller has to get
access to the source model first. We might want to adjust this.
For filtering, reuse the already existing show_cylinder function
and export it via CylindersModel.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
We tend to use lower-case filenames. Let's do it for these files
as well. Simple search & replace.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
It shouldn't happen, but currently we overwrite the displayed_dive
without updating the CylindersModel. Thus, CylindersModel may now
crash when the new displayed_dive has less cylinders than the old
one.
For now, catch this condition. Treat the root cause later.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Change the remove() function of the cylinder and weight models
to take the index by value. The code used to take it by reference
and the reference would be invalidated when removing rows from
the model!
Reported-by: Gaetan Bisson <bisson@archlinux.org>
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>
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>
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>
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>
The fill_default_cylinder() function calculated the MOD based
on the currently displayed dive. This does not seem to make sense:
- When importing dives, why would we care about the altitude and
salinity of the currently displayed dive, possibly from a different
trip.
- The planner is supposed to be thread-safe and should not touch
global variables.
Of course this means that the importing-functions have to fill
out altitude and salinity before creating the default cylinder,
but this is their problem. For a freshly created dive they will
get the default values, which still seems less random than the
values from the displayed dive.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>