Compare commits

..

119 Commits

Author SHA1 Message Date
Berthold Stoeger
4bf2daa732 import: use std::string for location in cobalt-import
One more strdup()/free() pair removed.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:59 +02:00
Berthold Stoeger
db0a220d52 core: remove add_to_string() function
Last user removed in 160f06db8d818632b9990c7bef736f1a0dacbbe3.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:59 +02:00
Berthold Stoeger
5ea71572e3 desktop: use std::string to format subtitles
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:59 +02:00
Berthold Stoeger
c97901afcf core: use std::string in error_callback
No naked free().

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:59 +02:00
Berthold Stoeger
b59682fa43 core: remove C-versions of (v)format_string()
Only users of the std::string versions are left.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:59 +02:00
Berthold Stoeger
d25e41a39d core: remove get_first_converted_string_c() in load-git.cpp
No more users of C-strings.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:59 +02:00
Berthold Stoeger
3f7daf225f download: replace progress_bar_text by std::string
No fixed buffers. Sadly, the thing is still a global variable.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:59 +02:00
Berthold Stoeger
ec88a9887e core: remove filterconstraint C boilerplate code
Since all code can now directly access C++ structures these
accessor functions were not necessary.

Split out the table from the filterconstraint source file
and include it directly into the divelog.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:59 +02:00
Berthold Stoeger
87fb957ad1 core: include trip table directly in divelog
Having this as a pointer is an artifact from the C/C++ split.
The triptable header is small enough so that we can
include it directly

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:59 +02:00
Berthold Stoeger
90c7c5d936 core: include divesite table directly in divelog
Having this as a pointer is an artifact from the C/C++ split.
The divesitetable header is small enough so that we can
include it directly.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:59 +02:00
Berthold Stoeger
d9fc0be382 core: move functions into trip-structure
Not strictly necessary, but a "natural" thing to do in a classical
C++ code base.

Move the tiny trip-table into its own source file, since it also
has its own header.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:59 +02:00
Berthold Stoeger
98c4242f31 fix divetable to owning
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:59 +02:00
Berthold Stoeger
aef45b4913 core: remove table.h
No more users of this.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:59 +02:00
Berthold Stoeger
adbd8b1a0a core: turn C dive-table into an owning table
This is a humongous commit, because it touches all parts of the
code. It removes the last user of our horrible TABLE macros, which
simulate std::vector<> in a very clumsy way.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:59 +02:00
Berthold Stoeger
aab88385ce fix mrege to unique: a5f291fa12
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:59 +02:00
Berthold Stoeger
c50c5d97bc core: move functions into struct dive
Nothing against free-standing functions, but in the case
of dc_watertemp(), dc_airtemp(), endtime() and totaltime(),
it seems natural to move this into the dive class and avoid
polution of the global name space.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:59 +02:00
Berthold Stoeger
2dd7ff6da8 core: introduce register_dive() function
There was a weird asymmetry, where the undo-commands would
register the fulltext index of the dive, but the core would
unregister the fulltext index in the "unregister_dive()"
function.

To make this more logical, create a "register_dive()" function
in core that does registers the fulltext index.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:59 +02:00
Berthold Stoeger
b46b42dabd core: return unique_ptr<> from merge-dive functions
Try to remove plain owning pointers.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:59 +02:00
Berthold Stoeger
7b0dff0dff core: make clone_* functions return a unique_ptr<>
Don't use plain pointers for owning pointers.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:59 +02:00
Berthold Stoeger
ffed8dfea6 core: make split_dive() and related functions return unique_ptrs
This prepares for turning the dive table into a list of owning
pointers.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:59 +02:00
Berthold Stoeger
02177e5d3b fulltext: replace plain pointer by std::unique_ptr<>
This was a plain pointer owing to C compatibility.

Replacing it by a unique_ptr<> allows us to make it
'self-desctruct' in the constructor. However, we do this
with a special twist: the data is _not_ copied when copying
the dive, since the copied dive is not registered in the fulltext
system. Hackish, but it should(!) work.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:59 +02:00
Berthold Stoeger
c1678c23fc core: replace list of dives in trip by std::vector<>
The dive_table will be converted into a table of owning pointers.
Since the trip has only non-owning pointers to dives, turn
its dive_table into an std::vector<dive *>.

Add a helper functions to add/remove items in a sorted list.
These could be used elsewhere.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:59 +02:00
Berthold Stoeger
4c9915b9bf core: make get_trip_by_uniq_id a member of trip_table
Don't access the global trip_table in an attempt to cut down
on implicit accesses of global variables.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:59 +02:00
Berthold Stoeger
34419ece66 core: turn trip-table into our own sorted_owning_table
Since the sorted_owning_table depends on the fact that
different elements never compare as equal, make the
comparison function safer in that respect. If all failes,
compare the pointers.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:59 +02:00
Berthold Stoeger
99f87cba4b core: remove utf8_string() function
That was used to parse C-style strings. It was fully replaced
the the std::string version utf8_string_std().

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:59 +02:00
Berthold Stoeger
e171cdf1eb core: turn dive-trip location and notes into std::string
Simpler memory management.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:58 +02:00
Berthold Stoeger
e06225eb8a core: remove device-fingerprint C access code
No need to have this code, as all callers are now C++.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:58 +02:00
Berthold Stoeger
1f8b151d8f core: remove device C access code
This was used from C, so there was lots of access code, which is
not necessary.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:58 +02:00
Berthold Stoeger
4a438bd63f core: turn picture-table into std::vector<>
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:58 +02:00
Berthold Stoeger
3bf8658861 core: turn struct dive string data into std::string
Much easier memory management!

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:58 +02:00
Berthold Stoeger
9015429c2a core: add default initialization to sruct deco_state
Don't memset() to clear deco_state, use assignment of
default constructed object (or better yet: just default
construct).

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:58 +02:00
Berthold Stoeger
040af97b82 core: port tag-list to C++
Also adds a new test, which tests merging of two tag-lists.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:58 +02:00
Berthold Stoeger
d59e80b44d core: convert weightsystem_t and weightsystem_table to C++
As for cylinders, this had to be done simultaneously,

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:58 +02:00
Berthold Stoeger
2e876f139d core: convert cylinder_t and cylinder_table to C++
This had to be done simultaneously, because the table macros
do not work properly with C++ objects.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:58 +02:00
Berthold Stoeger
c8389a5644 core: turn divecomputer list into std::vector<>
Since struct divecomputer is now fully C++ (i.e. cleans up
after itself), we can simply turn the list of divecomputers
into an std::vector<>. This makes the code quite a bit simpler,
because the first divecomputer was actually a subobject.

Yes, this makes the common case of a single divecomputer a
little bit less efficient, but it really shouldn't matter.
If it does, we can still write a special std::vector<>-
like container that keeps the first element inline.

This change makes pointers-to-divecomputers not stable.
So always access the divecomputer via its index. As
far as I can tell, most of the code already does this.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:58 +02:00
Berthold Stoeger
c0e834cffc core: fold event-related functions into event class
Not strictly necessary, but more idiomatic C++ and less
polution of the global namespace. This one is so trivial
that there seems to be no reason not to do it.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:58 +02:00
Berthold Stoeger
076d8a7fce core: replace add_sample() by append_sample()
add_sample() was used in only one place, and the return value was
always ignored. It took a time parameter, suggesting that a sample
could be added anywhere, but in reality the sample was added at
the end of the list. It used prepare_sample() that copies data
from the previous sample, just to overwrite it with the newly
added sample.

All in all very weird. Simplify the function: just append the
passed in sample and name it accordingly.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:58 +02:00
Berthold Stoeger
a72a0543e1 core: simplify default initialization of struct sample
Since the units got default constructors, we don't have to
manually initialize them.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:58 +02:00
Berthold Stoeger
a3f37e3c9e core: turn event-list of divecomputer into std::vector<>
This is a rather long commit, because it refactors lots of the event
code from pointer to value semantics: pointers to entries in an
std::vector<> are not stable, so better use indexes.

To step through the event-list at diven time stamps, add *_loop classes,
which encapsulate state that had to be manually handled before by
the caller. I'm not happy about the interface, but it tries to
mirror the one we had before.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:58 +02:00
Berthold Stoeger
42fbb40eab core: remove update_event_name
Since the name of an event is not incorporated into the even
structure anymore, we don't need these shenanigans. Just assign
the event name.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:58 +02:00
Berthold Stoeger
62303a015a core: use std::vector<> to store divecomputer samples
This is a hairy one, because the sample code is rather tricky.

There was a pattern of looping through pairs of adjacent samples,
for interpolation purposes. Add an range adapter to generalize
such loops.

Removes the finish_sample() function: The code would call
prepare_sample() to start parsing of samples and then
finish_sample() to actuall add it. I.e. a kind of commit().

Since, with one exception, all users of prepare_sample()
called finish_sample() in all code paths, we might just add
the sample in the first place. The exception was sample_end()
in parse.cpp. This brings a small change: samples are now
added, even if they could only be parsed partially. I doubt
that this makes any difference, since it will only happen
for broken divelogs anyway.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:58 +02:00
Berthold Stoeger
e29c044d91 core: convert dive computer extra data to C++
Use std::string and std::vector. Much simpler code.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:58 +02:00
Berthold Stoeger
21a28aeb62 core: turn string data in struct divecomputer into std::string
Simplifies memory management.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:58 +02:00
Berthold Stoeger
3447441e81 core: add constructor/destructor pairs to dive and divecomputer
This allows us to use non-C member variables. Convert a number
of pointers to unique_ptr<>s.

Code in uemis-downloader.cpp had to be refactored, because
it mixed owning and non-owning pointers. Mad.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:58 +02:00
Berthold Stoeger
965d1f359b core: remove typedefs in pref.h
Some compilers whine when using typedefs with non-C structs with
default initializers. Not yet the case here, but in the future
probably will. So remove them now. No point in C++ anyway.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:58 +02:00
Berthold Stoeger
2d18da5fc9 cleanup: remove enum typedef in color.h
Just call the enum that way and be done with it.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:58 +02:00
Berthold Stoeger
8962aadb23 cleanup: remove typedef in qtserialbluetooth.cpp
This was very odd: A typedef to the same name as the structure
was named. Huh?

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:58 +02:00
Berthold Stoeger
553151b12f core: remove typedefs in units.h
They make no sense under C++ and seem to produce tons of warnings
on some compilers (Apple).

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:58 +02:00
Berthold Stoeger
9fae66f163 core: remove typedefs in equipment.h
Wuth C++ the distinction between "typedef" and regular "struct"
makes no sense anymore. Remove the typedefs, it's just confusing.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:58 +02:00
Berthold Stoeger
a05d80f9ae divelog: turn owning-pointers into unique_ptr<>s
Since everything is C++ now, we can use unique_ptr<>s. This makes
the code significantly shorter, because we can now use the default
move constructor and assignment operators.

This has a semantic change when std::move()-ing the divelog:
now not the contents of the tables are moved, but the pointers.
That is, the moved-from object now has no more tables and
must not be used anymore. This made it necessary to replace
std::move()s by std::swap()s. In that regard, the old code was
in principle broken: it used moved-from objects, which may work
but usually doesn't.

This commit adds a myriad of .get() function calls where the code
expects a C-style pointer. The plan is to remove virtually all of
them, when we move free-standing functions into the class it acts
on. Or, replace C-style pointers by references where we don't support
NULL.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:58 +02:00
Berthold Stoeger
ec48e05dc6 core: move freestanding functions into divelog class
There were only two of them, from the time C-code had to access
the divelog: clear_divelog() and delete_single_dive().

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:58 +02:00
Berthold Stoeger
a584b4926b cleanup: pass location_t as value to divesite functions
These were passed as pointers, which makes no sense.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:58 +02:00
Berthold Stoeger
1fd7c2f091 import: initialize DiveSiteImportModel in constructor
The old code would construct and then initialize the object
in a separate function, which added lots of complication.

Just initialize the thing in the constructor, store a
reference, not a pointer to the table. And do a few other
code cleanups. The result is distinctly more pleasing.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:57 +02:00
Berthold Stoeger
1a6f42e781 core: move get_same_dive_site() into dive_site_table class
This was the only dive_site_table function that accessed
to global divelog, which is odd. Make it consistent with
the others.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:57 +02:00
Berthold Stoeger
ed302b373a core: move constructLocationTags from divesite.cpp to taxonomy.cpp
After all it doesn't access any dive_site structure.

Moreover, rename it, since we use mostly snake_case in core.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:57 +02:00
Berthold Stoeger
d611400897 core: move get_distance() from divesite.cpp to units.cpp
This gives the distance between to location_t objects. It is
unclear why this was in divesite.cpp.

Moreover pass by value, not raw pointer.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:57 +02:00
Berthold Stoeger
0cd01640e5 core: move dive-site functions into class
In analogy to the previous commit for dive-site-table.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:57 +02:00
Berthold Stoeger
e9b49d197b core: move dive-site-table functions into class
There were a number of free standing functions acting on a
dive-site-table. Make them member functions. This allows
for shorter names. Use the get_idx() function of the base
class, which returns a size_t instead of an int (since that
is what the standard, somewhat unfortunately, uses).

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:57 +02:00
Berthold Stoeger
fc725d0803 map: use value semantics for MapLocation
This makes memory management more simple, as not explicit deletion
is necessary.

A rather large commit, because changing QVector<> to std::vector<>
is propagated up the call chain.

Adds a new range_contains() helper function for collection
types such as std::vector<>. I didn't want to call it
contains(), since we already have a contains function
for strings and let's keep argument overloading simple.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:57 +02:00
Berthold Stoeger
98e9fee8bd core: replace divesite_table_t by a vector of std::unique_ptr<>s
This is a long commit, because it introduces a new abstraction:
a general std::vector<> of std::unique_ptrs<>.

Moreover, it replaces a number of pointers by C++ references,
when the callee does not suppoert null objects.

This simplifies memory management and makes ownership more
explicit. It is a proof-of-concept and a test-bed for
the other core data structrures.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:57 +02:00
Berthold Stoeger
c26b5e4706 core: return pressures structure from fill_pressures()
Instead of taking an out-parameter. That's more idiomatic C++.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:57 +02:00
Berthold Stoeger
eb5f8415e1 core: remove membufferpp
This the C++ version of membuffer. Since everything is C++, it can
just be made the default.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:57 +02:00
Berthold Stoeger
865cac7bf8 core: replace same_location by operator==()
And operator!=() in the negative case.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:57 +02:00
Berthold Stoeger
950578d2f9 core: remove ssrf.h include file
It didn't contain anything.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:57 +02:00
Berthold Stoeger
c3eadff759 cleanup: remove unnecessary Q_UNUSED macros
Also remove the UNUSED() macro, as there were no users left.

The macro was silly anyway - there were many falso positives.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:57 +02:00
Berthold Stoeger
d7b05ffff7 core: remove get_event_mutable()
We can now return mutable/imutable depending on const-ness of
the parameter, owing to parameter overloading.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:57 +02:00
Berthold Stoeger
50d65183e7 core: make event name an std::string
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:57 +02:00
Berthold Stoeger
43dc398f51 core: default initialize units-type objects to 0
Makes the code much nicer to read.

Default initialize cylinder_t to the empty cylinder.

This produces lots of warnings, because most structure are now
not PODs anymore and shouldn't be erased using memset().

These memset()s will be removed one-by-one and replaced by
proper constructors.

The whole ordeal made it necessary to add a constructor to
struct event. To simplify things the whole optimization of
the variable-size event names was removed. In upcoming commits
this will be replaced by std::string anyway.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:57 +02:00
Berthold Stoeger
92d3ae4029 general: remove (void) function parameter declarations
To my understanding, declaring empty parameter lists using "(void)"
is an artifact from the bad old K&R times, when functions were
declared without(!) parameters. Which in hindsight was an absolute
recipe for disaster. So for backwards compatibility, functions
without parameters had to be declared using "(void)" as "()"
could also mean "any function".

That was 40 years ago. Meanwhile, C++ introduced references,
which made it a necessity to declare the function parameters.
So "(void)" is redundant and inconsistent in C++ code and
just makes no sense.

Remove it.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:57 +02:00
Berthold Stoeger
6bdb8537e6 build: remove extern "C" linkage
No more C source files, no more necessity to use C-linkage.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:57 +02:00
Berthold Stoeger
86831aeffe core: remove __cplusplus ifdefs
Since all source files are now C++, this is redundant.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:57 +02:00
Berthold Stoeger
c28f9e7749 core: convert picture.c to C++
The last C-file in core. Yippie.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:57 +02:00
Berthold Stoeger
f65b656f23 core: convert divesite strings to std::string
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:57 +02:00
Berthold Stoeger
4c94dcaac0 core: convert trip.c to C++
Necessary so that we can continue porting the divesite code to C++.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:57 +02:00
Berthold Stoeger
fe41bc06d0 core: fold divesite-helper.cpp into divesite.cpp
The divesite-helper.cpp only existed because C-string manipulation
was too tedious. Now that divesite.cpp is C++ anyway, the split
is not necessary anymore.

Moreover, return an std::string, since this is a core-function.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:57 +02:00
Berthold Stoeger
a8fad49dd6 core: replace dive_site::dives by an std::vector<>
Since this is now in C++, we don't have to use our crazy
TABLE_* macros.

This contains a logic change: the dives associated to a
dive site are now unsorted.

The old code was subtly buggy: dives were added in a sorted
manner, but when the dive was edited the list was not
resorted. Very unlikely that this leads to a serious
problem, still not good.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:57 +02:00
Berthold Stoeger
a1e4cdb580 core: convert taxonomy.c to C++
Since the taxonomy is now a real C++ struct with constructor
and destructor, dive_site has to be converted to C++ as well.

A bit hairy for now, but will ultimately be distinctly simpler.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:56 +02:00
Berthold Stoeger
02fb80113b core: use C++ structures for tanksystem info
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:56 +02:00
Berthold Stoeger
d6796d2588 core: use C++ structures for weightsystem info
Use std::vector<> instead of fixed size array.
Doesn't do any logic change, even though the back-translation
logic is ominous.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:56 +02:00
Berthold Stoeger
51c4b83f55 core: convert equipment.c to C++
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:56 +02:00
Berthold Stoeger
34db7cf0e8 core: convert event.c to C++
No code changes.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:56 +02:00
Berthold Stoeger
d328ce5201 profile: C++-ify plot_info
Use more C++ style memory management for plot_info: Use std::vector
for array data. Return the plot_info instead of filling an output
parameter. Add a constructor/destructor pair so that the caller
isn't bothered with memory management.

The bulk of the commit is replacement of pointers with references,
which is kind of gratuitous. But I started and then went on...

Default initializiation of gas_pressures made it necessary to convert
gas.c to c++, though with minimal changes to the code.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:56 +02:00
Berthold Stoeger
3ff22eb9fb core: convert save-profiledata to C++
Leave the code as is for now. Just replace membuffer by its
C++ version.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:56 +02:00
Berthold Stoeger
54a3ffbff7 core: convert gaspressures.c to C++
Replace "poor man's" linked list implementation by std::vector<>.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:56 +02:00
Berthold Stoeger
c28586ea1e core: convert gas-model.c to C++
A nice one - nothing to do. Introduce an std::clamp(), just
because we can...

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:56 +02:00
Berthold Stoeger
6bf0345c13 core: convert ftdi.c to C++
Replace malloc/free of one structure by C++ idioms and add a
destructor to the struct. Otherwise, don't touch the code.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:56 +02:00
Berthold Stoeger
ca270a1c98 import: free dc_descriptor in new device_data_t destructor
It seems that smartrak was the only part of the code that cared
about freeing the dc_descriptor. Make that a general feature
of the new device_data_t destructor, which we could implement
now that things are in C++.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:56 +02:00
Berthold Stoeger
43491ea180 import: turn C-string in device_data_t into std::strings
It was never clear what was a pointer to a static string from
libdivecomputer and what was allocated.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-10 13:05:56 +02:00
Berthold Stoeger
02fdf6a609 cleanup: don't allocate device_data_t structure
These can all just be local objects.

Also, don't overwrite them with 0. We later want to convert the
string to std::string, where this would be very sketchy.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:38 +02:00
Berthold Stoeger
19a46de3d2 core: convert ostctools as C++
Replace some of the memory management by C++ idioms.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:38 +02:00
Berthold Stoeger
5128d0ed5f core: convert pref.c and units.c to C++
Convert both files simultanously, because the SI_UNITS define works
either under C or under C++.

This was painful, because initialization of struct-members has to
be done in order of definition in C++. And it was completely out
of order. However, as long as not all is C++, we can't use
default initialization directly in the struct definition. :(

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
Berthold Stoeger
a054d81e7c core: replace core/timer.c by std::chrono
No point in reimplementing the wheel.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
Berthold Stoeger
71418b116a core: C++-ify statistics.c
The old code was wild: For the yearly statistics it would allocate
one entry per dive in the log. Of course, it would also leak
C-style strings.

Convert the whole thing to somewhat idiomatic C++.

Somewhat wasted work, because I'd like to convert the whole thing
to the new statistics code. But let's finish the conversion to C++
first.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
Berthold Stoeger
1898036bff core: remove MIN() and MAX() macros
All have been converted to std::min() and std::max().

Moreover, this was Windows only and since we cross-compile, it
is not even clear if this is needed.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
Berthold Stoeger
3754e92d13 core: convert divecomputer.c to C++
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
Berthold Stoeger
7e5babf57f core: replace MIN() by type-sage std::min() in divelist.cpp
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
Berthold Stoeger
0c9de9bd98 core: convert divelist.c to C++
Fortunately, not much to do in this file.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
Berthold Stoeger
1dc31ecab6 uemis: replace a defines by numeric constants
Always good to use the type system of the language instead of
text substitution.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
Berthold Stoeger
cb5e5e77ce uemis: replace UEMIS_CHECK_* defines by enum
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
Berthold Stoeger
4eb054785b uemis: turn UEMIS_MEM_* defines into enum
One value (UEMIS_MEM_CRITICAL) wasn't ever used. Remove it.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
Berthold Stoeger
b884c4262c uemis: replace divespot_mapping by hash-map
This was the umpteenth inefficient reinvention of a trivial
map. Replace by a hash-map (std::unordered_map). Might just
as well use a balanced binary tree or a sorted array. In the
end, it probably doesn't matter at all.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
Berthold Stoeger
68630ffa30 uemis: unglobalize mindiveid
uemis_get_divenr() returns maxdiveid and passes mindiveid as a
global variable.

Make this more reasonable by returning a min, max pair.

The way mindiveid is an unsigned int and then reinterpreted as
int is very sketchy. This commit attempts to not change that
behavior.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
Berthold Stoeger
891e72fce2 uemis: unglobalize next_table_index
This is only initialized and used in one loop. Very mysterious
why this should be a global variable.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
Berthold Stoeger
74ab57feb2 uemis: unglobalize reqtxt_file
This global variable is used in two function independently of
each other. I don't see how there should be transport of this
value from one function to the other. Ominous.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
Berthold Stoeger
c5ba85e4da uemis: unglobalize response buffer
uemis_get_answer() would put the raw response into a global variable.
This could be anywhere in the call stack and thus you never knew
when the existing buffer was removed under your feet.

Instead, return the buffer explicitly from uemis_get_answer().

I'm nit perfectly happy about the new interface: an error is
indicated by an empty buffer, which is awkward to test for.
If an empty buffer turns out to be a valid response, this
should be replaced by an std::optional<> or std::expected<>.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
Berthold Stoeger
54c25c97b8 uemis: replace C-strings by std::string and std::string_view
The string code of uemis-downloader.cpp was broken in more ways
than can be listed here. Notably, it brazenly refused to free any
memory allocated for the parameters buffer.

Using std::string and std::string_view should plug all those
memory holes. That made it necessary to do some major refactoring.

This was done blind and therefore will break.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
Berthold Stoeger
37120003f0 uemis-downloader: use std::string for constructing strings
This extends work started by Richard Fuchs <dfx@dfx.at>

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
Berthold Stoeger
3c0398f9ff uemis-downloader: make newmax an integer variable
newmax was an integer variable kept as a string. Very ominous.

Moreover, memory management seems to be broken:

1) The string is never freed.

2) The string is passed as value from do_uemis_import() to
   get_matching_dives(), which passes it as reference to
   process_raw_buffer(), which may reallocate it, which means
   that do_uemis_import() now possesses a pointer to a free()d
   string.

Simplify all that by making newmax an integer variable and
passing it as a reference from do_uemis_import() to
get_matching_dives().

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
Berthold Stoeger
4a2b0b0fc9 uemis downloader: use report_info() for debugging
There were a number of fprintf()s that escaped the conversion
to report_info(), because they used "debugfile" instead of
"stderr" as target. However, debugfile was just #defined to
be stderr, so we might just use report_info() for consistency.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
Berthold Stoeger
2613e11b81 core: convert uemis.c to C++
The uemis code is wild. It simply doesn't deallocate memory
and uses global variables. To get this under control, create
a "struct uemis" and make the functions exported by "uemis.h"
members of "struct uemis". Thus, we don't have to carry around
a parameter for the state of the importing process.

Turn a linked list of "helper" structures (one per imported dive)
into a std::unordered_map, to fix leaking of the helper structures.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
Berthold Stoeger
f616538f52 uemis: pass dive pointer as dive pointer, not void pointer
This was very obscure: the function that parses into a struct
dive was passed a void-pointer instead of a struct dive-pointer.

Why? Just pass the correct type.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
Berthold Stoeger
8fec9bc49b core: C++-ify SHA1 interface
All callers of the SHA1 code are C++. Might just as well use
a C++ like interface.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
Berthold Stoeger
5835456235 cleanup: fix typo in comment
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
Berthold Stoeger
64ca63cb91 core: convert divesite.c to C++
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
Berthold Stoeger
37df7e8564 core: replace SHA1() function by SHA1_uint32()
The SHA1() helper function was only used when calculating a
SHA1 hash and taking the first four bytes of it as uint32.

Make that explicit by renaming the function into SHA1_uint32()
and directly returning an uint32_t.

Note that the usage in cochran.cpp is sketchy: it generates
a four-byte hash out of two-byte data. Why!?

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
Berthold Stoeger
25ecc3f45a core: convert version.c to C++
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
Berthold Stoeger
1f4def49aa core: convert strtod.c to C++
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
Berthold Stoeger
aaa4effa26 core: simplify custom strtod() interface
The strtod_flags() function allowed for fine control of how to
parse strings. However, only two different modes were actually
used: ascii mode ("C" locale) and permissive mode (accept ","
and "." as decimal separator).

The former had already its own function name (ascii_strtod).
Make the latter a separatge function as well (permissive_strtod)
and remove all the flags rigmarole.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
Berthold Stoeger
5c95ad6249 parser: remove int_or_float union & other small float-parsing cleanups
Owing to bitrot, this union only contained a float and therefore
is pointless. Let's remove it.

That makes the function name "integer_or_float()" non-sensical.
Call it "parse_float()" instead.

Moreover, change the output-arguments of "parse_float()" from
pointers to references, as null-pointers are not supported.

Finally, remove the "errno" check after "ascii_strtod()". As far as
I can tell, errno is not set in "ascii_strtod()" and using a global
variable for error-reporting it is an incredibly silly interface
anyway.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-06-09 11:20:37 +02:00
278 changed files with 10445 additions and 13744 deletions

View File

@ -10,7 +10,7 @@ ColumnLimit: 0
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 8
ContinuationIndentWidth: 8
ForEachMacros: [ 'for_each_dc', 'for_each_relevant_dc', 'for_each_dive', 'for_each_line' ]
ForEachMacros: [ 'for_each_line' ]
IndentFunctionDeclarationAfterType: false #personal taste, good for long methods
IndentWidth: 8
MaxEmptyLinesToKeep: 2

View File

@ -401,7 +401,6 @@ close to our coding standards.
filetype plugin indent on
filetype detect
set cindent tabstop=8 shiftwidth=8 cinoptions=l1,:0,(0,g0
" TODO: extern "C" gets indented
" And some sane defaults, optional, but quite nice
set nocompatible

View File

@ -45,28 +45,28 @@ SOURCES += subsurface-mobile-main.cpp \
core/fulltext.cpp \
core/subsurfacestartup.cpp \
core/subsurface-string.cpp \
core/pref.c \
core/pref.cpp \
core/profile.cpp \
core/device.cpp \
core/dive.cpp \
core/divecomputer.c \
core/divecomputer.cpp \
core/divefilter.cpp \
core/event.c \
core/event.cpp \
core/eventtype.cpp \
core/filterconstraint.cpp \
core/filterpreset.cpp \
core/divelist.c \
core/filterpresettable.cpp \
core/divelist.cpp \
core/divelog.cpp \
core/gas-model.c \
core/gaspressures.c \
core/gas-model.cpp \
core/gaspressures.cpp \
core/git-access.cpp \
core/globals.cpp \
core/liquivision.cpp \
core/load-git.cpp \
core/parse-xml.cpp \
core/parse.cpp \
core/picture.c \
core/pictureobj.cpp \
core/picture.cpp \
core/sample.cpp \
core/import-suunto.cpp \
core/import-shearwater.cpp \
@ -75,37 +75,38 @@ SOURCES += subsurface-mobile-main.cpp \
core/import-divinglog.cpp \
core/import-csv.cpp \
core/save-html.cpp \
core/statistics.c \
core/statistics.cpp \
core/worldmap-save.cpp \
core/libdivecomputer.cpp \
core/version.c \
core/version.cpp \
core/save-git.cpp \
core/datatrak.cpp \
core/ostctools.c \
core/ostctools.cpp \
core/planner.cpp \
core/save-xml.cpp \
core/cochran.cpp \
core/deco.cpp \
core/divesite.c \
core/equipment.c \
core/gas.c \
core/divesite.cpp \
core/equipment.cpp \
core/gas.cpp \
core/membuffer.cpp \
core/selection.cpp \
core/sha1.c \
core/sha1.cpp \
core/string-format.cpp \
core/strtod.c \
core/strtod.cpp \
core/tag.cpp \
core/taxonomy.c \
core/taxonomy.cpp \
core/time.cpp \
core/trip.c \
core/units.c \
core/uemis.c \
core/trip.cpp \
core/triptable.cpp \
core/units.cpp \
core/uemis.cpp \
core/btdiscovery.cpp \
core/connectionlistmodel.cpp \
core/qt-ble.cpp \
core/uploadDiveShare.cpp \
core/uploadDiveLogsDE.cpp \
core/save-profiledata.c \
core/save-profiledata.cpp \
core/xmlparams.cpp \
core/settings/qPref.cpp \
core/settings/qPrefCloudStorage.cpp \
@ -207,7 +208,6 @@ HEADERS += \
core/extradata.h \
core/git-access.h \
core/globals.h \
core/owning_ptrs.h \
core/pref.h \
core/profile.h \
core/qthelper.h \
@ -217,9 +217,9 @@ HEADERS += \
core/units.h \
core/version.h \
core/picture.h \
core/pictureobj.h \
core/planner.h \
core/divesite.h \
core/divesitetable.h \
core/checkcloudconnection.h \
core/cochran.h \
core/color.h \
@ -229,6 +229,7 @@ HEADERS += \
core/divefilter.h \
core/filterconstraint.h \
core/filterpreset.h \
core/filterpresettable.h \
core/divelist.h \
core/divelog.h \
core/divelogexportlogic.h \
@ -249,6 +250,8 @@ HEADERS += \
core/subsurfacestartup.h \
core/subsurfacesysinfo.h \
core/taxonomy.h \
core/trip.h \
core/triptable.h \
core/uemis.h \
core/webservice.h \
core/windowtitleupdate.h \

View File

@ -13,6 +13,7 @@
#include "core/divesite.h"
#include "core/picture.h"
#include "core/pref.h"
#include "core/range.h"
#include "core/sample.h"
#include "core/selection.h"
#include "core/taxonomy.h"
@ -40,12 +41,12 @@ static constexpr int profileScale = 4;
static constexpr int profileWidth = 800 * profileScale;
static constexpr int profileHeight = 600 * profileScale;
static void exportProfile(ProfileScene *profile, const struct dive *dive, const QString &filename)
static void exportProfile(ProfileScene &profile, const struct dive &dive, const QString &filename)
{
QImage image = QImage(QSize(profileWidth, profileHeight), QImage::Format_RGB32);
QPainter paint;
paint.begin(&image);
profile->draw(&paint, QRect(0, 0, profileWidth, profileHeight), dive, 0, nullptr, false);
profile.draw(&paint, QRect(0, 0, profileWidth, profileHeight), &dive, 0, nullptr, false);
image.save(filename);
}
@ -56,17 +57,15 @@ static std::unique_ptr<ProfileScene> getPrintProfile()
void exportProfile(QString filename, bool selected_only, ExportCallback &cb)
{
struct dive *dive;
int i;
int count = 0;
if (!filename.endsWith(".png", Qt::CaseInsensitive))
filename = filename.append(".png");
QFileInfo fi(filename);
int todo = selected_only ? amount_selected : divelog.dives->nr;
int todo = selected_only ? amount_selected : static_cast<int>(divelog.dives.size());
int done = 0;
auto profile = getPrintProfile();
for_each_dive (i, dive) {
for (auto &dive: divelog.dives) {
if (cb.canceled())
return;
if (selected_only && !dive->selected)
@ -74,7 +73,7 @@ void exportProfile(QString filename, bool selected_only, ExportCallback &cb)
cb.setProgress(done++ * 1000 / todo);
QString fn = count ? fi.path() + QDir::separator() + fi.completeBaseName().append(QString("-%1.").arg(count)) + fi.suffix()
: filename;
exportProfile(profile.get(), dive, fn);
exportProfile(*profile, *dive, fn);
++count;
}
}
@ -83,14 +82,12 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall
{
FILE *f;
QDir texdir = QFileInfo(filename).dir();
struct dive *dive;
const struct units *units = get_units();
const char *unit;
const char *ssrf;
int i;
bool need_pagebreak = false;
struct membufferpp buf;
membuffer buf;
if (plain) {
ssrf = "";
@ -132,34 +129,29 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall
put_format(&buf, "\n%%%%%%%%%% Begin Dive Data: %%%%%%%%%%\n");
int todo = selected_only ? amount_selected : divelog.dives->nr;
int todo = selected_only ? amount_selected : static_cast<int>(divelog.dives.size());
int done = 0;
auto profile = getPrintProfile();
for_each_dive (i, dive) {
for (auto &dive: divelog.dives) {
if (cb.canceled())
return;
if (selected_only && !dive->selected)
continue;
cb.setProgress(done++ * 1000 / todo);
exportProfile(profile.get(), dive, texdir.filePath(QString("profile%1.png").arg(dive->number)));
exportProfile(*profile, *dive, texdir.filePath(QString("profile%1.png").arg(dive->number)));
struct tm tm;
utc_mkdate(dive->when, &tm);
const char *country = NULL;
std::string country;
dive_site *site = dive->dive_site;
if (site)
country = taxonomy_get_country(&site->taxonomy);
pressure_t delta_p = {.mbar = 0};
country = taxonomy_get_country(site->taxonomy);
pressure_t delta_p;
QString star = "*";
QString viz = star.repeated(dive->visibility);
QString rating = star.repeated(dive->rating);
int i;
int qty_cyl;
int qty_weight;
double total_weight;
if (need_pagebreak) {
if (plain)
put_format(&buf, "\\vfill\\eject\n");
@ -174,13 +166,13 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall
put_format(&buf, "\\def\\%shour{%02u}\n", ssrf, tm.tm_hour);
put_format(&buf, "\\def\\%sminute{%02u}\n", ssrf, tm.tm_min);
put_format(&buf, "\\def\\%snumber{%d}\n", ssrf, dive->number);
put_format(&buf, "\\def\\%splace{%s}\n", ssrf, site ? site->name : "");
put_format(&buf, "\\def\\%splace{%s}\n", ssrf, site ? site->name.c_str() : "");
put_format(&buf, "\\def\\%sspot{}\n", ssrf);
put_format(&buf, "\\def\\%ssitename{%s}\n", ssrf, site ? site->name : "");
put_format(&buf, "\\def\\%ssitename{%s}\n", ssrf, site ? site->name.c_str() : "");
site ? put_format(&buf, "\\def\\%sgpslat{%f}\n", ssrf, site->location.lat.udeg / 1000000.0) : put_format(&buf, "\\def\\%sgpslat{}\n", ssrf);
site ? put_format(&buf, "\\def\\%sgpslon{%f}\n", ssrf, site->location.lon.udeg / 1000000.0) : put_format(&buf, "\\def\\gpslon{}\n");
put_format(&buf, "\\def\\%scomputer{%s}\n", ssrf, dive->dc.model);
put_format(&buf, "\\def\\%scountry{%s}\n", ssrf, country ?: "");
put_format(&buf, "\\def\\%scomputer{%s}\n", ssrf, dive->dcs[0].model.c_str());
put_format(&buf, "\\def\\%scountry{%s}\n", ssrf, country.c_str());
put_format(&buf, "\\def\\%stime{%u:%02u}\n", ssrf, FRACTION_TUPLE(dive->duration.seconds, 60));
put_format(&buf, "\n%% Dive Profile Details:\n");
@ -191,24 +183,23 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall
dive->maxdepth.mm ? put_format(&buf, "\\def\\%smaximumdepth{%.1f\\%sdepthunit}\n", ssrf, get_depth_units(dive->maxdepth.mm, NULL, &unit), ssrf) : put_format(&buf, "\\def\\%smaximumdepth{}\n", ssrf);
dive->meandepth.mm ? put_format(&buf, "\\def\\%smeandepth{%.1f\\%sdepthunit}\n", ssrf, get_depth_units(dive->meandepth.mm, NULL, &unit), ssrf) : put_format(&buf, "\\def\\%smeandepth{}\n", ssrf);
std::string tags = taglist_get_tagstring(dive->tag_list);
std::string tags = taglist_get_tagstring(dive->tags);
put_format(&buf, "\\def\\%stype{%s}\n", ssrf, tags.c_str());
put_format(&buf, "\\def\\%sviz{%s}\n", ssrf, qPrintable(viz));
put_format(&buf, "\\def\\%srating{%s}\n", ssrf, qPrintable(rating));
put_format(&buf, "\\def\\%splot{\\includegraphics[width=9cm,height=4cm]{profile%d}}\n", ssrf, dive->number);
put_format(&buf, "\\def\\%sprofilename{profile%d}\n", ssrf, dive->number);
put_format(&buf, "\\def\\%scomment{%s}\n", ssrf, dive->notes ? dive->notes : "");
put_format(&buf, "\\def\\%sbuddy{%s}\n", ssrf, dive->buddy ? dive->buddy : "");
put_format(&buf, "\\def\\%sdivemaster{%s}\n", ssrf, dive->diveguide ? dive->diveguide : "");
put_format(&buf, "\\def\\%ssuit{%s}\n", ssrf, dive->suit ? dive->suit : "");
put_format(&buf, "\\def\\%scomment{%s}\n", ssrf, dive->notes.c_str());
put_format(&buf, "\\def\\%sbuddy{%s}\n", ssrf, dive->buddy.c_str());
put_format(&buf, "\\def\\%sdivemaster{%s}\n", ssrf, dive->diveguide.c_str());
put_format(&buf, "\\def\\%ssuit{%s}\n", ssrf, dive->suit.c_str());
// Print cylinder data
put_format(&buf, "\n%% Gas use information:\n");
qty_cyl = 0;
for (i = 0; i < dive->cylinders.nr; i++){
const cylinder_t &cyl = *get_cylinder(dive, i);
if (is_cylinder_used(dive, i) || (prefs.include_unused_tanks && cyl.type.description)){
put_format(&buf, "\\def\\%scyl%cdescription{%s}\n", ssrf, 'a' + i, cyl.type.description);
int qty_cyl = 0;
for (auto [i, cyl]: enumerated_range(dive->cylinders)) {
if (is_cylinder_used(dive.get(), i) || (prefs.include_unused_tanks && !cyl.type.description.empty())){
put_format(&buf, "\\def\\%scyl%cdescription{%s}\n", ssrf, 'a' + i, cyl.type.description.c_str());
put_format(&buf, "\\def\\%scyl%cgasname{%s}\n", ssrf, 'a' + i, gasname(cyl.gasmix));
put_format(&buf, "\\def\\%scyl%cmixO2{%.1f\\%%}\n", ssrf, 'a' + i, get_o2(cyl.gasmix)/10.0);
put_format(&buf, "\\def\\%scyl%cmixHe{%.1f\\%%}\n", ssrf, 'a' + i, get_he(cyl.gasmix)/10.0);
@ -235,11 +226,10 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall
//Code block prints all weights listed in dive.
put_format(&buf, "\n%% Weighting information:\n");
qty_weight = 0;
total_weight = 0;
for (i = 0; i < dive->weightsystems.nr; i++) {
weightsystem_t w = dive->weightsystems.weightsystems[i];
put_format(&buf, "\\def\\%sweight%ctype{%s}\n", ssrf, 'a' + i, w.description);
int qty_weight = 0;
double total_weight = 0;
for (auto [i, w]: enumerated_range(dive->weightsystems)) {
put_format(&buf, "\\def\\%sweight%ctype{%s}\n", ssrf, 'a' + i, w.description.c_str());
put_format(&buf, "\\def\\%sweight%camt{%.3f\\%sweightunit}\n", ssrf, 'a' + i, get_weight_units(w.weight.grams, NULL, &unit), ssrf);
qty_weight += 1;
total_weight += get_weight_units(w.weight.grams, NULL, &unit);
@ -251,7 +241,7 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall
// Legacy fields
put_format(&buf, "\\def\\%sspot{}\n", ssrf);
put_format(&buf, "\\def\\%sentrance{}\n", ssrf);
put_format(&buf, "\\def\\%splace{%s}\n", ssrf, site ? site->name : "");
put_format(&buf, "\\def\\%splace{%s}\n", ssrf, site ? site->name.c_str() : "");
dive->maxdepth.mm ? put_format(&buf, "\\def\\%sdepth{%.1f\\%sdepthunit}\n", ssrf, get_depth_units(dive->maxdepth.mm, NULL, &unit), ssrf) : put_format(&buf, "\\def\\%sdepth{}\n", ssrf);
put_format(&buf, "\\%spage\n", ssrf);
@ -275,26 +265,22 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall
void export_depths(const char *filename, bool selected_only)
{
FILE *f;
struct dive *dive;
depth_t depth;
int i;
const char *unit = NULL;
struct membufferpp buf;
membuffer buf;
for_each_dive (i, dive) {
for (auto &dive: divelog.dives) {
if (selected_only && !dive->selected)
continue;
FOR_EACH_PICTURE (dive) {
int n = dive->dc.samples;
struct sample *s = dive->dc.sample;
depth.mm = 0;
while (--n >= 0 && (int32_t)s->time.seconds <= picture->offset.seconds) {
depth.mm = s->depth.mm;
s++;
for (auto &picture: dive->pictures) {
depth_t depth;
for (auto &s: dive->dcs[0].samples) {
if ((int32_t)s.time.seconds > picture.offset.seconds)
break;
depth = s.depth;
}
put_format(&buf, "%s\t%.1f", picture->filename, get_depth_units(depth.mm, NULL, &unit));
put_format(&buf, "%s\t%.1f", picture.filename.c_str(), get_depth_units(depth.mm, NULL, &unit));
put_format(&buf, "%s\n", unit);
}
}
@ -318,28 +304,23 @@ std::vector<const dive_site *> getDiveSitesToExport(bool selectedOnly)
if (selectedOnly && DiveFilter::instance()->diveSiteMode()) {
// Special case in dive site mode: export all selected dive sites,
// not the dive sites of selected dives.
QVector<dive_site *> sites = DiveFilter::instance()->filteredDiveSites();
res.reserve(sites.size());
for (const dive_site *ds: sites)
for (auto ds: DiveFilter::instance()->filteredDiveSites())
res.push_back(ds);
return res;
}
res.reserve(divelog.sites->nr);
for (int i = 0; i < divelog.sites->nr; i++) {
struct dive_site *ds = get_dive_site(i, divelog.sites);
if (dive_site_is_empty(ds))
res.reserve(divelog.sites.size());
for (const auto &ds: divelog.sites) {
if (ds->is_empty())
continue;
if (selectedOnly && !is_dive_site_selected(ds))
if (selectedOnly && !ds->is_selected())
continue;
res.push_back(ds);
res.push_back(ds.get());
}
#else
/* walk the dive site list */
int i;
const struct dive_site *ds;
for_each_dive_site (i, ds, divelog.sites)
res.push_back(get_dive_site(i, divelog.sites));
for (const auto &ds: divelog.sites)
res.push_back(ds.get());
#endif
return res;
}

View File

@ -134,9 +134,9 @@ void addDiveSite(const QString &name)
execute(new AddDiveSite(name));
}
void importDiveSites(struct dive_site_table *sites, const QString &source)
void importDiveSites(dive_site_table sites, const QString &source)
{
execute(new ImportDiveSites(sites, source));
execute(new ImportDiveSites(std::move(sites), source));
}
void mergeDiveSites(dive_site *ds, const QVector<dive_site *> &sites)
@ -352,14 +352,14 @@ void addEventSetpointChange(struct dive *d, int dcNr, int seconds, pressure_t pO
execute(new AddEventSetpointChange(d, dcNr, seconds, pO2));
}
void renameEvent(struct dive *d, int dcNr, struct event *ev, const char *name)
void renameEvent(struct dive *d, int dcNr, int idx, const std::string name)
{
execute(new RenameEvent(d, dcNr, ev, name));
execute(new RenameEvent(d, dcNr, idx, std::move(name)));
}
void removeEvent(struct dive *d, int dcNr, struct event *ev)
void removeEvent(struct dive *d, int dcNr, int idx)
{
execute(new RemoveEvent(d, dcNr, ev));
execute(new RemoveEvent(d, dcNr, idx));
}
void addGasSwitch(struct dive *d, int dcNr, int seconds, int tank)

View File

@ -4,7 +4,7 @@
#include "core/divelog.h"
#include "core/equipment.h"
#include "core/pictureobj.h"
#include "core/picture.h"
#include "core/taxonomy.h"
#include <QVector>
#include <QAction>
@ -68,7 +68,7 @@ void editDiveSiteCountry(dive_site *ds, const QString &value);
void editDiveSiteLocation(dive_site *ds, location_t value);
void editDiveSiteTaxonomy(dive_site *ds, taxonomy_data &value); // value is consumed (i.e. will be erased after call)!
void addDiveSite(const QString &name);
void importDiveSites(struct dive_site_table *sites, const QString &source);
void importDiveSites(dive_site_table sites, const QString &source); // takes ownership of dive site table
void mergeDiveSites(dive_site *ds, const QVector<dive_site *> &sites);
void purgeUnusedDiveSites();
@ -132,8 +132,8 @@ void editTripNotes(dive_trip *trip, const QString &s);
void addEventBookmark(struct dive *d, int dcNr, int seconds);
void addEventDivemodeSwitch(struct dive *d, int dcNr, int seconds, int divemode);
void addEventSetpointChange(struct dive *d, int dcNr, int seconds, pressure_t pO2);
void renameEvent(struct dive *d, int dcNr, struct event *ev, const char *name);
void removeEvent(struct dive *d, int dcNr, struct event *ev);
void renameEvent(struct dive *d, int dcNr, int idx, std::string name);
void removeEvent(struct dive *d, int dcNr, int idx);
void addGasSwitch(struct dive *d, int dcNr, int seconds, int tank);
// 7) Picture (media) commands
@ -144,7 +144,7 @@ struct PictureListForDeletion {
};
struct PictureListForAddition {
dive *d;
std::vector<PictureObj> pics;
std::vector<picture> pics;
};
void setPictureOffset(dive *d, const QString &filename, offset_t offset);
void removePictures(const std::vector<PictureListForDeletion> &pictures);

View File

@ -66,7 +66,7 @@ QString diveNumberOrDate(struct dive *d)
QString getListOfDives(const std::vector<struct dive*> &dives)
{
QString listOfDives;
if ((int)dives.size() == divelog.dives->nr)
if (dives.size() == divelog.dives.size())
return Base::tr("all dives");
int i = 0;
for (dive *d: dives) {

View File

@ -7,7 +7,6 @@
#include "core/divesite.h"
#include "core/trip.h"
#include "core/dive.h"
#include "core/owning_ptrs.h"
#include <QUndoCommand>
#include <QCoreApplication> // For Q_DECLARE_TR_FUNCTIONS
@ -106,9 +105,8 @@
// 1) Dive 2 was deleted with the "add dive 2" command, because that was the owner.
// 2) Dive 1 was not deleted, because it is owned by the backend.
//
// To take ownership of dives/trips, the OnwingDivePtr and OwningTripPtr types are used. These
// are simply derived from std::unique_ptr and therefore use well-established semantics.
// Expressed in C-terms: std::unique_ptr<T> is exactly the same as T* with the following
// To take ownership of dives/trips, std::unique_ptr<>s are used.
// Expressed in C-terms: std::unique_ptr<T> is the same as T* with the following
// twists:
// 1) default-initialized to NULL.
// 2) if it goes out of scope (local scope or containing object destroyed), it does:
@ -122,8 +120,8 @@
// move-semantics and Qt's containers are incompatible, owing to COW semantics.
//
// Usage:
// OwningDivePtr dPtr; // Initialize to null-state: not owning any dive.
// OwningDivePtr dPtr(dive); // Take ownership of dive (which is of type struct dive *).
// std::unique_ptr<dive> dPtr; // Initialize to null-state: not owning any dive.
// std::unique_ptr<dive> dPtr(dive); // Take ownership of dive (which is of type struct dive *).
// // If dPtr goes out of scope, the dive will be freed with free_dive().
// struct dive *d = dPtr.release(); // Give up ownership of dive. dPtr is reset to null.
// struct dive *d = d.get(); // Get pointer dive, but don't release ownership.
@ -131,10 +129,10 @@
// dPtr.reset(); // Delete currently owned dive and reset to null.
// dPtr2 = dPtr1; // Fails to compile.
// dPtr2 = std::move(dPtr1); // dPtr2 takes ownership, dPtr1 is reset to null.
// OwningDivePtr fun();
// std::unique_ptr<dive> fun();
// dPtr1 = fun(); // Compiles. Simply put: the compiler knows that the result of fun() will
// // be trashed and therefore can be moved-from.
// std::vector<OwningDivePtr> v: // Define an empty vector of owning pointers.
// std::vector<std::unique_ptr<dive>> v: // Define an empty vector of owning pointers.
// v.emplace_back(dive); // Take ownership of dive and add at end of vector
// // If the vector goes out of scope, all dives will be freed with free_dive().
// v.clear(v); // Reset the vector to zero length. If the elements weren't release()d,

View File

@ -13,20 +13,19 @@ EditDeviceNickname::EditDeviceNickname(const struct divecomputer *dc, const QStr
if (index == -1)
return;
setText(Command::Base::tr("Set nickname of device %1 (serial %2) to %3").arg(dc->model, dc->serial, nicknameIn));
setText(Command::Base::tr("Set nickname of device %1 (serial %2) to %3").arg(dc->model.c_str(), dc->serial.c_str(), nicknameIn));
}
bool EditDeviceNickname::workToBeDone()
{
return get_device(divelog.devices, index) != nullptr;
return index >= 0;
}
void EditDeviceNickname::redo()
{
device *dev = get_device_mutable(divelog.devices, index);
if (!dev)
if (index < 0 || static_cast<size_t>(index) >= divelog.devices.size())
return;
std::swap(dev->nickName, nickname);
std::swap(divelog.devices[index].nickName, nickname);
emit diveListNotifier.deviceEdited();
}

View File

@ -9,23 +9,22 @@
#include "qt-models/filtermodels.h"
#include "core/divefilter.h"
#include <array>
namespace Command {
// Helper function that takes care to unselect trips that are removed from the backend
static void remove_trip_from_backend(dive_trip *trip)
static std::unique_ptr<dive_trip> remove_trip_from_backend(dive_trip *trip)
{
if (trip->selected)
deselect_trip(trip);
remove_trip(trip, divelog.trips); // Remove trip from backend
auto [t, idx] = divelog.trips.pull(trip);
return std::move(t);
}
// This helper function removes a dive, takes ownership of the dive and adds it to a DiveToAdd structure.
// If the trip the dive belongs to becomes empty, it is removed and added to the tripsToAdd vector.
// It is crucial that dives are added in reverse order of deletion, so that the indices are correctly
// set and that the trips are added before they are used!
DiveToAdd DiveListBase::removeDive(struct dive *d, std::vector<OwningTripPtr> &tripsToAdd)
DiveToAdd DiveListBase::removeDive(struct dive *d, std::vector<std::unique_ptr<dive_trip>> &tripsToAdd)
{
// If the dive was the current dive, reset the current dive. The calling
// command is responsible of finding a new dive.
@ -39,18 +38,19 @@ DiveToAdd DiveListBase::removeDive(struct dive *d, std::vector<OwningTripPtr> &t
if (d->dive_site)
diveSiteCountChanged(d->dive_site);
res.site = unregister_dive_from_dive_site(d);
if (res.trip && res.trip->dives.nr == 0) {
remove_trip_from_backend(res.trip); // Remove trip from backend
tripsToAdd.emplace_back(res.trip); // Take ownership of trip
if (res.trip && res.trip->dives.empty()) {
divelog.trips.sort(); // Removal of dives has changed order of trips! (TODO: remove this)
auto trip = remove_trip_from_backend(res.trip); // Remove trip from backend
tripsToAdd.push_back(std::move(trip)); // Take ownership of trip
}
int idx = get_divenr(d);
if (idx < 0)
size_t idx = divelog.dives.get_idx(d);
if (idx == std::string::npos)
qWarning("Deletion of unknown dive!");
DiveFilter::instance()->diveRemoved(d);
res.dive.reset(unregister_dive(idx)); // Remove dive from backend
res.dive = std::move(divelog.dives.unregister_dive(idx)); // Remove dive from backend
return res;
}
@ -67,23 +67,12 @@ void DiveListBase::diveSiteCountChanged(struct dive_site *ds)
dive *DiveListBase::addDive(DiveToAdd &d)
{
if (d.trip)
add_dive_to_trip(d.dive.get(), d.trip);
d.trip->add_dive(d.dive.get());
if (d.site) {
add_dive_to_dive_site(d.dive.get(), d.site);
d.site->add_dive(d.dive.get());
diveSiteCountChanged(d.site);
}
dive *res = d.dive.release(); // Give up ownership of dive
// When we add dives, we start in hidden-by-filter status. Once all
// dives have been added, their status will be updated.
res->hidden_by_filter = true;
int idx = dive_table_get_insertion_index(divelog.dives, res);
fulltext_register(res); // Register the dive's fulltext cache
add_to_dive_table(divelog.dives, idx, res); // Return ownership to backend
invalidate_dive_cache(res); // Ensure that dive is written in git_save()
return res;
return register_dive(std::move(d.dive)); // Transfer ownership to core and update fulltext index
}
// Some signals are sent in batches per trip. To avoid writing the same loop
@ -99,7 +88,7 @@ void processByTrip(std::vector<std::pair<dive_trip *, dive *>> &dives, Function
// Sort lexicographically by trip then according to the dive_less_than() function.
std::sort(dives.begin(), dives.end(),
[](const std::pair<dive_trip *, dive *> &e1, const std::pair<dive_trip *, dive *> &e2)
{ return e1.first == e2.first ? dive_less_than(e1.second, e2.second) : e1.first < e2.first; });
{ return e1.first == e2.first ? dive_less_than(*e1.second, *e2.second) : e1.first < e2.first; });
// Then, process the dives in batches by trip
size_t i, j; // Begin and end of batch
@ -124,8 +113,8 @@ void processByTrip(std::vector<std::pair<dive_trip *, dive *>> &dives, Function
DivesAndTripsToAdd DiveListBase::removeDives(DivesAndSitesToRemove &divesAndSitesToDelete)
{
std::vector<DiveToAdd> divesToAdd;
std::vector<OwningTripPtr> tripsToAdd;
std::vector<OwningDiveSitePtr> sitesToAdd;
std::vector<std::unique_ptr<dive_trip>> tripsToAdd;
std::vector<std::unique_ptr<dive_site>> sitesToAdd;
divesToAdd.reserve(divesAndSitesToDelete.dives.size());
sitesToAdd.reserve(divesAndSitesToDelete.sites.size());
@ -135,16 +124,17 @@ DivesAndTripsToAdd DiveListBase::removeDives(DivesAndSitesToRemove &divesAndSite
// Make sure that the dive list is sorted. The added dives will be sent in a signal
// and the recipients assume that the dives are sorted the same way as they are
// in the core list.
std::sort(divesAndSitesToDelete.dives.begin(), divesAndSitesToDelete.dives.end(), dive_less_than);
std::sort(divesAndSitesToDelete.dives.begin(), divesAndSitesToDelete.dives.end(),
[](const dive *d1, const dive *d2) { return dive_less_than(*d1, *d2); });
for (dive *d: divesAndSitesToDelete.dives)
divesToAdd.push_back(removeDive(d, tripsToAdd));
divesAndSitesToDelete.dives.clear();
for (dive_site *ds: divesAndSitesToDelete.sites) {
int idx = unregister_dive_site(ds);
sitesToAdd.emplace_back(ds);
emit diveListNotifier.diveSiteDeleted(ds, idx);
auto res = divelog.sites.pull(ds);
sitesToAdd.push_back(std::move(res.ptr));
emit diveListNotifier.diveSiteDeleted(ds, res.idx);
}
divesAndSitesToDelete.sites.clear();
@ -159,7 +149,7 @@ DivesAndTripsToAdd DiveListBase::removeDives(DivesAndSitesToRemove &divesAndSite
processByTrip(dives, [&](dive_trip *trip, const QVector<dive *> &divesInTrip) {
// Check if this trip is supposed to be deleted, by checking if it was marked as "add it".
bool deleteTrip = trip &&
std::find_if(tripsToAdd.begin(), tripsToAdd.end(), [trip](const OwningTripPtr &ptr)
std::find_if(tripsToAdd.begin(), tripsToAdd.end(), [trip](const std::unique_ptr<dive_trip> &ptr)
{ return ptr.get() == trip; }) != tripsToAdd.end();
emit diveListNotifier.divesDeleted(trip, deleteTrip, divesInTrip);
});
@ -188,7 +178,7 @@ DivesAndSitesToRemove DiveListBase::addDives(DivesAndTripsToAdd &toAdd)
// in the core list.
std::sort(toAdd.dives.begin(), toAdd.dives.end(),
[](const DiveToAdd &d, const DiveToAdd &d2)
{ return dive_less_than(d.dive.get(), d2.dive.get()); });
{ return dive_less_than(*d.dive, *d2.dive); });
// Now, add the dives
// Note: the idiomatic STL-way would be std::transform, but let's use a loop since
@ -209,17 +199,17 @@ DivesAndSitesToRemove DiveListBase::addDives(DivesAndTripsToAdd &toAdd)
// Remember the pointers so that we can later check if a trip was newly added
std::vector<dive_trip *> addedTrips;
addedTrips.reserve(toAdd.trips.size());
for (OwningTripPtr &trip: toAdd.trips) {
addedTrips.push_back(trip.get());
insert_trip(trip.release(), divelog.trips); // Return ownership to backend
for (std::unique_ptr<dive_trip> &trip: toAdd.trips) {
auto [t, idx] = divelog.trips.put(std::move(trip)); // Return ownership to backend
addedTrips.push_back(t);
}
toAdd.trips.clear();
// Finally, add any necessary dive sites
for (OwningDiveSitePtr &ds: toAdd.sites) {
sites.push_back(ds.get());
int idx = register_dive_site(ds.release()); // Return ownership to backend
emit diveListNotifier.diveSiteAdded(sites.back(), idx);
for (std::unique_ptr<dive_site> &ds: toAdd.sites) {
auto res = divelog.sites.register_site(std::move(ds));
sites.push_back(res.ptr);
emit diveListNotifier.diveSiteAdded(sites.back(), res.idx);
}
toAdd.sites.clear();
@ -260,26 +250,24 @@ static void renumberDives(QVector<QPair<dive *, int>> &divesToRenumber)
// passed-in structure. This means that calling the function twice on the same
// object is a no-op concerning the dive. If the old trip was deleted from the
// core, an owning pointer to the removed trip is returned, otherwise a null pointer.
static OwningTripPtr moveDiveToTrip(DiveToTrip &diveToTrip)
static std::unique_ptr<dive_trip> moveDiveToTrip(DiveToTrip &diveToTrip)
{
// Firstly, check if we move to the same trip and bail if this is a no-op.
if (diveToTrip.trip == diveToTrip.dive->divetrip)
return {};
// Remove from old trip
OwningTripPtr res;
std::unique_ptr<dive_trip> res;
// Remove dive from trip - if this is the last dive in the trip, remove the whole trip.
dive_trip *trip = unregister_dive_from_trip(diveToTrip.dive);
if (trip && trip->dives.nr == 0) {
remove_trip_from_backend(trip); // Remove trip from backend
res.reset(trip);
}
if (trip && trip->dives.empty())
res = remove_trip_from_backend(trip); // Remove trip from backend
// Store old trip and get new trip we should associate this dive with
std::swap(trip, diveToTrip.trip);
if (trip)
add_dive_to_trip(diveToTrip.dive, trip);
trip->add_dive(diveToTrip.dive);
invalidate_dive_cache(diveToTrip.dive); // Ensure that dive is written in git_save()
return res;
}
@ -298,15 +286,14 @@ static void moveDivesBetweenTrips(DivesToTrip &dives)
createdTrips.reserve(dives.tripsToAdd.size());
// First, bring back the trip(s)
for (OwningTripPtr &trip: dives.tripsToAdd) {
dive_trip *t = trip.release(); // Give up ownership
for (std::unique_ptr<dive_trip> &trip: dives.tripsToAdd) {
auto [t, idx] = divelog.trips.put(std::move(trip)); // Return ownership to backend
createdTrips.push_back(t);
insert_trip(t, divelog.trips); // Return ownership to backend
}
dives.tripsToAdd.clear();
for (DiveToTrip &dive: dives.divesToMove) {
OwningTripPtr tripToAdd = moveDiveToTrip(dive);
std::unique_ptr<dive_trip> tripToAdd = moveDiveToTrip(dive);
// register trips that we'll have to readd
if (tripToAdd)
dives.tripsToAdd.push_back(std::move(tripToAdd));
@ -350,7 +337,7 @@ static void moveDivesBetweenTrips(DivesToTrip &dives)
std::find_if(divesMoved.begin() + j, divesMoved.end(), // Is this the last occurence of "from"?
[from](const DiveMoved &entry) { return entry.from == from; }) == divesMoved.end() &&
std::find_if(dives.tripsToAdd.begin(), dives.tripsToAdd.end(), // Is "from" in tripsToAdd?
[from](const OwningTripPtr &trip) { return trip.get() == from; }) != dives.tripsToAdd.end();
[from](const std::unique_ptr<dive_trip> &trip) { return trip.get() == from; }) != dives.tripsToAdd.end();
// Check if the to-trip has to be created. For this purpose, we saved an array of trips to be created.
bool createTo = false;
if (to) {
@ -404,20 +391,20 @@ AddDive::AddDive(dive *d, bool autogroup, bool newNumber)
setText(Command::Base::tr("add dive"));
// By convention, d is a pointer to "displayed dive" or a temporary variable and can be overwritten.
d->maxdepth.mm = 0;
d->dc.maxdepth.mm = 0;
d->dcs[0].maxdepth.mm = 0;
fixup_dive(d);
// this only matters if undoit were called before redoit
currentDive = nullptr;
// Get an owning pointer to a moved dive.
OwningDivePtr divePtr(move_dive(d));
std::unique_ptr<dive> divePtr = move_dive(d);
divePtr->selected = false; // If we clone a planned dive, it might have been selected.
// We have to clear the flag, as selections will be managed
// on dive-addition.
// If we alloc a new-trip for autogrouping, get an owning pointer to it.
OwningTripPtr allocTrip;
std::unique_ptr<dive_trip> allocTrip;
dive_trip *trip = divePtr->divetrip;
dive_site *site = divePtr->dive_site;
// We have to delete the pointers to trip and site, because this would prevent the core from adding to the
@ -425,13 +412,12 @@ AddDive::AddDive(dive *d, bool autogroup, bool newNumber)
divePtr->divetrip = nullptr;
divePtr->dive_site = nullptr;
if (!trip && autogroup) {
bool alloc;
trip = get_trip_for_new_dive(divePtr.get(), &alloc);
if (alloc)
allocTrip.reset(trip);
auto [t, allocated] = get_trip_for_new_dive(divelog, divePtr.get());
trip = t;
allocTrip = std::move(allocated);
}
int idx = dive_table_get_insertion_index(divelog.dives, divePtr.get());
int idx = divelog.dives.get_insertion_index(divePtr.get());
if (newNumber)
divePtr->number = get_dive_nr_at_idx(idx);
@ -452,7 +438,7 @@ void AddDive::redoit()
currentDive = current_dive;
divesAndSitesToRemove = addDives(divesToAdd);
sort_trip_table(divelog.trips); // Though unlikely, adding a dive may reorder trips
divelog.trips.sort(); // Though unlikely, adding a dive may reorder trips
// Select the newly added dive
setSelection(divesAndSitesToRemove.dives, divesAndSitesToRemove.dives[0], -1);
@ -462,7 +448,7 @@ void AddDive::undoit()
{
// Simply remove the dive that was previously added...
divesToAdd = removeDives(divesAndSitesToRemove);
sort_trip_table(divelog.trips); // Though unlikely, removing a dive may reorder trips
divelog.trips.sort(); // Though unlikely, removing a dive may reorder trips
// ...and restore the selection
setSelection(selection, currentDive, -1);
@ -470,33 +456,28 @@ void AddDive::undoit()
ImportDives::ImportDives(struct divelog *log, int flags, const QString &source)
{
setText(Command::Base::tr("import %n dive(s) from %1", "", log->dives->nr).arg(source));
setText(Command::Base::tr("import %n dive(s) from %1", "", log->dives.size()).arg(source));
// this only matters if undoit were called before redoit
currentDive = nullptr;
struct dive_table dives_to_add = empty_dive_table;
struct dive_table dives_to_remove = empty_dive_table;
struct trip_table trips_to_add = empty_trip_table;
struct dive_site_table sites_to_add = empty_dive_site_table;
process_imported_dives(log, flags,
&dives_to_add, &dives_to_remove, &trips_to_add,
&sites_to_add, &devicesToAddAndRemove);
auto [dives_to_add, dives_to_remove, trips_to_add, sites_to_add, devices_to_add] =
process_imported_dives(*log, flags);
// Add devices to devicesToAddAndRemove structure
devicesToAddAndRemove = std::move(devices_to_add);
// Add trips to the divesToAdd.trips structure
divesToAdd.trips.reserve(trips_to_add.nr);
for (int i = 0; i < trips_to_add.nr; ++i)
divesToAdd.trips.emplace_back(trips_to_add.trips[i]);
divesToAdd.trips.reserve(trips_to_add.size());
for (auto &trip: trips_to_add)
divesToAdd.trips.push_back(std::move(trip));
// Add sites to the divesToAdd.sites structure
divesToAdd.sites.reserve(sites_to_add.nr);
for (int i = 0; i < sites_to_add.nr; ++i)
divesToAdd.sites.emplace_back(sites_to_add.dive_sites[i]);
divesToAdd.sites = std::move(sites_to_add);
// Add dives to the divesToAdd.dives structure
divesToAdd.dives.reserve(dives_to_add.nr);
for (int i = 0; i < dives_to_add.nr; ++i) {
OwningDivePtr divePtr(dives_to_add.dives[i]);
divesToAdd.dives.reserve(dives_to_add.size());
for (auto &divePtr: dives_to_add) {
divePtr->selected = false; // See above in AddDive::AddDive()
dive_trip *trip = divePtr->divetrip;
divePtr->divetrip = nullptr; // See above in AddDive::AddDive()
@ -507,25 +488,18 @@ ImportDives::ImportDives(struct divelog *log, int flags, const QString &source)
}
// Add dive to be deleted to the divesToRemove structure
divesAndSitesToRemove.dives.reserve(dives_to_remove.nr);
for (int i = 0; i < dives_to_remove.nr; ++i)
divesAndSitesToRemove.dives.push_back(dives_to_remove.dives[i]);
divesAndSitesToRemove.dives = std::move(dives_to_remove);
// When encountering filter presets with equal names, check whether they are
// the same. If they are, ignore them.
for (const filter_preset &preset: *log->filter_presets) {
for (const filter_preset &preset: log->filter_presets) {
std::string name = preset.name;
auto it = std::find_if(divelog.filter_presets->begin(), divelog.filter_presets->end(),
auto it = std::find_if(divelog.filter_presets.begin(), divelog.filter_presets.end(),
[&name](const filter_preset &preset) { return preset.name == name; });
if (it != divelog.filter_presets->end() && it->data == preset.data)
if (it != divelog.filter_presets.end() && it->data == preset.data)
continue;
filterPresetsToAdd.emplace_back(preset.name, preset.data);
}
free(dives_to_add.dives);
free(dives_to_remove.dives);
free(trips_to_add.trips);
free(sites_to_add.dive_sites);
}
bool ImportDives::workToBeDone()
@ -551,12 +525,12 @@ void ImportDives::redoit()
divesAndSitesToRemove = std::move(divesAndSitesToRemoveNew);
// Add devices
for (const device &dev: devicesToAddAndRemove.devices)
add_to_device_table(divelog.devices, &dev);
for (const device &dev: devicesToAddAndRemove)
add_to_device_table(divelog.devices, dev);
// Add new filter presets
for (auto &it: filterPresetsToAdd) {
filterPresetsToRemove.push_back(filter_preset_add(it.first, it.second));
filterPresetsToRemove.push_back(divelog.filter_presets.add(it.first, it.second));
emit diveListNotifier.filterPresetAdded(filterPresetsToRemove.back());
}
filterPresetsToAdd.clear();
@ -579,15 +553,16 @@ void ImportDives::undoit()
setSelection(selection, currentDive, -1);
// Remove devices
for (const device &dev: devicesToAddAndRemove.devices)
remove_device(divelog.devices, &dev);
for (const device &dev: devicesToAddAndRemove)
remove_device(divelog.devices, dev);
// Remove filter presets. Do this in reverse order.
for (auto it = filterPresetsToRemove.rbegin(); it != filterPresetsToRemove.rend(); ++it) {
int index = *it;
std::string oldName = filter_preset_name(index);
FilterData oldData = filter_preset_get(index);
filter_preset_delete(index);
const filter_preset &preset = divelog.filter_presets[index];
std::string oldName = preset.name;
FilterData oldData = preset.data;
divelog.filter_presets.remove(index);
emit diveListNotifier.filterPresetRemoved(index);
filterPresetsToAdd.emplace_back(oldName, oldData);
}
@ -609,7 +584,7 @@ bool DeleteDive::workToBeDone()
void DeleteDive::undoit()
{
divesToDelete = addDives(divesToAdd);
sort_trip_table(divelog.trips); // Though unlikely, removing a dive may reorder trips
divelog.trips.sort(); // Though unlikely, removing a dive may reorder trips
// Select all re-added dives and make the first one current
dive *currentDive = !divesToDelete.dives.empty() ? divesToDelete.dives[0] : nullptr;
@ -619,7 +594,7 @@ void DeleteDive::undoit()
void DeleteDive::redoit()
{
divesToAdd = removeDives(divesToDelete);
sort_trip_table(divelog.trips); // Though unlikely, adding a dive may reorder trips
divelog.trips.sort(); // Though unlikely, adding a dive may reorder trips
// Deselect all dives and select dive that was close to the first deleted dive
dive *newCurrent = nullptr;
@ -647,10 +622,10 @@ void ShiftTime::redoit()
}
// Changing times may have unsorted the dive and trip tables
sort_dive_table(divelog.dives);
sort_trip_table(divelog.trips);
divelog.dives.sort();
divelog.trips.sort();
for (dive_trip *trip: trips)
sort_dive_table(&trip->dives); // Keep the trip-table in order
trip->sort_dives();
// Send signals
QVector<dive *> dives = stdToQt<dive *>(diveList);
@ -716,7 +691,7 @@ bool TripBase::workToBeDone()
void TripBase::redoit()
{
moveDivesBetweenTrips(divesToMove);
sort_trip_table(divelog.trips); // Though unlikely, moving dives may reorder trips
divelog.trips.sort(); // Though unlikely, moving dives may reorder trips
// Select the moved dives
std::vector<dive *> dives;
@ -754,11 +729,9 @@ RemoveAutogenTrips::RemoveAutogenTrips()
{
setText(Command::Base::tr("remove autogenerated trips"));
// TODO: don't touch core-innards directly
int i;
struct dive *dive;
for_each_dive(i, dive) {
if (dive->divetrip && dive->divetrip->autogen)
divesToMove.divesToMove.push_back( {dive, nullptr} );
for (auto &d: divelog.dives) {
if (d->divetrip && d->divetrip->autogen)
divesToMove.divesToMove.push_back( {d.get(), nullptr} );
}
}
@ -776,25 +749,22 @@ CreateTrip::CreateTrip(const QVector<dive *> &divesToAddIn)
if (divesToAddIn.isEmpty())
return;
dive_trip *trip = create_trip_from_dive(divesToAddIn[0]);
divesToMove.tripsToAdd.emplace_back(trip);
auto trip = create_trip_from_dive(divesToAddIn[0]);
for (dive *d: divesToAddIn)
divesToMove.divesToMove.push_back( {d, trip} );
divesToMove.divesToMove.push_back( { d, trip.get() });
divesToMove.tripsToAdd.push_back(std::move(trip));
}
AutogroupDives::AutogroupDives()
{
setText(Command::Base::tr("autogroup dives"));
dive_trip *trip;
bool alloc;
int from, to;
for(int i = 0; (trip = get_dives_to_autogroup(divelog.dives, i, &from, &to, &alloc)) != NULL; i = to) {
for (auto &entry: get_dives_to_autogroup(divelog.dives)) {
// If this is an allocated trip, take ownership
if (alloc)
divesToMove.tripsToAdd.emplace_back(trip);
for (int j = from; j < to; ++j)
divesToMove.divesToMove.push_back( { get_dive(j), trip } );
if (entry.created_trip)
divesToMove.tripsToAdd.push_back(std::move(entry.created_trip));
for (auto it = divelog.dives.begin() + entry.from; it != divelog.dives.begin() + entry.to; ++it)
divesToMove.divesToMove.push_back( { it->get(), entry.trip } );
}
}
@ -802,18 +772,15 @@ MergeTrips::MergeTrips(dive_trip *trip1, dive_trip *trip2)
{
if (trip1 == trip2)
return;
dive_trip *newTrip = combine_trips(trip1, trip2);
divesToMove.tripsToAdd.emplace_back(newTrip);
for (int i = 0; i < trip1->dives.nr; ++i)
divesToMove.divesToMove.push_back( { trip1->dives.dives[i], newTrip } );
for (int i = 0; i < trip2->dives.nr; ++i)
divesToMove.divesToMove.push_back( { trip2->dives.dives[i], newTrip } );
std::unique_ptr<dive_trip> newTrip = combine_trips(trip1, trip2);
for (dive *d: trip1->dives)
divesToMove.divesToMove.push_back( { d, newTrip.get() } );
for (dive *d: trip2->dives)
divesToMove.divesToMove.push_back( { d, newTrip.get() } );
divesToMove.tripsToAdd.push_back(std::move(newTrip));
}
// std::array<dive *, 2> is the same as struct *dive[2], with the fundamental
// difference that it can be returned from functions. Thus, this constructor
// can be chained with the result of a function.
SplitDivesBase::SplitDivesBase(dive *d, std::array<dive *, 2> newDives)
SplitDivesBase::SplitDivesBase(dive *d, std::array<std::unique_ptr<dive>, 2> newDives)
{
// If either of the new dives is null, simply return. Empty arrays indicate that nothing is to be done.
if (!newDives[0] || !newDives[1])
@ -833,10 +800,10 @@ SplitDivesBase::SplitDivesBase(dive *d, std::array<dive *, 2> newDives)
diveToSplit.dives.push_back(d);
splitDives.dives.resize(2);
splitDives.dives[0].dive.reset(newDives[0]);
splitDives.dives[0].dive = std::move(newDives[0]);
splitDives.dives[0].trip = d->divetrip;
splitDives.dives[0].site = d->dive_site;
splitDives.dives[1].dive.reset(newDives[1]);
splitDives.dives[1].dive = std::move(newDives[1]);
splitDives.dives[1].trip = d->divetrip;
splitDives.dives[1].site = d->dive_site;
}
@ -865,16 +832,13 @@ void SplitDivesBase::undoit()
setSelection(diveToSplit.dives, diveToSplit.dives[0], -1);
}
static std::array<dive *, 2> doSplitDives(const dive *d, duration_t time)
static std::array<std::unique_ptr<dive>, 2> doSplitDives(const dive *d, duration_t time)
{
// Split the dive
dive *new1, *new2;
if (time.seconds < 0)
split_dive(d, &new1, &new2);
return split_dive(*d);
else
split_dive_at_time(d, time, &new1, &new2);
return { new1, new2 };
return split_dive_at_time(*d, time);
}
SplitDives::SplitDives(dive *d, duration_t time) : SplitDivesBase(d, doSplitDives(d, time))
@ -882,25 +846,13 @@ SplitDives::SplitDives(dive *d, duration_t time) : SplitDivesBase(d, doSplitDive
setText(Command::Base::tr("split dive"));
}
static std::array<dive *, 2> splitDiveComputer(const dive *d, int dc_num)
{
// Refuse to do anything if the dive has only one dive computer.
// Yes, this should have been checked by the UI, but let's just make sure.
if (!d->dc.next)
return { nullptr, nullptr};
dive *new1, *new2;
split_divecomputer(d, dc_num, &new1, &new2);
return { new1, new2 };
}
SplitDiveComputer::SplitDiveComputer(dive *d, int dc_num) : SplitDivesBase(d, splitDiveComputer(d, dc_num))
SplitDiveComputer::SplitDiveComputer(dive *d, int dc_num) :
SplitDivesBase(d, split_divecomputer(*d, dc_num))
{
setText(Command::Base::tr("split dive computer"));
}
DiveComputerBase::DiveComputerBase(dive *old_dive, dive *new_dive, int dc_nr_before, int dc_nr_after) :
DiveComputerBase::DiveComputerBase(dive *old_dive, std::unique_ptr<dive> new_dive, int dc_nr_before, int dc_nr_after) :
dc_nr_before(dc_nr_before),
dc_nr_after(dc_nr_after)
{
@ -920,7 +872,7 @@ DiveComputerBase::DiveComputerBase(dive *old_dive, dive *new_dive, int dc_nr_bef
new_dive->dive_site = nullptr;
diveToAdd.dives.resize(1);
diveToAdd.dives[0].dive.reset(new_dive);
diveToAdd.dives[0].dive = std::move(new_dive);
diveToAdd.dives[0].trip = old_dive->divetrip;
diveToAdd.dives[0].site = old_dive->dive_site;
}
@ -950,13 +902,13 @@ void DiveComputerBase::undoit()
}
MoveDiveComputerToFront::MoveDiveComputerToFront(dive *d, int dc_num)
: DiveComputerBase(d, make_first_dc(d, dc_num), dc_num, 0)
: DiveComputerBase(d, clone_make_first_dc(*d, dc_num), dc_num, 0)
{
setText(Command::Base::tr("move dive computer to front"));
}
DeleteDiveComputer::DeleteDiveComputer(dive *d, int dc_num)
: DiveComputerBase(d, clone_delete_divecomputer(d, dc_num), dc_num, std::min((int)number_of_computers(d) - 1, dc_num))
: DiveComputerBase(d, clone_delete_divecomputer(*d, dc_num), dc_num, std::min((int)number_of_computers(d) - 1, dc_num))
{
setText(Command::Base::tr("delete dive computer"));
}
@ -965,16 +917,14 @@ MergeDives::MergeDives(const QVector <dive *> &dives)
{
setText(Command::Base::tr("merge dive"));
// Just a safety check - if there's not two or more dives - do nothing
// Just a safety check - if there's not two or more dives - do nothing.
// The caller should have made sure that this doesn't happen.
if (dives.count() < 2) {
qWarning("Merging less than two dives");
return;
}
dive_trip *preferred_trip;
dive_site *preferred_site;
OwningDivePtr d(merge_dives(dives[0], dives[1], dives[1]->when - dives[0]->when, false, &preferred_trip, &preferred_site));
auto [d, trip, site] = merge_dives(*dives[0], *dives[1], dives[1]->when - dives[0]->when, false);
// Currently, the core code selects the dive -> this is not what we want, as
// we manually manage the selection post-command.
@ -982,18 +932,15 @@ MergeDives::MergeDives(const QVector <dive *> &dives)
d->selected = false;
// Set the preferred dive trip, so that for subsequent merges the better trip can be selected
d->divetrip = preferred_trip;
d->divetrip = trip;
for (int i = 2; i < dives.count(); ++i) {
d.reset(merge_dives(d.get(), dives[i], dives[i]->when - d->when, false, &preferred_trip, &preferred_site));
auto [d2, trip, site] = merge_dives(*d, *dives[i], dives[i]->when - d->when, false);
d = std::move(d2);
// Set the preferred dive trip and site, so that for subsequent merges the better trip and site can be selected
d->divetrip = preferred_trip;
d->dive_site = preferred_site;
d->divetrip = trip;
d->dive_site = site;
}
// We got our preferred trip and site, so now the references can be deleted from the newly generated dive
d->divetrip = nullptr;
d->dive_site = nullptr;
// The merged dive gets the number of the first dive with a non-zero number
for (const dive *dive: dives) {
if (dive->number) {
@ -1005,9 +952,9 @@ MergeDives::MergeDives(const QVector <dive *> &dives)
// We will only renumber the remaining dives if the joined dives are consecutive.
// Otherwise all bets are off concerning what the user wanted and doing nothing seems
// like the best option.
int idx = get_divenr(dives[0]);
int num = dives.count();
if (idx < 0 || idx + num > divelog.dives->nr) {
size_t idx = divelog.dives.get_idx(dives[0]);
size_t num = dives.count();
if (idx == std::string::npos) {
// It was the callers responsibility to pass only known dives.
// Something is seriously wrong - give up.
qWarning("Merging unknown dives");
@ -1015,7 +962,8 @@ MergeDives::MergeDives(const QVector <dive *> &dives)
}
// std::equal compares two ranges. The parameters are (begin_range1, end_range1, begin_range2).
// Here, we can compare C-arrays, because QVector guarantees contiguous storage.
if (std::equal(&dives[0], &dives[0] + num, &divelog.dives->dives[idx]) &&
if (std::equal(&dives[0], &dives[0] + num, divelog.dives.begin() + idx, [](dive *d1,
const std::unique_ptr<dive> &d2) { return d1 == d2.get(); }) &&
dives[0]->number && dives.last()->number && dives[0]->number < dives.last()->number) {
// We have a consecutive set of dives. Rename all following dives according to the
// number of erased dives. This considers that there might be missing numbers.
@ -1031,24 +979,28 @@ MergeDives::MergeDives(const QVector <dive *> &dives)
// consecutive, and the difference will be 1, so the
// above example is not supposed to be normal.
int diff = dives.last()->number - dives[0]->number;
divesToRenumber.reserve(divelog.dives->nr - idx - num);
int previousnr = dives[0]->number;
for (int i = idx + num; i < divelog.dives->nr; ++i) {
int newnr = divelog.dives->dives[i]->number - diff;
for (size_t i = idx + num; i < divelog.dives.size(); ++i) {
int newnr = divelog.dives[i]->number - diff;
// Stop renumbering if stuff isn't in order (see also core/divelist.c)
if (newnr <= previousnr)
break;
divesToRenumber.append(QPair<dive *,int>(divelog.dives->dives[i], newnr));
divesToRenumber.append(QPair<dive *,int>(divelog.dives[i].get(), newnr));
previousnr = newnr;
}
}
mergedDive.dives.resize(1);
mergedDive.dives[0].dive = std::move(d);
mergedDive.dives[0].trip = preferred_trip;
mergedDive.dives[0].site = preferred_site;
mergedDive.dives[0].trip = d->divetrip;
mergedDive.dives[0].site = d->dive_site;
divesToMerge.dives = std::vector<dive *>(dives.begin(), dives.end());
// We got our preferred trip and site, so now the references can be deleted from the newly generated dive
d->divetrip = nullptr;
d->dive_site = nullptr;
mergedDive.dives[0].dive = std::move(d);
}
bool MergeDives::workToBeDone()

View File

@ -15,16 +15,16 @@ namespace Command {
// This helper structure describes a dive that we want to add.
struct DiveToAdd {
OwningDivePtr dive; // Dive to add
dive_trip *trip; // Trip the dive belongs to, may be null
dive_site *site; // Site the dive is associated with, may be null
std::unique_ptr<struct dive> dive; // Dive to add
dive_trip *trip; // Trip the dive belongs to, may be null
dive_site *site; // Site the dive is associated with, may be null
};
// Multiple trips, dives and dive sites that have to be added for a command
struct DivesAndTripsToAdd {
std::vector<DiveToAdd> dives;
std::vector<OwningTripPtr> trips;
std::vector<OwningDiveSitePtr> sites;
std::vector<std::unique_ptr<dive_trip>> trips;
std::vector<std::unique_ptr<dive_site>> sites;
};
// Dives and sites that have to be removed for a command
@ -48,7 +48,7 @@ struct DiveToTrip
struct DivesToTrip
{
std::vector<DiveToTrip> divesToMove; // If dive_trip is null, remove from trip
std::vector<OwningTripPtr> tripsToAdd;
std::vector<std::unique_ptr<dive_trip>> tripsToAdd;
};
// All divelist commands derive from a common base class. It keeps track
@ -58,7 +58,7 @@ struct DivesToTrip
class DiveListBase : public Base {
protected:
// These are helper functions to add / remove dive from the C-core structures.
DiveToAdd removeDive(struct dive *d, std::vector<OwningTripPtr> &tripsToAdd);
DiveToAdd removeDive(struct dive *d, std::vector<std::unique_ptr<dive_trip>> &tripsToAdd);
dive *addDive(DiveToAdd &d);
DivesAndTripsToAdd removeDives(DivesAndSitesToRemove &divesAndSitesToDelete);
DivesAndSitesToRemove addDives(DivesAndTripsToAdd &toAdd);
@ -108,10 +108,10 @@ private:
// For redo and undo
DivesAndTripsToAdd divesToAdd;
DivesAndSitesToRemove divesAndSitesToRemove;
struct device_table devicesToAddAndRemove;
device_table devicesToAddAndRemove;
// For redo
std::vector<OwningDiveSitePtr> sitesToAdd;
std::vector<std::unique_ptr<dive_site>> sitesToAdd;
std::vector<std::pair<std::string,FilterData>>
filterPresetsToAdd;
@ -133,7 +133,7 @@ private:
// For redo
DivesAndSitesToRemove divesToDelete;
std::vector<OwningTripPtr> tripsToAdd;
std::vector<std::unique_ptr<dive_trip>> tripsToAdd;
DivesAndTripsToAdd divesToAdd;
};
@ -196,7 +196,7 @@ struct MergeTrips : public TripBase {
class SplitDivesBase : public DiveListBase {
protected:
SplitDivesBase(dive *old, std::array<dive *, 2> newDives);
SplitDivesBase(dive *old, std::array<std::unique_ptr<dive>, 2> newDives);
private:
void undoit() override;
void redoit() override;
@ -237,7 +237,7 @@ class DiveComputerBase : public DiveListBase {
protected:
// old_dive must be a dive known to the core.
// new_dive must be new dive whose ownership is taken.
DiveComputerBase(dive *old_dive, dive *new_dive, int dc_nr_before, int dc_nr_after);
DiveComputerBase(dive *old_dive, std::unique_ptr<dive> new_dive, int dc_nr_before, int dc_nr_after);
private:
void undoit() override;
void redoit() override;

View File

@ -15,25 +15,24 @@ namespace Command {
// Add a set of dive sites to the core. The dives that were associated with
// that dive site will be restored to that dive site.
static std::vector<dive_site *> addDiveSites(std::vector<OwningDiveSitePtr> &sites)
static std::vector<dive_site *> addDiveSites(std::vector<std::unique_ptr<dive_site>> &sites)
{
std::vector<dive_site *> res;
QVector<dive *> changedDives;
res.reserve(sites.size());
for (OwningDiveSitePtr &ds: sites) {
for (std::unique_ptr<dive_site> &ds: sites) {
// Readd the dives that belonged to this site
for (int i = 0; i < ds->dives.nr; ++i) {
for (dive *d: ds->dives) {
// TODO: send dive site changed signal
struct dive *d = ds->dives.dives[i];
d->dive_site = ds.get();
changedDives.push_back(d);
}
// Add dive site to core, but remember a non-owning pointer first.
res.push_back(ds.get());
int idx = register_dive_site(ds.release()); // Return ownership to backend.
emit diveListNotifier.diveSiteAdded(res.back(), idx); // Inform frontend of new dive site.
auto add_res = divelog.sites.put(std::move(ds)); // Return ownership to backend.
res.push_back(add_res.ptr);
emit diveListNotifier.diveSiteAdded(res.back(), add_res.idx); // Inform frontend of new dive site.
}
emit diveListNotifier.divesChanged(changedDives, DiveField::DIVESITE);
@ -47,24 +46,23 @@ static std::vector<dive_site *> addDiveSites(std::vector<OwningDiveSitePtr> &sit
// Remove a set of dive sites. Get owning pointers to them. The dives are set to
// being at no dive site, but the dive site will retain a list of dives, so
// that the dives can be readded to the site on undo.
static std::vector<OwningDiveSitePtr> removeDiveSites(std::vector<dive_site *> &sites)
static std::vector<std::unique_ptr<dive_site>> removeDiveSites(std::vector<dive_site *> &sites)
{
std::vector<OwningDiveSitePtr> res;
std::vector<std::unique_ptr<dive_site>> res;
QVector<dive *> changedDives;
res.reserve(sites.size());
for (dive_site *ds: sites) {
// Reset the dive_site field of the affected dives
for (int i = 0; i < ds->dives.nr; ++i) {
struct dive *d = ds->dives.dives[i];
for (dive *d: ds->dives) {
d->dive_site = nullptr;
changedDives.push_back(d);
}
// Remove dive site from core and take ownership.
int idx = unregister_dive_site(ds);
res.emplace_back(ds);
emit diveListNotifier.diveSiteDeleted(ds, idx); // Inform frontend of removed dive site.
auto pull_res = divelog.sites.pull(ds);
res.push_back(std::move(pull_res.ptr));
emit diveListNotifier.diveSiteDeleted(ds, pull_res.idx); // Inform frontend of removed dive site.
}
emit diveListNotifier.divesChanged(changedDives, DiveField::DIVESITE);
@ -77,8 +75,8 @@ static std::vector<OwningDiveSitePtr> removeDiveSites(std::vector<dive_site *> &
AddDiveSite::AddDiveSite(const QString &name)
{
setText(Command::Base::tr("add dive site"));
sitesToAdd.emplace_back(alloc_dive_site());
sitesToAdd.back()->name = copy_qstring(name);
sitesToAdd.push_back(std::make_unique<dive_site>());
sitesToAdd.back()->name = name.toStdString();
}
bool AddDiveSite::workToBeDone()
@ -96,25 +94,17 @@ void AddDiveSite::undo()
sitesToAdd = removeDiveSites(sitesToRemove);
}
ImportDiveSites::ImportDiveSites(struct dive_site_table *sites, const QString &source)
ImportDiveSites::ImportDiveSites(dive_site_table sites, const QString &source)
{
setText(Command::Base::tr("import dive sites from %1").arg(source));
for (int i = 0; i < sites->nr; ++i) {
struct dive_site *new_ds = sites->dive_sites[i];
// Don't import dive sites that already exist. Currently we only check for
// the same name. We might want to be smarter here and merge dive site data, etc.
struct dive_site *old_ds = get_same_dive_site(new_ds);
if (old_ds) {
free_dive_site(new_ds);
for (auto &new_ds: sites) {
// Don't import dive sites that already exist.
// We might want to be smarter here and merge dive site data, etc.
if (divelog.sites.get_same(*new_ds))
continue;
}
sitesToAdd.emplace_back(new_ds);
sitesToAdd.push_back(std::move(new_ds));
}
// All site have been consumed
sites->nr = 0;
}
bool ImportDiveSites::workToBeDone()
@ -155,10 +145,9 @@ void DeleteDiveSites::undo()
PurgeUnusedDiveSites::PurgeUnusedDiveSites()
{
setText(Command::Base::tr("purge unused dive sites"));
for (int i = 0; i < divelog.sites->nr; ++i) {
dive_site *ds = divelog.sites->dive_sites[i];
if (ds->dives.nr == 0)
sitesToRemove.push_back(ds);
for (const auto &ds: divelog.sites) {
if (ds->dives.empty())
sitesToRemove.push_back(ds.get());
}
}
@ -177,24 +166,15 @@ void PurgeUnusedDiveSites::undo()
sitesToRemove = addDiveSites(sitesToAdd);
}
// Helper function: swap C and Qt string
static void swap(char *&c, QString &q)
{
QString s = c;
free(c);
c = copy_qstring(q);
q = s;
}
EditDiveSiteName::EditDiveSiteName(dive_site *dsIn, const QString &name) : ds(dsIn),
value(name)
value(name.toStdString())
{
setText(Command::Base::tr("Edit dive site name"));
}
bool EditDiveSiteName::workToBeDone()
{
return value != QString(ds->name);
return value != ds->name;
}
void EditDiveSiteName::redo()
@ -210,14 +190,14 @@ void EditDiveSiteName::undo()
}
EditDiveSiteDescription::EditDiveSiteDescription(dive_site *dsIn, const QString &description) : ds(dsIn),
value(description)
value(description.toStdString())
{
setText(Command::Base::tr("Edit dive site description"));
}
bool EditDiveSiteDescription::workToBeDone()
{
return value != QString(ds->description);
return value != ds->description;
}
void EditDiveSiteDescription::redo()
@ -233,14 +213,14 @@ void EditDiveSiteDescription::undo()
}
EditDiveSiteNotes::EditDiveSiteNotes(dive_site *dsIn, const QString &notes) : ds(dsIn),
value(notes)
value(notes.toStdString())
{
setText(Command::Base::tr("Edit dive site notes"));
}
bool EditDiveSiteNotes::workToBeDone()
{
return value != QString(ds->notes);
return value != ds->notes;
}
void EditDiveSiteNotes::redo()
@ -256,20 +236,20 @@ void EditDiveSiteNotes::undo()
}
EditDiveSiteCountry::EditDiveSiteCountry(dive_site *dsIn, const QString &country) : ds(dsIn),
value(country)
value(country.toStdString())
{
setText(Command::Base::tr("Edit dive site country"));
}
bool EditDiveSiteCountry::workToBeDone()
{
return !same_string(qPrintable(value), taxonomy_get_country(&ds->taxonomy));
return value == taxonomy_get_country(ds->taxonomy);
}
void EditDiveSiteCountry::redo()
{
QString old = taxonomy_get_country(&ds->taxonomy);
taxonomy_set_country(&ds->taxonomy, qPrintable(value), taxonomy_origin::GEOMANUAL);
std::string old = taxonomy_get_country(ds->taxonomy);
taxonomy_set_country(ds->taxonomy, value, taxonomy_origin::GEOMANUAL);
value = old;
emit diveListNotifier.diveSiteChanged(ds, LocationInformationModel::TAXONOMY); // Inform frontend of changed dive site.
}
@ -292,7 +272,7 @@ bool EditDiveSiteLocation::workToBeDone()
bool old_ok = has_location(&ds->location);
if (ok != old_ok)
return true;
return ok && !same_location(&value, &ds->location);
return ok && value != ds->location;
}
void EditDiveSiteLocation::redo()
@ -310,14 +290,11 @@ void EditDiveSiteLocation::undo()
EditDiveSiteTaxonomy::EditDiveSiteTaxonomy(dive_site *dsIn, taxonomy_data &taxonomy) : ds(dsIn),
value(taxonomy)
{
// We did a dumb copy. Erase the source to remove double references to strings.
memset(&taxonomy, 0, sizeof(taxonomy));
setText(Command::Base::tr("Edit dive site taxonomy"));
}
EditDiveSiteTaxonomy::~EditDiveSiteTaxonomy()
{
free_taxonomy(&value);
}
bool EditDiveSiteTaxonomy::workToBeDone()
@ -364,10 +341,10 @@ void MergeDiveSites::redo()
// The dives of the above dive sites were reset to no dive sites.
// Add them to the merged-into dive site. Thankfully, we remember
// the dives in the sitesToAdd vector.
for (const OwningDiveSitePtr &site: sitesToAdd) {
for (int i = 0; i < site->dives.nr; ++i) {
add_dive_to_dive_site(site->dives.dives[i], ds);
divesChanged.push_back(site->dives.dives[i]);
for (const std::unique_ptr<dive_site> &site: sitesToAdd) {
for (dive *d: site->dives) {
ds->add_dive(d);
divesChanged.push_back(d);
}
}
emit diveListNotifier.divesChanged(divesChanged, DiveField::DIVESITE);
@ -380,10 +357,10 @@ void MergeDiveSites::undo()
// Before readding the dive sites, unregister the corresponding dives so that they can be
// readded to their old dive sites.
for (const OwningDiveSitePtr &site: sitesToAdd) {
for (int i = 0; i < site->dives.nr; ++i) {
unregister_dive_from_dive_site(site->dives.dives[i]);
divesChanged.push_back(site->dives.dives[i]);
for (const std::unique_ptr<dive_site> &site: sitesToAdd) {
for (dive *d: site->dives) {
unregister_dive_from_dive_site(d);
divesChanged.push_back(d);
}
}
@ -405,9 +382,9 @@ ApplyGPSFixes::ApplyGPSFixes(const std::vector<DiveAndLocation> &fixes)
siteLocations.push_back({ ds, dl.location });
}
} else {
ds = create_dive_site(qPrintable(dl.name), divelog.sites);
ds = divelog.sites.create(dl.name.toStdString());
ds->location = dl.location;
add_dive_to_dive_site(dl.d, ds);
ds->add_dive(dl.d);
dl.d->dive_site = nullptr; // This will be set on redo()
sitesToAdd.emplace_back(ds);
}

View File

@ -31,13 +31,13 @@ private:
std::vector<dive_site *> sitesToRemove;
// For redo
std::vector<OwningDiveSitePtr> sitesToAdd;
std::vector<std::unique_ptr<dive_site>> sitesToAdd;
};
class ImportDiveSites : public Base {
public:
// Note: the dive site table is consumed after the call it will be empty.
ImportDiveSites(struct dive_site_table *sites, const QString &source);
// Note: Takes ownership of dive site table
ImportDiveSites(dive_site_table sites, const QString &source);
private:
bool workToBeDone() override;
void undo() override;
@ -47,7 +47,7 @@ private:
std::vector<dive_site *> sitesToRemove;
// For redo
std::vector<OwningDiveSitePtr> sitesToAdd;
std::vector<std::unique_ptr<dive_site>> sitesToAdd;
};
class DeleteDiveSites : public Base {
@ -62,7 +62,7 @@ private:
std::vector<dive_site *> sitesToRemove;
// For undo
std::vector<OwningDiveSitePtr> sitesToAdd;
std::vector<std::unique_ptr<dive_site>> sitesToAdd;
};
class PurgeUnusedDiveSites : public Base {
@ -77,7 +77,7 @@ private:
std::vector<dive_site *> sitesToRemove;
// For undo
std::vector<OwningDiveSitePtr> sitesToAdd;
std::vector<std::unique_ptr<dive_site>> sitesToAdd;
};
class EditDiveSiteName : public Base {
@ -89,7 +89,7 @@ private:
void redo() override;
dive_site *ds;
QString value; // Value to be set
std::string value; // Value to be set
};
class EditDiveSiteDescription : public Base {
@ -101,7 +101,7 @@ private:
void redo() override;
dive_site *ds;
QString value; // Value to be set
std::string value; // Value to be set
};
class EditDiveSiteNotes : public Base {
@ -113,7 +113,7 @@ private:
void redo() override;
dive_site *ds;
QString value; // Value to be set
std::string value; // Value to be set
};
class EditDiveSiteCountry : public Base {
@ -125,7 +125,7 @@ private:
void redo() override;
dive_site *ds;
QString value; // Value to be set
std::string value; // Value to be set
};
class EditDiveSiteLocation : public Base {
@ -167,7 +167,7 @@ private:
std::vector<dive_site *> sitesToRemove;
// For undo
std::vector<OwningDiveSitePtr> sitesToAdd;
std::vector<std::unique_ptr<dive_site>> sitesToAdd;
};
class ApplyGPSFixes : public Base {
@ -183,7 +183,7 @@ private:
std::vector<dive_site *> sitesToRemove;
// For redo
std::vector<OwningDiveSitePtr> sitesToAdd;
std::vector<std::unique_ptr<dive_site>> sitesToAdd;
// For redo and undo
struct SiteAndLocation {

View File

@ -3,8 +3,10 @@
#include "command_edit.h"
#include "core/divelist.h"
#include "core/divelog.h"
#include "core/event.h"
#include "core/fulltext.h"
#include "core/qthelper.h" // for copy_qstring
#include "core/range.h"
#include "core/sample.h"
#include "core/selection.h"
#include "core/subsurface-string.h"
@ -41,17 +43,16 @@ T EditDefaultSetter<T, ID, PTR>::data(struct dive *d) const
return d->*PTR;
}
template <DiveField::Flags ID, char *dive::*PTR>
template <DiveField::Flags ID, std::string dive::*PTR>
void EditStringSetter<ID, PTR>::set(struct dive *d, QString v) const
{
free(d->*PTR);
d->*PTR = copy_qstring(v);
d->*PTR = v.toStdString();
}
template <DiveField::Flags ID, char *dive::*PTR>
template <DiveField::Flags ID, std::string dive::*PTR>
QString EditStringSetter<ID, PTR>::data(struct dive *d) const
{
return QString(d->*PTR);
return QString::fromStdString(d->*PTR);
}
static std::vector<dive *> getDives(bool currentDiveOnly)
@ -61,11 +62,9 @@ static std::vector<dive *> getDives(bool currentDiveOnly)
: std::vector<dive *> { };
std::vector<dive *> res;
struct dive *d;
int i;
for_each_dive (i, d) {
for (auto &d: divelog.dives) {
if (d->selected)
res.push_back(d);
res.push_back(d.get());
}
return res;
}
@ -305,11 +304,11 @@ QString EditAtmPress::fieldName() const
// ***** Duration *****
void EditDuration::set(struct dive *d, int value) const
{
d->dc.duration.seconds = value;
d->duration = d->dc.duration;
d->dc.meandepth.mm = 0;
d->dc.samples = 0;
fake_dc(&d->dc);
d->dcs[0].duration.seconds = value;
d->duration = d->dcs[0].duration;
d->dcs[0].meandepth.mm = 0;
d->dcs[0].samples.clear();
fake_dc(&d->dcs[0]);
}
int EditDuration::data(struct dive *d) const
@ -325,11 +324,11 @@ QString EditDuration::fieldName() const
// ***** Depth *****
void EditDepth::set(struct dive *d, int value) const
{
d->dc.maxdepth.mm = value;
d->maxdepth = d->dc.maxdepth;
d->dc.meandepth.mm = 0;
d->dc.samples = 0;
fake_dc(&d->dc);
d->dcs[0].maxdepth.mm = value;
d->maxdepth = d->dcs[0].maxdepth;
d->dcs[0].meandepth.mm = 0;
d->dcs[0].samples.clear();
fake_dc(&d->dcs[0]);
}
int EditDepth::data(struct dive *d) const
@ -346,7 +345,7 @@ QString EditDepth::fieldName() const
void EditDiveSite::set(struct dive *d, struct dive_site *dive_site) const
{
unregister_dive_from_dive_site(d);
add_dive_to_dive_site(d, dive_site);
dive_site->add_dive(d);
}
struct dive_site *EditDiveSite::data(struct dive *d) const
@ -374,14 +373,11 @@ void EditDiveSite::redo()
EditDiveSite::undo(); // Undo and redo do the same
}
static struct dive_site *createDiveSite(const QString &name)
static struct dive_site *createDiveSite(const std::string &name)
{
struct dive_site *ds = alloc_dive_site();
struct dive_site *old = current_dive ? current_dive->dive_site : nullptr;
if (old) {
copy_dive_site(old, ds);
free(ds->name); // Free name, as we will overwrite it with our own version
}
struct dive_site *ds = new dive_site;
if (current_dive && current_dive->dive_site)
*ds = *current_dive->dive_site;
// If the current dive has a location, use that as location for the new dive site
if (current_dive) {
@ -390,12 +386,12 @@ static struct dive_site *createDiveSite(const QString &name)
ds->location = loc;
}
ds->name = copy_qstring(name);
ds->name = name;
return ds;
}
EditDiveSiteNew::EditDiveSiteNew(const QString &newName, bool currentDiveOnly) :
EditDiveSite(createDiveSite(newName), currentDiveOnly),
EditDiveSite(createDiveSite(newName.toStdString()), currentDiveOnly),
diveSiteToAdd(value),
diveSiteToRemove(nullptr)
{
@ -404,17 +400,17 @@ EditDiveSiteNew::EditDiveSiteNew(const QString &newName, bool currentDiveOnly) :
void EditDiveSiteNew::undo()
{
EditDiveSite::undo();
int idx = unregister_dive_site(diveSiteToRemove);
diveSiteToAdd.reset(diveSiteToRemove);
emit diveListNotifier.diveSiteDeleted(diveSiteToRemove, idx); // Inform frontend of removed dive site.
auto res = divelog.sites.pull(diveSiteToRemove);
diveSiteToAdd = std::move(res.ptr);
emit diveListNotifier.diveSiteDeleted(diveSiteToRemove, res.idx); // Inform frontend of removed dive site.
diveSiteToRemove = nullptr;
}
void EditDiveSiteNew::redo()
{
diveSiteToRemove = diveSiteToAdd.get();
int idx = register_dive_site(diveSiteToAdd.release()); // Return ownership to backend.
emit diveListNotifier.diveSiteAdded(diveSiteToRemove, idx); // Inform frontend of new dive site.
auto res = divelog.sites.register_site(std::move(diveSiteToAdd)); // Return ownership to backend.
diveSiteToRemove = res.ptr;
emit diveListNotifier.diveSiteAdded(diveSiteToRemove, res.idx); // Inform frontend of new dive site.
EditDiveSite::redo();
}
@ -565,18 +561,17 @@ void EditTagsBase::redo()
QStringList EditTags::data(struct dive *d) const
{
QStringList res;
for (const struct tag_entry *tag = d->tag_list; tag; tag = tag->next)
res.push_back(QString::fromStdString(tag->tag->name));
for (const divetag *tag: d->tags)
res.push_back(QString::fromStdString(tag->name));
return res;
}
void EditTags::set(struct dive *d, const QStringList &v) const
{
taglist_free(d->tag_list);
d->tag_list = NULL;
d->tags.clear();
for (const QString &tag: v)
taglist_add_tag(&d->tag_list, qPrintable(tag));
taglist_cleanup(&d->tag_list);
taglist_add_tag(d->tags, tag.toStdString());
taglist_cleanup(d->tags);
}
QString EditTags::fieldName() const
@ -587,14 +582,13 @@ QString EditTags::fieldName() const
// ***** Buddies *****
QStringList EditBuddies::data(struct dive *d) const
{
return stringToList(d->buddy);
return stringToList(QString::fromStdString(d->buddy));
}
void EditBuddies::set(struct dive *d, const QStringList &v) const
{
QString text = v.join(", ");
free(d->buddy);
d->buddy = copy_qstring(text);
d->buddy = text.toStdString();
}
QString EditBuddies::fieldName() const
@ -605,14 +599,13 @@ QString EditBuddies::fieldName() const
// ***** DiveGuide *****
QStringList EditDiveGuide::data(struct dive *d) const
{
return stringToList(d->diveguide);
return stringToList(QString::fromStdString(d->diveguide));
}
void EditDiveGuide::set(struct dive *d, const QStringList &v) const
{
QString text = v.join(", ");
free(d->diveguide);
d->diveguide = copy_qstring(text);
d->diveguide = text.toStdString();
}
QString EditDiveGuide::fieldName() const
@ -620,19 +613,8 @@ QString EditDiveGuide::fieldName() const
return Command::Base::tr("dive guide");
}
static void swapCandQString(QString &q, char *&c)
PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dIn)
{
QString tmp(c);
free(c);
c = copy_qstring(q);
q = std::move(tmp);
}
PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dIn),
tags(nullptr)
{
memset(&cylinders, 0, sizeof(cylinders));
memset(&weightsystems, 0, sizeof(weightsystems));
if (what.notes)
notes = data->notes;
if (what.diveguide)
@ -656,16 +638,16 @@ PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dI
if (what.divesite)
divesite = data->dive_site;
if (what.tags)
tags = taglist_copy(data->tag_list);
tags = data->tags;
if (what.cylinders) {
copy_cylinders(&data->cylinders, &cylinders);
cylinders = data->cylinders;
// Paste cylinders is "special":
// 1) For cylinders that exist in the destination dive we keep the gas-mix and pressures.
// 2) For cylinders that do not yet exist in the destination dive, we set the pressures to 0, i.e. unset.
// Moreover, for these we set the manually_added flag, because they weren't downloaded from a DC.
for (int i = 0; i < d->cylinders.nr && i < cylinders.nr; ++i) {
const cylinder_t &src = *get_cylinder(d, i);
cylinder_t &dst = cylinders.cylinders[i];
for (size_t i = 0; i < d->cylinders.size() && i < cylinders.size(); ++i) {
const cylinder_t &src = d->cylinders[i];
cylinder_t &dst = cylinders[i];
dst.gasmix = src.gasmix;
dst.start = src.start;
dst.end = src.end;
@ -679,8 +661,8 @@ PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dI
dst.bestmix_o2 = src.bestmix_o2;
dst.bestmix_he = src.bestmix_he;
}
for (int i = d->cylinders.nr; i < cylinders.nr; ++i) {
cylinder_t &cyl = cylinders.cylinders[i];
for (size_t i = d->cylinders.size(); i < cylinders.size(); ++i) {
cylinder_t &cyl = cylinders[i];
cyl.start.mbar = 0;
cyl.end.mbar = 0;
cyl.sample_start.mbar = 0;
@ -689,7 +671,7 @@ PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dI
}
}
if (what.weights)
copy_weights(&data->weightsystems, &weightsystems);
weightsystems = data->weightsystems;
if (what.number)
number = data->number;
if (what.when)
@ -698,22 +680,18 @@ PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dI
PasteState::~PasteState()
{
taglist_free(tags);
clear_cylinder_table(&cylinders);
clear_weightsystem_table(&weightsystems);
free(weightsystems.weightsystems);
}
void PasteState::swap(dive_components what)
{
if (what.notes)
swapCandQString(notes, d->notes);
std::swap(notes, d->notes);
if (what.diveguide)
swapCandQString(diveguide, d->diveguide);
std::swap(diveguide, d->diveguide);
if (what.buddy)
swapCandQString(buddy, d->buddy);
std::swap(buddy, d->buddy);
if (what.suit)
swapCandQString(suit, d->suit);
std::swap(suit, d->suit);
if (what.rating)
std::swap(rating, d->rating);
if (what.visibility)
@ -729,7 +707,7 @@ void PasteState::swap(dive_components what)
if (what.divesite)
std::swap(divesite, d->dive_site);
if (what.tags)
std::swap(tags, d->tag_list);
std::swap(tags, d->tags);
if (what.cylinders)
std::swap(cylinders, d->cylinders);
if (what.weights)
@ -800,40 +778,34 @@ ReplanDive::ReplanDive(dive *source) : d(current_dive),
when(0),
maxdepth({0}),
meandepth({0}),
dc({ 0 }),
notes(nullptr),
surface_pressure({0}),
duration({0}),
salinity(0)
{
memset(&cylinders, 0, sizeof(cylinders));
if (!d)
return;
// Fix source. Things might be inconsistent after modifying the profile.
source->maxdepth.mm = source->dc.maxdepth.mm = 0;
source->maxdepth.mm = source->dcs[0].maxdepth.mm = 0;
fixup_dive(source);
when = source->when;
maxdepth = source->maxdepth;
meandepth = source->meandepth;
notes = copy_string(source->notes);
notes = source->notes;
duration = source->duration;
salinity = source->salinity;
surface_pressure = source->surface_pressure;
// This resets the dive computers and cylinders of the source dive, avoiding deep copies.
std::swap(source->cylinders, cylinders);
std::swap(source->dc, dc);
std::swap(source->dcs[0], dc);
setText(Command::Base::tr("Replan dive"));
}
ReplanDive::~ReplanDive()
{
clear_cylinder_table(&cylinders);
free_dive_dcs(&dc);
free(notes);
}
bool ReplanDive::workToBeDone()
@ -847,7 +819,7 @@ void ReplanDive::undo()
std::swap(d->maxdepth, maxdepth);
std::swap(d->meandepth, meandepth);
std::swap(d->cylinders, cylinders);
std::swap(d->dc, dc);
std::swap(d->dcs[0], dc);
std::swap(d->notes, notes);
std::swap(d->surface_pressure, surface_pressure);
std::swap(d->duration, duration);
@ -888,10 +860,9 @@ EditProfile::EditProfile(const dive *source, int dcNr, EditProfileType type, int
maxdepth({0}),
meandepth({0}),
dcmaxdepth({0}),
duration({0}),
dc({ 0 })
duration({0})
{
const struct divecomputer *sdc = get_dive_dc_const(source, dcNr);
const struct divecomputer *sdc = get_dive_dc(source, dcNr);
if (!sdc)
d = nullptr; // Signal that we refuse to do anything.
if (!d)
@ -902,15 +873,14 @@ EditProfile::EditProfile(const dive *source, int dcNr, EditProfileType type, int
meandepth = source->meandepth;
duration = source->duration;
copy_samples(sdc, &dc);
copy_events(sdc, &dc);
dc.samples = sdc->samples;
dc.events = sdc->events;
setText(editProfileTypeToString(type, count) + " " + diveNumberOrDate(d));
}
EditProfile::~EditProfile()
{
free_dive_dcs(&dc);
}
bool EditProfile::workToBeDone()
@ -924,8 +894,6 @@ void EditProfile::undo()
if (!sdc)
return;
std::swap(sdc->samples, dc.samples);
std::swap(sdc->alloc_samples, dc.alloc_samples);
std::swap(sdc->sample, dc.sample);
std::swap(sdc->events, dc.events);
std::swap(sdc->maxdepth, dc.maxdepth);
std::swap(d->maxdepth, maxdepth);
@ -964,10 +932,10 @@ bool AddWeight::workToBeDone()
void AddWeight::undo()
{
for (dive *d: dives) {
if (d->weightsystems.nr <= 0)
if (d->weightsystems.empty())
continue;
remove_weightsystem(d, d->weightsystems.nr - 1);
emit diveListNotifier.weightRemoved(d, d->weightsystems.nr);
d->weightsystems.pop_back();
emit diveListNotifier.weightRemoved(d, d->weightsystems.size());
invalidate_dive_cache(d); // Ensure that dive is written in git_save()
}
}
@ -975,31 +943,26 @@ void AddWeight::undo()
void AddWeight::redo()
{
for (dive *d: dives) {
add_cloned_weightsystem(&d->weightsystems, empty_weightsystem);
emit diveListNotifier.weightAdded(d, d->weightsystems.nr - 1);
d->weightsystems.emplace_back();
emit diveListNotifier.weightAdded(d, d->weightsystems.size() - 1);
invalidate_dive_cache(d); // Ensure that dive is written in git_save()
}
}
static int find_weightsystem_index(const struct dive *d, weightsystem_t ws)
static int find_weightsystem_index(const struct dive *d, const weightsystem_t &ws)
{
for (int idx = 0; idx < d->weightsystems.nr; ++idx) {
if (same_weightsystem(d->weightsystems.weightsystems[idx], ws))
return idx;
}
return -1;
return index_of_if(d->weightsystems, [&ws](auto &ws2) { return same_weightsystem(ws2, ws); });
}
EditWeightBase::EditWeightBase(int index, bool currentDiveOnly) :
EditDivesBase(currentDiveOnly),
ws(empty_weightsystem)
EditDivesBase(currentDiveOnly)
{
// Get the old weightsystem, bail if index is invalid
if (!current || index < 0 || index >= current->weightsystems.nr) {
if (!current || index < 0 || static_cast<size_t>(index) >= current->weightsystems.size()) {
dives.clear();
return;
}
ws = clone_weightsystem(current->weightsystems.weightsystems[index]);
ws = current->weightsystems[index];
// Deleting a weightsystem from multiple dives is semantically ill-defined.
// What we will do is trying to delete the same weightsystem if it exists.
@ -1025,7 +988,6 @@ EditWeightBase::EditWeightBase(int index, bool currentDiveOnly) :
EditWeightBase::~EditWeightBase()
{
free_weightsystem(ws);
}
bool EditWeightBase::workToBeDone()
@ -1047,7 +1009,7 @@ RemoveWeight::RemoveWeight(int index, bool currentDiveOnly) :
void RemoveWeight::undo()
{
for (size_t i = 0; i < dives.size(); ++i) {
add_to_weightsystem_table(&dives[i]->weightsystems, indices[i], clone_weightsystem(ws));
add_to_weightsystem_table(&dives[i]->weightsystems, indices[i], ws);
emit diveListNotifier.weightAdded(dives[i], indices[i]);
invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save()
}
@ -1064,8 +1026,7 @@ void RemoveWeight::redo()
// ***** Edit Weight *****
EditWeight::EditWeight(int index, weightsystem_t wsIn, bool currentDiveOnly) :
EditWeightBase(index, currentDiveOnly),
new_ws(empty_weightsystem)
EditWeightBase(index, currentDiveOnly)
{
if (dives.empty())
return;
@ -1077,15 +1038,13 @@ EditWeight::EditWeight(int index, weightsystem_t wsIn, bool currentDiveOnly) :
setText(QStringLiteral("%1 [%2]").arg(Command::Base::tr("Edit weight (%n dive(s))", "", num_dives)).arg(getListOfDives(dives)));
// Try to untranslate the weightsystem name
new_ws = clone_weightsystem(wsIn);
QString vString(new_ws.description);
for (int i = 0; i < MAX_WS_INFO && ws_info[i].name; ++i) {
if (gettextFromC::tr(ws_info[i].name) == vString) {
free_weightsystem(new_ws);
new_ws.description = copy_string(ws_info[i].name);
break;
}
}
new_ws = std::move(wsIn);
QString vString = QString::fromStdString(new_ws.description);
auto it = std::find_if(ws_info_table.begin(), ws_info_table.end(),
[&vString](const ws_info &info)
{ return gettextFromC::tr(info.name.c_str()) == vString; });
if (it != ws_info_table.end())
new_ws.description = it->name;
// If that doesn't change anything, do nothing
if (same_weightsystem(ws, new_ws)) {
@ -1096,13 +1055,12 @@ EditWeight::EditWeight(int index, weightsystem_t wsIn, bool currentDiveOnly) :
EditWeight::~EditWeight()
{
free_weightsystem(new_ws);
}
void EditWeight::redo()
{
for (size_t i = 0; i < dives.size(); ++i) {
add_weightsystem_description(&new_ws); // This updates the weightsystem info table
add_weightsystem_description(new_ws); // This updates the weightsystem info table
set_weightsystem(dives[i], indices[i], new_ws);
emit diveListNotifier.weightEdited(dives[i], indices[i]);
invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save()
@ -1118,8 +1076,7 @@ void EditWeight::undo()
// ***** Add Cylinder *****
AddCylinder::AddCylinder(bool currentDiveOnly) :
EditDivesBase(currentDiveOnly),
cyl(empty_cylinder)
EditDivesBase(currentDiveOnly)
{
if (dives.empty())
return;
@ -1133,7 +1090,6 @@ AddCylinder::AddCylinder(bool currentDiveOnly) :
AddCylinder::~AddCylinder()
{
free_cylinder(cyl);
}
bool AddCylinder::workToBeDone()
@ -1157,7 +1113,7 @@ void AddCylinder::redo()
for (dive *d: dives) {
int index = first_hidden_cylinder(d);
indexes.push_back(index);
add_cylinder(&d->cylinders, index, clone_cylinder(cyl));
add_cylinder(&d->cylinders, index, cyl);
update_cylinder_related_info(d);
emit diveListNotifier.cylinderAdded(d, index);
invalidate_dive_cache(d); // Ensure that dive is written in git_save()
@ -1166,14 +1122,14 @@ void AddCylinder::redo()
static bool same_cylinder_type(const cylinder_t &cyl1, const cylinder_t &cyl2)
{
return same_string(cyl1.type.description, cyl2.type.description) &&
cyl1.cylinder_use == cyl2.cylinder_use;
return std::tie(cyl1.cylinder_use, cyl1.type.description) ==
std::tie(cyl2.cylinder_use, cyl2.type.description);
}
static bool same_cylinder_size(const cylinder_t &cyl1, const cylinder_t &cyl2)
{
return cyl1.type.size.mliter == cyl2.type.size.mliter &&
cyl1.type.workingpressure.mbar == cyl2.type.workingpressure.mbar;
return std::tie(cyl1.type.size.mliter, cyl1.type.workingpressure.mbar) ==
std::tie(cyl2.type.size.mliter, cyl2.type.workingpressure.mbar);
}
// Flags for comparing cylinders
@ -1186,7 +1142,7 @@ EditCylinderBase::EditCylinderBase(int index, bool currentDiveOnly, bool nonProt
EditDivesBase(currentDiveOnly)
{
// Get the old cylinder, bail if index is invalid
if (!current || index < 0 || index >= current->cylinders.nr) {
if (!current || index < 0 || index >= static_cast<int>(current->cylinders.size())) {
dives.clear();
return;
}
@ -1198,12 +1154,12 @@ EditCylinderBase::EditCylinderBase(int index, bool currentDiveOnly, bool nonProt
cyl.reserve(dives.size());
for (dive *d: dives) {
if (index >= d->cylinders.nr)
if (index >= static_cast<int>(d->cylinders.size()))
continue;
if (nonProtectedOnly && is_cylinder_prot(d, index))
continue;
// We checked that the cylinder exists above.
const cylinder_t &cylinder = *get_cylinder(d, index);
const cylinder_t &cylinder = d->cylinders[index];
if (d != current &&
(!same_cylinder_size(orig, cylinder) || !same_cylinder_type(orig, cylinder))) {
// when editing cylinders, we assume that the user wanted to edit the 'n-th' cylinder
@ -1215,15 +1171,13 @@ EditCylinderBase::EditCylinderBase(int index, bool currentDiveOnly, bool nonProt
// that's silly as it's always the same value - but we need this vector of indices in the case where we add
// a cylinder to several dives as the spot will potentially be different in different dives
indexes.push_back(index);
cyl.push_back(clone_cylinder(cylinder));
cyl.push_back(cylinder);
}
dives = std::move(divesNew);
}
EditCylinderBase::~EditCylinderBase()
{
for (cylinder_t c: cyl)
free_cylinder(c);
}
bool EditCylinderBase::workToBeDone()
@ -1244,9 +1198,9 @@ RemoveCylinder::RemoveCylinder(int index, bool currentDiveOnly) :
void RemoveCylinder::undo()
{
for (size_t i = 0; i < dives.size(); ++i) {
std::vector<int> mapping = get_cylinder_map_for_add(dives[i]->cylinders.nr, indexes[i]);
add_cylinder(&dives[i]->cylinders, indexes[i], clone_cylinder(cyl[i]));
cylinder_renumber(dives[i], &mapping[0]);
std::vector<int> mapping = get_cylinder_map_for_add(dives[i]->cylinders.size(), indexes[i]);
add_cylinder(&dives[i]->cylinders, indexes[i], cyl[i]);
cylinder_renumber(*dives[i], &mapping[0]);
update_cylinder_related_info(dives[i]);
emit diveListNotifier.cylinderAdded(dives[i], indexes[i]);
invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save()
@ -1256,9 +1210,9 @@ void RemoveCylinder::undo()
void RemoveCylinder::redo()
{
for (size_t i = 0; i < dives.size(); ++i) {
std::vector<int> mapping = get_cylinder_map_for_remove(dives[i]->cylinders.nr, indexes[i]);
std::vector<int> mapping = get_cylinder_map_for_remove(dives[i]->cylinders.size(), indexes[i]);
remove_cylinder(dives[i], indexes[i]);
cylinder_renumber(dives[i], &mapping[0]);
cylinder_renumber(*dives[i], &mapping[0]);
update_cylinder_related_info(dives[i]);
emit diveListNotifier.cylinderRemoved(dives[i], indexes[i]);
invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save()
@ -1291,15 +1245,12 @@ EditCylinder::EditCylinder(int index, cylinder_t cylIn, EditCylinderType typeIn,
else
setText(Command::Base::tr("Edit cylinder (%n dive(s))", "", dives.size()));
QString description = cylIn.type.description;
// The base class copied the cylinders for us, let's edit them
for (int i = 0; i < (int)indexes.size(); ++i) {
switch (type) {
case EditCylinderType::TYPE:
free((void *)cyl[i].type.description);
cyl[i].type = cylIn.type;
cyl[i].type.description = copy_qstring(description);
cyl[i].type.description = cylIn.type.description;
cyl[i].cylinder_use = cylIn.cylinder_use;
break;
case EditCylinderType::PRESSURE:
@ -1310,7 +1261,7 @@ EditCylinder::EditCylinder(int index, cylinder_t cylIn, EditCylinderType typeIn,
cyl[i].gasmix = cylIn.gasmix;
cyl[i].bestmix_o2 = cylIn.bestmix_o2;
cyl[i].bestmix_he = cylIn.bestmix_he;
sanitize_gasmix(&cyl[i].gasmix);
sanitize_gasmix(cyl[i].gasmix);
break;
}
}
@ -1319,7 +1270,8 @@ EditCylinder::EditCylinder(int index, cylinder_t cylIn, EditCylinderType typeIn,
void EditCylinder::redo()
{
for (size_t i = 0; i < dives.size(); ++i) {
set_tank_info_data(&tank_info_table, cyl[i].type.description, cyl[i].type.size, cyl[i].type.workingpressure);
const std::string &name = cyl[i].type.description;
set_tank_info_data(tank_info_table, name, cyl[i].type.size, cyl[i].type.workingpressure);
std::swap(*get_cylinder(dives[i], indexes[i]), cyl[i]);
update_cylinder_related_info(dives[i]);
emit diveListNotifier.cylinderEdited(dives[i], indexes[i]);
@ -1344,13 +1296,13 @@ EditSensors::EditSensors(int toCylinderIn, int fromCylinderIn, int dcNr)
void EditSensors::mapSensors(int toCyl, int fromCyl)
{
for (int i = 0; i < dc->samples; ++i) {
for (auto &sample: dc->samples) {
for (int s = 0; s < MAX_SENSORS; ++s) {
if (dc->sample[i].pressure[s].mbar && dc->sample[i].sensor[s] == fromCyl)
dc->sample[i].sensor[s] = toCyl;
if (sample.pressure[s].mbar && sample.sensor[s] == fromCyl)
sample.sensor[s] = toCyl;
// In case the cylinder we are moving to has a sensor attached, move it to the other cylinder
else if (dc->sample[i].pressure[s].mbar && dc->sample[i].sensor[s] == toCyl)
dc->sample[i].sensor[s] = fromCyl;
else if (sample.pressure[s].mbar && sample.sensor[s] == toCyl)
sample.sensor[s] = fromCyl;
}
}
emit diveListNotifier.diveComputerEdited(dc);
@ -1409,9 +1361,9 @@ EditDive::EditDive(dive *oldDiveIn, dive *newDiveIn, dive_site *createDs, dive_s
changedFields |= DiveField::ATM_PRESS;
if (oldDive->dive_site != newDive->dive_site)
changedFields |= DiveField::DIVESITE;
if (!same_string(oldDive->diveguide, newDive->diveguide))
if (oldDive->diveguide != newDive->diveguide)
changedFields |= DiveField::DIVEGUIDE;
if (!same_string(oldDive->buddy, newDive->buddy))
if (oldDive->buddy != newDive->buddy)
changedFields |= DiveField::BUDDY;
if (oldDive->rating != newDive->rating)
changedFields |= DiveField::RATING;
@ -1425,13 +1377,13 @@ EditDive::EditDive(dive *oldDiveIn, dive *newDiveIn, dive_site *createDs, dive_s
changedFields |= DiveField::SURGE;
if (oldDive->chill != newDive->chill)
changedFields |= DiveField::CHILL;
if (!same_string(oldDive->suit, newDive->suit))
if (oldDive->suit != newDive->suit)
changedFields |= DiveField::SUIT;
if (taglist_get_tagstring(oldDive->tag_list) != taglist_get_tagstring(newDive->tag_list)) // This is cheating. Do we have a taglist comparison function?
if (taglist_get_tagstring(oldDive->tags) != taglist_get_tagstring(newDive->tags)) // This is cheating. Do we have a taglist comparison function?
changedFields |= DiveField::TAGS;
if (oldDive->dc.divemode != newDive->dc.divemode)
if (oldDive->dcs[0].divemode != newDive->dcs[0].divemode)
changedFields |= DiveField::MODE;
if (!same_string(oldDive->notes, newDive->notes))
if (oldDive->notes != newDive->notes)
changedFields |= DiveField::NOTES;
if (oldDive->salinity != newDive->salinity)
changedFields |= DiveField::SALINITY;
@ -1442,9 +1394,9 @@ EditDive::EditDive(dive *oldDiveIn, dive *newDiveIn, dive_site *createDs, dive_s
void EditDive::undo()
{
if (siteToRemove) {
int idx = unregister_dive_site(siteToRemove);
siteToAdd.reset(siteToRemove);
emit diveListNotifier.diveSiteDeleted(siteToRemove, idx); // Inform frontend of removed dive site.
auto res = divelog.sites.pull(siteToRemove);
siteToAdd = std::move(res.ptr);
emit diveListNotifier.diveSiteDeleted(siteToRemove, res.idx); // Inform frontend of removed dive site.
}
exchangeDives();
@ -1454,9 +1406,9 @@ void EditDive::undo()
void EditDive::redo()
{
if (siteToAdd) {
siteToRemove = siteToAdd.get();
int idx = register_dive_site(siteToAdd.release()); // Return ownership to backend.
emit diveListNotifier.diveSiteAdded(siteToRemove, idx); // Inform frontend of new dive site.
auto res = divelog.sites.register_site(std::move(siteToAdd)); // Return ownership to backend.
siteToRemove = res.ptr;
emit diveListNotifier.diveSiteAdded(siteToRemove, res.idx); // Inform frontend of new dive site.
}
exchangeDives();
@ -1481,7 +1433,7 @@ void EditDive::exchangeDives()
std::swap(*newDive, *oldDive);
fulltext_register(oldDive);
if (newDiveSite)
add_dive_to_dive_site(oldDive, newDiveSite);
newDiveSite->add_dive(oldDive);
newDiveSite = oldDiveSite; // remember the previous dive site
invalidate_dive_cache(oldDive);
@ -1489,12 +1441,12 @@ void EditDive::exchangeDives()
QVector<dive *> dives = { oldDive };
timestamp_t delta = oldDive->when - newDive->when;
if (delta != 0) {
sort_dive_table(divelog.dives);
sort_trip_table(divelog.trips);
divelog.dives.sort();
divelog.trips.sort();
if (newDive->divetrip != oldDive->divetrip)
qWarning("Command::EditDive::redo(): This command does not support moving between trips!");
if (oldDive->divetrip)
sort_dive_table(&newDive->divetrip->dives); // Keep the trip-table in order
newDive->divetrip->sort_dives(); // Keep the trip-table in order
emit diveListNotifier.divesTimeChanged(delta, dives);
}

View File

@ -88,7 +88,7 @@ private:
// Automatically generate getter and setter in the case for string assignments.
// The third parameter is a pointer to a C-style string in the dive structure.
template <DiveField::Flags ID, char *dive::*PTR>
template <DiveField::Flags ID, std::string dive::*PTR>
class EditStringSetter : public EditTemplate<QString, ID> {
private:
using EditTemplate<QString, ID>::EditTemplate;
@ -208,7 +208,7 @@ public:
// deriving from it and hooks into undo() and redo() to add / remove the dive site.
class EditDiveSiteNew : public EditDiveSite {
public:
OwningDiveSitePtr diveSiteToAdd;
std::unique_ptr<dive_site> diveSiteToAdd;
struct dive_site *diveSiteToRemove;
EditDiveSiteNew(const QString &newName, bool currentDiveOnly);
void undo() override;
@ -289,19 +289,19 @@ public:
struct PasteState {
dive *d;
dive_site *divesite;
QString notes;
QString diveguide;
QString buddy;
QString suit;
std::string notes;
std::string diveguide;
std::string buddy;
std::string suit;
int rating;
int wavesize;
int visibility;
int current;
int surge;
int chill;
tag_entry *tags;
struct cylinder_table cylinders;
struct weightsystem_table weightsystems;
tag_list tags;
cylinder_table cylinders;
weightsystem_table weightsystems;
int number;
timestamp_t when;
@ -329,7 +329,7 @@ class ReplanDive : public Base {
depth_t maxdepth, meandepth;
struct cylinder_table cylinders;
struct divecomputer dc;
char *notes;
std::string notes;
pressure_t surface_pressure;
duration_t duration;
int salinity;
@ -465,12 +465,12 @@ public:
EditDive(dive *oldDive, dive *newDive, dive_site *createDs, dive_site *editDs, location_t dsLocation); // Takes ownership of newDive
private:
dive *oldDive; // Dive that is going to be overwritten
OwningDivePtr newDive; // New data
std::unique_ptr<dive> newDive; // New data
dive_site *newDiveSite;
int changedFields;
dive_site *siteToRemove;
OwningDiveSitePtr siteToAdd;
std::unique_ptr<dive_site> siteToAdd;
dive_site *siteToEdit;
location_t dsLocation;

View File

@ -41,13 +41,12 @@ void EditTripBase::redo()
// ***** Location *****
void EditTripLocation::set(dive_trip *t, const QString &s) const
{
free(t->location);
t->location = copy_qstring(s);
t->location = s.toStdString();
}
QString EditTripLocation::data(dive_trip *t) const
{
return QString(t->location);
return QString::fromStdString(t->location);
}
QString EditTripLocation::fieldName() const
@ -63,13 +62,12 @@ TripField EditTripLocation::fieldId() const
// ***** Notes *****
void EditTripNotes::set(dive_trip *t, const QString &s) const
{
free(t->notes);
t->notes = copy_qstring(s);
t->notes = s.toStdString();
}
QString EditTripNotes::data(dive_trip *t) const
{
return QString(t->notes);
return QString::fromStdString(t->notes);
}
QString EditTripNotes::fieldName() const

View File

@ -30,7 +30,7 @@ protected:
void redo() override;
// Get and set functions to be overriden by sub-classes.
virtual void set(struct dive_trip *t, const QString &) const = 0;
virtual void set(dive_trip *t, const QString &) const = 0;
virtual QString data(struct dive_trip *t) const = 0;
virtual QString fieldName() const = 0; // Name of the field, used to create the undo menu-entry
virtual TripField fieldId() const = 0;

View File

@ -2,7 +2,6 @@
#include "command_event.h"
#include "core/dive.h"
#include "core/event.h"
#include "core/selection.h"
#include "core/subsurface-qt/divelistnotifier.h"
#include "core/libdivecomputer.h"
@ -35,8 +34,8 @@ void EventBase::updateDive()
setSelection({ d }, d, dcNr);
}
AddEventBase::AddEventBase(struct dive *d, int dcNr, struct event *ev) : EventBase(d, dcNr),
eventToAdd(ev)
AddEventBase::AddEventBase(struct dive *d, int dcNr, struct event ev) : EventBase(d, dcNr),
ev(std::move(ev))
{
}
@ -48,32 +47,29 @@ bool AddEventBase::workToBeDone()
void AddEventBase::redoit()
{
struct divecomputer *dc = get_dive_dc(d, dcNr);
eventToRemove = eventToAdd.get();
add_event_to_dc(dc, eventToAdd.release()); // return ownership to backend
idx = add_event_to_dc(dc, ev); // return ownership to backend
}
void AddEventBase::undoit()
{
struct divecomputer *dc = get_dive_dc(d, dcNr);
remove_event_from_dc(dc, eventToRemove);
eventToAdd.reset(eventToRemove); // take ownership of event
eventToRemove = nullptr;
ev = remove_event_from_dc(dc, idx);
}
AddEventBookmark::AddEventBookmark(struct dive *d, int dcNr, int seconds) :
AddEventBase(d, dcNr, create_event(seconds, SAMPLE_EVENT_BOOKMARK, 0, 0, "bookmark"))
AddEventBase(d, dcNr, event(seconds, SAMPLE_EVENT_BOOKMARK, 0, 0, "bookmark"))
{
setText(Command::Base::tr("Add bookmark"));
}
AddEventDivemodeSwitch::AddEventDivemodeSwitch(struct dive *d, int dcNr, int seconds, int divemode) :
AddEventBase(d, dcNr, create_event(seconds, SAMPLE_EVENT_BOOKMARK, 0, divemode, QT_TRANSLATE_NOOP("gettextFromC", "modechange")))
AddEventBase(d, dcNr, event(seconds, SAMPLE_EVENT_BOOKMARK, 0, divemode, QT_TRANSLATE_NOOP("gettextFromC", "modechange")))
{
setText(Command::Base::tr("Add dive mode switch to %1").arg(gettextFromC::tr(divemode_text_ui[divemode])));
}
AddEventSetpointChange::AddEventSetpointChange(struct dive *d, int dcNr, int seconds, pressure_t pO2) :
AddEventBase(d, dcNr, create_event(seconds, SAMPLE_EVENT_PO2, 0, pO2.mbar, QT_TRANSLATE_NOOP("gettextFromC", "SP change"))),
AddEventBase(d, dcNr, event(seconds, SAMPLE_EVENT_PO2, 0, pO2.mbar, QT_TRANSLATE_NOOP("gettextFromC", "SP change"))),
divemode(CCR)
{
setText(Command::Base::tr("Add set point change")); // TODO: format pO2 value in bar or psi.
@ -91,11 +87,11 @@ void AddEventSetpointChange::redoit()
std::swap(get_dive_dc(d, dcNr)->divemode, divemode);
}
RenameEvent::RenameEvent(struct dive *d, int dcNr, struct event *ev, const char *name) : EventBase(d, dcNr),
eventToAdd(clone_event_rename(ev, name)),
eventToRemove(ev)
RenameEvent::RenameEvent(struct dive *d, int dcNr, int idx, const std::string name) : EventBase(d, dcNr),
idx(idx),
name(std::move(name))
{
setText(Command::Base::tr("Rename bookmark to %1").arg(name));
setText(Command::Base::tr("Rename bookmark to %1").arg(name.c_str()));
}
bool RenameEvent::workToBeDone()
@ -106,24 +102,25 @@ bool RenameEvent::workToBeDone()
void RenameEvent::redoit()
{
struct divecomputer *dc = get_dive_dc(d, dcNr);
swap_event(dc, eventToRemove, eventToAdd.get());
event *tmp = eventToRemove;
eventToRemove = eventToAdd.release();
eventToAdd.reset(tmp);
event *ev = get_event(dc, idx);
if (ev)
std::swap(ev->name, name);
}
void RenameEvent::undoit()
{
// Undo and redo do the same thing - they simply swap events
// Undo and redo do the same thing - they simply swap names
redoit();
}
RemoveEvent::RemoveEvent(struct dive *d, int dcNr, struct event *ev) : EventBase(d, dcNr),
eventToRemove(ev),
cylinder(ev->type == SAMPLE_EVENT_GASCHANGE2 || ev->type == SAMPLE_EVENT_GASCHANGE ?
ev->gas.index : -1)
RemoveEvent::RemoveEvent(struct dive *d, int dcNr, int idx) : EventBase(d, dcNr),
idx(idx), cylinder(-1)
{
setText(Command::Base::tr("Remove %1 event").arg(ev->name));
struct divecomputer *dc = get_dive_dc(d, dcNr);
event *ev = get_event(dc, idx);
if (ev && (ev->type == SAMPLE_EVENT_GASCHANGE2 || ev->type == SAMPLE_EVENT_GASCHANGE))
cylinder = ev->gas.index;
setText(Command::Base::tr("Remove %1 event").arg(ev->name.c_str()));
}
bool RemoveEvent::workToBeDone()
@ -134,16 +131,13 @@ bool RemoveEvent::workToBeDone()
void RemoveEvent::redoit()
{
struct divecomputer *dc = get_dive_dc(d, dcNr);
remove_event_from_dc(dc, eventToRemove);
eventToAdd.reset(eventToRemove); // take ownership of event
eventToRemove = nullptr;
ev = remove_event_from_dc(dc, idx);
}
void RemoveEvent::undoit()
{
struct divecomputer *dc = get_dive_dc(d, dcNr);
eventToRemove = eventToAdd.get();
add_event_to_dc(dc, eventToAdd.release()); // return ownership to backend
idx = add_event_to_dc(dc, std::move(ev));
}
void RemoveEvent::post() const
@ -165,18 +159,18 @@ AddGasSwitch::AddGasSwitch(struct dive *d, int dcNr, int seconds, int tank) : Ev
// There shouldn't be more than one gas change per time stamp. Just in case we'll
// support that anyway.
struct divecomputer *dc = get_dive_dc(d, dcNr);
struct event *gasChangeEvent = dc->events;
while ((gasChangeEvent = get_next_event_mutable(gasChangeEvent, "gaschange")) != NULL) {
if (gasChangeEvent->time.seconds == seconds) {
eventsToRemove.push_back(gasChangeEvent);
int idx = gasChangeEvent->gas.index;
if (std::find(cylinders.begin(), cylinders.end(), idx) == cylinders.end())
cylinders.push_back(idx); // cylinders might have changed their status
}
gasChangeEvent = gasChangeEvent->next;
// Note that we remove events in reverse order so that the indexes don't change
// meaning while removing. This should be an extremely rare case anyway.
for (int idx = static_cast<int>(dc->events.size()) - 1; idx > 0; --idx) {
const event &ev = dc->events[idx];
if (ev.time.seconds == seconds && ev.name == "gaschange")
eventsToRemove.push_back(idx);
if (std::find(cylinders.begin(), cylinders.end(), ev.gas.index) == cylinders.end())
cylinders.push_back(ev.gas.index); // cylinders might have changed their status
}
eventsToAdd.emplace_back(create_gas_switch_event(d, dc, seconds, tank));
eventsToAdd.push_back(create_gas_switch_event(d, dc, seconds, tank));
}
bool AddGasSwitch::workToBeDone()
@ -186,20 +180,21 @@ bool AddGasSwitch::workToBeDone()
void AddGasSwitch::redoit()
{
std::vector<OwningEventPtr> newEventsToAdd;
std::vector<event *> newEventsToRemove;
std::vector<event> newEventsToAdd;
std::vector<int> newEventsToRemove;
newEventsToAdd.reserve(eventsToRemove.size());
newEventsToRemove.reserve(eventsToAdd.size());
struct divecomputer *dc = get_dive_dc(d, dcNr);
for (event *ev: eventsToRemove) {
remove_event_from_dc(dc, ev);
newEventsToAdd.emplace_back(ev); // take ownership of event
}
for (OwningEventPtr &ev: eventsToAdd) {
newEventsToRemove.push_back(ev.get());
add_event_to_dc(dc, ev.release()); // return ownership to backend
}
for (int idx: eventsToRemove)
newEventsToAdd.push_back(remove_event_from_dc(dc, idx));
for (auto &ev: eventsToAdd)
newEventsToRemove.push_back(add_event_to_dc(dc, std::move(ev)));
// Make sure that events are removed in reverse order
std::sort(newEventsToRemove.begin(), newEventsToRemove.end(), std::greater<int>());
eventsToAdd = std::move(newEventsToAdd);
eventsToRemove = std::move(newEventsToRemove);

View File

@ -6,15 +6,12 @@
#include "command_base.h"
#include "core/divemode.h"
#include "core/event.h"
// We put everything in a namespace, so that we can shorten names without polluting the global namespace
namespace Command {
// Events are a strange thing: they contain there own description which means
// that on changing the description a new object must be allocated. Moreover,
// it means that these objects can't be collected in a table.
// Therefore, the undo commands work on events as they do with dives: using
// owning pointers. See comments in command_base.h
// Pointers to events are not stable, so we always store indexes.
class EventBase : public Base {
protected:
@ -25,8 +22,7 @@ protected:
virtual void undoit() = 0;
// Note: we store dive and the divecomputer-number instead of a pointer to the divecomputer.
// Since one divecomputer is integrated into the dive structure, pointers to divecomputers
// are probably not stable.
// Pointers to divecomputers are not stable.
struct dive *d;
int dcNr;
private:
@ -35,15 +31,15 @@ private:
class AddEventBase : public EventBase {
public:
AddEventBase(struct dive *d, int dcNr, struct event *ev); // Takes ownership of event!
AddEventBase(struct dive *d, int dcNr, struct event ev); // Takes ownership of event!
protected:
void undoit() override;
void redoit() override;
private:
bool workToBeDone() override;
OwningEventPtr eventToAdd; // for redo
event *eventToRemove; // for undo
struct event ev; // for redo
int idx; // for undo
};
class AddEventBookmark : public AddEventBase {
@ -67,28 +63,28 @@ private:
class RenameEvent : public EventBase {
public:
RenameEvent(struct dive *d, int dcNr, struct event *ev, const char *name);
RenameEvent(struct dive *d, int dcNr, int idx, const std::string name);
private:
bool workToBeDone() override;
void undoit() override;
void redoit() override;
OwningEventPtr eventToAdd; // for undo and redo
event *eventToRemove; // for undo and redo
int idx; // for undo and redo
std::string name; // for undo and redo
};
class RemoveEvent : public EventBase {
public:
RemoveEvent(struct dive *d, int dcNr, struct event *ev);
RemoveEvent(struct dive *d, int dcNr, int idx);
private:
bool workToBeDone() override;
void undoit() override;
void redoit() override;
void post() const; // Called to fix up dives should a gas-change have happened.
OwningEventPtr eventToAdd; // for undo
event *eventToRemove; // for redo
int cylinder; // affected cylinder (if removing gas switch). <0: not a gas switch.
event ev; // for undo
int idx; // for redo
int cylinder; // affected cylinder (if removing gas switch). <0: not a gas switch.
};
class AddGasSwitch : public EventBase {
@ -100,8 +96,8 @@ private:
void redoit() override;
std::vector<int> cylinders; // cylinders that are modified
std::vector<OwningEventPtr> eventsToAdd;
std::vector<event *> eventsToRemove;
std::vector<event> eventsToAdd;
std::vector<int> eventsToRemove;
};
} // namespace Command

View File

@ -1,23 +1,26 @@
// SPDX-License-Identifier: GPL-2.0
#include "command_filter.h"
#include "core/divelog.h"
#include "core/filterpreset.h"
#include "core/filterpresettable.h"
#include "core/subsurface-qt/divelistnotifier.h"
namespace Command {
static int createFilterPreset(const std::string &name, const FilterData &data)
{
int index = filter_preset_add(name, data);
int index = divelog.filter_presets.add(name, data);
emit diveListNotifier.filterPresetAdded(index);
return index;
}
static std::pair<std::string, FilterData> removeFilterPreset(int index)
{
std::string oldName = filter_preset_name(index);
FilterData oldData = filter_preset_get(index);
filter_preset_delete(index);
const filter_preset &preset = divelog.filter_presets[index];
std::string oldName = preset.name;
FilterData oldData = preset.data;
divelog.filter_presets.remove(index);
emit diveListNotifier.filterPresetRemoved(index);
return { oldName, oldData };
}
@ -46,7 +49,8 @@ void CreateFilterPreset::undo()
RemoveFilterPreset::RemoveFilterPreset(int indexIn) : index(indexIn)
{
setText(Command::Base::tr("Delete filter preset %1").arg(QString(filter_preset_name(index).c_str())));
const std::string &name = divelog.filter_presets[index].name;
setText(Command::Base::tr("Delete filter preset %1").arg(QString::fromStdString(name)));
}
bool RemoveFilterPreset::workToBeDone()
@ -68,7 +72,8 @@ void RemoveFilterPreset::undo()
EditFilterPreset::EditFilterPreset(int indexIn, const FilterData &dataIn) :
index(indexIn), data(dataIn)
{
setText(Command::Base::tr("Edit filter preset %1").arg(QString(filter_preset_name(index).c_str())));
const std::string &name = divelog.filter_presets[index].name;
setText(Command::Base::tr("Edit filter preset %1").arg(QString::fromStdString(name)));
}
bool EditFilterPreset::workToBeDone()
@ -78,9 +83,8 @@ bool EditFilterPreset::workToBeDone()
void EditFilterPreset::redo()
{
FilterData oldData = filter_preset_get(index);
filter_preset_set(index, data);
data = std::move(oldData);
filter_preset &preset = divelog.filter_presets[index];
std::swap(data, preset.data);
}
void EditFilterPreset::undo()

View File

@ -2,15 +2,16 @@
#include "command_pictures.h"
#include "core/errorhelper.h"
#include "core/range.h"
#include "core/subsurface-qt/divelistnotifier.h"
#include "qt-models/divelocationmodel.h"
namespace Command {
static picture *dive_get_picture(const dive *d, const QString &fn)
static picture *dive_get_picture(dive *d, const QString &fn)
{
int idx = get_picture_idx(&d->pictures, qPrintable(fn));
return idx < 0 ? nullptr : &d->pictures.pictures[idx];
int idx = get_picture_idx(d->pictures, fn.toStdString());
return idx < 0 ? nullptr : &d->pictures[idx];
}
SetPictureOffset::SetPictureOffset(dive *dIn, const QString &filenameIn, offset_t offsetIn) :
@ -33,7 +34,7 @@ void SetPictureOffset::redo()
// Instead of trying to be smart, let's simply resort the picture table.
// If someone complains about speed, do our usual "smart" thing.
sort_picture_table(&d->pictures);
std::sort(d->pictures.begin(), d->pictures.end());
emit diveListNotifier.pictureOffsetChanged(d, filename, newOffset);
invalidate_dive_cache(d);
}
@ -55,10 +56,9 @@ static PictureListForDeletion filterPictureListForDeletion(const PictureListForD
PictureListForDeletion res;
res.d = p.d;
res.filenames.reserve(p.filenames.size());
for (int i = 0; i < p.d->pictures.nr; ++i) {
std::string fn = p.d->pictures.pictures[i].filename;
if (std::find(p.filenames.begin(), p.filenames.end(), fn) != p.filenames.end())
res.filenames.push_back(fn);
for (auto &pic: p.d->pictures) {
if (range_contains(p.filenames, pic.filename))
res.filenames.push_back(pic.filename);
}
return res;
}
@ -72,14 +72,14 @@ static std::vector<PictureListForAddition> removePictures(std::vector<PictureLis
PictureListForAddition toAdd;
toAdd.d = list.d;
for (const std::string &fn: list.filenames) {
int idx = get_picture_idx(&list.d->pictures, fn.c_str());
int idx = get_picture_idx(list.d->pictures, fn);
if (idx < 0) {
report_info("removePictures(): picture disappeared!");
continue; // Huh? We made sure that this can't happen by filtering out non-existent pictures.
}
filenames.push_back(QString::fromStdString(fn));
toAdd.pics.emplace_back(list.d->pictures.pictures[idx]);
remove_from_picture_table(&list.d->pictures, idx);
toAdd.pics.emplace_back(list.d->pictures[idx]);
list.d->pictures.erase(list.d->pictures.begin() + idx);
}
if (!toAdd.pics.empty())
res.push_back(toAdd);
@ -98,17 +98,17 @@ static std::vector<PictureListForDeletion> addPictures(std::vector<PictureListFo
// happen, as we checked that before.
std::vector<PictureListForDeletion> res;
for (const PictureListForAddition &list: picturesToAdd) {
QVector<PictureObj> picsForSignal;
QVector<picture> picsForSignal;
PictureListForDeletion toRemove;
toRemove.d = list.d;
for (const PictureObj &pic: list.pics) {
int idx = get_picture_idx(&list.d->pictures, pic.filename.c_str()); // This should *not* already exist!
for (const picture &pic: list.pics) {
int idx = get_picture_idx(list.d->pictures, pic.filename); // This should *not* already exist!
if (idx >= 0) {
report_info("addPictures(): picture disappeared!");
continue; // Huh? We made sure that this can't happen by filtering out existing pictures.
}
picsForSignal.push_back(pic);
add_picture(&list.d->pictures, pic.toCore());
add_picture(list.d->pictures, pic);
toRemove.filenames.push_back(pic.filename);
}
if (!toRemove.filenames.empty())
@ -164,16 +164,15 @@ AddPictures::AddPictures(const std::vector<PictureListForAddition> &pictures) :
std::sort(p.pics.begin(), p.pics.end());
// Find a picture with a location
auto it = std::find_if(p.pics.begin(), p.pics.end(), [](const PictureObj &p) { return has_location(&p.location); });
auto it = std::find_if(p.pics.begin(), p.pics.end(), [](const picture &p) { return has_location(&p.location); });
if (it != p.pics.end()) {
// There is a dive with a location, we might want to modify the dive accordingly.
struct dive_site *ds = p.d->dive_site;
if (!ds) {
// This dive doesn't yet have a dive site -> add a new dive site.
QString name = Command::Base::tr("unnamed dive site");
dive_site *ds = alloc_dive_site_with_gps(qPrintable(name), &it->location);
sitesToAdd.emplace_back(ds);
sitesToSet.push_back({ p.d, ds });
sitesToAdd.push_back(std::make_unique<dive_site>(qPrintable(name), it->location));
sitesToSet.push_back({ p.d, sitesToAdd.back().get() });
} else if (!dive_site_has_gps_location(ds)) {
// This dive has a dive site, but without coordinates. Let's add them.
sitesToEdit.push_back({ ds, it->location });
@ -201,7 +200,7 @@ void AddPictures::swapDiveSites()
unregister_dive_from_dive_site(entry.d); // the dive-site pointer in the dive is now NULL
std::swap(ds, entry.ds);
if (ds)
add_dive_to_dive_site(entry.d, ds);
ds->add_dive(entry.d);
emit diveListNotifier.divesChanged(QVector<dive *>{ entry.d }, DiveField::DIVESITE);
}
@ -218,9 +217,9 @@ void AddPictures::undo()
// Remove dive sites
for (dive_site *siteToRemove: sitesToRemove) {
int idx = unregister_dive_site(siteToRemove);
sitesToAdd.emplace_back(siteToRemove);
emit diveListNotifier.diveSiteDeleted(siteToRemove, idx); // Inform frontend of removed dive site.
auto res = divelog.sites.pull(siteToRemove);
sitesToAdd.push_back(std::move(res.ptr));
emit diveListNotifier.diveSiteDeleted(siteToRemove, res.idx); // Inform frontend of removed dive site.
}
sitesToRemove.clear();
}
@ -228,10 +227,10 @@ void AddPictures::undo()
void AddPictures::redo()
{
// Add dive sites
for (OwningDiveSitePtr &siteToAdd: sitesToAdd) {
sitesToRemove.push_back(siteToAdd.get());
int idx = register_dive_site(siteToAdd.release()); // Return ownership to backend.
emit diveListNotifier.diveSiteAdded(sitesToRemove.back(), idx); // Inform frontend of new dive site.
for (std::unique_ptr<dive_site> &siteToAdd: sitesToAdd) {
auto res = divelog.sites.register_site(std::move(siteToAdd)); // Return ownership to backend.
sitesToRemove.push_back(res.ptr);
emit diveListNotifier.diveSiteAdded(sitesToRemove.back(), res.idx); // Inform frontend of new dive site.
}
sitesToAdd.clear();

View File

@ -48,7 +48,7 @@ private:
location_t location;
};
std::vector<PictureListForAddition> picturesToAdd; // for redo
std::vector<OwningDiveSitePtr> sitesToAdd; //for redo
std::vector<std::unique_ptr<dive_site>> sitesToAdd; //for redo
std::vector<PictureListForDeletion> picturesToRemove; // for undo
std::vector<dive_site *> sitesToRemove; // for undo
std::vector<DiveSiteEntry> sitesToSet; // for redo and undo

View File

@ -17,7 +17,7 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
endif()
if(FTDISUPPORT)
set(SERIAL_FTDI serial_ftdi.c)
set(SERIAL_FTDI serial_ftdi.cpp)
endif()
if(BTSUPPORT)
@ -61,29 +61,29 @@ set(SUBSURFACE_CORE_LIB_SRCS
devicedetails.h
dive.cpp
dive.h
divecomputer.c
divecomputer.cpp
divecomputer.h
dive.h
divefilter.cpp
divefilter.h
divelist.c
divelist.cpp
divelist.h
divelog.cpp
divelog.h
divelogexportlogic.cpp
divelogexportlogic.h
divesite-helper.cpp
divesite.c
divesite.cpp
divesite.h
divesitetable.h
divesitehelpers.cpp
divesitehelpers.h
downloadfromdcthread.cpp
downloadfromdcthread.h
event.c
event.cpp
event.h
eventtype.cpp
eventtype.h
equipment.c
equipment.cpp
equipment.h
errorhelper.cpp
exif.cpp
@ -95,14 +95,16 @@ set(SUBSURFACE_CORE_LIB_SRCS
filterconstraint.h
filterpreset.cpp
filterpreset.h
filterpresettable.cpp
filterpresettable.h
format.cpp
format.h
fulltext.cpp
fulltext.h
gas.c
gas.cpp
gas.h
gas-model.c
gaspressures.c
gas-model.cpp
gaspressures.cpp
gaspressures.h
gettext.h
gettextfromc.cpp
@ -131,21 +133,18 @@ set(SUBSURFACE_CORE_LIB_SRCS
metadata.h
metrics.cpp
metrics.h
ostctools.c
owning_ptrs.h
ostctools.cpp
parse-gpx.cpp
parse-xml.cpp
parse.cpp
parse.h
picture.c
picture.cpp
picture.h
pictureobj.cpp
pictureobj.h
planner.cpp
planner.h
plannernotes.cpp
pref.h
pref.c
pref.cpp
profile.cpp
profile.h
qt-gui.h
@ -158,18 +157,17 @@ set(SUBSURFACE_CORE_LIB_SRCS
save-git.cpp
save-html.cpp
save-html.h
save-profiledata.c
save-profiledata.cpp
save-xml.cpp
selection.cpp
selection.h
sha1.c
sha1.cpp
sha1.h
ssrf.h
statistics.c
statistics.cpp
statistics.h
string-format.h
string-format.cpp
strtod.c
strtod.cpp
subsurface-float.h
subsurface-string.cpp
subsurface-string.h
@ -179,23 +177,23 @@ set(SUBSURFACE_CORE_LIB_SRCS
subsurfacesysinfo.h
tag.cpp
tag.h
taxonomy.c
taxonomy.cpp
taxonomy.h
time.cpp
timer.c
timer.h
trip.c
trip.cpp
trip.h
triptable.cpp
triptable.h
uemis-downloader.cpp
uemis.c
uemis.cpp
uemis.h
units.h
units.c
units.cpp
uploadDiveShare.cpp
uploadDiveShare.h
uploadDiveLogsDE.cpp
uploadDiveLogsDE.h
version.c
version.cpp
version.h
videoframeextractor.cpp
videoframeextractor.h

View File

@ -41,14 +41,12 @@ static std::string make_default_filename()
return system_default_path() + "/subsurface.xml";
}
extern "C" {
const char android_system_divelist_default_font[] = "Roboto";
const char *system_divelist_default_font = android_system_divelist_default_font;
double system_divelist_default_font_size = -1;
int get_usb_fd(uint16_t idVendor, uint16_t idProduct);
void subsurface_OS_pref_setup(void)
void subsurface_OS_pref_setup()
{
}
@ -58,13 +56,13 @@ bool subsurface_ignore_font(const char *font)
return false;
}
const char *system_default_directory(void)
const char *system_default_directory()
{
static const std::string path = system_default_path();
return path.c_str();
}
const char *system_default_filename(void)
const char *system_default_filename()
{
static const std::string fn = make_default_filename();
return fn.c_str();
@ -158,12 +156,10 @@ int get_usb_fd(uint16_t idVendor, uint16_t idProduct)
}
JNIEXPORT void JNICALL
Java_org_subsurfacedivelog_mobile_SubsurfaceMobileActivity_setUsbDevice(JNIEnv *env,
jobject obj,
Java_org_subsurfacedivelog_mobile_SubsurfaceMobileActivity_setUsbDevice(JNIEnv *,
jobject,
jobject javaUsbDevice)
{
Q_UNUSED (obj)
Q_UNUSED (env)
QAndroidJniObject usbDevice(javaUsbDevice);
if (usbDevice.isValid()) {
android_usb_serial_device_descriptor descriptor = getDescriptor(usbDevice);
@ -177,12 +173,10 @@ Java_org_subsurfacedivelog_mobile_SubsurfaceMobileActivity_setUsbDevice(JNIEnv *
}
JNIEXPORT void JNICALL
Java_org_subsurfacedivelog_mobile_SubsurfaceMobileActivity_restartDownload(JNIEnv *env,
jobject obj,
Java_org_subsurfacedivelog_mobile_SubsurfaceMobileActivity_restartDownload(JNIEnv *,
jobject,
jobject javaUsbDevice)
{
Q_UNUSED (obj)
Q_UNUSED (env)
QAndroidJniObject usbDevice(javaUsbDevice);
if (usbDevice.isValid()) {
android_usb_serial_device_descriptor descriptor = getDescriptor(usbDevice);
@ -237,12 +231,12 @@ int subsurface_zip_close(struct zip *zip)
}
/* win32 console */
void subsurface_console_init(void)
void subsurface_console_init()
{
/* NOP */
}
void subsurface_console_exit(void)
void subsurface_console_exit()
{
/* NOP */
}
@ -251,7 +245,6 @@ bool subsurface_user_is_root()
{
return false;
}
}
/* called from QML manager */
void checkPendingIntents()

View File

@ -200,7 +200,7 @@ void CheckCloudConnection::gotContinent(QNetworkReply *reply)
}
// helper to be used from C code
extern "C" bool canReachCloudServer(struct git_info *info)
bool canReachCloudServer(struct git_info *info)
{
if (verbose)
qWarning() << "Cloud storage: checking connection to cloud server" << info->url.c_str();

View File

@ -4,7 +4,6 @@
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
#endif
#include "ssrf.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@ -445,7 +444,7 @@ static void cochran_parse_samples(struct dive *dive, const unsigned char *log,
unsigned int ndl = 0;
unsigned int in_deco = 0, deco_ceiling = 0, deco_time = 0;
struct divecomputer *dc = &dive->dc;
struct divecomputer *dc = &dive->dcs[0];
struct sample *sample;
// Initialize stat variables
@ -495,10 +494,6 @@ static void cochran_parse_samples(struct dive *dive, const unsigned char *log,
while (offset + config.sample_size < size) {
s = samples + offset;
// Start with an empty sample
sample = prepare_sample(dc);
sample->time.seconds = sample_cnt * profile_period;
// Check for event
if (s[0] & 0x80) {
cochran_dive_event(dc, s, sample_cnt * profile_period, &in_deco, &deco_ceiling, &deco_time);
@ -506,6 +501,10 @@ static void cochran_parse_samples(struct dive *dive, const unsigned char *log,
continue;
}
// Start with an empty sample
sample = prepare_sample(dc);
sample->time.seconds = sample_cnt * profile_period;
// Depth is in every sample
depth_sample = (double)(s[0] & 0x3F) / 4 * (s[0] & 0x40 ? -1 : 1);
depth += depth_sample;
@ -592,8 +591,6 @@ static void cochran_parse_samples(struct dive *dive, const unsigned char *log,
sample->sensor[0] = 0;
sample->pressure[0].mbar = lrint(psi * PSI / 100);
finish_sample(dc);
offset += config.sample_size;
sample_cnt++;
}
@ -604,13 +601,11 @@ static void cochran_parse_samples(struct dive *dive, const unsigned char *log,
static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
const unsigned char *in, unsigned size,
struct dive_table *table)
struct dive_table &table)
{
unsigned char *buf = (unsigned char *)malloc(size);
struct dive *dive;
struct divecomputer *dc;
struct tm tm = {0};
uint32_t csum[5];
double max_depth, avg_depth, min_temp;
unsigned int duration = 0, corrupt_dive = 0;
@ -668,8 +663,8 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
puts("\nSample Data\n");
#endif
dive = alloc_dive();
dc = &dive->dc;
auto dive = std::make_unique<struct dive>();
dc = &dive->dcs[0];
unsigned char *log = (buf + 0x4914);
@ -677,24 +672,22 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
case TYPE_GEMINI:
case TYPE_COMMANDER:
if (config.type == TYPE_GEMINI) {
cylinder_t cyl = empty_cylinder;
dc->model = "Gemini";
dc->deviceid = buf[0x18c] * 256 + buf[0x18d]; // serial no
fill_default_cylinder(dive, &cyl);
cylinder_t cyl = default_cylinder(dive.get());
cyl.gasmix.o2.permille = (log[CMD_O2_PERCENT] / 256
+ log[CMD_O2_PERCENT + 1]) * 10;
cyl.gasmix.he.permille = 0;
add_cylinder(&dive->cylinders, 0, cyl);
add_cylinder(&dive->cylinders, 0, std::move(cyl));
} else {
dc->model = "Commander";
dc->deviceid = array_uint32_le(buf + 0x31e); // serial no
for (g = 0; g < 2; g++) {
cylinder_t cyl = empty_cylinder;
fill_default_cylinder(dive, &cyl);
cylinder_t cyl = default_cylinder(dive.get());
cyl.gasmix.o2.permille = (log[CMD_O2_PERCENT + g * 2] / 256
+ log[CMD_O2_PERCENT + g * 2 + 1]) * 10;
cyl.gasmix.he.permille = 0;
add_cylinder(&dive->cylinders, g, cyl);
add_cylinder(&dive->cylinders, g, std::move(cyl));
}
}
@ -719,8 +712,7 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
* (double) log[CMD_ALTITUDE] * 250 * FEET, 5.25588) * 1000);
dc->salinity = 10000 + 150 * log[CMD_WATER_CONDUCTIVITY];
SHA1(log + CMD_NUMBER, 2, (unsigned char *)csum);
dc->diveid = csum[0];
dc->diveid = SHA1_uint32(log + CMD_NUMBER, 2);
if (log[CMD_MAX_DEPTH] == 0xff && log[CMD_MAX_DEPTH + 1] == 0xff)
corrupt_dive = 1;
@ -733,15 +725,14 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
dc->model = "EMC";
dc->deviceid = array_uint32_le(buf + 0x31e); // serial no
for (g = 0; g < 4; g++) {
cylinder_t cyl = empty_cylinder;
fill_default_cylinder(dive, &cyl);
cylinder_t cyl = default_cylinder(dive.get());
cyl.gasmix.o2.permille =
(log[EMC_O2_PERCENT + g * 2] / 256
+ log[EMC_O2_PERCENT + g * 2 + 1]) * 10;
cyl.gasmix.he.permille =
(log[EMC_HE_PERCENT + g * 2] / 256
+ log[EMC_HE_PERCENT + g * 2 + 1]) * 10;
add_cylinder(&dive->cylinders, g, cyl);
add_cylinder(&dive->cylinders, g, std::move(cyl));
}
tm.tm_year = log[EMC_YEAR];
@ -765,8 +756,7 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
* (double) log[EMC_ALTITUDE] * 250 * FEET, 5.25588) * 1000);
dc->salinity = 10000 + 150 * (log[EMC_WATER_CONDUCTIVITY] & 0x3);
SHA1(log + EMC_NUMBER, 2, (unsigned char *)csum);
dc->diveid = csum[0];
dc->diveid = SHA1_uint32(log + EMC_NUMBER, 2);
if (log[EMC_MAX_DEPTH] == 0xff && log[EMC_MAX_DEPTH + 1] == 0xff)
corrupt_dive = 1;
@ -782,7 +772,7 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
if (sample_pre_offset < sample_end_offset && sample_end_offset != 0xffffffff)
sample_size = sample_end_offset - sample_pre_offset;
cochran_parse_samples(dive, buf + 0x4914, buf + 0x4914
cochran_parse_samples(dive.get(), buf + 0x4914, buf + 0x4914
+ config.logbook_size, sample_size,
&duration, &max_depth, &avg_depth, &min_temp);
@ -794,7 +784,7 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
dc->duration.seconds = duration;
}
record_dive_to_table(dive, table);
table.record_dive(std::move(dive));
free(buf);
}

View File

@ -81,7 +81,7 @@ static inline QColor makeColor(double r, double g, double b, double a = 1.0)
#define VELOCITY_COLORS_START_IDX VELO_STABLE
#define VELOCITY_COLORS 5
typedef enum {
enum color_index_t {
/* SAC colors. Order is important, the SAC_COLORS_START_IDX define above. */
SAC_1,
SAC_2,
@ -145,7 +145,7 @@ typedef enum {
CALC_CEILING_DEEP,
TISSUE_PERCENTAGE,
DURATION_LINE
} color_index_t;
};
QColor getColor(const color_index_t i, bool isGrayscale = false);
QColor getSacColor(int sac, int diveSac);

View File

@ -68,16 +68,14 @@ static QString writeGasDetails(gas g)
bool ConfigureDiveComputer::saveXMLBackup(const QString &fileName, const DeviceDetails &details, device_data_t *data)
{
QString xml = "";
QString vendor = data->vendor;
QString product = data->product;
QXmlStreamWriter writer(&xml);
writer.setAutoFormatting(true);
writer.writeStartDocument();
writer.writeStartElement("DiveComputerSettingsBackup");
writer.writeStartElement("DiveComputer");
writer.writeTextElement("Vendor", vendor);
writer.writeTextElement("Product", product);
writer.writeTextElement("Vendor", QString::fromStdString(data->vendor));
writer.writeTextElement("Product", QString::fromStdString(data->product));
writer.writeEndElement();
writer.writeStartElement("Settings");
writer.writeTextElement("CustomText", details.customText);

View File

@ -15,11 +15,11 @@
#include "units.h"
#include "device.h"
#include "file.h"
#include "format.h"
#include "divesite.h"
#include "dive.h"
#include "divelog.h"
#include "errorhelper.h"
#include "ssrf.h"
#include "tag.h"
static unsigned int two_bytes_to_int(unsigned char x, unsigned char y)
@ -104,21 +104,23 @@ static int read_file_header(unsigned char *buffer)
* Returns libdc's equivalent model number (also from g_models) or zero if
* this a manual dive.
*/
static int dtrak_prepare_data(int model, device_data_t *dev_data)
static int dtrak_prepare_data(int model, device_data_t &dev_data)
{
dc_descriptor_t *d = NULL;
int i = 0;
while (model != g_models[i].model_num && g_models[i].model_num != 0xEE)
i++;
dev_data->model = copy_string(g_models[i].name);
dev_data->vendor = (const char *)malloc(strlen(g_models[i].name) + 1);
sscanf(g_models[i].name, "%[A-Za-z] ", (char *)dev_data->vendor);
dev_data->product = copy_string(strchr(g_models[i].name, ' ') + 1);
dev_data.model = g_models[i].name;
dev_data.vendor = (const char *)malloc(strlen(g_models[i].name) + 1);
dev_data.vendor.clear();
for (const char *s = g_models[i].name; isalpha(*s); ++s)
dev_data.vendor += *s;
dev_data.product = strchr(g_models[i].name, ' ') + 1;
d = get_descriptor(g_models[i].type, g_models[i].libdc_num);
if (d)
dev_data->descriptor = d;
dev_data.descriptor = d;
else
return 0;
return g_models[i].libdc_num;
@ -129,14 +131,11 @@ static int dtrak_prepare_data(int model, device_data_t *dev_data)
* Just get the first in the user's list for given size.
* Reaching the end of the list means there is no tank of this size.
*/
static const char *cyl_type_by_size(int size)
static std::string cyl_type_by_size(int size)
{
for (int i = 0; i < tank_info_table.nr; ++i) {
const struct tank_info *ti = &tank_info_table.infos[i];
if (ti->ml == size)
return ti->name;
}
return "";
auto it = std::find_if(tank_info_table.begin(), tank_info_table.end(),
[size] (const tank_info &info) { return info.ml == size; });
return it != tank_info_table.end() ? it->name : std::string();
}
/*
@ -161,21 +160,19 @@ static dc_status_t dt_libdc_buffer(unsigned char *ptr, int prf_length, int dc_mo
static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct divelog *log, char *maxbuf)
{
int rc, profile_length, libdc_model;
char *tmp_notes_str = NULL;
unsigned char *tmp_string1 = NULL,
*locality = NULL,
*dive_point = NULL,
*compl_buffer,
*membuf = runner;
char buffer[1024];
unsigned char tmp_1byte;
unsigned int tmp_2bytes;
unsigned long tmp_4bytes;
struct dive_site *ds;
std::string tmp_notes_str;
char is_nitrox = 0, is_O2 = 0, is_SCR = 0;
device_data_t *devdata = (device_data_t *)calloc(1, sizeof(device_data_t));
devdata->log = log;
device_data_t devdata;
devdata.log = log;
/*
* Parse byte to byte till next dive entry
@ -195,7 +192,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
* Next, Time in minutes since 00:00
*/
read_bytes(2);
dt_dive->dc.when = dt_dive->when = (timestamp_t)date_time_to_ssrfc(tmp_4bytes, tmp_2bytes);
dt_dive->dcs[0].when = dt_dive->when = (timestamp_t)date_time_to_ssrfc(tmp_4bytes, tmp_2bytes);
/*
* Now, Locality, 1st byte is long of string, rest is string
@ -213,11 +210,13 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
* Subsurface only have a location variable, so we have to merge DTrak's
* Locality and Dive points.
*/
snprintf(buffer, sizeof(buffer), "%s, %s", locality, dive_point);
ds = get_dive_site_by_name(buffer, log->sites);
if (!ds)
ds = create_dive_site(buffer, log->sites);
add_dive_to_dive_site(dt_dive, ds);
{
std::string buffer2 = std::string((char *)locality) + " " + (char *)dive_point;
struct dive_site *ds = log->sites.get_by_name(buffer2);
if (!ds)
ds = log->sites.create(buffer2);
ds->add_dive(dt_dive);
}
free(locality);
locality = NULL;
free(dive_point);
@ -241,19 +240,19 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
read_bytes(1);
switch (tmp_1byte) {
case 1:
dt_dive->dc.surface_pressure.mbar = 1013;
dt_dive->dcs[0].surface_pressure.mbar = 1013;
break;
case 2:
dt_dive->dc.surface_pressure.mbar = 932;
dt_dive->dcs[0].surface_pressure.mbar = 932;
break;
case 3:
dt_dive->dc.surface_pressure.mbar = 828;
dt_dive->dcs[0].surface_pressure.mbar = 828;
break;
case 4:
dt_dive->dc.surface_pressure.mbar = 735;
dt_dive->dcs[0].surface_pressure.mbar = 735;
break;
default:
dt_dive->dc.surface_pressure.mbar = 1013;
dt_dive->dcs[0].surface_pressure.mbar = 1013;
}
/*
@ -261,32 +260,32 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
*/
read_bytes(2);
if (tmp_2bytes != 0x7FFF)
dt_dive->dc.surfacetime.seconds = (uint32_t) tmp_2bytes * 60;
dt_dive->dcs[0].surfacetime.seconds = (uint32_t) tmp_2bytes * 60;
/*
* Weather, values table, 0 to 6
* Subsurface don't have this record but we can use tags
*/
dt_dive->tag_list = NULL;
dt_dive->tags.clear();
read_bytes(1);
switch (tmp_1byte) {
case 1:
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "clear")));
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "clear"));
break;
case 2:
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "misty")));
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "misty"));
break;
case 3:
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "fog")));
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "fog"));
break;
case 4:
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "rain")));
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "rain"));
break;
case 5:
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "storm")));
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "storm"));
break;
case 6:
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "snow")));
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "snow"));
break;
default:
// unknown, do nothing
@ -298,7 +297,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
*/
read_bytes(2);
if (tmp_2bytes != 0x7FFF)
dt_dive->dc.airtemp.mkelvin = C_to_mkelvin((double)(tmp_2bytes / 100));
dt_dive->dcs[0].airtemp.mkelvin = C_to_mkelvin((double)(tmp_2bytes / 100));
/*
* Dive suit, values table, 0 to 6
@ -306,22 +305,22 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
read_bytes(1);
switch (tmp_1byte) {
case 1:
dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "No suit"));
dt_dive->suit = "No suit";
break;
case 2:
dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Shorty"));
dt_dive->suit = "Shorty";
break;
case 3:
dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Combi"));
dt_dive->suit = "Combi";
break;
case 4:
dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Wet suit"));
dt_dive->suit = "Wet suit";
break;
case 5:
dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Semidry suit"));
dt_dive->suit = "Semidry suit";
break;
case 6:
dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Dry suit"));
dt_dive->suit = "Dry suit";
break;
default:
// unknown, do nothing
@ -335,14 +334,14 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
*/
read_bytes(2);
if (tmp_2bytes != 0x7FFF) {
cylinder_t cyl = empty_cylinder;
cylinder_t cyl;
cyl.type.size.mliter = tmp_2bytes * 10;
cyl.type.description = cyl_type_by_size(tmp_2bytes * 10);
cyl.start.mbar = 200000;
cyl.gasmix.he.permille = 0;
cyl.gasmix.o2.permille = 210;
cyl.manually_added = true;
add_cloned_cylinder(&dt_dive->cylinders, cyl);
dt_dive->cylinders.push_back(std::move(cyl));
}
/*
@ -350,14 +349,14 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
*/
read_bytes(2);
if (tmp_2bytes != 0x7FFF)
dt_dive->maxdepth.mm = dt_dive->dc.maxdepth.mm = (int32_t)tmp_2bytes * 10;
dt_dive->maxdepth.mm = dt_dive->dcs[0].maxdepth.mm = (int32_t)tmp_2bytes * 10;
/*
* Dive time in minutes.
*/
read_bytes(2);
if (tmp_2bytes != 0x7FFF)
dt_dive->duration.seconds = dt_dive->dc.duration.seconds = (uint32_t)tmp_2bytes * 60;
dt_dive->duration.seconds = dt_dive->dcs[0].duration.seconds = (uint32_t)tmp_2bytes * 60;
/*
* Minimum water temperature in C*100. If unknown, set it to 0K which
@ -365,7 +364,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
*/
read_bytes(2);
if (tmp_2bytes != 0x7fff)
dt_dive->watertemp.mkelvin = dt_dive->dc.watertemp.mkelvin = C_to_mkelvin((double)(tmp_2bytes / 100));
dt_dive->watertemp.mkelvin = dt_dive->dcs[0].watertemp.mkelvin = C_to_mkelvin((double)(tmp_2bytes / 100));
else
dt_dive->watertemp.mkelvin = 0;
@ -373,7 +372,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
* Air used in bar*100.
*/
read_bytes(2);
if (tmp_2bytes != 0x7FFF && dt_dive->cylinders.nr > 0)
if (tmp_2bytes != 0x7FFF && dt_dive->cylinders.size() > 0)
get_cylinder(dt_dive, 0)->gas_used.mliter = lrint(get_cylinder(dt_dive, 0)->type.size.mliter * (tmp_2bytes / 100.0));
/*
@ -382,30 +381,30 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
*/
read_bytes(1);
if (bit_set(tmp_1byte, 2))
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "no stop")));
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "no stop"));
if (bit_set(tmp_1byte, 3))
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "deco")));
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "deco"));
if (bit_set(tmp_1byte, 4))
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "single ascent")));
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "single ascent"));
if (bit_set(tmp_1byte, 5))
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "multiple ascent")));
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "multiple ascent"));
if (bit_set(tmp_1byte, 6))
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "fresh water")));
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "fresh water"));
if (bit_set(tmp_1byte, 7))
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "salt water")));
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "salt water"));
/*
* Dive Type 2 - Bit table, use tags again
*/
read_bytes(1);
if (bit_set(tmp_1byte, 0)) {
taglist_add_tag(&dt_dive->tag_list, strdup("nitrox"));
taglist_add_tag(dt_dive->tags, "nitrox");
is_nitrox = 1;
}
if (bit_set(tmp_1byte, 1)) {
taglist_add_tag(&dt_dive->tag_list, strdup("rebreather"));
taglist_add_tag(dt_dive->tags, "rebreather");
is_SCR = 1;
dt_dive->dc.divemode = PSCR;
dt_dive->dcs[0].divemode = PSCR;
}
/*
@ -413,36 +412,36 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
*/
read_bytes(1);
if (bit_set(tmp_1byte, 0))
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "sight seeing")));
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "sight seeing"));
if (bit_set(tmp_1byte, 1))
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "club dive")));
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "club dive"));
if (bit_set(tmp_1byte, 2))
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "instructor")));
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "instructor"));
if (bit_set(tmp_1byte, 3))
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "instruction")));
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "instruction"));
if (bit_set(tmp_1byte, 4))
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "night")));
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "night"));
if (bit_set(tmp_1byte, 5))
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "cave")));
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "cave"));
if (bit_set(tmp_1byte, 6))
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "ice")));
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "ice"));
if (bit_set(tmp_1byte, 7))
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "search")));
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "search"));
/*
* Dive Activity 2 - Bit table, use tags again
*/
read_bytes(1);
if (bit_set(tmp_1byte, 0))
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "wreck")));
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "wreck"));
if (bit_set(tmp_1byte, 1))
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "river")));
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "river"));
if (bit_set(tmp_1byte, 2))
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "drift")));
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "drift"));
if (bit_set(tmp_1byte, 3))
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "photo")));
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "photo"));
if (bit_set(tmp_1byte, 4))
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "other")));
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "other"));
/*
* Other activities - String 1st byte = long
@ -451,10 +450,9 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
read_bytes(1);
if (tmp_1byte != 0) {
read_string(tmp_string1);
snprintf(buffer, sizeof(buffer), "%s: %s\n",
QT_TRANSLATE_NOOP("gettextFromC", "Other activities"),
tmp_notes_str= format_string_std("%s: %s\n",
translate("gettextFromC", "Other activities"),
tmp_string1);
tmp_notes_str = strdup(buffer);
free(tmp_string1);
}
@ -464,7 +462,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
read_bytes(1);
if (tmp_1byte != 0) {
read_string(tmp_string1);
dt_dive->buddy = strdup((char *)tmp_string1);
dt_dive->buddy = (const char *)tmp_string1;
free(tmp_string1);
}
@ -474,15 +472,12 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
read_bytes(1);
if (tmp_1byte != 0) {
read_string(tmp_string1);
int len = snprintf(buffer, sizeof(buffer), "%s%s:\n%s",
tmp_notes_str ? tmp_notes_str : "",
QT_TRANSLATE_NOOP("gettextFromC", "Datatrak/Wlog notes"),
dt_dive->notes = format_string_std("%s%s:\n%s",
tmp_notes_str.c_str(),
translate("gettextFromC", "Datatrak/Wlog notes"),
tmp_string1);
dt_dive->notes = (char *)calloc((len +1), 1);
memcpy(dt_dive->notes, buffer, len);
free(tmp_string1);
}
free(tmp_notes_str);
/*
* Alarms 1 and Alarms2 - Bit tables - Not in Subsurface, we use the profile
@ -520,7 +515,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
libdc_model = dtrak_prepare_data(tmp_1byte, devdata);
if (!libdc_model)
report_error(translate("gettextFromC", "[Warning] Manual dive # %d\n"), dt_dive->number);
dt_dive->dc.model = copy_string(devdata->model);
dt_dive->dcs[0].model = devdata.model;
/*
* Air usage, unknown use. Probably allows or deny manually entering gas
@ -543,16 +538,16 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
compl_buffer = (unsigned char *) calloc(18 + profile_length, 1);
rc = dt_libdc_buffer(membuf, profile_length, libdc_model, compl_buffer);
if (rc == DC_STATUS_SUCCESS) {
libdc_buffer_parser(dt_dive, devdata, compl_buffer, profile_length + 18);
libdc_buffer_parser(dt_dive, &devdata, compl_buffer, profile_length + 18);
} else {
report_error(translate("gettextFromC", "[Error] Out of memory for dive %d. Abort parsing."), dt_dive->number);
free(compl_buffer);
goto bail;
}
if (is_nitrox && dt_dive->cylinders.nr > 0)
if (is_nitrox && dt_dive->cylinders.size() > 0)
get_cylinder(dt_dive, 0)->gasmix.o2.permille =
lrint(membuf[23] & 0x0F ? 20.0 + 2 * (membuf[23] & 0x0F) : 21.0) * 10;
if (is_O2 && dt_dive->cylinders.nr > 0)
if (is_O2 && dt_dive->cylinders.size() > 0)
get_cylinder(dt_dive, 0)->gasmix.o2.permille = membuf[23] * 10;
free(compl_buffer);
}
@ -562,19 +557,16 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
* Initialize some dive data not supported by Datatrak/WLog
*/
if (!libdc_model)
dt_dive->dc.deviceid = 0;
dt_dive->dcs[0].deviceid = 0;
else
dt_dive->dc.deviceid = 0xffffffff;
dt_dive->dc.next = NULL;
if (!is_SCR && dt_dive->cylinders.nr > 0) {
dt_dive->dcs[0].deviceid = 0xffffffff;
if (!is_SCR && dt_dive->cylinders.size() > 0) {
get_cylinder(dt_dive, 0)->end.mbar = get_cylinder(dt_dive, 0)->start.mbar -
((get_cylinder(dt_dive, 0)->gas_used.mliter / get_cylinder(dt_dive, 0)->type.size.mliter) * 1000);
}
free(devdata);
return (char *)membuf;
bail:
free(locality);
free(devdata);
return NULL;
}
@ -607,7 +599,7 @@ static void wlog_compl_parser(std::string &wl_mem, struct dive *dt_dive, int dco
pos_viz = offset + 258,
pos_tank_init = offset + 266,
pos_suit = offset + 268;
char *wlog_notes = NULL, *wlog_suit = NULL, *buffer = NULL;
char *wlog_notes = NULL, *wlog_suit = NULL;
unsigned char *runner = (unsigned char *) wl_mem.data();
/*
@ -619,24 +611,16 @@ static void wlog_compl_parser(std::string &wl_mem, struct dive *dt_dive, int dco
(void)memcpy(wlog_notes_temp, runner + offset, NOTES_LENGTH);
wlog_notes = to_utf8((unsigned char *) wlog_notes_temp);
}
if (dt_dive->notes && wlog_notes) {
buffer = (char *)calloc (strlen(dt_dive->notes) + strlen(wlog_notes) + 1, 1);
sprintf(buffer, "%s%s", dt_dive->notes, wlog_notes);
free(dt_dive->notes);
dt_dive->notes = copy_string(buffer);
} else if (wlog_notes) {
dt_dive->notes = copy_string(wlog_notes);
}
free(buffer);
free(wlog_notes);
if (wlog_notes)
dt_dive->notes += wlog_notes;
/*
* Weight in Kg * 100
*/
tmp = (int) two_bytes_to_int(runner[pos_weight + 1], runner[pos_weight]);
if (tmp != 0x7fff) {
weightsystem_t ws = { {tmp * 10}, QT_TRANSLATE_NOOP("gettextFromC", "unknown"), false };
add_cloned_weightsystem(&dt_dive->weightsystems, ws);
weightsystem_t ws = { {tmp * 10}, translate("gettextFromC", "unknown"), false };
dt_dive->weightsystems.push_back(std::move(ws));
}
/*
@ -670,7 +654,7 @@ static void wlog_compl_parser(std::string &wl_mem, struct dive *dt_dive, int dco
wlog_suit = to_utf8((unsigned char *) wlog_suit_temp);
}
if (wlog_suit)
dt_dive->suit = copy_string(wlog_suit);
dt_dive->suit = wlog_suit;
free(wlog_suit);
}
@ -703,25 +687,24 @@ int datatrak_import(std::string &mem, std::string &wl_mem, struct divelog *log)
runner = mem.data();
JUMP(runner, 12);
// Secuential parsing. Abort if received NULL from dt_dive_parser.
// Sequential parsing. Abort if received NULL from dt_dive_parser.
while ((i < numdives) && (runner < maxbuf)) {
struct dive *ptdive = alloc_dive();
auto ptdive = std::make_unique<dive>();
runner = dt_dive_parser((unsigned char *)runner, ptdive, log, maxbuf);
runner = dt_dive_parser((unsigned char *)runner, ptdive.get(), log, maxbuf);
if (!wl_mem.empty())
wlog_compl_parser(wl_mem, ptdive, i);
wlog_compl_parser(wl_mem, ptdive.get(), i);
if (runner == NULL) {
report_error("%s", translate("gettextFromC", "Error: no dive"));
free(ptdive);
rc = 1;
goto out;
} else {
record_dive_to_table(ptdive, log->dives);
log->dives.record_dive(std::move(ptdive));
}
i++;
}
out:
sort_dive_table(log->dives);
log->dives.sort();
return rc;
bail:
return 1;

View File

@ -21,7 +21,6 @@
#include <assert.h>
#include "deco.h"
#include "ssrf.h"
#include "dive.h"
#include "gas.h"
#include "subsurface-string.h"
@ -216,7 +215,7 @@ static double vpmb_tolerated_ambient_pressure(struct deco_state *ds, double refe
return ds->tissue_n2_sat[ci] + ds->tissue_he_sat[ci] + vpmb_config.other_gases_pressure - total_gradient;
}
extern "C" double tissue_tolerance_calc(struct deco_state *ds, const struct dive *dive, double pressure, bool in_planner)
double tissue_tolerance_calc(struct deco_state *ds, const struct dive *dive, double pressure, bool in_planner)
{
int ci = -1;
double ret_tolerance_limit_ambient_pressure = 0.0;
@ -323,7 +322,7 @@ static double calc_surface_phase(double surface_pressure, double he_pressure, do
return 0;
}
extern "C" void vpmb_start_gradient(struct deco_state *ds)
void vpmb_start_gradient(struct deco_state *ds)
{
int ci;
@ -333,7 +332,7 @@ extern "C" void vpmb_start_gradient(struct deco_state *ds)
}
}
extern "C" void vpmb_next_gradient(struct deco_state *ds, double deco_time, double surface_pressure, bool in_planner)
void vpmb_next_gradient(struct deco_state *ds, double deco_time, double surface_pressure, bool in_planner)
{
int ci;
double n2_b, n2_c;
@ -379,7 +378,7 @@ static double solve_cubic(double A, double B, double C)
}
extern "C" void nuclear_regeneration(struct deco_state *ds, double time)
void nuclear_regeneration(struct deco_state *ds, double time)
{
time /= 60.0;
int ci;
@ -411,7 +410,7 @@ static double calc_inner_pressure(double crit_radius, double onset_tension, doub
}
// Calculates the crushing pressure in the given moment. Updates crushing_onset_tension and critical radius if needed
extern "C" void calc_crushing_pressure(struct deco_state *ds, double pressure)
void calc_crushing_pressure(struct deco_state *ds, double pressure)
{
int ci;
double gradient;
@ -443,12 +442,11 @@ extern "C" void calc_crushing_pressure(struct deco_state *ds, double pressure)
}
/* add period_in_seconds at the given pressure and gas to the deco calculation */
extern "C" void add_segment(struct deco_state *ds, double pressure, struct gasmix gasmix, int period_in_seconds, int ccpo2, enum divemode_t divemode, int, bool in_planner)
void add_segment(struct deco_state *ds, double pressure, struct gasmix gasmix, int period_in_seconds, int ccpo2, enum divemode_t divemode, int, bool in_planner)
{
int ci;
struct gas_pressures pressures;
bool icd = false;
fill_pressures(&pressures, pressure - ((in_planner && (decoMode(true) == VPMB)) ? WV_PRESSURE_SCHREINER : WV_PRESSURE),
gas_pressures pressures = fill_pressures(pressure - ((in_planner && (decoMode(true) == VPMB)) ? WV_PRESSURE_SCHREINER : WV_PRESSURE),
gasmix, (double) ccpo2 / 1000.0, divemode);
for (ci = 0; ci < 16; ci++) {
@ -476,7 +474,7 @@ extern "C" void add_segment(struct deco_state *ds, double pressure, struct gasmi
}
#if DECO_CALC_DEBUG
extern "C" void dump_tissues(struct deco_state *ds)
void dump_tissues(struct deco_state *ds)
{
int ci;
printf("N2 tissues:");
@ -489,7 +487,7 @@ extern "C" void dump_tissues(struct deco_state *ds)
}
#endif
extern "C" void clear_vpmb_state(struct deco_state *ds)
void clear_vpmb_state(struct deco_state *ds)
{
int ci;
for (ci = 0; ci < 16; ci++) {
@ -501,11 +499,11 @@ extern "C" void clear_vpmb_state(struct deco_state *ds)
ds->max_bottom_ceiling_pressure.mbar = 0;
}
extern "C" void clear_deco(struct deco_state *ds, double surface_pressure, bool in_planner)
void clear_deco(struct deco_state *ds, double surface_pressure, bool in_planner)
{
int ci;
memset(ds, 0, sizeof(*ds));
*ds = deco_state();
clear_vpmb_state(ds);
for (ci = 0; ci < 16; ci++) {
ds->tissue_n2_sat[ci] = (surface_pressure - ((in_planner && (decoMode(true) == VPMB)) ? WV_PRESSURE_SCHREINER : WV_PRESSURE)) * N2_IN_AIR / 1000;
@ -545,7 +543,7 @@ void deco_state_cache::restore(struct deco_state *target, bool keep_vpmb_state)
*target = *data;
}
extern "C" int deco_allowed_depth(double tissues_tolerance, double surface_pressure, const struct dive *dive, bool smooth)
int deco_allowed_depth(double tissues_tolerance, double surface_pressure, const struct dive *dive, bool smooth)
{
int depth;
double pressure_delta;
@ -564,7 +562,7 @@ extern "C" int deco_allowed_depth(double tissues_tolerance, double surface_press
return depth;
}
extern "C" void set_gf(short gflow, short gfhigh)
void set_gf(short gflow, short gfhigh)
{
if (gflow != -1)
buehlmann_config.gf_low = (double)gflow / 100.0;
@ -572,7 +570,7 @@ extern "C" void set_gf(short gflow, short gfhigh)
buehlmann_config.gf_high = (double)gfhigh / 100.0;
}
extern "C" void set_vpmb_conservatism(short conservatism)
void set_vpmb_conservatism(short conservatism)
{
if (conservatism < 0)
vpmb_config.conservatism = 0;
@ -582,7 +580,7 @@ extern "C" void set_vpmb_conservatism(short conservatism)
vpmb_config.conservatism = conservatism;
}
extern "C" double get_gf(struct deco_state *ds, double ambpressure_bar, const struct dive *dive)
double get_gf(struct deco_state *ds, double ambpressure_bar, const struct dive *dive)
{
double surface_pressure_bar = get_surface_pressure_in_mbar(dive, true) / 1000.0;
double gf_low = buehlmann_config.gf_low;
@ -596,7 +594,7 @@ extern "C" double get_gf(struct deco_state *ds, double ambpressure_bar, const st
return gf;
}
extern "C" double regressiona(const struct deco_state *ds)
double regressiona(const struct deco_state *ds)
{
if (ds->sum1 > 1) {
double avxy = ds->sumxy / ds->sum1;
@ -609,7 +607,7 @@ extern "C" double regressiona(const struct deco_state *ds)
return 0.0;
}
extern "C" double regressionb(const struct deco_state *ds)
double regressionb(const struct deco_state *ds)
{
if (ds->sum1)
return ds->sumy / ds->sum1 - ds->sumx * regressiona(ds) / ds->sum1;
@ -617,14 +615,14 @@ extern "C" double regressionb(const struct deco_state *ds)
return 0.0;
}
extern "C" void reset_regression(struct deco_state *ds)
void reset_regression(struct deco_state *ds)
{
ds->sum1 = 0;
ds->sumxx = ds->sumx = 0L;
ds->sumy = ds->sumxy = 0.0;
}
extern "C" void update_regression(struct deco_state *ds, const struct dive *dive)
void update_regression(struct deco_state *ds, const struct dive *dive)
{
if (!ds->plot_depth)
return;

View File

@ -5,46 +5,43 @@
#include "units.h"
#include "gas.h"
#include "divemode.h"
#ifdef __cplusplus
extern "C" {
#endif
#include <memory>
struct dive;
struct divecomputer;
struct decostop;
struct deco_state {
double tissue_n2_sat[16];
double tissue_he_sat[16];
double tolerated_by_tissue[16];
double tissue_inertgas_saturation[16];
double buehlmann_inertgas_a[16];
double buehlmann_inertgas_b[16];
double tissue_n2_sat[16] = {};
double tissue_he_sat[16] = {};
double tolerated_by_tissue[16] = {};
double tissue_inertgas_saturation[16] = {};
double buehlmann_inertgas_a[16] = {};
double buehlmann_inertgas_b[16] = {};
double max_n2_crushing_pressure[16];
double max_he_crushing_pressure[16];
double max_n2_crushing_pressure[16] = {};
double max_he_crushing_pressure[16] = {};
double crushing_onset_tension[16]; // total inert gas tension in the t* moment
double n2_regen_radius[16]; // rs
double he_regen_radius[16];
double max_ambient_pressure; // last moment we were descending
double crushing_onset_tension[16] = {}; // total inert gas tension in the t* moment
double n2_regen_radius[16] = {}; // rs
double he_regen_radius[16] = {};
double max_ambient_pressure = 0.0; // last moment we were descending
double bottom_n2_gradient[16];
double bottom_he_gradient[16];
double bottom_n2_gradient[16] = {};
double bottom_he_gradient[16] = {};
double initial_n2_gradient[16];
double initial_he_gradient[16];
double initial_n2_gradient[16] = {};
double initial_he_gradient[16] = {};
pressure_t first_ceiling_pressure;
pressure_t max_bottom_ceiling_pressure;
int ci_pointing_to_guiding_tissue;
double gf_low_pressure_this_dive;
int deco_time;
bool icd_warning;
int sum1;
long sumx, sumxx;
double sumy, sumxy;
int plot_depth;
int ci_pointing_to_guiding_tissue = 0;
double gf_low_pressure_this_dive = 0.0;
int deco_time = 0;
bool icd_warning = false;
int sum1 = 0;
long sumx = 0, sumxx = 0;
double sumy = 0, sumxy = 0;
int plot_depth = 0;
};
extern const double buehlmann_N2_t_halflife[];
@ -70,12 +67,6 @@ extern double regressionb(const struct deco_state *ds);
extern void reset_regression(struct deco_state *ds);
extern void update_regression(struct deco_state *ds, const struct dive *dive);
#ifdef __cplusplus
}
// C++ only functions
#include <memory>
struct deco_state_cache {
// Test if there is cached data
operator bool () {
@ -87,6 +78,4 @@ private:
std::unique_ptr<deco_state> data;
};
#endif
#endif // DECO_H

View File

@ -8,7 +8,7 @@
#include "selection.h"
#include "core/settings/qPrefDiveComputer.h"
struct fingerprint_table fingerprint_table;
fingerprint_table fingerprints;
static bool same_device(const device &dev1, const device &dev2)
{
@ -18,42 +18,35 @@ static bool same_device(const device &dev1, const device &dev2)
bool device::operator<(const device &a) const
{
int diff;
diff = model.compare(a.model);
if (diff)
return diff < 0;
return serialNumber < a.serialNumber;
return std::tie(model, serialNumber) < std::tie(a.model, a.serialNumber);
}
extern "C" const struct device *get_device_for_dc(const struct device_table *table, const struct divecomputer *dc)
const struct device *get_device_for_dc(const device_table &table, const struct divecomputer *dc)
{
if (!dc->model || !dc->serial)
if (dc->model.empty() || dc->serial.empty())
return NULL;
const std::vector<device> &dcs = table->devices;
device dev { dc->model, dc->serial };
auto it = std::lower_bound(dcs.begin(), dcs.end(), dev);
return it != dcs.end() && same_device(*it, dev) ? &*it : NULL;
auto it = std::lower_bound(table.begin(), table.end(), dev);
return it != table.end() && same_device(*it, dev) ? &*it : NULL;
}
extern "C" int get_or_add_device_for_dc(struct device_table *table, const struct divecomputer *dc)
int get_or_add_device_for_dc(device_table &table, const struct divecomputer *dc)
{
if (!dc->model || !dc->serial)
if (dc->model.empty() || dc->serial.empty())
return -1;
const struct device *dev = get_device_for_dc(table, dc);
if (dev) {
auto it = std::lower_bound(table->devices.begin(), table->devices.end(), *dev);
return it - table->devices.begin();
auto it = std::lower_bound(table.begin(), table.end(), *dev);
return it - table.begin();
}
return create_device_node(table, dc->model, dc->serial, "");
return create_device_node(table, dc->model, dc->serial, std::string());
}
extern "C" bool device_exists(const struct device_table *device_table, const struct device *dev)
bool device_exists(const device_table &table, const struct device &dev)
{
auto it = std::lower_bound(device_table->devices.begin(), device_table->devices.end(), *dev);
return it != device_table->devices.end() && same_device(*it, *dev);
auto it = std::lower_bound(table.begin(), table.end(), dev);
return it != table.end() && same_device(*it, dev);
}
void device::showchanges(const std::string &n) const
@ -66,7 +59,7 @@ void device::showchanges(const std::string &n) const
}
}
static int addDC(std::vector<device> &dcs, const std::string &m, const std::string &s, const std::string &n)
int create_device_node(device_table &dcs, const std::string &m, const std::string &s, const std::string &n)
{
if (m.empty() || s.empty())
return -1;
@ -86,220 +79,122 @@ static int addDC(std::vector<device> &dcs, const std::string &m, const std::stri
}
}
extern "C" int create_device_node(struct device_table *device_table, const char *model, const char *serial, const char *nickname)
int add_to_device_table(device_table &device_table, const struct device &dev)
{
return addDC(device_table->devices, model ?: "", serial ?: "", nickname ?: "");
return create_device_node(device_table, dev.model, dev.serialNumber, dev.nickName);
}
extern "C" int add_to_device_table(struct device_table *device_table, const struct device *dev)
int remove_device(device_table &table, const struct device &dev)
{
return create_device_node(device_table, dev->model.c_str(), dev->serialNumber.c_str(), dev->nickName.c_str());
}
extern "C" int remove_device(struct device_table *device_table, const struct device *dev)
{
auto it = std::lower_bound(device_table->devices.begin(), device_table->devices.end(), *dev);
if (it != device_table->devices.end() && same_device(*it, *dev)) {
int idx = it - device_table->devices.begin();
device_table->devices.erase(it);
auto it = std::lower_bound(table.begin(), table.end(), dev);
if (it != table.end() && same_device(*it, dev)) {
int idx = it - table.begin();
table.erase(it);
return idx;
} else {
return -1;
}
}
extern "C" void remove_from_device_table(struct device_table *device_table, int idx)
void remove_from_device_table(device_table &table, int idx)
{
if (idx < 0 || idx >= (int)device_table->devices.size())
if (idx < 0 || idx >= (int)table.size())
return;
device_table->devices.erase(device_table->devices.begin() + idx);
}
extern "C" void clear_device_table(struct device_table *device_table)
{
device_table->devices.clear();
table.erase(table.begin() + idx);
}
/* Returns whether the given device is used by a selected dive. */
extern "C" bool device_used_by_selected_dive(const struct device *dev)
bool device_used_by_selected_dive(const struct device &dev)
{
for (dive *d: getDiveSelection()) {
struct divecomputer *dc;
for_each_dc (d, dc) {
if (dc->deviceid == dev->deviceId)
for (auto &dc: d->dcs) {
if (dc.deviceid == dev.deviceId)
return true;
}
}
return false;
}
extern "C" int is_default_dive_computer_device(const char *name)
int is_default_dive_computer_device(const char *name)
{
return qPrefDiveComputer::device() == name;
}
const char *get_dc_nickname(const struct divecomputer *dc)
std::string get_dc_nickname(const struct divecomputer *dc)
{
const device *existNode = get_device_for_dc(divelog.devices, dc);
if (existNode && !existNode->nickName.empty())
return existNode->nickName.c_str();
return existNode->nickName;
else
return dc->model;
}
extern "C" int nr_devices(const struct device_table *table)
{
return (int)table->devices.size();
}
extern "C" const struct device *get_device(const struct device_table *table, int i)
{
if (i < 0 || i > nr_devices(table))
return NULL;
return &table->devices[i];
}
extern "C" struct device *get_device_mutable(struct device_table *table, int i)
{
if (i < 0 || i > nr_devices(table))
return NULL;
return &table->devices[i];
}
extern "C" const char *device_get_model(const struct device *dev)
{
return dev ? dev->model.c_str() : NULL;
}
extern "C" const char *device_get_serial(const struct device *dev)
{
return dev ? dev->serialNumber.c_str() : NULL;
}
extern "C" const char *device_get_nickname(const struct device *dev)
{
return dev ? dev->nickName.c_str() : NULL;
}
extern "C" struct device_table *alloc_device_table()
{
return new struct device_table;
}
extern "C" void free_device_table(struct device_table *devices)
{
delete devices;
}
// managing fingerprint data
bool fingerprint_record::operator<(const fingerprint_record &a) const
{
if (model == a.model)
return serial < a.serial;
return model < a.model;
return std::tie(model, serial) < std::tie(a.model, a.serial);
}
// annoyingly, the Cressi Edy doesn't support a serial number (it's always 0), but still uses fingerprints
// so we can't bail on the serial number being 0
extern "C" unsigned int get_fingerprint_data(const struct fingerprint_table *table, uint32_t model, uint32_t serial, const unsigned char **fp_out)
std::pair<int, const unsigned char *> get_fingerprint_data(const fingerprint_table &table, uint32_t model, uint32_t serial)
{
if (model == 0 || fp_out == nullptr)
return 0;
if (model == 0)
return { 0, nullptr };
struct fingerprint_record fpr = { model, serial };
auto it = std::lower_bound(table->fingerprints.begin(), table->fingerprints.end(), fpr);
if (it != table->fingerprints.end() && it->model == model && it->serial == serial) {
auto it = std::lower_bound(table.begin(), table.end(), fpr);
if (it != table.end() && it->model == model && it->serial == serial) {
// std::lower_bound gets us the first element that isn't smaller than what we are looking
// for - so if one is found, we still need to check for equality
if (has_dive(it->fdeviceid, it->fdiveid)) {
*fp_out = it->raw_data;
return it->fsize;
}
if (has_dive(it->fdeviceid, it->fdiveid))
return { it->fsize, it->raw_data.get() };
}
return 0;
return { 0, nullptr };
}
extern "C" void create_fingerprint_node(struct fingerprint_table *table, uint32_t model, uint32_t serial,
const unsigned char *raw_data_in, unsigned int fsize, uint32_t fdeviceid, uint32_t fdiveid)
void create_fingerprint_node(fingerprint_table &table, uint32_t model, uint32_t serial,
const unsigned char *raw_data_in, unsigned int fsize, uint32_t fdeviceid, uint32_t fdiveid)
{
// since raw data can contain \0 we copy this manually, not as string
unsigned char *raw_data = (unsigned char *)malloc(fsize);
if (!raw_data)
return;
memcpy(raw_data, raw_data_in, fsize);
auto raw_data = std::make_unique<unsigned char []>(fsize);
std::copy(raw_data_in, raw_data_in + fsize, raw_data.get());
struct fingerprint_record fpr = { model, serial, raw_data, fsize, fdeviceid, fdiveid };
auto it = std::lower_bound(table->fingerprints.begin(), table->fingerprints.end(), fpr);
if (it != table->fingerprints.end() && it->model == model && it->serial == serial) {
struct fingerprint_record fpr = { model, serial, std::move(raw_data), fsize, fdeviceid, fdiveid };
auto it = std::lower_bound(table.begin(), table.end(), fpr);
if (it != table.end() && it->model == model && it->serial == serial) {
// std::lower_bound gets us the first element that isn't smaller than what we are looking
// for - so if one is found, we still need to check for equality - and then we
// can update the existing entry; first we free the memory for the stored raw data
free(it->raw_data);
it->fdeviceid = fdeviceid;
it->fdiveid = fdiveid;
it->raw_data = raw_data;
it->raw_data = std::move(fpr.raw_data);
it->fsize = fsize;
} else {
// insert a new one
table->fingerprints.insert(it, fpr);
table.insert(it, std::move(fpr));
}
}
extern "C" void create_fingerprint_node_from_hex(struct fingerprint_table *table, uint32_t model, uint32_t serial,
const char *hex_data, uint32_t fdeviceid, uint32_t fdiveid)
void create_fingerprint_node_from_hex(fingerprint_table &table, uint32_t model, uint32_t serial,
const std::string &hex_data, uint32_t fdeviceid, uint32_t fdiveid)
{
QByteArray raw = QByteArray::fromHex(hex_data);
QByteArray raw = QByteArray::fromHex(hex_data.c_str());
create_fingerprint_node(table, model, serial,
(const unsigned char *)raw.constData(), raw.size(), fdeviceid, fdiveid);
}
extern "C" int nr_fingerprints(struct fingerprint_table *table)
{
return table->fingerprints.size();
}
extern "C" uint32_t fp_get_model(struct fingerprint_table *table, unsigned int i)
{
if (!table || i >= table->fingerprints.size())
return 0;
return table->fingerprints[i].model;
}
extern "C" uint32_t fp_get_serial(struct fingerprint_table *table, unsigned int i)
{
if (!table || i >= table->fingerprints.size())
return 0;
return table->fingerprints[i].serial;
}
extern "C" uint32_t fp_get_deviceid(struct fingerprint_table *table, unsigned int i)
{
if (!table || i >= table->fingerprints.size())
return 0;
return table->fingerprints[i].fdeviceid;
}
extern "C" uint32_t fp_get_diveid(struct fingerprint_table *table, unsigned int i)
{
if (!table || i >= table->fingerprints.size())
return 0;
return table->fingerprints[i].fdiveid;
}
static char to_hex_digit(unsigned char d)
{
return d <= 9 ? d + '0' : d - 10 + 'a';
}
std::string fp_get_data(struct fingerprint_table *table, unsigned int i)
std::string fingerprint_record::get_data() const
{
if (!table || i >= table->fingerprints.size())
return std::string();
struct fingerprint_record *fpr = &table->fingerprints[i];
std::string res(fpr->fsize * 2, ' ');
for (unsigned int i = 0; i < fpr->fsize; ++i) {
res[2 * i] = to_hex_digit((fpr->raw_data[i] >> 4) & 0xf);
res[2 * i + 1] = to_hex_digit(fpr->raw_data[i] & 0xf);
std::string res(fsize * 2, ' ');
for (unsigned int i = 0; i < fsize; ++i) {
res[2 * i] = to_hex_digit((raw_data[i] >> 4) & 0xf);
res[2 * i + 1] = to_hex_digit(raw_data[i] & 0xf);
}
return res;
}

View File

@ -3,73 +3,13 @@
#define DEVICE_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
struct divecomputer;
struct device;
struct device_table;
struct dive_table;
// global device table
extern struct fingerprint_table fingerprint_table;
extern int create_device_node(struct device_table *table, const char *model, const char *serial, const char *nickname);
extern int nr_devices(const struct device_table *table);
extern const struct device *get_device(const struct device_table *table, int i);
extern struct device *get_device_mutable(struct device_table *table, int i);
extern void clear_device_table(struct device_table *table);
const char *get_dc_nickname(const struct divecomputer *dc);
extern bool device_used_by_selected_dive(const struct device *dev);
extern const struct device *get_device_for_dc(const struct device_table *table, const struct divecomputer *dc);
extern int get_or_add_device_for_dc(struct device_table *table, const struct divecomputer *dc);
extern bool device_exists(const struct device_table *table, const struct device *dev);
extern int add_to_device_table(struct device_table *table, const struct device *dev); // returns index
extern int remove_device(struct device_table *table, const struct device *dev); // returns index or -1 if not found
extern void remove_from_device_table(struct device_table *table, int idx);
// struct device accessors for C-code. The returned strings are not stable!
const char *device_get_model(const struct device *dev);
const char *device_get_serial(const struct device *dev);
const char *device_get_nickname(const struct device *dev);
// for C code that needs to alloc/free a device table. (Let's try to get rid of those)
extern struct device_table *alloc_device_table();
extern void free_device_table(struct device_table *devices);
// create fingerprint entry - raw data remains owned by caller
extern void create_fingerprint_node(struct fingerprint_table *table, uint32_t model, uint32_t serial,
const unsigned char *raw_data, unsigned int fsize, uint32_t fdeviceid, uint32_t fdiveid);
extern void create_fingerprint_node_from_hex(struct fingerprint_table *table, uint32_t model, uint32_t serial,
const char *hex_data, uint32_t fdeviceid, uint32_t fdiveid);
// look up the fingerprint for model/serial - returns the number of bytes in the fingerprint; memory owned by the table
extern unsigned int get_fingerprint_data(const struct fingerprint_table *table, uint32_t model, uint32_t serial, const unsigned char **fp_out);
// access the fingerprint data from C
extern int nr_fingerprints(struct fingerprint_table *table);
extern uint32_t fp_get_model(struct fingerprint_table *table, unsigned int i);
extern uint32_t fp_get_serial(struct fingerprint_table *table, unsigned int i);
extern uint32_t fp_get_deviceid(struct fingerprint_table *table, unsigned int i);
extern uint32_t fp_get_diveid(struct fingerprint_table *table, unsigned int i);
extern int is_default_dive_computer_device(const char *);
typedef void (*device_callback_t)(const char *name, void *userdata);
extern int enumerate_devices(device_callback_t callback, void *userdata, unsigned int transport);
#ifdef __cplusplus
}
#endif
// Functions and global variables that are only available to C++ code
#ifdef __cplusplus
#include <memory>
#include <string>
#include <vector>
struct divecomputer;
struct dive_table;
struct device {
bool operator<(const device &a) const;
void showchanges(const std::string &n) const;
@ -79,28 +19,48 @@ struct device {
uint32_t deviceId; // Always the string hash of the serialNumber
};
using device_table = std::vector<device>;
extern int create_device_node(device_table &table, const std::string &model, const std::string &serial, const std::string &nickname);
std::string get_dc_nickname(const struct divecomputer *dc);
extern bool device_used_by_selected_dive(const struct device &dev);
extern const struct device *get_device_for_dc(const device_table &table, const struct divecomputer *dc);
extern int get_or_add_device_for_dc(device_table &table, const struct divecomputer *dc);
extern bool device_exists(const device_table &table, const struct device &dev);
extern int add_to_device_table(device_table &table, const struct device &dev); // returns index
extern int remove_device(device_table &table, const struct device &dev); // returns index or -1 if not found
extern void remove_from_device_table(device_table &table, int idx);
struct fingerprint_record {
bool operator<(const fingerprint_record &a) const;
uint32_t model; // model and libdivecomputer serial number to
uint32_t serial; // look up the fingerprint
unsigned char *raw_data; // fingerprint data as provided by libdivecomputer
std::unique_ptr<unsigned char[]> raw_data; // fingerprint data as provided by libdivecomputer
unsigned int fsize; // size of raw fingerprint data
unsigned int fdeviceid; // corresponding deviceid
unsigned int fdiveid; // corresponding diveid
std::string get_data() const; // As hex-string
};
struct device_table {
// Keep the dive computers in a vector sorted by (model, serial)
std::vector<device> devices;
};
using fingerprint_table = std::vector<fingerprint_record>;
struct fingerprint_table {
// Keep the fingerprint records in a vector sorted by (model, serial) - these are uint32_t here
std::vector<fingerprint_record> fingerprints;
};
// global device table
extern fingerprint_table fingerprints;
std::string fp_get_data(struct fingerprint_table *table, unsigned int i);
// create fingerprint entry - raw data remains owned by caller
extern void create_fingerprint_node(fingerprint_table &table, uint32_t model, uint32_t serial,
const unsigned char *raw_data, unsigned int fsize, uint32_t fdeviceid, uint32_t fdiveid);
extern void create_fingerprint_node_from_hex(fingerprint_table &table, uint32_t model, uint32_t serial,
const std::string &hex_data, uint32_t fdeviceid, uint32_t fdiveid);
// look up the fingerprint for model/serial - returns the number of bytes in the fingerprint; memory owned by the table
extern std::pair <int, const unsigned char *> get_fingerprint_data(const fingerprint_table &table, uint32_t model, uint32_t serial);
extern int is_default_dive_computer_device(const char *);
typedef void (*device_callback_t)(const char *name, void *userdata);
extern int enumerate_devices(device_callback_t callback, void *userdata, unsigned int transport);
#endif
#endif // DEVICE_H

File diff suppressed because it is too large Load Diff

View File

@ -7,14 +7,13 @@
#include "divemode.h"
#include "divecomputer.h"
#include "equipment.h"
#include "picture.h"
#include "picture.h" // TODO: remove
#include "tag.h"
#include <stdio.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <array>
#include <memory>
#include <string>
#include <vector>
extern int last_xml_version;
@ -22,44 +21,72 @@ extern const char *divemode_text_ui[];
extern const char *divemode_text[];
struct dive_site;
struct dive_site_table;
struct dive_table;
struct dive_trip;
struct full_text_cache;
struct event;
struct trip_table;
/* A unique_ptr that will not be copied if the parent class is copied.
* This is used to keep a pointer to the fulltext cache and avoid
* having it copied when the dive is copied, since the new dive is
* not (yet) registered in the fulltext system. Quite hackish.
*/
template<typename T>
struct non_copying_unique_ptr : public std::unique_ptr<T> {
using std::unique_ptr<T>::unique_ptr;
using std::unique_ptr<T>::operator=;
non_copying_unique_ptr(const non_copying_unique_ptr<T> &) { }
void operator=(const non_copying_unique_ptr<T> &) { }
};
struct dive {
struct dive_trip *divetrip;
timestamp_t when;
struct dive_site *dive_site;
char *notes;
char *diveguide, *buddy;
struct cylinder_table cylinders;
struct weightsystem_table weightsystems;
char *suit;
int number;
int rating;
int wavesize, current, visibility, surge, chill; /* 0 - 5 star ratings */
int sac, otu, cns, maxcns;
struct dive_trip *divetrip = nullptr;
timestamp_t when = 0;
struct dive_site *dive_site = nullptr;
std::string notes;
std::string diveguide, buddy;
std::string suit;
cylinder_table cylinders;
weightsystem_table weightsystems;
int number = 0;
int rating = 0;
int wavesize = 0, current = 0, visibility = 0, surge = 0, chill = 0; /* 0 - 5 star ratings */
int sac = 0, otu = 0, cns = 0, maxcns = 0;
/* Calculated based on dive computer data */
temperature_t mintemp, maxtemp, watertemp, airtemp;
depth_t maxdepth, meandepth;
pressure_t surface_pressure;
duration_t duration;
int salinity; // kg per 10000 l
int user_salinity; // water density reflecting a user-specified type
int salinity = 0; // kg per 10000 l
int user_salinity = 0; // water density reflecting a user-specified type
tag_list tags;
std::vector<divecomputer> dcs; // Attn: pointers to divecomputers are not stable!
int id = 0; // unique ID for this dive
picture_table pictures;
unsigned char git_id[20] = {};
bool notrip = false; /* Don't autogroup this dive to a trip */
bool selected = false;
bool hidden_by_filter = false;
non_copying_unique_ptr<full_text_cache> full_text; /* word cache for full text search */
bool invalid = false;
dive();
~dive();
dive(const dive &);
dive(dive &&);
dive &operator=(const dive &);
timestamp_t endtime() const; /* maximum over divecomputers (with samples) */
duration_t totaltime() const; /* maximum over divecomputers (with samples) */
temperature_t dc_airtemp() const; /* average over divecomputers */
temperature_t dc_watertemp() const; /* average over divecomputers */
bool is_planned() const;
bool is_logged() const;
struct tag_entry *tag_list;
struct divecomputer dc;
int id; // unique ID for this dive
struct picture_table pictures;
unsigned char git_id[20];
bool notrip; /* Don't autogroup this dive to a trip */
bool selected;
bool hidden_by_filter;
struct full_text_cache *full_text; /* word cache for full text search */
bool invalid;
};
/* For the top-level list: an entry is either a dive or a trip */
@ -72,8 +99,8 @@ extern void invalidate_dive_cache(struct dive *dive);
extern bool dive_cache_is_valid(const struct dive *dive);
extern int get_cylinder_idx_by_use(const struct dive *dive, enum cylinderuse cylinder_use_type);
extern void cylinder_renumber(struct dive *dive, int mapping[]);
extern int same_gasmix_cylinder(const cylinder_t *cyl, int cylid, const struct dive *dive, bool check_unused);
extern void cylinder_renumber(struct dive &dive, int mapping[]);
extern int same_gasmix_cylinder(const cylinder_t &cyl, int cylid, const struct dive *dive, bool check_unused);
/* when selectively copying dive information, which parts should be copied? */
struct dive_components {
@ -112,39 +139,19 @@ extern depth_t gas_mod(struct gasmix mix, pressure_t po2_limit, const struct div
extern depth_t gas_mnd(struct gasmix mix, depth_t end, const struct dive *dive, int roundto);
extern struct dive *get_dive(int nr);
extern struct dive *get_dive_from_table(int nr, const struct dive_table *dt);
extern struct dive_site *get_dive_site_for_dive(const struct dive *dive);
extern const char *get_dive_country(const struct dive *dive);
extern const char *get_dive_location(const struct dive *dive);
extern std::string get_dive_country(const struct dive *dive);
extern std::string get_dive_location(const struct dive *dive);
extern unsigned int number_of_computers(const struct dive *dive);
extern struct divecomputer *get_dive_dc(struct dive *dive, int nr);
extern const struct divecomputer *get_dive_dc_const(const struct dive *dive, int nr);
extern timestamp_t dive_endtime(const struct dive *dive);
extern const struct divecomputer *get_dive_dc(const struct dive *dive, int nr);
extern void set_git_prefs(const char *prefs);
extern struct dive *make_first_dc(const struct dive *d, int dc_number);
extern struct dive *clone_delete_divecomputer(const struct dive *d, int dc_number);
void split_divecomputer(const struct dive *src, int num, struct dive **out1, struct dive **out2);
extern std::unique_ptr<dive> clone_make_first_dc(const struct dive &d, int dc_number);
extern std::unique_ptr<dive> clone_delete_divecomputer(const struct dive &d, int dc_number);
extern std::array<std::unique_ptr<dive>, 2> split_divecomputer(const struct dive &src, int num);
/*
* Iterate over each dive, with the first parameter being the index
* iterator variable, and the second one being the dive one.
*
* I don't think anybody really wants the index, and we could make
* it local to the for-loop, but that would make us requires C99.
*/
#define for_each_dive(_i, _x) \
for ((_i) = 0; ((_x) = get_dive(_i)) != NULL; (_i)++)
#define for_each_dc(_dive, _dc) \
for (_dc = &_dive->dc; _dc; _dc = _dc->next)
#define for_each_relevant_dc(_dive, _dc) \
for (_dc = &_dive->dc; _dc; _dc = _dc->next) if (!is_logged(_dive) || !is_dc_planner(_dc))
extern struct dive *get_dive_by_uniq_id(int id);
extern int get_idx_by_uniq_id(int id);
extern bool dive_site_has_gps_location(const struct dive_site *ds);
extern int dive_has_gps_location(const struct dive *dive);
extern location_t dive_get_gps_location(const struct dive *d);
@ -153,51 +160,53 @@ extern bool time_during_dive_with_offset(const struct dive *dive, timestamp_t wh
extern int save_dives(const char *filename);
extern int save_dives_logic(const char *filename, bool select_only, bool anonymize);
extern int save_dive(FILE *f, struct dive *dive, bool anonymize);
extern int save_dive(FILE *f, const struct dive &dive, bool anonymize);
extern int export_dives_xslt(const char *filename, bool selected, const int units, const char *export_xslt, bool anonymize);
extern int save_dive_sites_logic(const char *filename, const struct dive_site *sites[], int nr_sites, bool anonymize);
struct membuffer;
extern void save_one_dive_to_mb(struct membuffer *b, struct dive *dive, bool anonymize);
extern void save_one_dive_to_mb(struct membuffer *b, const struct dive &dive, bool anonymize);
extern void subsurface_console_init(void);
extern void subsurface_console_exit(void);
extern bool subsurface_user_is_root(void);
extern void subsurface_console_init();
extern void subsurface_console_exit();
extern bool subsurface_user_is_root();
extern struct dive *alloc_dive(void);
extern void free_dive(struct dive *);
extern void record_dive_to_table(struct dive *dive, struct dive_table *table);
extern void clear_dive(struct dive *dive);
extern void copy_dive(const struct dive *s, struct dive *d);
extern void selective_copy_dive(const struct dive *s, struct dive *d, struct dive_components what, bool clear);
extern struct dive *move_dive(struct dive *s);
extern struct std::unique_ptr<dive> move_dive(struct dive *s);
extern int legacy_format_o2pressures(const struct dive *dive, const struct divecomputer *dc);
extern bool dive_less_than(const struct dive *a, const struct dive *b);
extern bool dive_less_than(const struct dive &a, const struct dive &b);
extern bool dive_less_than_ptr(const struct dive *a, const struct dive *b);
extern bool dive_or_trip_less_than(struct dive_or_trip a, struct dive_or_trip b);
extern struct dive *fixup_dive(struct dive *dive);
extern pressure_t calculate_surface_pressure(const struct dive *dive);
extern pressure_t un_fixup_surface_pressure(const struct dive *d);
extern int get_dive_salinity(const struct dive *dive);
extern int dive_getUniqID();
extern int split_dive(const struct dive *dive, struct dive **new1, struct dive **new2);
extern int split_dive_at_time(const struct dive *dive, duration_t time, struct dive **new1, struct dive **new2);
extern struct dive *merge_dives(const struct dive *a, const struct dive *b, int offset, bool prefer_downloaded, struct dive_trip **trip, struct dive_site **site);
extern struct dive *try_to_merge(struct dive *a, struct dive *b, bool prefer_downloaded);
extern std::array<std::unique_ptr<dive>, 2> split_dive(const struct dive &dive);
extern std::array<std::unique_ptr<dive>, 2> split_dive_at_time(const struct dive &dive, duration_t time);
struct merge_result {
std::unique_ptr<struct dive> dive;
dive_trip *trip;
dive_site *site;
};
extern merge_result merge_dives(const struct dive &a, const struct dive &b, int offset, bool prefer_downloaded);
extern std::unique_ptr<dive> try_to_merge(const struct dive &a, const struct dive &b, bool prefer_downloaded);
extern void copy_events_until(const struct dive *sd, struct dive *dd, int dcNr, int time);
extern void copy_used_cylinders(const struct dive *s, struct dive *d, bool used_only);
extern bool is_cylinder_used(const struct dive *dive, int idx);
extern bool is_cylinder_prot(const struct dive *dive, int idx);
extern void add_gas_switch_event(struct dive *dive, struct divecomputer *dc, int time, int idx);
extern struct event *create_gas_switch_event(struct dive *dive, struct divecomputer *dc, int seconds, int idx);
extern void update_event_name(struct dive *d, int dc_number, struct event *event, const char *name);
extern struct event create_gas_switch_event(struct dive *dive, struct divecomputer *dc, int seconds, int idx);
extern void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, int *mean, int *duration);
extern int get_cylinder_index(const struct dive *dive, const struct event *ev);
extern struct gasmix get_gasmix_from_event(const struct dive *, const struct event *ev);
extern int nr_cylinders(const struct dive *dive);
extern int nr_weightsystems(const struct dive *dive);
extern int get_cylinder_index(const struct dive *dive, const struct event &ev);
extern struct gasmix get_gasmix_from_event(const struct dive *, const struct event &ev);
extern bool cylinder_with_sensor_sample(const struct dive *dive, int cylinder_id);
/* UI related protopypes */
@ -206,32 +215,17 @@ extern void invalidate_dive_cache(struct dive *dc);
extern int total_weight(const struct dive *);
extern bool is_planned(const struct dive *dive);
extern bool is_logged(const struct dive *dive);
/* Get gasmixes at increasing timestamps.
* In "evp", pass a pointer to a "struct event *" which is NULL-initialized on first invocation.
* On subsequent calls, pass the same "evp" and the "gasmix" from previous calls.
*/
extern struct gasmix get_gasmix(const struct dive *dive, const struct divecomputer *dc, int time, const struct event **evp, struct gasmix gasmix);
/* Get gasmix at a given time */
extern struct gasmix get_gasmix_at_time(const struct dive *dive, const struct divecomputer *dc, duration_t time);
extern struct gasmix get_gasmix_at_time(const struct dive &dive, const struct divecomputer &dc, duration_t time);
extern void update_setpoint_events(const struct dive *dive, struct divecomputer *dc);
#ifdef __cplusplus
}
/* Make pointers to dive and dive_trip "Qt metatypes" so that they can be passed through
* QVariants and through QML.
*/
#include <QObject>
#include <string>
Q_DECLARE_METATYPE(struct dive *);
extern std::string existing_filename;
#endif
#endif // DIVE_H

View File

@ -1,570 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
#include "divecomputer.h"
#include "event.h"
#include "extradata.h"
#include "pref.h"
#include "sample.h"
#include "structured_list.h"
#include "subsurface-string.h"
#include <string.h>
#include <stdlib.h>
/*
* Good fake dive profiles are hard.
*
* "depthtime" is the integral of the dive depth over
* time ("area" of the dive profile). We want that
* area to match the average depth (avg_d*max_t).
*
* To do that, we generate a 6-point profile:
*
* (0, 0)
* (t1, max_d)
* (t2, max_d)
* (t3, d)
* (t4, d)
* (max_t, 0)
*
* with the same ascent/descent rates between the
* different depths.
*
* NOTE: avg_d, max_d and max_t are given constants.
* The rest we can/should play around with to get a
* good-looking profile.
*
* That six-point profile gives a total area of:
*
* (max_d*max_t) - (max_d*t1) - (max_d-d)*(t4-t3)
*
* And the "same ascent/descent rates" requirement
* gives us (time per depth must be same):
*
* t1 / max_d = (t3-t2) / (max_d-d)
* t1 / max_d = (max_t-t4) / d
*
* We also obviously require:
*
* 0 <= t1 <= t2 <= t3 <= t4 <= max_t
*
* Let us call 'd_frac = d / max_d', and we get:
*
* Total area must match average depth-time:
*
* (max_d*max_t) - (max_d*t1) - (max_d-d)*(t4-t3) = avg_d*max_t
* max_d*(max_t-t1-(1-d_frac)*(t4-t3)) = avg_d*max_t
* max_t-t1-(1-d_frac)*(t4-t3) = avg_d*max_t/max_d
* t1+(1-d_frac)*(t4-t3) = max_t*(1-avg_d/max_d)
*
* and descent slope must match ascent slopes:
*
* t1 / max_d = (t3-t2) / (max_d*(1-d_frac))
* t1 = (t3-t2)/(1-d_frac)
*
* and
*
* t1 / max_d = (max_t-t4) / (max_d*d_frac)
* t1 = (max_t-t4)/d_frac
*
* In general, we have more free variables than we have constraints,
* but we can aim for certain basics, like a good ascent slope.
*/
static int fill_samples(struct sample *s, int max_d, int avg_d, int max_t, double slope, double d_frac)
{
double t_frac = max_t * (1 - avg_d / (double)max_d);
int t1 = lrint(max_d / slope);
int t4 = lrint(max_t - t1 * d_frac);
int t3 = lrint(t4 - (t_frac - t1) / (1 - d_frac));
int t2 = lrint(t3 - t1 * (1 - d_frac));
if (t1 < 0 || t1 > t2 || t2 > t3 || t3 > t4 || t4 > max_t)
return 0;
s[1].time.seconds = t1;
s[1].depth.mm = max_d;
s[2].time.seconds = t2;
s[2].depth.mm = max_d;
s[3].time.seconds = t3;
s[3].depth.mm = lrint(max_d * d_frac);
s[4].time.seconds = t4;
s[4].depth.mm = lrint(max_d * d_frac);
return 1;
}
/* we have no average depth; instead of making up a random average depth
* we should assume either a PADI rectangular profile (for short and/or
* shallow dives) or more reasonably a six point profile with a 3 minute
* safety stop at 5m */
static void fill_samples_no_avg(struct sample *s, int max_d, int max_t, double slope)
{
// shallow or short dives are just trapecoids based on the given slope
if (max_d < 10000 || max_t < 600) {
s[1].time.seconds = lrint(max_d / slope);
s[1].depth.mm = max_d;
s[2].time.seconds = max_t - lrint(max_d / slope);
s[2].depth.mm = max_d;
} else {
s[1].time.seconds = lrint(max_d / slope);
s[1].depth.mm = max_d;
s[2].time.seconds = max_t - lrint(max_d / slope) - 180;
s[2].depth.mm = max_d;
s[3].time.seconds = max_t - lrint(5000 / slope) - 180;
s[3].depth.mm = 5000;
s[4].time.seconds = max_t - lrint(5000 / slope);
s[4].depth.mm = 5000;
}
}
void fake_dc(struct divecomputer *dc)
{
alloc_samples(dc, 6);
struct sample *fake = dc->sample;
int i;
dc->samples = 6;
/* The dive has no samples, so create a few fake ones */
int max_t = dc->duration.seconds;
int max_d = dc->maxdepth.mm;
int avg_d = dc->meandepth.mm;
memset(fake, 0, 6 * sizeof(struct sample));
fake[5].time.seconds = max_t;
for (i = 0; i < 6; i++) {
fake[i].bearing.degrees = -1;
fake[i].ndl.seconds = -1;
}
if (!max_t || !max_d) {
dc->samples = 0;
return;
}
/* Set last manually entered time to the total dive length */
dc->last_manual_time = dc->duration;
/*
* We want to fake the profile so that the average
* depth ends up correct. However, in the absence of
* a reasonable average, let's just make something
* up. Note that 'avg_d == max_d' is _not_ a reasonable
* average.
* We explicitly treat avg_d == 0 differently */
if (avg_d == 0) {
/* we try for a sane slope, but bow to the insanity of
* the user supplied data */
fill_samples_no_avg(fake, max_d, max_t, MAX(2.0 * max_d / max_t, (double)prefs.ascratelast6m));
if (fake[3].time.seconds == 0) { // just a 4 point profile
dc->samples = 4;
fake[3].time.seconds = max_t;
}
return;
}
if (avg_d < max_d / 10 || avg_d >= max_d) {
avg_d = (max_d + 10000) / 3;
if (avg_d > max_d)
avg_d = max_d * 2 / 3;
}
if (!avg_d)
avg_d = 1;
/*
* Ok, first we try a basic profile with a specific ascent
* rate (5 meters per minute) and d_frac (1/3).
*/
if (fill_samples(fake, max_d, avg_d, max_t, (double)prefs.ascratelast6m, 0.33))
return;
/*
* Ok, assume that didn't work because we cannot make the
* average come out right because it was a quick deep dive
* followed by a much shallower region
*/
if (fill_samples(fake, max_d, avg_d, max_t, 10000.0 / 60, 0.10))
return;
/*
* Uhhuh. That didn't work. We'd need to find a good combination that
* satisfies our constraints. Currently, we don't, we just give insane
* slopes.
*/
if (fill_samples(fake, max_d, avg_d, max_t, 10000.0, 0.01))
return;
/* Even that didn't work? Give up, there's something wrong */
}
/* Find the divemode at time 'time' (in seconds) into the dive. Sequentially step through the divemode-change events,
* saving the dive mode for each event. When the events occur AFTER 'time' seconds, the last stored divemode
* is returned. This function is self-tracking, relying on setting the event pointer 'evp' so that, in each iteration
* that calls this function, the search does not have to begin at the first event of the dive */
enum divemode_t get_current_divemode(const struct divecomputer *dc, int time, const struct event **evp, enum divemode_t *divemode)
{
const struct event *ev = *evp;
if (dc) {
if (*divemode == UNDEF_COMP_TYPE) {
*divemode = dc->divemode;
ev = get_next_event(dc->events, "modechange");
}
} else {
ev = NULL;
}
while (ev && ev->time.seconds < time) {
*divemode = (enum divemode_t) ev->value;
ev = get_next_event(ev->next, "modechange");
}
*evp = ev;
return *divemode;
}
/* helper function to make it easier to work with our structures
* we don't interpolate here, just use the value from the last sample up to that time */
int get_depth_at_time(const struct divecomputer *dc, unsigned int time)
{
int depth = 0;
if (dc && dc->sample)
for (int i = 0; i < dc->samples; i++) {
if (dc->sample[i].time.seconds > time)
break;
depth = dc->sample[i].depth.mm;
}
return depth;
}
/* The first divecomputer is embedded in the dive structure. Free its data but not
* the structure itself. For all remainding dcs in the list, free data *and* structures. */
void free_dive_dcs(struct divecomputer *dc)
{
free_dc_contents(dc);
STRUCTURED_LIST_FREE(struct divecomputer, dc->next, free_dc);
}
/* make room for num samples; if not enough space is available, the sample
* array is reallocated and the existing samples are copied. */
void alloc_samples(struct divecomputer *dc, int num)
{
if (num > dc->alloc_samples) {
dc->alloc_samples = (num * 3) / 2 + 10;
dc->sample = realloc(dc->sample, dc->alloc_samples * sizeof(struct sample));
if (!dc->sample)
dc->samples = dc->alloc_samples = 0;
}
}
void free_samples(struct divecomputer *dc)
{
if (dc) {
free(dc->sample);
dc->sample = 0;
dc->samples = 0;
dc->alloc_samples = 0;
}
}
struct sample *prepare_sample(struct divecomputer *dc)
{
if (dc) {
int nr = dc->samples;
struct sample *sample;
alloc_samples(dc, nr + 1);
if (!dc->sample)
return NULL;
sample = dc->sample + nr;
memset(sample, 0, sizeof(*sample));
// Copy the sensor numbers - but not the pressure values
// from the previous sample if any.
if (nr) {
for (int idx = 0; idx < MAX_SENSORS; idx++)
sample->sensor[idx] = sample[-1].sensor[idx];
}
// Init some values with -1
sample->bearing.degrees = -1;
sample->ndl.seconds = -1;
return sample;
}
return NULL;
}
void finish_sample(struct divecomputer *dc)
{
dc->samples++;
}
struct sample *add_sample(const struct sample *sample, int time, struct divecomputer *dc)
{
struct sample *p = prepare_sample(dc);
if (p) {
*p = *sample;
p->time.seconds = time;
finish_sample(dc);
}
return p;
}
/*
* Calculate how long we were actually under water, and the average
* depth while under water.
*
* This ignores any surface time in the middle of the dive.
*/
void fixup_dc_duration(struct divecomputer *dc)
{
int duration, i;
int lasttime, lastdepth, depthtime;
duration = 0;
lasttime = 0;
lastdepth = 0;
depthtime = 0;
for (i = 0; i < dc->samples; i++) {
struct sample *sample = dc->sample + i;
int time = sample->time.seconds;
int depth = sample->depth.mm;
/* We ignore segments at the surface */
if (depth > SURFACE_THRESHOLD || lastdepth > SURFACE_THRESHOLD) {
duration += time - lasttime;
depthtime += (time - lasttime) * (depth + lastdepth) / 2;
}
lastdepth = depth;
lasttime = time;
}
if (duration) {
dc->duration.seconds = duration;
dc->meandepth.mm = (depthtime + duration / 2) / duration;
}
}
/*
* What do the dive computers say the water temperature is?
* (not in the samples, but as dc property for dcs that support that)
*/
unsigned int dc_watertemp(const struct divecomputer *dc)
{
int sum = 0, nr = 0;
do {
if (dc->watertemp.mkelvin) {
sum += dc->watertemp.mkelvin;
nr++;
}
} while ((dc = dc->next) != NULL);
if (!nr)
return 0;
return (sum + nr / 2) / nr;
}
/*
* What do the dive computers say the air temperature is?
*/
unsigned int dc_airtemp(const struct divecomputer *dc)
{
int sum = 0, nr = 0;
do {
if (dc->airtemp.mkelvin) {
sum += dc->airtemp.mkelvin;
nr++;
}
} while ((dc = dc->next) != NULL);
if (!nr)
return 0;
return (sum + nr / 2) / nr;
}
/* copies all events in this dive computer */
void copy_events(const struct divecomputer *s, struct divecomputer *d)
{
const struct event *ev;
struct event **pev;
if (!s || !d)
return;
ev = s->events;
pev = &d->events;
while (ev != NULL) {
struct event *new_ev = clone_event(ev);
*pev = new_ev;
pev = &new_ev->next;
ev = ev->next;
}
*pev = NULL;
}
void copy_samples(const struct divecomputer *s, struct divecomputer *d)
{
/* instead of carefully copying them one by one and calling add_sample
* over and over again, let's just copy the whole blob */
if (!s || !d)
return;
int nr = s->samples;
d->samples = nr;
d->alloc_samples = nr;
// We expect to be able to read the memory in the other end of the pointer
// if its a valid pointer, so don't expect malloc() to return NULL for
// zero-sized malloc, do it ourselves.
d->sample = NULL;
if(!nr)
return;
d->sample = malloc(nr * sizeof(struct sample));
if (d->sample)
memcpy(d->sample, s->sample, nr * sizeof(struct sample));
}
void add_event_to_dc(struct divecomputer *dc, struct event *ev)
{
struct event **p;
p = &dc->events;
/* insert in the sorted list of events */
while (*p && (*p)->time.seconds <= ev->time.seconds)
p = &(*p)->next;
ev->next = *p;
*p = ev;
}
struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const char *name)
{
struct event *ev = create_event(time, type, flags, value, name);
if (!ev)
return NULL;
add_event_to_dc(dc, ev);
return ev;
}
/* Substitutes an event in a divecomputer for another. No reordering is performed! */
void swap_event(struct divecomputer *dc, struct event *from, struct event *to)
{
for (struct event **ep = &dc->events; *ep; ep = &(*ep)->next) {
if (*ep == from) {
to->next = from->next;
*ep = to;
from->next = NULL; // For good measure.
break;
}
}
}
/* Remove given event from dive computer. Does *not* free the event. */
void remove_event_from_dc(struct divecomputer *dc, struct event *event)
{
for (struct event **ep = &dc->events; *ep; ep = &(*ep)->next) {
if (*ep == event) {
*ep = event->next;
event->next = NULL; // For good measure.
break;
}
}
}
void add_extra_data(struct divecomputer *dc, const char *key, const char *value)
{
struct extra_data **ed = &dc->extra_data;
if (!strcasecmp(key, "Serial")) {
dc->deviceid = calculate_string_hash(value);
dc->serial = strdup(value);
}
if (!strcmp(key, "FW Version")) {
dc->fw_version = strdup(value);
}
while (*ed)
ed = &(*ed)->next;
*ed = malloc(sizeof(struct extra_data));
if (*ed) {
(*ed)->key = strdup(key);
(*ed)->value = strdup(value);
(*ed)->next = NULL;
}
}
/*
* Match two dive computer entries against each other, and
* tell if it's the same dive. Return 0 if "don't know",
* positive for "same dive" and negative for "definitely
* not the same dive"
*/
int match_one_dc(const struct divecomputer *a, const struct divecomputer *b)
{
/* Not same model? Don't know if matching.. */
if (!a->model || !b->model)
return 0;
if (strcasecmp(a->model, b->model))
return 0;
/* Different device ID's? Don't know */
if (a->deviceid != b->deviceid)
return 0;
/* Do we have dive IDs? */
if (!a->diveid || !b->diveid)
return 0;
/*
* If they have different dive ID's on the same
* dive computer, that's a definite "same or not"
*/
return a->diveid == b->diveid && a->when == b->when ? 1 : -1;
}
static void free_extra_data(struct extra_data *ed)
{
free((void *)ed->key);
free((void *)ed->value);
}
void free_dc_contents(struct divecomputer *dc)
{
free(dc->sample);
free((void *)dc->model);
free((void *)dc->serial);
free((void *)dc->fw_version);
free_events(dc->events);
STRUCTURED_LIST_FREE(struct extra_data, dc->extra_data, free_extra_data);
}
void free_dc(struct divecomputer *dc)
{
free_dc_contents(dc);
free(dc);
}
static const char *planner_dc_name = "planned dive";
bool is_dc_planner(const struct divecomputer *dc)
{
return dc && same_string(dc->model, planner_dc_name);
}
void make_planner_dc(struct divecomputer *dc)
{
free((void *)dc->model);
dc->model = strdup(planner_dc_name);
}
const char *manual_dc_name = "manually added dive";
bool is_dc_manually_added_dive(const struct divecomputer *dc)
{
return dc && same_string(dc->model, manual_dc_name);
}
void make_manually_added_dive_dc(struct divecomputer *dc)
{
free((void *)dc->model);
dc->model = strdup(manual_dc_name);
}

399
core/divecomputer.cpp Normal file
View File

@ -0,0 +1,399 @@
// SPDX-License-Identifier: GPL-2.0
#include "divecomputer.h"
#include "errorhelper.h"
#include "event.h"
#include "extradata.h"
#include "pref.h"
#include "sample.h"
#include "structured_list.h"
#include "subsurface-string.h"
#include <string.h>
#include <stdlib.h>
divecomputer::divecomputer() = default;
divecomputer::~divecomputer() = default;
divecomputer::divecomputer(const divecomputer &) = default;
divecomputer::divecomputer(divecomputer &&) = default;
divecomputer &divecomputer::operator=(const divecomputer &) = default;
/*
* Good fake dive profiles are hard.
*
* "depthtime" is the integral of the dive depth over
* time ("area" of the dive profile). We want that
* area to match the average depth (avg_d*max_t).
*
* To do that, we generate a 6-point profile:
*
* (0, 0)
* (t1, max_d)
* (t2, max_d)
* (t3, d)
* (t4, d)
* (max_t, 0)
*
* with the same ascent/descent rates between the
* different depths.
*
* NOTE: avg_d, max_d and max_t are given constants.
* The rest we can/should play around with to get a
* good-looking profile.
*
* That six-point profile gives a total area of:
*
* (max_d*max_t) - (max_d*t1) - (max_d-d)*(t4-t3)
*
* And the "same ascent/descent rates" requirement
* gives us (time per depth must be same):
*
* t1 / max_d = (t3-t2) / (max_d-d)
* t1 / max_d = (max_t-t4) / d
*
* We also obviously require:
*
* 0 <= t1 <= t2 <= t3 <= t4 <= max_t
*
* Let us call 'd_frac = d / max_d', and we get:
*
* Total area must match average depth-time:
*
* (max_d*max_t) - (max_d*t1) - (max_d-d)*(t4-t3) = avg_d*max_t
* max_d*(max_t-t1-(1-d_frac)*(t4-t3)) = avg_d*max_t
* max_t-t1-(1-d_frac)*(t4-t3) = avg_d*max_t/max_d
* t1+(1-d_frac)*(t4-t3) = max_t*(1-avg_d/max_d)
*
* and descent slope must match ascent slopes:
*
* t1 / max_d = (t3-t2) / (max_d*(1-d_frac))
* t1 = (t3-t2)/(1-d_frac)
*
* and
*
* t1 / max_d = (max_t-t4) / (max_d*d_frac)
* t1 = (max_t-t4)/d_frac
*
* In general, we have more free variables than we have constraints,
* but we can aim for certain basics, like a good ascent slope.
*/
static int fill_samples(std::vector<sample> &s, int max_d, int avg_d, int max_t, double slope, double d_frac)
{
double t_frac = max_t * (1 - avg_d / (double)max_d);
int t1 = lrint(max_d / slope);
int t4 = lrint(max_t - t1 * d_frac);
int t3 = lrint(t4 - (t_frac - t1) / (1 - d_frac));
int t2 = lrint(t3 - t1 * (1 - d_frac));
if (t1 < 0 || t1 > t2 || t2 > t3 || t3 > t4 || t4 > max_t)
return 0;
s[1].time.seconds = t1;
s[1].depth.mm = max_d;
s[2].time.seconds = t2;
s[2].depth.mm = max_d;
s[3].time.seconds = t3;
s[3].depth.mm = lrint(max_d * d_frac);
s[4].time.seconds = t4;
s[4].depth.mm = lrint(max_d * d_frac);
return 1;
}
/* we have no average depth; instead of making up a random average depth
* we should assume either a PADI rectangular profile (for short and/or
* shallow dives) or more reasonably a six point profile with a 3 minute
* safety stop at 5m */
static void fill_samples_no_avg(std::vector<sample> &s, int max_d, int max_t, double slope)
{
// shallow or short dives are just trapecoids based on the given slope
if (max_d < 10000 || max_t < 600) {
s[1].time.seconds = lrint(max_d / slope);
s[1].depth.mm = max_d;
s[2].time.seconds = max_t - lrint(max_d / slope);
s[2].depth.mm = max_d;
} else {
s[1].time.seconds = lrint(max_d / slope);
s[1].depth.mm = max_d;
s[2].time.seconds = max_t - lrint(max_d / slope) - 180;
s[2].depth.mm = max_d;
s[3].time.seconds = max_t - lrint(5000 / slope) - 180;
s[3].depth.mm = 5000;
s[4].time.seconds = max_t - lrint(5000 / slope);
s[4].depth.mm = 5000;
}
}
void fake_dc(struct divecomputer *dc)
{
/* The dive has no samples, so create a few fake ones */
int max_t = dc->duration.seconds;
int max_d = dc->maxdepth.mm;
int avg_d = dc->meandepth.mm;
if (!max_t || !max_d) {
dc->samples.clear();
return;
}
std::vector<struct sample> &fake = dc->samples;
fake.resize(6);
fake[5].time.seconds = max_t;
for (int i = 0; i < 6; i++) {
fake[i].bearing.degrees = -1;
fake[i].ndl.seconds = -1;
}
/* Set last manually entered time to the total dive length */
dc->last_manual_time = dc->duration;
/*
* We want to fake the profile so that the average
* depth ends up correct. However, in the absence of
* a reasonable average, let's just make something
* up. Note that 'avg_d == max_d' is _not_ a reasonable
* average.
* We explicitly treat avg_d == 0 differently */
if (avg_d == 0) {
/* we try for a sane slope, but bow to the insanity of
* the user supplied data */
fill_samples_no_avg(fake, max_d, max_t, std::max(2.0 * max_d / max_t, (double)prefs.ascratelast6m));
if (fake[3].time.seconds == 0) { // just a 4 point profile
dc->samples.resize(4);
fake[3].time.seconds = max_t;
}
return;
}
if (avg_d < max_d / 10 || avg_d >= max_d) {
avg_d = (max_d + 10000) / 3;
if (avg_d > max_d)
avg_d = max_d * 2 / 3;
}
if (!avg_d)
avg_d = 1;
/*
* Ok, first we try a basic profile with a specific ascent
* rate (5 meters per minute) and d_frac (1/3).
*/
if (fill_samples(fake, max_d, avg_d, max_t, (double)prefs.ascratelast6m, 0.33))
return;
/*
* Ok, assume that didn't work because we cannot make the
* average come out right because it was a quick deep dive
* followed by a much shallower region
*/
if (fill_samples(fake, max_d, avg_d, max_t, 10000.0 / 60, 0.10))
return;
/*
* Uhhuh. That didn't work. We'd need to find a good combination that
* satisfies our constraints. Currently, we don't, we just give insane
* slopes.
*/
if (fill_samples(fake, max_d, avg_d, max_t, 10000.0, 0.01))
return;
/* Even that didn't work? Give up, there's something wrong */
}
divemode_loop::divemode_loop(const struct divecomputer &dc) :
dc(dc), last(dc.divemode), loop("modechange")
{
/* on first invocation, get first event (if any) */
ev = loop.next(dc);
}
divemode_t divemode_loop::next(int time)
{
while (ev && ev->time.seconds <= time) {
last = static_cast<divemode_t>(ev->value);
ev = loop.next(dc);
}
return last;
}
/* helper function to make it easier to work with our structures
* we don't interpolate here, just use the value from the last sample up to that time */
int get_depth_at_time(const struct divecomputer *dc, unsigned int time)
{
int depth = 0;
if (dc) {
for (const auto &sample: dc->samples) {
if (sample.time.seconds > (int)time)
break;
depth = sample.depth.mm;
}
}
return depth;
}
struct sample *prepare_sample(struct divecomputer *dc)
{
if (dc) {
dc->samples.emplace_back();
auto &sample = dc->samples.back();
// Copy the sensor numbers - but not the pressure values
// from the previous sample if any.
if (dc->samples.size() >= 2) {
auto &prev = dc->samples[dc->samples.size() - 2];
for (int idx = 0; idx < MAX_SENSORS; idx++)
sample.sensor[idx] = prev.sensor[idx];
}
// Init some values with -1
sample.bearing.degrees = -1;
sample.ndl.seconds = -1;
return &sample;
}
return NULL;
}
void append_sample(const struct sample &sample, struct divecomputer *dc)
{
dc->samples.push_back(sample);
}
/*
* Calculate how long we were actually under water, and the average
* depth while under water.
*
* This ignores any surface time in the middle of the dive.
*/
void fixup_dc_duration(struct divecomputer &dc)
{
int duration = 0;
int lasttime = 0, lastdepth = 0, depthtime = 0;
for (const auto &sample: dc.samples) {
int time = sample.time.seconds;
int depth = sample.depth.mm;
/* We ignore segments at the surface */
if (depth > SURFACE_THRESHOLD || lastdepth > SURFACE_THRESHOLD) {
duration += time - lasttime;
depthtime += (time - lasttime) * (depth + lastdepth) / 2;
}
lastdepth = depth;
lasttime = time;
}
if (duration) {
dc.duration.seconds = duration;
dc.meandepth.mm = (depthtime + duration / 2) / duration;
}
}
static bool operator<(const event &ev1, const event &ev2)
{
if (ev1.time.seconds < ev2.time.seconds)
return -1;
if (ev1.time.seconds > ev2.time.seconds)
return 1;
return ev1.name < ev2.name;
}
int add_event_to_dc(struct divecomputer *dc, struct event ev)
{
// Do a binary search for insertion point
auto it = std::lower_bound(dc->events.begin(), dc->events.end(), ev);
int idx = it - dc->events.begin();
dc->events.insert(it, ev);
return idx;
}
struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const std::string &name)
{
struct event ev(time, type, flags, value, name);
int idx = add_event_to_dc(dc, std::move(ev));
return &dc->events[idx];
}
/* Remove given event from dive computer. Returns the removed event. */
struct event remove_event_from_dc(struct divecomputer *dc, int idx)
{
if (idx < 0 || static_cast<size_t>(idx) > dc->events.size()) {
report_info("removing invalid event %d", idx);
return event();
}
event res = std::move(dc->events[idx]);
dc->events.erase(dc->events.begin() + idx);
return res;
}
struct event *get_event(struct divecomputer *dc, int idx)
{
if (idx < 0 || static_cast<size_t>(idx) > dc->events.size()) {
report_info("accessing invalid event %d", idx);
return nullptr;
}
return &dc->events[idx];
}
void add_extra_data(struct divecomputer *dc, const std::string &key, const std::string &value)
{
if (key == "Serial") {
dc->deviceid = calculate_string_hash(value.c_str());
dc->serial = value;
}
if (key == "FW Version")
dc->fw_version = value;
dc->extra_data.push_back(extra_data { key, value });
}
/*
* Match two dive computer entries against each other, and
* tell if it's the same dive. Return 0 if "don't know",
* positive for "same dive" and negative for "definitely
* not the same dive"
*/
int match_one_dc(const struct divecomputer &a, const struct divecomputer &b)
{
/* Not same model? Don't know if matching.. */
if (a.model.empty() || b.model.empty())
return 0;
if (strcasecmp(a.model.c_str(), b.model.c_str()))
return 0;
/* Different device ID's? Don't know */
if (a.deviceid != b.deviceid)
return 0;
/* Do we have dive IDs? */
if (!a.diveid || !b.diveid)
return 0;
/*
* If they have different dive ID's on the same
* dive computer, that's a definite "same or not"
*/
return a.diveid == b.diveid && a.when == b.when ? 1 : -1;
}
static const char *planner_dc_name = "planned dive";
bool is_dc_planner(const struct divecomputer *dc)
{
return dc->model == planner_dc_name;
}
void make_planner_dc(struct divecomputer *dc)
{
dc->model = planner_dc_name;
}
const char *manual_dc_name = "manually added dive";
bool is_dc_manually_added_dive(const struct divecomputer *dc)
{
return dc->model == manual_dc_name;
}
void make_manually_added_dive_dc(struct divecomputer *dc)
{
dc->model = manual_dc_name;
}

View File

@ -4,12 +4,11 @@
#include "divemode.h"
#include "units.h"
#ifdef __cplusplus
extern "C" {
#endif
#include <string>
#include <vector>
struct extra_data;
struct event;
struct sample;
/* Is this header the correct place? */
@ -29,44 +28,40 @@ struct sample;
* A deviceid or diveid of zero is assumed to be "no ID".
*/
struct divecomputer {
timestamp_t when;
timestamp_t when = 0;
duration_t duration, surfacetime, last_manual_time;
depth_t maxdepth, meandepth;
temperature_t airtemp, watertemp;
pressure_t surface_pressure;
enum divemode_t divemode; // dive computer type: OC(default) or CCR
uint8_t no_o2sensors; // rebreathers: number of O2 sensors used
int salinity; // kg per 10000 l
const char *model, *serial, *fw_version;
uint32_t deviceid, diveid;
int samples, alloc_samples;
struct sample *sample;
struct event *events;
struct extra_data *extra_data;
struct divecomputer *next;
enum divemode_t divemode = OC; // dive computer type: OC(default) or CCR
uint8_t no_o2sensors = 0; // rebreathers: number of O2 sensors used
int salinity = 0; // kg per 10000 l
std::string model, serial, fw_version;
uint32_t deviceid = 0, diveid = 0;
// Note: ve store samples, events and extra_data in std::vector<>s.
// This means that pointers to these items are *not* stable.
std::vector<struct sample> samples;
std::vector<struct event> events;
std::vector<struct extra_data> extra_data;
divecomputer();
~divecomputer();
divecomputer(const divecomputer &);
divecomputer(divecomputer &&);
divecomputer &operator=(const divecomputer &);
};
extern void fake_dc(struct divecomputer *dc);
extern void free_dc(struct divecomputer *dc);
extern void free_dc_contents(struct divecomputer *dc);
extern enum divemode_t get_current_divemode(const struct divecomputer *dc, int time, const struct event **evp, enum divemode_t *divemode);
extern int get_depth_at_time(const struct divecomputer *dc, unsigned int time);
extern void free_dive_dcs(struct divecomputer *dc);
extern void alloc_samples(struct divecomputer *dc, int num);
extern void free_samples(struct divecomputer *dc);
extern struct sample *prepare_sample(struct divecomputer *dc);
extern void finish_sample(struct divecomputer *dc);
extern struct sample *add_sample(const struct sample *sample, int time, struct divecomputer *dc);
extern void fixup_dc_duration(struct divecomputer *dc);
extern unsigned int dc_airtemp(const struct divecomputer *dc);
extern unsigned int dc_watertemp(const struct divecomputer *dc);
extern void copy_events(const struct divecomputer *s, struct divecomputer *d);
extern void swap_event(struct divecomputer *dc, struct event *from, struct event *to);
extern void copy_samples(const struct divecomputer *s, struct divecomputer *d);
extern void add_event_to_dc(struct divecomputer *dc, struct event *ev);
extern struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const char *name);
extern void remove_event_from_dc(struct divecomputer *dc, struct event *event);
extern void add_extra_data(struct divecomputer *dc, const char *key, const char *value);
extern void append_sample(const struct sample &sample, struct divecomputer *dc);
extern void fixup_dc_duration(struct divecomputer &dc);
extern int add_event_to_dc(struct divecomputer *dc, struct event ev); // event structure is consumed, returns index of inserted event
extern struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const std::string &name);
extern struct event remove_event_from_dc(struct divecomputer *dc, int idx);
struct event *get_event(struct divecomputer *dc, int idx);
extern void add_extra_data(struct divecomputer *dc, const std::string &key, const std::string &value);
extern uint32_t calculate_string_hash(const char *str);
extern bool is_dc_planner(const struct divecomputer *dc);
extern void make_planner_dc(struct divecomputer *dc);
@ -75,10 +70,6 @@ extern bool is_dc_manually_added_dive(const struct divecomputer *dc);
extern void make_manually_added_dive_dc(struct divecomputer *dc);
/* Check if two dive computer entries are the exact same dive (-1=no/0=maybe/1=yes) */
extern int match_one_dc(const struct divecomputer *a, const struct divecomputer *b);
#ifdef __cplusplus
}
#endif
extern int match_one_dc(const struct divecomputer &a, const struct divecomputer &b);
#endif

View File

@ -5,6 +5,7 @@
#include "divelog.h"
#include "gettextfromc.h"
#include "qthelper.h"
#include "range.h"
#include "selection.h"
#include "subsurface-qt/divelistnotifier.h"
#if !defined(SUBSURFACE_MOBILE) && !defined(SUBSURFACE_DOWNLOADER)
@ -61,7 +62,7 @@ ShownChange DiveFilter::update(const QVector<dive *> &dives) const
std::vector<dive *> removeFromSelection;
for (dive *d: dives) {
// There are three modes: divesite, fulltext, normal
bool newStatus = doDS ? dive_sites.contains(d->dive_site) :
bool newStatus = doDS ? range_contains(dive_sites, d->dive_site) :
doFullText ? fulltext_dive_matches(d, filterData.fullText, filterData.fulltextStringMode) && showDive(d) :
showDive(d);
updateDiveStatus(d, newStatus, res, removeFromSelection);
@ -73,10 +74,8 @@ ShownChange DiveFilter::update(const QVector<dive *> &dives) const
void DiveFilter::reset()
{
int i;
dive *d;
shown_dives = divelog.dives->nr;
for_each_dive(i, d)
shown_dives = static_cast<int>(divelog.dives.size());
for (auto &d: divelog.dives)
d->hidden_by_filter = false;
updateAll();
}
@ -84,26 +83,24 @@ void DiveFilter::reset()
ShownChange DiveFilter::updateAll() const
{
ShownChange res;
int i;
dive *d;
std::vector<dive *> selection = getDiveSelection();
std::vector<dive *> removeFromSelection;
// There are three modes: divesite, fulltext, normal
if (diveSiteMode()) {
for_each_dive(i, d) {
bool newStatus = dive_sites.contains(d->dive_site);
updateDiveStatus(d, newStatus, res, removeFromSelection);
for (auto &d: divelog.dives) {
bool newStatus = range_contains(dive_sites, d->dive_site);
updateDiveStatus(d.get(), newStatus, res, removeFromSelection);
}
} else if (filterData.fullText.doit()) {
FullTextResult ft = fulltext_find_dives(filterData.fullText, filterData.fulltextStringMode);
for_each_dive(i, d) {
bool newStatus = ft.dive_matches(d) && showDive(d);
updateDiveStatus(d, newStatus, res, removeFromSelection);
for (auto &d: divelog.dives) {
bool newStatus = ft.dive_matches(d.get()) && showDive(d.get());
updateDiveStatus(d.get(), newStatus, res, removeFromSelection);
}
} else {
for_each_dive(i, d) {
bool newStatus = showDive(d);
updateDiveStatus(d, newStatus, res, removeFromSelection);
for (auto &d: divelog.dives) {
bool newStatus = showDive(d.get());
updateDiveStatus(d.get(), newStatus, res, removeFromSelection);
}
}
updateSelection(selection, std::vector<dive *>(), removeFromSelection);
@ -142,7 +139,7 @@ bool DiveFilter::showDive(const struct dive *d) const
}
#if !defined(SUBSURFACE_MOBILE) && !defined(SUBSURFACE_DOWNLOADER)
void DiveFilter::startFilterDiveSites(QVector<dive_site *> ds)
void DiveFilter::startFilterDiveSites(std::vector<dive_site *> ds)
{
if (++diveSiteRefCount > 1) {
setFilterDiveSite(std::move(ds));
@ -169,7 +166,7 @@ void DiveFilter::stopFilterDiveSites()
#endif
}
void DiveFilter::setFilterDiveSite(QVector<dive_site *> ds)
void DiveFilter::setFilterDiveSite(std::vector<dive_site *> ds)
{
// If the filter didn't change, return early to avoid a full
// map reload. For a well-defined comparison, sort the vector first.
@ -185,7 +182,7 @@ void DiveFilter::setFilterDiveSite(QVector<dive_site *> ds)
MainWindow::instance()->diveList->expandAll();
}
const QVector<dive_site *> &DiveFilter::filteredDiveSites() const
const std::vector<dive_site *> &DiveFilter::filteredDiveSites() const
{
return dive_sites;
}
@ -203,7 +200,7 @@ bool DiveFilter::diveSiteMode() const
QString DiveFilter::shownText() const
{
int num = divelog.dives->nr;
size_t num = divelog.dives.size();
if (diveSiteMode() || filterData.validFilter())
return gettextFromC::tr("%L1/%L2 shown").arg(shown_dives).arg(num);
else
@ -229,11 +226,9 @@ std::vector<dive *> DiveFilter::visibleDives() const
std::vector<dive *> res;
res.reserve(shown_dives);
int i;
dive *d;
for_each_dive(i, d) {
for (auto &d: divelog.dives) {
if (!d->hidden_by_filter)
res.push_back(d);
res.push_back(d.get());
}
return res;
}

View File

@ -45,9 +45,9 @@ public:
bool diveSiteMode() const; // returns true if we're filtering on dive site (on mobile always returns false)
std::vector<dive *> visibleDives() const;
#ifndef SUBSURFACE_MOBILE
const QVector<dive_site *> &filteredDiveSites() const;
void startFilterDiveSites(QVector<dive_site *> ds);
void setFilterDiveSite(QVector<dive_site *> ds);
const std::vector<dive_site *> &filteredDiveSites() const;
void startFilterDiveSites(std::vector<dive_site *> ds);
void setFilterDiveSite(std::vector<dive_site *> ds);
void stopFilterDiveSites();
#endif
void setFilter(const FilterData &data);
@ -62,7 +62,7 @@ private:
void updateDiveStatus(dive *d, bool newStatus, ShownChange &change,
std::vector<dive *> &removeFromSelection) const;
QVector<dive_site *> dive_sites;
std::vector<dive_site *> dive_sites;
FilterData filterData;
mutable int shown_dives;

File diff suppressed because it is too large Load Diff

View File

@ -2,29 +2,29 @@
#ifndef DIVELIST_H
#define DIVELIST_H
#include "triptable.h"
#include "divesitetable.h"
#include "units.h"
#ifdef __cplusplus
extern "C" {
#endif
#include <memory>
#include <vector>
struct dive;
struct divelog;
struct trip_table;
struct dive_site_table;
struct device_table;
struct device;
struct deco_state;
struct dive_table {
int nr, allocated;
struct dive **dives;
int comp_dives(const struct dive &a, const struct dive &b);
int comp_dives_ptr(const struct dive *a, const struct dive *b);
struct dive_table : public sorted_owning_table<dive, &comp_dives> {
dive *get_by_uniq_id(int id) const;
void record_dive(std::unique_ptr<dive> d); // call fixup_dive() before adding dive to table.
std::unique_ptr<dive> unregister_dive(int idx);
};
static const struct dive_table empty_dive_table = { 0, 0, (struct dive **)0 };
/* this is used for both git and xml format */
#define DATAFORMAT_VERSION 3
extern void sort_dive_table(struct dive_table *table);
extern void update_cylinder_related_info(struct dive *);
extern int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_planner);
@ -35,37 +35,29 @@ extern void process_loaded_dives();
#define IMPORT_IS_DOWNLOADED (1 << 1)
#define IMPORT_MERGE_ALL_TRIPS (1 << 2)
#define IMPORT_ADD_TO_NEW_TRIP (1 << 3)
extern void add_imported_dives(struct divelog *log, int flags);
extern void process_imported_dives(struct divelog *import_log, int flags,
struct dive_table *dives_to_add, struct dive_table *dives_to_remove,
struct trip_table *trips_to_add, struct dive_site_table *sites_to_add,
struct device_table *devices_to_add);
extern void add_imported_dives(struct divelog &log, int flags);
struct process_imported_dives_result {
dive_table dives_to_add;
std::vector<dive *> dives_to_remove;
trip_table trips_to_add;
dive_site_table sites_to_add;
std::vector<device> devices_to_add;
};
extern process_imported_dives_result process_imported_dives(struct divelog &import_log, int flags);
extern int dive_table_get_insertion_index(struct dive_table *table, struct dive *dive);
extern void add_to_dive_table(struct dive_table *table, int idx, struct dive *dive);
extern void insert_dive(struct dive_table *table, struct dive *d);
extern void get_dive_gas(const struct dive *dive, int *o2_p, int *he_p, int *o2low_p);
extern int get_divenr(const struct dive *dive);
extern int remove_dive(const struct dive *dive, struct dive_table *table);
extern int get_dive_nr_at_idx(int idx);
extern timestamp_t get_surface_interval(timestamp_t when);
extern void delete_dive_from_table(struct dive_table *table, int idx);
extern struct dive *find_next_visible_dive(timestamp_t when);
extern int comp_dives(const struct dive *a, const struct dive *b);
int get_min_datafile_version();
void reset_min_datafile_version();
void report_datafile_version(int version);
int get_dive_id_closest_to(timestamp_t when);
void clear_dive_file_data();
void clear_dive_table(struct dive_table *table);
void move_dive_table(struct dive_table *src, struct dive_table *dst);
struct dive *unregister_dive(int idx);
struct dive *register_dive(std::unique_ptr<dive> d);
extern bool has_dive(unsigned int deviceid, unsigned int diveid);
#ifdef __cplusplus
}
#endif
#endif // DIVELIST_H

View File

@ -3,96 +3,89 @@
#include "divelist.h"
#include "divesite.h"
#include "device.h"
#include "dive.h"
#include "errorhelper.h"
#include "filterpreset.h"
#include "filterpresettable.h"
#include "trip.h"
struct divelog divelog;
// We can't use smart pointers, since this is used from C
// and it would be bold to presume that std::unique_ptr<>
// and a plain pointer have the same memory layout.
divelog::divelog() :
dives(new dive_table),
trips(new trip_table),
sites(new dive_site_table),
devices(new device_table),
filter_presets(new filter_preset_table),
autogroup(false)
{
*dives = empty_dive_table;
*trips = empty_trip_table;
*sites = empty_dive_site_table;
}
divelog::~divelog()
{
clear_dive_table(dives);
clear_trip_table(trips);
clear_dive_site_table(sites);
delete dives;
delete trips;
delete sites;
delete devices;
delete filter_presets;
}
divelog::divelog(divelog &&log) :
dives(new dive_table),
trips(new trip_table),
sites(new dive_site_table),
devices(new device_table),
filter_presets(new filter_preset_table)
{
*dives = empty_dive_table;
*trips = empty_trip_table;
*sites = empty_dive_site_table;
move_dive_table(log.dives, dives);
move_trip_table(log.trips, trips);
move_dive_site_table(log.sites, sites);
*devices = std::move(*log.devices);
*filter_presets = std::move(*log.filter_presets);
}
struct divelog &divelog::operator=(divelog &&log)
{
move_dive_table(log.dives, dives);
move_trip_table(log.trips, trips);
move_dive_site_table(log.sites, sites);
*devices = std::move(*log.devices);
*filter_presets = std::move(*log.filter_presets);
return *this;
}
divelog::divelog() = default;
divelog::~divelog() = default;
divelog::divelog(divelog &&) = default;
struct divelog &divelog::operator=(divelog &&) = default;
/* this implements the mechanics of removing the dive from the
* dive log and the trip, but doesn't deal with updating dive trips, etc */
void delete_single_dive(struct divelog *log, int idx)
void divelog::delete_single_dive(int idx)
{
if (idx < 0 || idx > log->dives->nr) {
report_info("Warning: deleting unexisting dive with index %d", idx);
if (idx < 0 || static_cast<size_t>(idx) >= dives.size()) {
report_info("Warning: deleting non-existing dive with index %d", idx);
return;
}
struct dive *dive = log->dives->dives[idx];
remove_dive_from_trip(dive, log->trips);
struct dive *dive = dives[idx].get();
struct dive_trip *trip = unregister_dive_from_trip(dive);
// Deleting a dive may change the order of trips!
if (trip)
trips.sort();
if (trip && trip->dives.empty())
trips.pull(trip);
unregister_dive_from_dive_site(dive);
delete_dive_from_table(log->dives, idx);
dives.erase(dives.begin() + idx);
}
void divelog::delete_multiple_dives(const std::vector<dive *> &dives_to_delete)
{
bool trips_changed = false;
for (dive *d: dives_to_delete) {
// Delete dive from trip and delete trip if empty
struct dive_trip *trip = unregister_dive_from_trip(d);
if (trip && trip->dives.empty()) {
trips_changed = true;
trips.pull(trip);
}
unregister_dive_from_dive_site(d);
dives.pull(d);
}
// Deleting a dive may change the order of trips!
if (trips_changed)
trips.sort();
}
void divelog::clear()
{
while (dives->nr > 0)
delete_single_dive(this, dives->nr - 1);
while (sites->nr)
delete_dive_site(get_dive_site(0, sites), sites);
if (trips->nr != 0) {
report_info("Warning: trip table not empty in divelog::clear()!");
trips->nr = 0;
}
clear_device_table(devices);
filter_presets->clear();
dives.clear();
sites.clear();
trips.clear();
devices.clear();
filter_presets.clear();
}
extern "C" void clear_divelog(struct divelog *log)
/* check if we have a trip right before / after this dive */
bool divelog::is_trip_before_after(const struct dive *dive, bool before) const
{
log->clear();
auto it = std::find_if(dives.begin(), dives.end(),
[dive](auto &d) { return d.get() == dive; });
if (it == dives.end())
return false;
if (before) {
do {
if (it == dives.begin())
return false;
--it;
} while ((*it)->invalid);
return (*it)->divetrip != nullptr;
} else {
++it;
while (it != dives.end() && (*it)->invalid)
++it;
return it != dives.end() && (*it)->divetrip != nullptr;
}
}

View File

@ -1,43 +1,36 @@
// SPDX-License-Identifier: GPL-2.0
// A structure that contains all the values we save in a divelog file
// A structure that contains all the data we store in divelog files
#ifndef DIVELOG_H
#define DIVELOG_H
struct dive_table;
struct trip_table;
struct dive_site_table;
struct device_table;
struct filter_preset_table;
#include "divelist.h"
#include "divesitetable.h"
#include "filterpresettable.h"
#include "triptable.h"
#include <stdbool.h>
#include <vector>
struct device;
struct divelog {
struct dive_table *dives;
struct trip_table *trips;
struct dive_site_table *sites;
struct device_table *devices;
struct filter_preset_table *filter_presets;
bool autogroup;
#ifdef __cplusplus
void clear();
dive_table dives;
trip_table trips;
dive_site_table sites;
std::vector<device> devices;
filter_preset_table filter_presets;
bool autogroup = false;
divelog();
~divelog();
divelog(divelog &&log); // move constructor (argument is consumed).
divelog &operator=(divelog &&log); // move assignment (argument is consumed).
#endif
divelog(divelog &&); // move constructor (argument is consumed).
divelog &operator=(divelog &&); // move assignment (argument is consumed).
void delete_single_dive(int idx);
void delete_multiple_dives(const std::vector<dive *> &dives);
void clear();
bool is_trip_before_after(const struct dive *dive, bool before) const;
};
extern struct divelog divelog;
#ifdef __cplusplus
extern "C" {
#endif
void clear_divelog(struct divelog *);
extern void delete_single_dive(struct divelog *, int idx);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -75,45 +75,42 @@ static void exportHTMLstatistics(const QString filename, struct htmlExportSettin
QFile file(filename);
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream out(&file);
stats_summary_auto_free stats;
stats_t total_stats;
calculate_stats_summary(&stats, hes.selectedOnly);
stats_summary stats = calculate_stats_summary(hes.selectedOnly);
total_stats.selection_size = 0;
total_stats.total_time.seconds = 0;
int i = 0;
out << "divestat=[";
if (hes.yearlyStatistics) {
while (stats.stats_yearly != NULL && stats.stats_yearly[i].period) {
for (const auto &s: stats.stats_yearly) {
out << "{";
out << "\"YEAR\":\"" << stats.stats_yearly[i].period << "\",";
out << "\"DIVES\":\"" << stats.stats_yearly[i].selection_size << "\",";
out << "\"TOTAL_TIME\":\"" << get_dive_duration_string(stats.stats_yearly[i].total_time.seconds,
out << "\"YEAR\":\"" << s.period << "\",";
out << "\"DIVES\":\"" << s.selection_size << "\",";
out << "\"TOTAL_TIME\":\"" << get_dive_duration_string(s.total_time.seconds,
gettextFromC::tr("h"), gettextFromC::tr("min"), gettextFromC::tr("sec"), " ") << "\",";
out << "\"AVERAGE_TIME\":\"" << formatMinutes(stats.stats_yearly[i].total_time.seconds / stats.stats_yearly[i].selection_size) << "\",";
out << "\"SHORTEST_TIME\":\"" << formatMinutes(stats.stats_yearly[i].shortest_time.seconds) << "\",";
out << "\"LONGEST_TIME\":\"" << formatMinutes(stats.stats_yearly[i].longest_time.seconds) << "\",";
out << "\"AVG_DEPTH\":\"" << get_depth_string(stats.stats_yearly[i].avg_depth) << "\",";
out << "\"MIN_DEPTH\":\"" << get_depth_string(stats.stats_yearly[i].min_depth) << "\",";
out << "\"MAX_DEPTH\":\"" << get_depth_string(stats.stats_yearly[i].max_depth) << "\",";
out << "\"AVG_SAC\":\"" << get_volume_string(stats.stats_yearly[i].avg_sac) << "\",";
out << "\"MIN_SAC\":\"" << get_volume_string(stats.stats_yearly[i].min_sac) << "\",";
out << "\"MAX_SAC\":\"" << get_volume_string(stats.stats_yearly[i].max_sac) << "\",";
if (stats.stats_yearly[i].combined_count) {
out << "\"AVERAGE_TIME\":\"" << formatMinutes(s.total_time.seconds / s.selection_size) << "\",";
out << "\"SHORTEST_TIME\":\"" << formatMinutes(s.shortest_time.seconds) << "\",";
out << "\"LONGEST_TIME\":\"" << formatMinutes(s.longest_time.seconds) << "\",";
out << "\"AVG_DEPTH\":\"" << get_depth_string(s.avg_depth) << "\",";
out << "\"MIN_DEPTH\":\"" << get_depth_string(s.min_depth) << "\",";
out << "\"MAX_DEPTH\":\"" << get_depth_string(s.max_depth) << "\",";
out << "\"AVG_SAC\":\"" << get_volume_string(s.avg_sac) << "\",";
out << "\"MIN_SAC\":\"" << get_volume_string(s.min_sac) << "\",";
out << "\"MAX_SAC\":\"" << get_volume_string(s.max_sac) << "\",";
if (s.combined_count) {
temperature_t avg_temp;
avg_temp.mkelvin = stats.stats_yearly[i].combined_temp.mkelvin / stats.stats_yearly[i].combined_count;
avg_temp.mkelvin = s.combined_temp.mkelvin / s.combined_count;
out << "\"AVG_TEMP\":\"" << get_temperature_string(avg_temp) << "\",";
} else {
out << "\"AVG_TEMP\":\"0.0\",";
}
out << "\"MIN_TEMP\":\"" << (stats.stats_yearly[i].min_temp.mkelvin == 0 ? 0 : get_temperature_string(stats.stats_yearly[i].min_temp)) << "\",";
out << "\"MAX_TEMP\":\"" << (stats.stats_yearly[i].max_temp.mkelvin == 0 ? 0 : get_temperature_string(stats.stats_yearly[i].max_temp)) << "\",";
out << "\"MIN_TEMP\":\"" << (s.min_temp.mkelvin == 0 ? 0 : get_temperature_string(s.min_temp)) << "\",";
out << "\"MAX_TEMP\":\"" << (s.max_temp.mkelvin == 0 ? 0 : get_temperature_string(s.max_temp)) << "\",";
out << "},";
total_stats.selection_size += stats.stats_yearly[i].selection_size;
total_stats.total_time.seconds += stats.stats_yearly[i].total_time.seconds;
i++;
total_stats.selection_size += s.selection_size;
total_stats.total_time.seconds += s.total_time.seconds;
}
exportHTMLstatisticsTotal(out, &total_stats);
}

View File

@ -1,53 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
#include "divesite.h"
#include "pref.h"
#include "gettextfromc.h"
QString constructLocationTags(struct taxonomy_data *taxonomy, bool for_maintab)
{
QString locationTag;
if (!taxonomy->nr)
return locationTag;
/* Check if the user set any of the 3 geocoding categories */
bool prefs_set = false;
for (int i = 0; i < 3; i++) {
if (prefs.geocoding.category[i] != TC_NONE)
prefs_set = true;
}
if (!prefs_set && !for_maintab) {
locationTag = QString("<small><small>") + gettextFromC::tr("No dive site layout categories set in preferences!") +
QString("</small></small>");
return locationTag;
}
else if (!prefs_set)
return locationTag;
if (for_maintab)
locationTag = QString("<small><small>(") + gettextFromC::tr("Tags") + QString(": ");
else
locationTag = QString("<small><small>");
QString connector;
for (int i = 0; i < 3; i++) {
if (prefs.geocoding.category[i] == TC_NONE)
continue;
for (int j = 0; j < taxonomy->nr; j++) {
if (taxonomy->category[j].category == prefs.geocoding.category[i]) {
QString tag = taxonomy->category[j].value;
if (!tag.isEmpty()) {
locationTag += connector + tag;
connector = " / ";
}
break;
}
}
}
if (for_maintab)
locationTag += ")</small></small>";
else
locationTag += "</small></small>";
return locationTag;
}

View File

@ -1,401 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/* divesite.c */
#include "divesite.h"
#include "dive.h"
#include "divelist.h"
#include "divelog.h"
#include "errorhelper.h"
#include "membuffer.h"
#include "subsurface-string.h"
#include "table.h"
#include "sha1.h"
#include <math.h>
int get_divesite_idx(const struct dive_site *ds, struct dive_site_table *ds_table)
{
int i;
const struct dive_site *d;
// tempting as it may be, don't die when called with ds=NULL
if (ds)
for_each_dive_site(i, d, ds_table) {
if (d == ds)
return i;
}
return -1;
}
// TODO: keep table sorted by UUID and do a binary search?
struct dive_site *get_dive_site_by_uuid(uint32_t uuid, struct dive_site_table *ds_table)
{
int i;
struct dive_site *ds;
for_each_dive_site (i, ds, ds_table)
if (ds->uuid == uuid)
return get_dive_site(i, ds_table);
return NULL;
}
/* there could be multiple sites of the same name - return the first one */
struct dive_site *get_dive_site_by_name(const char *name, struct dive_site_table *ds_table)
{
int i;
struct dive_site *ds;
for_each_dive_site (i, ds, ds_table) {
if (same_string(ds->name, name))
return ds;
}
return NULL;
}
/* there could be multiple sites at the same GPS fix - return the first one */
struct dive_site *get_dive_site_by_gps(const location_t *loc, struct dive_site_table *ds_table)
{
int i;
struct dive_site *ds;
for_each_dive_site (i, ds, ds_table) {
if (same_location(loc, &ds->location))
return ds;
}
return NULL;
}
/* to avoid a bug where we have two dive sites with different name and the same GPS coordinates
* and first get the gps coordinates (reading a V2 file) and happen to get back "the other" name,
* this function allows us to verify if a very specific name/GPS combination already exists */
struct dive_site *get_dive_site_by_gps_and_name(const char *name, const location_t *loc, struct dive_site_table *ds_table)
{
int i;
struct dive_site *ds;
for_each_dive_site (i, ds, ds_table) {
if (same_location(loc, &ds->location) && same_string(ds->name, name))
return ds;
}
return NULL;
}
// Calculate the distance in meters between two coordinates.
unsigned int get_distance(const location_t *loc1, const location_t *loc2)
{
double lat1_r = udeg_to_radians(loc1->lat.udeg);
double lat2_r = udeg_to_radians(loc2->lat.udeg);
double lat_d_r = udeg_to_radians(loc2->lat.udeg - loc1->lat.udeg);
double lon_d_r = udeg_to_radians(loc2->lon.udeg - loc1->lon.udeg);
double a = sin(lat_d_r/2) * sin(lat_d_r/2) +
cos(lat1_r) * cos(lat2_r) * sin(lon_d_r/2) * sin(lon_d_r/2);
if (a < 0.0) a = 0.0;
if (a > 1.0) a = 1.0;
double c = 2 * atan2(sqrt(a), sqrt(1.0 - a));
// Earth radius in metres
return lrint(6371000 * c);
}
/* find the closest one, no more than distance meters away - if more than one at same distance, pick the first */
struct dive_site *get_dive_site_by_gps_proximity(const location_t *loc, int distance, struct dive_site_table *ds_table)
{
int i;
struct dive_site *ds, *res = NULL;
unsigned int cur_distance, min_distance = distance;
for_each_dive_site (i, ds, ds_table) {
if (dive_site_has_gps_location(ds) &&
(cur_distance = get_distance(&ds->location, loc)) < min_distance) {
min_distance = cur_distance;
res = ds;
}
}
return res;
}
int register_dive_site(struct dive_site *ds)
{
return add_dive_site_to_table(ds, divelog.sites);
}
static int compare_sites(const struct dive_site *a, const struct dive_site *b)
{
return a->uuid > b->uuid ? 1 : a->uuid == b->uuid ? 0 : -1;
}
static int site_less_than(const struct dive_site *a, const struct dive_site *b)
{
return compare_sites(a, b) < 0;
}
static MAKE_GROW_TABLE(dive_site_table, struct dive_site *, dive_sites)
static MAKE_GET_INSERTION_INDEX(dive_site_table, struct dive_site *, dive_sites, site_less_than)
static MAKE_ADD_TO(dive_site_table, struct dive_site *, dive_sites)
static MAKE_REMOVE_FROM(dive_site_table, dive_sites)
static MAKE_GET_IDX(dive_site_table, struct dive_site *, dive_sites)
MAKE_SORT(dive_site_table, struct dive_site *, dive_sites, compare_sites)
static MAKE_REMOVE(dive_site_table, struct dive_site *, dive_site)
MAKE_CLEAR_TABLE(dive_site_table, dive_sites, dive_site)
MAKE_MOVE_TABLE(dive_site_table, dive_sites)
int add_dive_site_to_table(struct dive_site *ds, struct dive_site_table *ds_table)
{
/* If the site doesn't yet have an UUID, create a new one.
* Make this deterministic for testing. */
if (!ds->uuid) {
SHA_CTX ctx;
uint32_t csum[5];
SHA1_Init(&ctx);
if (ds->name)
SHA1_Update(&ctx, ds->name, strlen(ds->name));
if (ds->description)
SHA1_Update(&ctx, ds->description, strlen(ds->description));
if (ds->notes)
SHA1_Update(&ctx, ds->notes, strlen(ds->notes));
SHA1_Final((unsigned char *)csum, &ctx);
ds->uuid = csum[0];
}
/* Take care to never have the same uuid twice. This could happen on
* reimport of a log where the dive sites have diverged */
while (ds->uuid == 0 || get_dive_site_by_uuid(ds->uuid, ds_table) != NULL)
++ds->uuid;
int idx = dive_site_table_get_insertion_index(ds_table, ds);
add_to_dive_site_table(ds_table, idx, ds);
return idx;
}
struct dive_site *alloc_dive_site()
{
struct dive_site *ds;
ds = calloc(1, sizeof(*ds));
if (!ds)
exit(1);
return ds;
}
struct dive_site *alloc_dive_site_with_name(const char *name)
{
struct dive_site *ds = alloc_dive_site();
ds->name = copy_string(name);
return ds;
}
struct dive_site *alloc_dive_site_with_gps(const char *name, const location_t *loc)
{
struct dive_site *ds = alloc_dive_site_with_name(name);
ds->location = *loc;
return ds;
}
/* when parsing, dive sites are identified by uuid */
struct dive_site *alloc_or_get_dive_site(uint32_t uuid, struct dive_site_table *ds_table)
{
struct dive_site *ds;
if (uuid && (ds = get_dive_site_by_uuid(uuid, ds_table)) != NULL)
return ds;
ds = alloc_dive_site();
ds->uuid = uuid;
add_dive_site_to_table(ds, ds_table);
return ds;
}
int nr_of_dives_at_dive_site(struct dive_site *ds)
{
return ds->dives.nr;
}
bool is_dive_site_selected(struct dive_site *ds)
{
int i;
for (i = 0; i < ds->dives.nr; i++) {
if (ds->dives.dives[i]->selected)
return true;
}
return false;
}
void free_dive_site(struct dive_site *ds)
{
if (ds) {
free(ds->name);
free(ds->notes);
free(ds->description);
free(ds->dives.dives);
free_taxonomy(&ds->taxonomy);
free(ds);
}
}
int unregister_dive_site(struct dive_site *ds)
{
return remove_dive_site(ds, divelog.sites);
}
void delete_dive_site(struct dive_site *ds, struct dive_site_table *ds_table)
{
if (!ds)
return;
remove_dive_site(ds, ds_table);
free_dive_site(ds);
}
/* allocate a new site and add it to the table */
struct dive_site *create_dive_site(const char *name, struct dive_site_table *ds_table)
{
struct dive_site *ds = alloc_dive_site_with_name(name);
add_dive_site_to_table(ds, ds_table);
return ds;
}
/* same as before, but with GPS data */
struct dive_site *create_dive_site_with_gps(const char *name, const location_t *loc, struct dive_site_table *ds_table)
{
struct dive_site *ds = alloc_dive_site_with_gps(name, loc);
add_dive_site_to_table(ds, ds_table);
return ds;
}
/* if all fields are empty, the dive site is pointless */
bool dive_site_is_empty(struct dive_site *ds)
{
return !ds ||
(empty_string(ds->name) &&
empty_string(ds->description) &&
empty_string(ds->notes) &&
!has_location(&ds->location));
}
void copy_dive_site(struct dive_site *orig, struct dive_site *copy)
{
free(copy->name);
free(copy->notes);
free(copy->description);
copy->location = orig->location;
copy->name = copy_string(orig->name);
copy->notes = copy_string(orig->notes);
copy->description = copy_string(orig->description);
copy_taxonomy(&orig->taxonomy, &copy->taxonomy);
}
static void merge_string(char **a, char **b)
{
char *s1 = *a, *s2 = *b;
if (!s2)
return;
if (same_string(s1, s2))
return;
if (!s1) {
*a = strdup(s2);
return;
}
*a = format_string("(%s) or (%s)", s1, s2);
free(s1);
}
/* Used to check on import if two dive sites are equivalent.
* Since currently no merging is performed, be very conservative
* and only consider equal dive sites that are exactly the same.
* Taxonomy is not compared, as no taxonomy is generated on
* import.
*/
static bool same_dive_site(const struct dive_site *a, const struct dive_site *b)
{
return same_string(a->name, b->name)
&& same_location(&a->location, &b->location)
&& same_string(a->description, b->description)
&& same_string(a->notes, b->notes);
}
struct dive_site *get_same_dive_site(const struct dive_site *site)
{
int i;
struct dive_site *ds;
for_each_dive_site (i, ds, divelog.sites)
if (same_dive_site(ds, site))
return ds;
return NULL;
}
void merge_dive_site(struct dive_site *a, struct dive_site *b)
{
if (!has_location(&a->location)) a->location = b->location;
merge_string(&a->name, &b->name);
merge_string(&a->notes, &b->notes);
merge_string(&a->description, &b->description);
if (!a->taxonomy.category) {
a->taxonomy = b->taxonomy;
memset(&b->taxonomy, 0, sizeof(b->taxonomy));
}
}
struct dive_site *find_or_create_dive_site_with_name(const char *name, struct dive_site_table *ds_table)
{
int i;
struct dive_site *ds;
for_each_dive_site(i,ds, ds_table) {
if (same_string(name, ds->name))
break;
}
if (ds)
return ds;
return create_dive_site(name, ds_table);
}
void purge_empty_dive_sites(struct dive_site_table *ds_table)
{
int i, j;
struct dive *d;
struct dive_site *ds;
for (i = 0; i < ds_table->nr; i++) {
ds = get_dive_site(i, ds_table);
if (!dive_site_is_empty(ds))
continue;
for_each_dive(j, d) {
if (d->dive_site == ds)
unregister_dive_from_dive_site(d);
}
}
}
void add_dive_to_dive_site(struct dive *d, struct dive_site *ds)
{
int idx;
if (!d) {
report_info("Warning: add_dive_to_dive_site called with NULL dive");
return;
}
if (!ds) {
report_info("Warning: add_dive_to_dive_site called with NULL dive site");
return;
}
if (d->dive_site == ds)
return;
if (d->dive_site) {
report_info("Warning: adding dive that already belongs to a dive site to a different site");
unregister_dive_from_dive_site(d);
}
idx = dive_table_get_insertion_index(&ds->dives, d);
add_to_dive_table(&ds->dives, idx, d);
d->dive_site = ds;
}
struct dive_site *unregister_dive_from_dive_site(struct dive *d)
{
struct dive_site *ds = d->dive_site;
if (!ds)
return NULL;
remove_dive(d, &ds->dives);
d->dive_site = NULL;
return ds;
}

257
core/divesite.cpp Normal file
View File

@ -0,0 +1,257 @@
// SPDX-License-Identifier: GPL-2.0
/* divesite.c */
#include "divesite.h"
#include "dive.h"
#include "divelist.h"
#include "errorhelper.h"
#include "format.h"
#include "membuffer.h"
#include "subsurface-string.h"
#include "sha1.h"
#include <math.h>
int divesite_comp_uuid(const dive_site &ds1, const dive_site &ds2)
{
if (ds1.uuid == ds2.uuid)
return 0;
return ds1.uuid < ds2.uuid ? -1 : 1;
}
template <typename PRED>
dive_site *get_by_predicate(const dive_site_table &ds_table, PRED pred)
{
auto it = std::find_if(ds_table.begin(), ds_table.end(), pred);
return it != ds_table.end() ? it->get() : NULL;
}
dive_site *dive_site_table::get_by_uuid(uint32_t uuid) const
{
// The table is sorted by uuid
auto it = std::lower_bound(begin(), end(), uuid,
[] (const auto &ds, auto uuid) { return ds->uuid < uuid; });
return it != end() && (*it)->uuid == uuid ? it->get() : NULL;
}
/* there could be multiple sites of the same name - return the first one */
dive_site *dive_site_table::get_by_name(const std::string &name) const
{
return get_by_predicate(*this, [&name](const auto &ds) { return ds->name == name; });
}
/* there could be multiple sites at the same GPS fix - return the first one */
dive_site *dive_site_table::get_by_gps(const location_t *loc) const
{
return get_by_predicate(*this, [loc](const auto &ds) { return ds->location == *loc; });
}
/* to avoid a bug where we have two dive sites with different name and the same GPS coordinates
* and first get the gps coordinates (reading a V2 file) and happen to get back "the other" name,
* this function allows us to verify if a very specific name/GPS combination already exists */
dive_site *dive_site_table::get_by_gps_and_name(const std::string &name, const location_t loc) const
{
return get_by_predicate(*this, [&name, loc](const auto &ds) { return ds->location == loc &&
ds->name == name; });
}
/* find the closest one, no more than distance meters away - if more than one at same distance, pick the first */
dive_site *dive_site_table::get_by_gps_proximity(location_t loc, int distance) const
{
struct dive_site *res = nullptr;
unsigned int cur_distance, min_distance = distance;
for (const auto &ds: *this) {
if (dive_site_has_gps_location(ds.get()) &&
(cur_distance = get_distance(ds->location, loc)) < min_distance) {
min_distance = cur_distance;
res = ds.get();
}
}
return res;
}
dive_site_table::put_result dive_site_table::register_site(std::unique_ptr<dive_site> ds)
{
/* If the site doesn't yet have an UUID, create a new one.
* Make this deterministic for testing. */
if (!ds->uuid) {
SHA1 sha;
if (!ds->name.empty())
sha.update(ds->name);
if (!ds->description.empty())
sha.update(ds->description);
if (!ds->notes.empty())
sha.update(ds->notes);
ds->uuid = sha.hash_uint32();
}
/* Take care to never have the same uuid twice. This could happen on
* reimport of a log where the dive sites have diverged */
while (ds->uuid == 0 || get_by_uuid(ds->uuid) != NULL)
++ds->uuid;
return put(std::move(ds));
}
dive_site::dive_site()
{
}
dive_site::dive_site(const std::string &name) : name(name)
{
}
dive_site::dive_site(const std::string &name, const location_t loc) : name(name), location(loc)
{
}
dive_site::dive_site(uint32_t uuid) : uuid(uuid)
{
}
dive_site::~dive_site()
{
}
/* when parsing, dive sites are identified by uuid */
dive_site *dive_site_table::alloc_or_get(uint32_t uuid)
{
struct dive_site *ds;
if (uuid && (ds = get_by_uuid(uuid)) != NULL)
return ds;
return register_site(std::make_unique<dive_site>(uuid)).ptr;
}
size_t dive_site::nr_of_dives() const
{
return dives.size();
}
bool dive_site::is_selected() const
{
return any_of(dives.begin(), dives.end(),
[](dive *dive) { return dive->selected; });
}
/* allocate a new site and add it to the table */
dive_site *dive_site_table::create(const std::string &name)
{
return register_site(std::make_unique<dive_site>(name)).ptr;
}
/* same as before, but with GPS data */
dive_site *dive_site_table::create(const std::string &name, const location_t loc)
{
return register_site(std::make_unique<dive_site>(name, loc)).ptr;
}
/* if all fields are empty, the dive site is pointless */
bool dive_site::is_empty() const
{
return name.empty() &&
description.empty() &&
notes.empty() &&
!has_location(&location);
}
static void merge_string(std::string &a, const std::string &b)
{
if (b.empty())
return;
if (a == b)
return;
if (a.empty()) {
a = b;
return;
}
a = format_string_std("(%s) or (%s)", a.c_str(), b.c_str());
}
/* Used to check on import if two dive sites are equivalent.
* Since currently no merging is performed, be very conservative
* and only consider equal dive sites that are exactly the same.
* Taxonomy is not compared, as no taxonomy is generated on
* import.
*/
static bool same(const struct dive_site &a, const struct dive_site &b)
{
return a.name == b.name
&& a.location == b.location
&& a.description == b.description
&& a.notes == b.notes;
}
dive_site *dive_site_table::get_same(const struct dive_site &site) const
{
return get_by_predicate(*this, [site](const auto &ds) { return same(*ds, site); });
}
void dive_site::merge(dive_site &b)
{
if (!has_location(&location)) location = b.location;
merge_string(name, b.name);
merge_string(notes, b.notes);
merge_string(description, b.description);
if (taxonomy.empty())
taxonomy = std::move(b.taxonomy);
}
dive_site *dive_site_table::find_or_create(const std::string &name)
{
struct dive_site *ds = get_by_name(name);
if (ds)
return ds;
return create(name);
}
void dive_site_table::purge_empty()
{
for (const auto &ds: *this) {
if (!ds->is_empty())
continue;
while (!ds->dives.empty()) {
struct dive *d = ds->dives.back();
if (d->dive_site != ds.get()) {
report_info("Warning: dive %d registered to wrong dive site in %s", d->number, __func__);
ds->dives.pop_back();
} else {
unregister_dive_from_dive_site(d);
}
}
}
}
void dive_site::add_dive(struct dive *d)
{
if (!d) {
report_info("Warning: dive_site::add_dive() called with NULL dive");
return;
}
if (d->dive_site == this)
return;
if (d->dive_site) {
report_info("Warning: adding dive that already belongs to a dive site to a different site");
unregister_dive_from_dive_site(d);
}
dives.push_back(d);
d->dive_site = this;
}
struct dive_site *unregister_dive_from_dive_site(struct dive *d)
{
struct dive_site *ds = d->dive_site;
if (!ds)
return nullptr;
auto it = std::find(ds->dives.begin(), ds->dives.end(), d);
if (it != ds->dives.end())
ds->dives.erase(it);
else
report_info("Warning: dive not found in divesite table, even though it should be registered there.");
d->dive_site = nullptr;
return ds;
}

View File

@ -2,88 +2,39 @@
#ifndef DIVESITE_H
#define DIVESITE_H
#include "units.h"
#include "taxonomy.h"
#include "divelist.h"
#include "taxonomy.h"
#include "units.h"
#include <stdlib.h>
#ifdef __cplusplus
#include <QString>
#include <QObject>
extern "C" {
#else
#include <stdbool.h>
#endif
struct dive_site
{
uint32_t uuid;
char *name;
struct dive_table dives;
uint32_t uuid = 0;
std::string name;
std::vector<dive *> dives;
location_t location;
char *description;
char *notes;
struct taxonomy_data taxonomy;
std::string description;
std::string notes;
taxonomy_data taxonomy;
dive_site();
dive_site(const std::string &name);
dive_site(const std::string &name, const location_t loc);
dive_site(uint32_t uuid);
~dive_site();
size_t nr_of_dives() const;
bool is_selected() const;
bool is_empty() const;
void merge(struct dive_site &b); // Note: b is consumed
void add_dive(struct dive *d);
};
typedef struct dive_site_table {
int nr, allocated;
struct dive_site **dive_sites;
} dive_site_table_t;
static const dive_site_table_t empty_dive_site_table = { 0, 0, (struct dive_site **)0 };
static inline struct dive_site *get_dive_site(int nr, struct dive_site_table *ds_table)
{
if (nr >= ds_table->nr || nr < 0)
return NULL;
return ds_table->dive_sites[nr];
}
/* iterate over each dive site */
#define for_each_dive_site(_i, _x, _ds_table) \
for ((_i) = 0; ((_x) = get_dive_site(_i, _ds_table)) != NULL; (_i)++)
int get_divesite_idx(const struct dive_site *ds, struct dive_site_table *ds_table);
struct dive_site *get_dive_site_by_uuid(uint32_t uuid, struct dive_site_table *ds_table);
void sort_dive_site_table(struct dive_site_table *ds_table);
int add_dive_site_to_table(struct dive_site *ds, struct dive_site_table *ds_table);
struct dive_site *alloc_or_get_dive_site(uint32_t uuid, struct dive_site_table *ds_table);
struct dive_site *alloc_dive_site();
struct dive_site *alloc_dive_site_with_name(const char *name);
struct dive_site *alloc_dive_site_with_gps(const char *name, const location_t *loc);
int nr_of_dives_at_dive_site(struct dive_site *ds);
bool is_dive_site_selected(struct dive_site *ds);
void free_dive_site(struct dive_site *ds);
int unregister_dive_site(struct dive_site *ds);
int register_dive_site(struct dive_site *ds);
void delete_dive_site(struct dive_site *ds, struct dive_site_table *ds_table);
struct dive_site *create_dive_site(const char *name, struct dive_site_table *ds_table);
struct dive_site *create_dive_site_with_gps(const char *name, const location_t *, struct dive_site_table *ds_table);
struct dive_site *get_dive_site_by_name(const char *name, struct dive_site_table *ds_table);
struct dive_site *get_dive_site_by_gps(const location_t *, struct dive_site_table *ds_table);
struct dive_site *get_dive_site_by_gps_and_name(const char *name, const location_t *, struct dive_site_table *ds_table);
struct dive_site *get_dive_site_by_gps_proximity(const location_t *, int distance, struct dive_site_table *ds_table);
struct dive_site *get_same_dive_site(const struct dive_site *);
bool dive_site_is_empty(struct dive_site *ds);
void copy_dive_site_taxonomy(struct dive_site *orig, struct dive_site *copy);
void copy_dive_site(struct dive_site *orig, struct dive_site *copy);
void merge_dive_site(struct dive_site *a, struct dive_site *b);
unsigned int get_distance(const location_t *loc1, const location_t *loc2);
struct dive_site *find_or_create_dive_site_with_name(const char *name, struct dive_site_table *ds_table);
void purge_empty_dive_sites(struct dive_site_table *ds_table);
void clear_dive_site_table(struct dive_site_table *ds_table);
void move_dive_site_table(struct dive_site_table *src, struct dive_site_table *dst);
void add_dive_to_dive_site(struct dive *d, struct dive_site *ds);
struct dive_site *unregister_dive_from_dive_site(struct dive *d);
#ifdef __cplusplus
}
QString constructLocationTags(struct taxonomy_data *taxonomy, bool for_maintab);
int divesite_comp_uuid(const dive_site &ds1, const dive_site &ds2);
/* Make pointer-to-dive_site a "Qt metatype" so that we can pass it through QVariants */
#include <QObject>
Q_DECLARE_METATYPE(dive_site *);
#endif
#endif // DIVESITE_H

View File

@ -84,14 +84,14 @@ taxonomy_data reverseGeoLookup(degrees_t latitude, degrees_t longitude)
QString url;
QJsonObject obj;
taxonomy_data taxonomy = { 0, 0 };
taxonomy_data taxonomy;
// check the oceans API to figure out the body of water
url = geonamesOceanURL.arg(getUiLanguage().section(QRegularExpression("[-_ ]"), 0, 0)).arg(latitude.udeg / 1000000.0).arg(longitude.udeg / 1000000.0);
obj = doAsyncRESTGetRequest(url, 5000); // 5 secs. timeout
QVariantMap oceanName = obj.value("ocean").toVariant().toMap();
if (oceanName["name"].isValid())
taxonomy_set_category(&taxonomy, TC_OCEAN, qPrintable(oceanName["name"].toString()), taxonomy_origin::GEOCODED);
taxonomy_set_category(taxonomy, TC_OCEAN, oceanName["name"].toString().toStdString(), taxonomy_origin::GEOCODED);
// check the findNearbyPlaces API from geonames - that should give us country, state, city
url = geonamesNearbyPlaceNameURL.arg(getUiLanguage().section(QRegularExpression("[-_ ]"), 0, 0)).arg(latitude.udeg / 1000000.0).arg(longitude.udeg / 1000000.0);
@ -110,16 +110,16 @@ taxonomy_data reverseGeoLookup(degrees_t latitude, degrees_t longitude)
for (int idx = TC_COUNTRY; idx < TC_NR_CATEGORIES; idx++) {
if (firstData[taxonomy_api_names[idx]].isValid()) {
QString value = firstData[taxonomy_api_names[idx]].toString();
taxonomy_set_category(&taxonomy, (taxonomy_category)idx, qPrintable(value), taxonomy_origin::GEOCODED);
taxonomy_set_category(taxonomy, (taxonomy_category)idx, value.toStdString(), taxonomy_origin::GEOCODED);
}
}
const char *l3 = taxonomy_get_value(&taxonomy, TC_ADMIN_L3);
const char *lt = taxonomy_get_value(&taxonomy, TC_LOCALNAME);
if (empty_string(l3) && !empty_string(lt)) {
std::string l3 = taxonomy_get_value(taxonomy, TC_ADMIN_L3);
std::string lt = taxonomy_get_value(taxonomy, TC_LOCALNAME);
if (!l3.empty() && !lt.empty()) {
// basically this means we did get a local name (what we call town), but just like most places
// we didn't get an adminName_3 - which in some regions is the actual city that town belongs to,
// then we copy the town into the city
taxonomy_set_category(&taxonomy, TC_ADMIN_L3, lt, taxonomy_origin::GEOCOPIED);
taxonomy_set_category(taxonomy, TC_ADMIN_L3, lt, taxonomy_origin::GEOCOPIED);
}
} else {
report_error("geonames.org did not provide reverse lookup information");

27
core/divesitetable.h Normal file
View File

@ -0,0 +1,27 @@
// SPDX-License-Identifier: GPL-2.0
#ifndef DIVESITETABLE_H
#define DIVESITETABLE_H
#include "owning_table.h"
#include "units.h"
struct dive_site;
int divesite_comp_uuid(const dive_site &ds1, const dive_site &ds2);
class dive_site_table : public sorted_owning_table<dive_site, &divesite_comp_uuid> {
public:
put_result register_site(std::unique_ptr<dive_site> site); // Creates or changes UUID if duplicate
dive_site *get_by_uuid(uint32_t uuid) const;
dive_site *alloc_or_get(uint32_t uuid);
dive_site *create(const std::string &name);
dive_site *create(const std::string &name, const location_t);
dive_site *find_or_create(const std::string &name);
dive_site *get_by_name(const std::string &name) const;
dive_site *get_by_gps(const location_t *) const;
dive_site *get_by_gps_and_name(const std::string &name, const location_t) const;
dive_site *get_by_gps_proximity(location_t, int distance) const;
dive_site *get_same(const struct dive_site &) const;
void purge_empty();
};
#endif // DIVESITETABLE_H

View File

@ -1,8 +1,10 @@
#include "downloadfromdcthread.h"
#include "core/errorhelper.h"
#include "core/format.h"
#include "core/libdivecomputer.h"
#include "core/qthelper.h"
#include "core/range.h"
#include "core/uemis.h"
#include "core/settings/qPrefDiveComputer.h"
#include "core/divelist.h"
#if defined(Q_OS_ANDROID)
@ -15,16 +17,6 @@ static QHash<QString, QStringList> mobileProductList; // BT, BLE or FTDI support
QMap<QString, dc_descriptor_t *> descriptorLookup;
ConnectionListModel connectionListModel;
static QString str_error(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
const QString str = QString::vasprintf(fmt, args);
va_end(args);
return str;
}
static void updateRememberedDCs()
{
QString current = qPrefDiveComputer::vendor() + " - " + qPrefDiveComputer::product() + " - " + qPrefDiveComputer::device();
@ -89,7 +81,7 @@ void DownloadThread::run()
auto internalData = m_data->internalData();
internalData->descriptor = descriptorLookup[m_data->vendor().toLower() + m_data->product().toLower()];
internalData->log = &log;
internalData->btname = strdup(m_data->devBluetoothName().toUtf8());
internalData->btname = m_data->devBluetoothName().toStdString();
if (!internalData->descriptor) {
report_info("No download possible when DC type is unknown");
return;
@ -105,27 +97,28 @@ void DownloadThread::run()
report_info("Starting download from %s", qPrintable(getTransportString(transports)));
report_info("downloading %s dives", internalData->force_download ? "all" : "only new");
clear_divelog(&log);
log.clear();
Q_ASSERT(internalData->log != nullptr);
const char *errorText;
std::string errorText;
import_thread_cancelled = false;
error.clear();
if (!strcmp(internalData->vendor, "Uemis"))
if (internalData->vendor == "Uemis")
errorText = do_uemis_import(internalData);
else
errorText = do_libdivecomputer_import(internalData);
if (errorText) {
error = str_error(errorText, internalData->devname, internalData->vendor, internalData->product);
report_info("Finishing download thread: %s", qPrintable(error));
if (!errorText.empty()) {
error = format_string_std(errorText.c_str(), internalData->devname.c_str(),
internalData->vendor.c_str(), internalData->product.c_str());
report_info("Finishing download thread: %s", error.c_str());
} else {
if (!log.dives->nr)
error = tr("No new dives downloaded from dive computer");
report_info("Finishing download thread: %d dives downloaded", log.dives->nr);
if (log.dives.empty())
error = tr("No new dives downloaded from dive computer").toStdString();
report_info("Finishing download thread: %d dives downloaded", static_cast<int>(log.dives.size()));
}
qPrefDiveComputer::set_vendor(internalData->vendor);
qPrefDiveComputer::set_product(internalData->product);
qPrefDiveComputer::set_device(internalData->devname);
qPrefDiveComputer::set_vendor(internalData->vendor.c_str());
qPrefDiveComputer::set_product(internalData->product.c_str());
qPrefDiveComputer::set_device(internalData->devname.c_str());
qPrefDiveComputer::set_device_name(m_data->devBluetoothName());
updateRememberedDCs();
@ -209,7 +202,6 @@ void show_computer_list()
DCDeviceData::DCDeviceData()
{
memset(&data, 0, sizeof(data));
data.log = nullptr;
data.diveid = 0;
#if defined(BT_SUPPORT)
@ -241,9 +233,8 @@ QStringList DCDeviceData::getProductListFromVendor(const QString &vendor)
return productList[vendor];
}
int DCDeviceData::getMatchingAddress(const QString &vendor, const QString &product)
int DCDeviceData::getMatchingAddress(const QString &, const QString &product)
{
Q_UNUSED(vendor)
return connectionListModel.indexOf(product);
}
@ -254,17 +245,17 @@ DCDeviceData *DownloadThread::data()
QString DCDeviceData::vendor() const
{
return data.vendor;
return QString::fromStdString(data.vendor);
}
QString DCDeviceData::product() const
{
return data.product;
return QString::fromStdString(data.product);
}
QString DCDeviceData::devName() const
{
return data.devname;
return QString::fromStdString(data.devname);
}
QString DCDeviceData::devBluetoothName() const
@ -299,12 +290,12 @@ bool DCDeviceData::syncTime() const
void DCDeviceData::setVendor(const QString &vendor)
{
data.vendor = copy_qstring(vendor);
data.vendor = vendor.toStdString();
}
void DCDeviceData::setProduct(const QString &product)
{
data.product = copy_qstring(product);
data.product = product.toStdString();
}
void DCDeviceData::setDevName(const QString &devName)
@ -321,11 +312,11 @@ void DCDeviceData::setDevName(const QString &devName)
QString back = devName.mid(idx1 + 1, idx2 - idx1 - 1);
QString newDevName = back.indexOf(':') >= 0 ? back : front;
qWarning() << "Found invalid bluetooth device" << devName << "corrected to" << newDevName << ".";
data.devname = copy_qstring(newDevName);
data.devname = newDevName.toStdString();
return;
}
}
data.devname = copy_qstring(devName);
data.devname = devName.toStdString();
}
#if defined(Q_OS_ANDROID)

View File

@ -74,7 +74,7 @@ public:
void run() override;
DCDeviceData *data();
QString error;
std::string error;
struct divelog log;
private:

View File

@ -1,591 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
#ifdef __clang__
// Clang has a bug on zero-initialization of C structs.
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
#endif
/* equipment.c */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#include <limits.h>
#include "equipment.h"
#include "gettext.h"
#include "dive.h"
#include "divelist.h"
#include "divelog.h"
#include "errorhelper.h"
#include "pref.h"
#include "subsurface-string.h"
#include "table.h"
/* Warning: this has strange semantics for C-code! Not the weightsystem object
* is freed, but the data it references. The object itself is passed in by value.
* This is due to the fact how the table macros work.
*/
void free_weightsystem(weightsystem_t ws)
{
free((void *)ws.description);
ws.description = NULL;
}
void free_cylinder(cylinder_t c)
{
free((void *)c.type.description);
c.type.description = NULL;
}
void copy_weights(const struct weightsystem_table *s, struct weightsystem_table *d)
{
clear_weightsystem_table(d);
for (int i = 0; i < s->nr; i++)
add_cloned_weightsystem(d, s->weightsystems[i]);
}
void copy_cylinders(const struct cylinder_table *s, struct cylinder_table *d)
{
int i;
clear_cylinder_table(d);
for (i = 0; i < s->nr; i++)
add_cloned_cylinder(d, s->cylinders[i]);
}
static void free_tank_info(struct tank_info info)
{
free((void *)info.name);
}
/* weightsystem table functions */
//static MAKE_GET_IDX(weightsystem_table, weightsystem_t, weightsystems)
static MAKE_GROW_TABLE(weightsystem_table, weightsystem_t, weightsystems)
//static MAKE_GET_INSERTION_INDEX(weightsystem_table, weightsystem_t, weightsystems, weightsystem_less_than)
MAKE_ADD_TO(weightsystem_table, weightsystem_t, weightsystems)
static MAKE_REMOVE_FROM(weightsystem_table, weightsystems)
//MAKE_SORT(weightsystem_table, weightsystem_t, weightsystems, comp_weightsystems)
//MAKE_REMOVE(weightsystem_table, weightsystem_t, weightsystem)
MAKE_CLEAR_TABLE(weightsystem_table, weightsystems, weightsystem)
/* cylinder table functions */
//static MAKE_GET_IDX(cylinder_table, cylinder_t, cylinders)
static MAKE_GROW_TABLE(cylinder_table, cylinder_t, cylinders)
//static MAKE_GET_INSERTION_INDEX(cylinder_table, cylinder_t, cylinders, cylinder_less_than)
static MAKE_ADD_TO(cylinder_table, cylinder_t, cylinders)
static MAKE_REMOVE_FROM(cylinder_table, cylinders)
//MAKE_SORT(cylinder_table, cylinder_t, cylinders, comp_cylinders)
//MAKE_REMOVE(cylinder_table, cylinder_t, cylinder)
MAKE_CLEAR_TABLE(cylinder_table, cylinders, cylinder)
/* tank_info table functions */
static MAKE_GROW_TABLE(tank_info_table, tank_info_t, infos)
static MAKE_ADD_TO(tank_info_table, tank_info_t, infos)
//static MAKE_REMOVE_FROM(tank_info_table, infos)
MAKE_CLEAR_TABLE(tank_info_table, infos, tank_info)
const char *cylinderuse_text[NUM_GAS_USE] = {
QT_TRANSLATE_NOOP("gettextFromC", "OC-gas"), QT_TRANSLATE_NOOP("gettextFromC", "diluent"), QT_TRANSLATE_NOOP("gettextFromC", "oxygen"), QT_TRANSLATE_NOOP("gettextFromC", "not used")
};
enum cylinderuse cylinderuse_from_text(const char *text)
{
for (enum cylinderuse i = 0; i < NUM_GAS_USE; i++) {
if (same_string(text, cylinderuse_text[i]) || same_string(text, translate("gettextFromC", cylinderuse_text[i])))
return i;
}
return (enum cylinderuse)-1;
}
/* Add a metric or an imperial tank info structure. Copies the passed-in string. */
void add_tank_info_metric(struct tank_info_table *table, const char *name, int ml, int bar)
{
struct tank_info info = { strdup(name), .ml = ml, .bar = bar };
add_to_tank_info_table(table, table->nr, info);
}
void add_tank_info_imperial(struct tank_info_table *table, const char *name, int cuft, int psi)
{
struct tank_info info = { strdup(name), .cuft = cuft, .psi = psi };
add_to_tank_info_table(table, table->nr, info);
}
static struct tank_info *get_tank_info(struct tank_info_table *table, const char *name)
{
for (int i = 0; i < table->nr; ++i) {
if (same_string(table->infos[i].name, name))
return &table->infos[i];
}
return NULL;
}
extern void set_tank_info_data(struct tank_info_table *table, const char *name, volume_t size, pressure_t working_pressure)
{
struct tank_info *info = get_tank_info(table, name);
if (info) {
if (info->ml != 0 || info->bar != 0) {
info->bar = working_pressure.mbar / 1000;
info->ml = size.mliter;
} else {
info->psi = lrint(to_PSI(working_pressure));
info->cuft = lrint(ml_to_cuft(size.mliter) * mbar_to_atm(working_pressure.mbar));
}
} else {
// Metric is a better choice as the volume is independent of the working pressure
add_tank_info_metric(table, name, size.mliter, working_pressure.mbar / 1000);
}
}
extern void extract_tank_info(const struct tank_info *info, volume_t *size, pressure_t *working_pressure)
{
working_pressure->mbar = info->bar != 0 ? info->bar * 1000 : psi_to_mbar(info->psi);
if (info->ml != 0)
size->mliter = info->ml;
else if (working_pressure->mbar != 0)
size->mliter = lrint(cuft_to_l(info->cuft) * 1000 / mbar_to_atm(working_pressure->mbar));
}
extern bool get_tank_info_data(struct tank_info_table *table, const char *name, volume_t *size, pressure_t *working_pressure)
{
struct tank_info *info = get_tank_info(table, name);
if (info) {
extract_tank_info(info, size, working_pressure);
return true;
}
return false;
}
/* placeholders for a few functions that we need to redesign for the Qt UI */
void add_cylinder_description(const cylinder_type_t *type)
{
const char *desc = type->description;
if (empty_string(desc))
return;
for (int i = 0; i < tank_info_table.nr; i++) {
if (strcmp(tank_info_table.infos[i].name, desc) == 0)
return;
}
add_tank_info_metric(&tank_info_table, desc, type->size.mliter,
type->workingpressure.mbar / 1000);
}
void add_weightsystem_description(const weightsystem_t *weightsystem)
{
const char *desc;
int i;
desc = weightsystem->description;
if (!desc)
return;
for (i = 0; i < MAX_WS_INFO && ws_info[i].name != NULL; i++) {
if (same_string(ws_info[i].name, desc)) {
ws_info[i].grams = weightsystem->weight.grams;
return;
}
}
if (i < MAX_WS_INFO) {
// FIXME: leaked on exit
ws_info[i].name = strdup(desc);
ws_info[i].grams = weightsystem->weight.grams;
}
}
struct ws_info_t *get_weightsystem_description(const char *name)
{
for (int i = 0; i < MAX_WS_INFO && ws_info[i].name != NULL; i++) {
// Also finds translated names (TODO: should only consider non-user items).
if (same_string(ws_info[i].name, name) ||
same_string(translate("gettextFromC", ws_info[i].name), name))
return &ws_info[i];
}
return NULL;
}
weightsystem_t clone_weightsystem(weightsystem_t ws)
{
weightsystem_t res = { ws.weight, copy_string(ws.description), ws.auto_filled };
return res;
}
/* Add a clone of a weightsystem to the end of a weightsystem table.
* Cloned means that the description-string is copied. */
void add_cloned_weightsystem(struct weightsystem_table *t, weightsystem_t ws)
{
add_to_weightsystem_table(t, t->nr, clone_weightsystem(ws));
}
cylinder_t clone_cylinder(cylinder_t cyl)
{
cylinder_t res = cyl;
res.type.description = copy_string(res.type.description);
return res;
}
void add_cylinder(struct cylinder_table *t, int idx, cylinder_t cyl)
{
add_to_cylinder_table(t, idx, cyl);
/* FIXME: This is a horrible hack: we make sure that at the end of
* every single cylinder table there is an empty cylinder that can
* be used by the planner as "surface air" cylinder. Fix this.
*/
add_to_cylinder_table(t, t->nr, empty_cylinder);
t->nr--;
t->cylinders[t->nr].cylinder_use = NOT_USED;
}
/* Add a clone of a cylinder to the end of a cylinder table.
* Cloned means that the description-string is copied. */
void add_cloned_cylinder(struct cylinder_table *t, cylinder_t cyl)
{
add_cylinder(t, t->nr, clone_cylinder(cyl));
}
bool same_weightsystem(weightsystem_t w1, weightsystem_t w2)
{
return w1.weight.grams == w2.weight.grams &&
same_string(w1.description, w2.description);
}
void get_gas_string(struct gasmix gasmix, char *text, int len)
{
if (gasmix_is_air(gasmix))
snprintf(text, len, "%s", translate("gettextFromC", "air"));
else if (get_he(gasmix) == 0 && get_o2(gasmix) < 1000)
snprintf(text, len, translate("gettextFromC", "EAN%d"), (get_o2(gasmix) + 5) / 10);
else if (get_he(gasmix) == 0 && get_o2(gasmix) == 1000)
snprintf(text, len, "%s", translate("gettextFromC", "oxygen"));
else
snprintf(text, len, "(%d/%d)", (get_o2(gasmix) + 5) / 10, (get_he(gasmix) + 5) / 10);
}
/* Returns a static char buffer - only good for immediate use by printf etc */
const char *gasname(struct gasmix gasmix)
{
static char gas[64];
get_gas_string(gasmix, gas, sizeof(gas));
return gas;
}
int gas_volume(const cylinder_t *cyl, pressure_t p)
{
double bar = p.mbar / 1000.0;
double z_factor = gas_compressibility_factor(cyl->gasmix, bar);
return lrint(cyl->type.size.mliter * bar_to_atm(bar) / z_factor);
}
int find_best_gasmix_match(struct gasmix mix, const struct cylinder_table *cylinders)
{
int i;
int best = -1, score = INT_MAX;
for (i = 0; i < cylinders->nr; i++) {
const cylinder_t *match;
int distance;
match = cylinders->cylinders + i;
distance = gasmix_distance(mix, match->gasmix);
if (distance >= score)
continue;
best = i;
score = distance;
}
return best;
}
/*
* We hardcode the most common standard cylinders,
* we should pick up any other names from the dive
* logs directly.
*/
static void add_default_tank_infos(struct tank_info_table *table)
{
/* Size-only metric cylinders */
add_tank_info_metric(table, "10.0", 10000, 0);
add_tank_info_metric(table, "11.1", 11100, 0);
/* Most common AL cylinders */
add_tank_info_imperial(table, "AL40", 40, 3000);
add_tank_info_imperial(table, "AL50", 50, 3000);
add_tank_info_imperial(table, "AL63", 63, 3000);
add_tank_info_imperial(table, "AL72", 72, 3000);
add_tank_info_imperial(table, "AL80", 80, 3000);
add_tank_info_imperial(table, "AL100", 100, 3300);
/* Metric AL cylinders */
add_tank_info_metric(table, "ALU7", 7000, 200);
/* Somewhat common LP steel cylinders */
add_tank_info_imperial(table, "LP85", 85, 2640);
add_tank_info_imperial(table, "LP95", 95, 2640);
add_tank_info_imperial(table, "LP108", 108, 2640);
add_tank_info_imperial(table, "LP121", 121, 2640);
/* Somewhat common HP steel cylinders */
add_tank_info_imperial(table, "HP65", 65, 3442);
add_tank_info_imperial(table, "HP80", 80, 3442);
add_tank_info_imperial(table, "HP100", 100, 3442);
add_tank_info_imperial(table, "HP117", 117, 3442);
add_tank_info_imperial(table, "HP119", 119, 3442);
add_tank_info_imperial(table, "HP130", 130, 3442);
/* Common European steel cylinders */
add_tank_info_metric(table, "3 232 bar", 3000, 232);
add_tank_info_metric(table, "3 300 bar", 3000, 300);
add_tank_info_metric(table, "10 200 bar", 10000, 200);
add_tank_info_metric(table, "10 232 bar", 10000, 232);
add_tank_info_metric(table, "10 300 bar", 10000, 300);
add_tank_info_metric(table, "12 200 bar", 12000, 200);
add_tank_info_metric(table, "12 232 bar", 12000, 232);
add_tank_info_metric(table, "12 300 bar", 12000, 300);
add_tank_info_metric(table, "15 200 bar", 15000, 200);
add_tank_info_metric(table, "15 232 bar", 15000, 232);
add_tank_info_metric(table, "D7 300 bar", 14000, 300);
add_tank_info_metric(table, "D8.5 232 bar", 17000, 232);
add_tank_info_metric(table, "D12 232 bar", 24000, 232);
add_tank_info_metric(table, "D13 232 bar", 26000, 232);
add_tank_info_metric(table, "D15 232 bar", 30000, 232);
add_tank_info_metric(table, "D16 232 bar", 32000, 232);
add_tank_info_metric(table, "D18 232 bar", 36000, 232);
add_tank_info_metric(table, "D20 232 bar", 40000, 232);
}
struct tank_info_table tank_info_table;
void reset_tank_info_table(struct tank_info_table *table)
{
clear_tank_info_table(table);
if (prefs.display_default_tank_infos)
add_default_tank_infos(table);
/* Add cylinders from dive list */
for (int i = 0; i < divelog.dives->nr; ++i) {
const struct dive *dive = divelog.dives->dives[i];
for (int j = 0; j < dive->cylinders.nr; j++) {
const cylinder_t *cyl = get_cylinder(dive, j);
add_cylinder_description(&cyl->type);
}
}
}
/*
* We hardcode the most common weight system types
* This is a bit odd as the weight system types don't usually encode weight
*/
struct ws_info_t ws_info[MAX_WS_INFO] = {
{ QT_TRANSLATE_NOOP("gettextFromC", "integrated"), 0 },
{ QT_TRANSLATE_NOOP("gettextFromC", "belt"), 0 },
{ QT_TRANSLATE_NOOP("gettextFromC", "ankle"), 0 },
{ QT_TRANSLATE_NOOP("gettextFromC", "backplate"), 0 },
{ QT_TRANSLATE_NOOP("gettextFromC", "clip-on"), 0 },
};
void remove_cylinder(struct dive *dive, int idx)
{
remove_from_cylinder_table(&dive->cylinders, idx);
}
void remove_weightsystem(struct dive *dive, int idx)
{
remove_from_weightsystem_table(&dive->weightsystems, idx);
}
// ws is cloned.
void set_weightsystem(struct dive *dive, int idx, weightsystem_t ws)
{
if (idx < 0 || idx >= dive->weightsystems.nr)
return;
free_weightsystem(dive->weightsystems.weightsystems[idx]);
dive->weightsystems.weightsystems[idx] = clone_weightsystem(ws);
}
/* when planning a dive we need to make sure that all cylinders have a sane depth assigned
* and if we are tracking gas consumption the pressures need to be reset to start = end = workingpressure */
void reset_cylinders(struct dive *dive, bool track_gas)
{
pressure_t decopo2 = {.mbar = prefs.decopo2};
for (int i = 0; i < dive->cylinders.nr; i++) {
cylinder_t *cyl = get_cylinder(dive, i);
if (cyl->depth.mm == 0) /* if the gas doesn't give a mod, calculate based on prefs */
cyl->depth = gas_mod(cyl->gasmix, decopo2, dive, M_OR_FT(3,10));
if (track_gas)
cyl->start.mbar = cyl->end.mbar = cyl->type.workingpressure.mbar;
cyl->gas_used.mliter = 0;
cyl->deco_gas_used.mliter = 0;
}
}
static void copy_cylinder_type(const cylinder_t *s, cylinder_t *d)
{
free_cylinder(*d);
d->type = s->type;
d->type.description = s->type.description ? strdup(s->type.description) : NULL;
d->gasmix = s->gasmix;
d->depth = s->depth;
d->cylinder_use = s->cylinder_use;
d->manually_added = true;
}
/* copy the equipment data part of the cylinders but keep pressures */
void copy_cylinder_types(const struct dive *s, struct dive *d)
{
int i;
if (!s || !d)
return;
for (i = 0; i < s->cylinders.nr && i < d->cylinders.nr; i++)
copy_cylinder_type(get_cylinder(s, i), get_cylinder(d, i));
for ( ; i < s->cylinders.nr; i++)
add_cloned_cylinder(&d->cylinders, *get_cylinder(s, i));
}
cylinder_t *add_empty_cylinder(struct cylinder_table *t)
{
cylinder_t cyl = empty_cylinder;
cyl.type.description = strdup("");
add_cylinder(t, t->nr, cyl);
return &t->cylinders[t->nr - 1];
}
/* access to cylinders is controlled by two functions:
* - get_cylinder() returns the cylinder of a dive and supposes that
* the cylinder with the given index exists. If it doesn't, an error
* message is printed and NULL returned.
* - get_or_create_cylinder() creates an empty cylinder if it doesn't exist.
* Multiple cylinders might be created if the index is bigger than the
* number of existing cylinders
*/
cylinder_t *get_cylinder(const struct dive *d, int idx)
{
/* FIXME: The planner uses a dummy cylinder one past the official number of cylinders
* in the table to mark no-cylinder surface interavals. This is horrendous. Fix ASAP. */
// if (idx < 0 || idx >= d->cylinders.nr) {
if (idx < 0 || idx >= d->cylinders.nr + 1 || idx >= d->cylinders.allocated) {
report_info("Warning: accessing invalid cylinder %d (%d existing)", idx, d->cylinders.nr);
return NULL;
}
return &d->cylinders.cylinders[idx];
}
cylinder_t *get_or_create_cylinder(struct dive *d, int idx)
{
if (idx < 0) {
report_info("Warning: accessing invalid cylinder %d", idx);
return NULL;
}
while (idx >= d->cylinders.nr)
add_empty_cylinder(&d->cylinders);
return &d->cylinders.cylinders[idx];
}
/* if a default cylinder is set, use that */
void fill_default_cylinder(const struct dive *dive, cylinder_t *cyl)
{
const char *cyl_name = prefs.default_cylinder;
pressure_t pO2 = {.mbar = lrint(prefs.modpO2 * 1000.0)};
if (!cyl_name)
return;
for (int i = 0; i < tank_info_table.nr; ++i) {
struct tank_info *ti = &tank_info_table.infos[i];
if (strcmp(ti->name, cyl_name) == 0) {
cyl->type.description = strdup(ti->name);
if (ti->ml) {
cyl->type.size.mliter = ti->ml;
cyl->type.workingpressure.mbar = ti->bar * 1000;
} else {
cyl->type.workingpressure.mbar = psi_to_mbar(ti->psi);
if (ti->psi)
cyl->type.size.mliter = lrint(cuft_to_l(ti->cuft) * 1000 / bar_to_atm(psi_to_bar(ti->psi)));
}
// MOD of air
cyl->depth = gas_mod(cyl->gasmix, pO2, dive, 1);
return;
}
}
}
cylinder_t create_new_cylinder(const struct dive *d)
{
cylinder_t cyl = empty_cylinder;
fill_default_cylinder(d, &cyl);
cyl.start = cyl.type.workingpressure;
cyl.cylinder_use = OC_GAS;
return cyl;
}
cylinder_t create_new_manual_cylinder(const struct dive *d)
{
cylinder_t cyl = create_new_cylinder(d);
cyl.manually_added = true;
return cyl;
}
void add_default_cylinder(struct dive *d)
{
// Only add if there are no cylinders yet
if (d->cylinders.nr > 0)
return;
cylinder_t cyl;
if (!empty_string(prefs.default_cylinder)) {
cyl = create_new_cylinder(d);
} else {
cyl = empty_cylinder;
// roughly an AL80
cyl.type.description = strdup(translate("gettextFromC", "unknown"));
cyl.type.size.mliter = 11100;
cyl.type.workingpressure.mbar = 207000;
}
add_cylinder(&d->cylinders, 0, cyl);
reset_cylinders(d, false);
}
static bool show_cylinder(const struct dive *d, int i)
{
if (is_cylinder_used(d, i))
return true;
const cylinder_t *cyl = &d->cylinders.cylinders[i];
if (cyl->start.mbar || cyl->sample_start.mbar ||
cyl->end.mbar || cyl->sample_end.mbar)
return true;
if (cyl->manually_added)
return true;
/*
* The cylinder has some data, but none of it is very interesting,
* it has no pressures and no gas switches. Do we want to show it?
*/
return false;
}
/* The unused cylinders at the end of the cylinder list are hidden. */
int first_hidden_cylinder(const struct dive *d)
{
int res = d->cylinders.nr;
while (res > 0 && !show_cylinder(d, res - 1))
--res;
return res;
}
#ifdef DEBUG_CYL
void dump_cylinders(struct dive *dive, bool verbose)
{
printf("Cylinder list:\n");
for (int i = 0; i < dive->cylinders; i++) {
cylinder_t *cyl = get_cylinder(dive, i);
printf("%02d: Type %s, %3.1fl, %3.0fbar\n", i, cyl->type.description, cyl->type.size.mliter / 1000.0, cyl->type.workingpressure.mbar / 1000.0);
printf(" Gasmix O2 %2.0f%% He %2.0f%%\n", cyl->gasmix.o2.permille / 10.0, cyl->gasmix.he.permille / 10.0);
printf(" Pressure Start %3.0fbar End %3.0fbar Sample start %3.0fbar Sample end %3.0fbar\n", cyl->start.mbar / 1000.0, cyl->end.mbar / 1000.0, cyl->sample_start.mbar / 1000.0, cyl->sample_end.mbar / 1000.0);
if (verbose) {
printf(" Depth %3.0fm\n", cyl->depth.mm / 1000.0);
printf(" Added %s\n", (cyl->manually_added ? "manually" : ""));
printf(" Gas used Bottom %5.0fl Deco %5.0fl\n", cyl->gas_used.mliter / 1000.0, cyl->deco_gas_used.mliter / 1000.0);
printf(" Use %d\n", cyl->cylinder_use);
printf(" Bestmix %s %s\n", (cyl->bestmix_o2 ? "O2" : " "), (cyl->bestmix_he ? "He" : " "));
}
}
}
#endif

516
core/equipment.cpp Normal file
View File

@ -0,0 +1,516 @@
// SPDX-License-Identifier: GPL-2.0
#ifdef __clang__
// Clang has a bug on zero-initialization of C structs.
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
#endif
/* equipment.cpp */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#include <limits.h>
#include "equipment.h"
#include "gettext.h"
#include "dive.h"
#include "divelist.h"
#include "divelog.h"
#include "errorhelper.h"
#include "pref.h"
#include "range.h"
#include "subsurface-string.h"
cylinder_t::cylinder_t() = default;
cylinder_t::~cylinder_t() = default;
static cylinder_t make_surface_air_cylinder()
{
cylinder_t res;
res.cylinder_use = NOT_USED;
return res;
}
static const cylinder_t surface_air_cylinder = make_surface_air_cylinder();
static void warn_index(size_t i, size_t max)
{
if (i >= max + 1) {
report_info("Warning: accessing invalid cylinder %lu (%lu existing)",
static_cast<unsigned long>(i), static_cast<unsigned long>(max));
}
}
cylinder_t &cylinder_table::operator[](size_t i)
{
warn_index(i, size());
return i < size() ? std::vector<cylinder_t>::operator[](i)
: const_cast<cylinder_t &>(surface_air_cylinder);
}
const cylinder_t &cylinder_table::operator[](size_t i) const
{
warn_index(i, size());
return i < size() ? std::vector<cylinder_t>::operator[](i)
: surface_air_cylinder;
}
weightsystem_t::weightsystem_t() = default;
weightsystem_t::~weightsystem_t() = default;
weightsystem_t::weightsystem_t(weight_t w, std::string desc, bool auto_filled)
: weight(w), description(std::move(desc)), auto_filled(auto_filled)
{
}
const char *cylinderuse_text[NUM_GAS_USE] = {
QT_TRANSLATE_NOOP("gettextFromC", "OC-gas"), QT_TRANSLATE_NOOP("gettextFromC", "diluent"), QT_TRANSLATE_NOOP("gettextFromC", "oxygen"), QT_TRANSLATE_NOOP("gettextFromC", "not used")
};
enum cylinderuse cylinderuse_from_text(const char *text)
{
for (int i = 0; i < static_cast<int>(NUM_GAS_USE); i++) {
if (same_string(text, cylinderuse_text[i]) || same_string(text, translate("gettextFromC", cylinderuse_text[i])))
return static_cast<enum cylinderuse>(i);
}
return static_cast<enum cylinderuse>(-1);
}
/* Add a metric or an imperial tank info structure. Copies the passed-in string. */
static void add_tank_info_metric(std::vector<tank_info> &table, const std::string &name, int ml, int bar)
{
tank_info_table.push_back(tank_info{ name, .ml = ml, .bar = bar });
}
static void add_tank_info_imperial(std::vector<tank_info> &table, const std::string &name, int cuft, int psi)
{
tank_info_table.push_back(tank_info{ name, .cuft = cuft, .psi = psi });
}
struct tank_info *get_tank_info(std::vector<tank_info> &table, const std::string &name)
{
auto it = std::find_if(table.begin(), table.end(), [&name](const tank_info &info)
{ return info.name == name; });
return it != table.end() ? &*it : nullptr;
}
void set_tank_info_data(std::vector<tank_info> &table, const std::string &name, volume_t size, pressure_t working_pressure)
{
struct tank_info *info = get_tank_info(table, name);
if (info) {
if (info->ml != 0 || info->bar != 0) {
info->bar = working_pressure.mbar / 1000;
info->ml = size.mliter;
} else {
info->psi = lrint(to_PSI(working_pressure));
info->cuft = lrint(ml_to_cuft(size.mliter) * mbar_to_atm(working_pressure.mbar));
}
} else {
// Metric is a better choice as the volume is independent of the working pressure
add_tank_info_metric(table, name, size.mliter, working_pressure.mbar / 1000);
}
}
std::pair<volume_t, pressure_t> extract_tank_info(const struct tank_info &info)
{
pressure_t working_pressure {
static_cast<int32_t>(info.bar != 0 ? info.bar * 1000 : psi_to_mbar(info.psi))
};
volume_t size { 0 };
if (info.ml != 0)
size.mliter = info.ml;
else if (working_pressure.mbar != 0)
size.mliter = lrint(cuft_to_l(info.cuft) * 1000 / mbar_to_atm(working_pressure.mbar));
return std::make_pair(size, working_pressure);
}
std::pair<volume_t, pressure_t> get_tank_info_data(const std::vector<tank_info> &table, const std::string &name)
{
// Here, we would need a const version of get_tank_info().
auto it = std::find_if(table.begin(), table.end(), [&name](const tank_info &info)
{ return info.name == name; });
return it != table.end() ? extract_tank_info(*it)
: std::make_pair(volume_t{0}, pressure_t{0});
}
void add_cylinder_description(const cylinder_type_t &type)
{
const std::string &desc = type.description;
if (desc.empty())
return;
if (std::any_of(tank_info_table.begin(), tank_info_table.end(),
[&desc](const tank_info &info) { return info.name == desc; }))
return;
add_tank_info_metric(tank_info_table, desc, type.size.mliter,
type.workingpressure.mbar / 1000);
}
void add_weightsystem_description(const weightsystem_t &weightsystem)
{
if (weightsystem.description.empty())
return;
auto it = std::find_if(ws_info_table.begin(), ws_info_table.end(),
[&weightsystem](const ws_info &info)
{ return info.name == weightsystem.description; });
if (it != ws_info_table.end()) {
it->weight = weightsystem.weight;
return;
}
ws_info_table.push_back(ws_info { std::string(weightsystem.description), weightsystem.weight });
}
weight_t get_weightsystem_weight(const std::string &name)
{
// Also finds translated names (TODO: should only consider non-user items).
auto it = std::find_if(ws_info_table.begin(), ws_info_table.end(),
[&name](const ws_info &info)
{ return info.name == name || translate("gettextFromC", info.name.c_str()) == name; });
return it != ws_info_table.end() ? it->weight : weight_t();
}
void add_cylinder(struct cylinder_table *t, int idx, cylinder_t cyl)
{
t->insert(t->begin() + idx, std::move(cyl));
}
bool same_weightsystem(weightsystem_t w1, weightsystem_t w2)
{
return w1.weight.grams == w2.weight.grams &&
w1.description == w2.description;
}
void get_gas_string(struct gasmix gasmix, char *text, int len)
{
if (gasmix_is_air(gasmix))
snprintf(text, len, "%s", translate("gettextFromC", "air"));
else if (get_he(gasmix) == 0 && get_o2(gasmix) < 1000)
snprintf(text, len, translate("gettextFromC", "EAN%d"), (get_o2(gasmix) + 5) / 10);
else if (get_he(gasmix) == 0 && get_o2(gasmix) == 1000)
snprintf(text, len, "%s", translate("gettextFromC", "oxygen"));
else
snprintf(text, len, "(%d/%d)", (get_o2(gasmix) + 5) / 10, (get_he(gasmix) + 5) / 10);
}
/* Returns a static char buffer - only good for immediate use by printf etc */
const char *gasname(struct gasmix gasmix)
{
static char gas[64];
get_gas_string(gasmix, gas, sizeof(gas));
return gas;
}
int gas_volume(const cylinder_t *cyl, pressure_t p)
{
double bar = p.mbar / 1000.0;
double z_factor = gas_compressibility_factor(cyl->gasmix, bar);
return lrint(cyl->type.size.mliter * bar_to_atm(bar) / z_factor);
}
int find_best_gasmix_match(struct gasmix mix, const struct cylinder_table &cylinders)
{
int best = -1, score = INT_MAX;
for (auto [i, match]: enumerated_range(cylinders)) {
int distance = gasmix_distance(mix, match.gasmix);
if (distance >= score)
continue;
best = i;
score = distance;
}
return best;
}
/*
* We hardcode the most common standard cylinders,
* we should pick up any other names from the dive
* logs directly.
*/
static void add_default_tank_infos(std::vector<tank_info> &table)
{
/* Size-only metric cylinders */
add_tank_info_metric(table, "10.0", 10000, 0);
add_tank_info_metric(table, "11.1", 11100, 0);
/* Most common AL cylinders */
add_tank_info_imperial(table, "AL40", 40, 3000);
add_tank_info_imperial(table, "AL50", 50, 3000);
add_tank_info_imperial(table, "AL63", 63, 3000);
add_tank_info_imperial(table, "AL72", 72, 3000);
add_tank_info_imperial(table, "AL80", 80, 3000);
add_tank_info_imperial(table, "AL100", 100, 3300);
/* Metric AL cylinders */
add_tank_info_metric(table, "ALU7", 7000, 200);
/* Somewhat common LP steel cylinders */
add_tank_info_imperial(table, "LP85", 85, 2640);
add_tank_info_imperial(table, "LP95", 95, 2640);
add_tank_info_imperial(table, "LP108", 108, 2640);
add_tank_info_imperial(table, "LP121", 121, 2640);
/* Somewhat common HP steel cylinders */
add_tank_info_imperial(table, "HP65", 65, 3442);
add_tank_info_imperial(table, "HP80", 80, 3442);
add_tank_info_imperial(table, "HP100", 100, 3442);
add_tank_info_imperial(table, "HP117", 117, 3442);
add_tank_info_imperial(table, "HP119", 119, 3442);
add_tank_info_imperial(table, "HP130", 130, 3442);
/* Common European steel cylinders */
add_tank_info_metric(table, "3 232 bar", 3000, 232);
add_tank_info_metric(table, "3 300 bar", 3000, 300);
add_tank_info_metric(table, "10 200 bar", 10000, 200);
add_tank_info_metric(table, "10 232 bar", 10000, 232);
add_tank_info_metric(table, "10 300 bar", 10000, 300);
add_tank_info_metric(table, "12 200 bar", 12000, 200);
add_tank_info_metric(table, "12 232 bar", 12000, 232);
add_tank_info_metric(table, "12 300 bar", 12000, 300);
add_tank_info_metric(table, "15 200 bar", 15000, 200);
add_tank_info_metric(table, "15 232 bar", 15000, 232);
add_tank_info_metric(table, "D7 300 bar", 14000, 300);
add_tank_info_metric(table, "D8.5 232 bar", 17000, 232);
add_tank_info_metric(table, "D12 232 bar", 24000, 232);
add_tank_info_metric(table, "D13 232 bar", 26000, 232);
add_tank_info_metric(table, "D15 232 bar", 30000, 232);
add_tank_info_metric(table, "D16 232 bar", 32000, 232);
add_tank_info_metric(table, "D18 232 bar", 36000, 232);
add_tank_info_metric(table, "D20 232 bar", 40000, 232);
}
std::vector<tank_info> tank_info_table;
void reset_tank_info_table(std::vector<tank_info> &table)
{
table.clear();
if (prefs.display_default_tank_infos)
add_default_tank_infos(table);
/* Add cylinders from dive list */
for (auto &dive: divelog.dives) {
for (auto &cyl: dive->cylinders)
add_cylinder_description(cyl.type);
}
}
/*
* We hardcode the most common weight system types
* This is a bit odd as the weight system types don't usually encode weight
*/
struct std::vector<ws_info> ws_info_table = {
{ QT_TRANSLATE_NOOP("gettextFromC", "integrated"), weight_t() },
{ QT_TRANSLATE_NOOP("gettextFromC", "belt"), weight_t() },
{ QT_TRANSLATE_NOOP("gettextFromC", "ankle"), weight_t() },
{ QT_TRANSLATE_NOOP("gettextFromC", "backplate"), weight_t() },
{ QT_TRANSLATE_NOOP("gettextFromC", "clip-on"), weight_t() },
};
void remove_cylinder(struct dive *dive, int idx)
{
dive->cylinders.erase(dive->cylinders.begin() + idx);
}
void remove_weightsystem(struct dive *dive, int idx)
{
dive->weightsystems.erase(dive->weightsystems.begin() + idx);
}
void add_to_weightsystem_table(weightsystem_table *table, int idx, weightsystem_t ws)
{
idx = std::clamp(idx, 0, static_cast<int>(table->size()));
table->insert(table->begin() + idx, std::move(ws));
}
void set_weightsystem(struct dive *dive, int idx, weightsystem_t ws)
{
if (idx < 0 || static_cast<size_t>(idx) >= dive->weightsystems.size())
return;
dive->weightsystems[idx] = std::move(ws);
}
/* when planning a dive we need to make sure that all cylinders have a sane depth assigned
* and if we are tracking gas consumption the pressures need to be reset to start = end = workingpressure */
void reset_cylinders(struct dive *dive, bool track_gas)
{
pressure_t decopo2 = {.mbar = prefs.decopo2};
for (cylinder_t &cyl: dive->cylinders) {
if (cyl.depth.mm == 0) /* if the gas doesn't give a mod, calculate based on prefs */
cyl.depth = gas_mod(cyl.gasmix, decopo2, dive, M_OR_FT(3,10));
if (track_gas)
cyl.start.mbar = cyl.end.mbar = cyl.type.workingpressure.mbar;
cyl.gas_used.mliter = 0;
cyl.deco_gas_used.mliter = 0;
}
}
static void copy_cylinder_type(const cylinder_t &s, cylinder_t &d)
{
d.type = s.type;
d.gasmix = s.gasmix;
d.depth = s.depth;
d.cylinder_use = s.cylinder_use;
d.manually_added = true;
}
/* copy the equipment data part of the cylinders but keep pressures */
void copy_cylinder_types(const struct dive *s, struct dive *d)
{
if (!s || !d)
return;
for (size_t i = 0; i < s->cylinders.size() && i < d->cylinders.size(); i++)
copy_cylinder_type(s->cylinders[i], d->cylinders[i]);
for (size_t i = d->cylinders.size(); i < s->cylinders.size(); i++)
d->cylinders.push_back(s->cylinders[i]);
}
cylinder_t *add_empty_cylinder(struct cylinder_table *t)
{
t->emplace_back();
return &t->back();
}
/* access to cylinders is controlled by two functions:
* - get_cylinder() returns the cylinder of a dive and supposes that
* the cylinder with the given index exists. If it doesn't, an error
* message is printed and the "surface air" cylinder returned.
* (NOTE: this MUST not be written into!).
* - get_or_create_cylinder() creates an empty cylinder if it doesn't exist.
* Multiple cylinders might be created if the index is bigger than the
* number of existing cylinders
*/
cylinder_t *get_cylinder(struct dive *d, int idx)
{
return &d->cylinders[idx];
}
const cylinder_t *get_cylinder(const struct dive *d, int idx)
{
return &d->cylinders[idx];
}
cylinder_t *get_or_create_cylinder(struct dive *d, int idx)
{
if (idx < 0) {
report_info("Warning: accessing invalid cylinder %d", idx);
return NULL;
}
while (static_cast<size_t>(idx) >= d->cylinders.size())
add_empty_cylinder(&d->cylinders);
return &d->cylinders[idx];
}
/* if a default cylinder is set, use that */
void fill_default_cylinder(const struct dive *dive, cylinder_t *cyl)
{
const char *cyl_name = prefs.default_cylinder;
pressure_t pO2 = {.mbar = static_cast<int>(lrint(prefs.modpO2 * 1000.0))};
if (!cyl_name)
return;
for (auto &ti: tank_info_table) {
if (ti.name == cyl_name) {
cyl->type.description = ti.name;
if (ti.ml) {
cyl->type.size.mliter = ti.ml;
cyl->type.workingpressure.mbar = ti.bar * 1000;
} else {
cyl->type.workingpressure.mbar = psi_to_mbar(ti.psi);
if (ti.psi)
cyl->type.size.mliter = lrint(cuft_to_l(ti.cuft) * 1000 / bar_to_atm(psi_to_bar(ti.psi)));
}
// MOD of air
cyl->depth = gas_mod(cyl->gasmix, pO2, dive, 1);
return;
}
}
}
cylinder_t default_cylinder(const struct dive *d)
{
cylinder_t res;
fill_default_cylinder(d, &res);
return res;
}
cylinder_t create_new_cylinder(const struct dive *d)
{
cylinder_t cyl = default_cylinder(d);
cyl.start = cyl.type.workingpressure;
cyl.cylinder_use = OC_GAS;
return cyl;
}
cylinder_t create_new_manual_cylinder(const struct dive *d)
{
cylinder_t cyl = create_new_cylinder(d);
cyl.manually_added = true;
return cyl;
}
void add_default_cylinder(struct dive *d)
{
// Only add if there are no cylinders yet
if (!d->cylinders.empty())
return;
cylinder_t cyl;
if (!empty_string(prefs.default_cylinder)) {
cyl = create_new_cylinder(d);
} else {
// roughly an AL80
cyl.type.description = translate("gettextFromC", "unknown");
cyl.type.size.mliter = 11100;
cyl.type.workingpressure.mbar = 207000;
}
add_cylinder(&d->cylinders, 0, cyl);
reset_cylinders(d, false);
}
static bool show_cylinder(const struct dive *d, int i)
{
if (is_cylinder_used(d, i))
return true;
const cylinder_t &cyl = d->cylinders[i];
if (cyl.start.mbar || cyl.sample_start.mbar ||
cyl.end.mbar || cyl.sample_end.mbar)
return true;
if (cyl.manually_added)
return true;
/*
* The cylinder has some data, but none of it is very interesting,
* it has no pressures and no gas switches. Do we want to show it?
*/
return false;
}
/* The unused cylinders at the end of the cylinder list are hidden. */
int first_hidden_cylinder(const struct dive *d)
{
size_t res = d->cylinders.size();
while (res > 0 && !show_cylinder(d, res - 1))
--res;
return static_cast<int>(res);
}
#ifdef DEBUG_CYL
void dump_cylinders(struct dive *dive, bool verbose)
{
printf("Cylinder list:\n");
for (int i = 0; i < dive->cylinders; i++) {
cylinder_t *cyl = get_cylinder(dive, i);
printf("%02d: Type %s, %3.1fl, %3.0fbar\n", i, cyl->type.description.c_str(), cyl->type.size.mliter / 1000.0, cyl->type.workingpressure.mbar / 1000.0);
printf(" Gasmix O2 %2.0f%% He %2.0f%%\n", cyl->gasmix.o2.permille / 10.0, cyl->gasmix.he.permille / 10.0);
printf(" Pressure Start %3.0fbar End %3.0fbar Sample start %3.0fbar Sample end %3.0fbar\n", cyl->start.mbar / 1000.0, cyl->end.mbar / 1000.0, cyl->sample_start.mbar / 1000.0, cyl->sample_end.mbar / 1000.0);
if (verbose) {
printf(" Depth %3.0fm\n", cyl->depth.mm / 1000.0);
printf(" Added %s\n", (cyl->manually_added ? "manually" : ""));
printf(" Gas used Bottom %5.0fl Deco %5.0fl\n", cyl->gas_used.mliter / 1000.0, cyl->deco_gas_used.mliter / 1000.0);
printf(" Use %d\n", cyl->cylinder_use);
printf(" Bestmix %s %s\n", (cyl->bestmix_o2 ? "O2" : " "), (cyl->bestmix_he ? "He" : " "));
}
}
}
#endif

View File

@ -4,95 +4,85 @@
#include "gas.h"
#ifdef __cplusplus
extern "C" {
#endif
#include <memory>
#include <string>
#include <vector>
struct dive;
enum cylinderuse {OC_GAS, DILUENT, OXYGEN, NOT_USED, NUM_GAS_USE}; // The different uses for cylinders
extern const char *cylinderuse_text[NUM_GAS_USE];
typedef struct
struct cylinder_type_t
{
volume_t size;
pressure_t workingpressure;
const char *description; /* "LP85", "AL72", "AL80", "HP100+" or whatever */
} cylinder_type_t;
std::string description; /* "LP85", "AL72", "AL80", "HP100+" or whatever */
};
typedef struct
struct cylinder_t
{
cylinder_type_t type;
struct gasmix gasmix;
struct gasmix gasmix = gasmix_air;
pressure_t start, end, sample_start, sample_end;
depth_t depth;
bool manually_added;
bool manually_added = false;
volume_t gas_used;
volume_t deco_gas_used;
enum cylinderuse cylinder_use;
bool bestmix_o2;
bool bestmix_he;
} cylinder_t;
enum cylinderuse cylinder_use = OC_GAS;
bool bestmix_o2 = false;
bool bestmix_he = false;
static const cylinder_t empty_cylinder = { { { 0 }, { 0 }, (const char *)0}, { { 0 }, { 0 } } , { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, false, { 0 }, { 0 }, OC_GAS, false, false };
/* Table of cylinders. Attention: this stores cylinders,
* *not* pointers to cylinders. This has two crucial consequences:
* 1) Pointers to cylinders are not stable. They may be
* invalidated if the table is reallocated.
* 2) add_cylinder(), etc. take ownership of the
* cylinder. Notably of the description string. */
struct cylinder_table {
int nr, allocated;
cylinder_t *cylinders;
cylinder_t();
~cylinder_t();
};
typedef struct
/* Table of cylinders.
* This is a crazy class: it is basically a std::vector<>, but overrides
* the [] accessor functions and allows out-of-bound accesses.
* This is used in the planner, which uses "max_index + 1" for the
* surface air cylinder.
* Note: an out-of-bound access returns a reference to an object with
* static linkage that MUST NOT be written into.
* Yes, this is all very mad, but it grew historically.
*/
struct cylinder_table : public std::vector<cylinder_t> {
cylinder_t &operator[](size_t i);
const cylinder_t &operator[](size_t i) const;
};
struct weightsystem_t
{
weight_t weight;
const char *description; /* "integrated", "belt", "ankle" */
bool auto_filled; /* weight was automatically derived from the type */
} weightsystem_t;
std::string description; /* "integrated", "belt", "ankle" */
bool auto_filled = false; /* weight was automatically derived from the type */
static const weightsystem_t empty_weightsystem = { { 0 }, 0, false };
/* Table of weightsystems. Attention: this stores weightsystems,
* *not* pointers * to weightsystems. This has two crucial
* consequences:
* 1) Pointers to weightsystems are not stable. They may be
* invalidated if the table is reallocated.
* 2) add_to_weightsystem_table(), etc. takes ownership of the
* weightsystem. Notably of the description string */
struct weightsystem_table {
int nr, allocated;
weightsystem_t *weightsystems;
weightsystem_t();
weightsystem_t(weight_t w, std::string desc, bool auto_filled);
~weightsystem_t();
};
#define MAX_WS_INFO (100)
/* Table of weightsystems. Attention: this stores weightsystems,
* *not* pointers * to weightsystems. Therefore pointers to
* weightsystems are *not* stable.
*/
using weightsystem_table = std::vector<weightsystem_t>;
extern enum cylinderuse cylinderuse_from_text(const char *text);
extern void copy_weights(const struct weightsystem_table *s, struct weightsystem_table *d);
extern void copy_cylinders(const struct cylinder_table *s, struct cylinder_table *d);
extern weightsystem_t clone_weightsystem(weightsystem_t ws);
extern void free_weightsystem(weightsystem_t ws);
extern void copy_cylinder_types(const struct dive *s, struct dive *d);
extern void add_cloned_weightsystem(struct weightsystem_table *t, weightsystem_t ws);
extern cylinder_t clone_cylinder(cylinder_t cyl);
extern void free_cylinder(cylinder_t cyl);
extern cylinder_t *add_empty_cylinder(struct cylinder_table *t);
extern void add_cloned_cylinder(struct cylinder_table *t, cylinder_t cyl);
extern cylinder_t *get_cylinder(const struct dive *d, int idx);
extern cylinder_t *get_cylinder(struct dive *d, int idx);
extern const cylinder_t *get_cylinder(const struct dive *d, int idx);
extern cylinder_t *get_or_create_cylinder(struct dive *d, int idx);
extern void add_cylinder_description(const cylinder_type_t *);
extern void add_weightsystem_description(const weightsystem_t *);
extern bool same_weightsystem(weightsystem_t w1, weightsystem_t w2);
extern void remove_cylinder(struct dive *dive, int idx);
extern void remove_weightsystem(struct dive *dive, int idx);
extern void set_weightsystem(struct dive *dive, int idx, weightsystem_t ws);
extern void reset_cylinders(struct dive *dive, bool track_gas);
extern int gas_volume(const cylinder_t *cyl, pressure_t p); /* Volume in mliter of a cylinder at pressure 'p' */
extern int find_best_gasmix_match(struct gasmix mix, const struct cylinder_table *cylinders);
extern int find_best_gasmix_match(struct gasmix mix, const struct cylinder_table &cylinders);
extern void fill_default_cylinder(const struct dive *dive, cylinder_t *cyl); /* dive is needed to fill out MOD, which depends on salinity. */
extern cylinder_t default_cylinder(const struct dive *d);
extern cylinder_t create_new_manual_cylinder(const struct dive *dive); /* dive is needed to fill out MOD, which depends on salinity. */
extern void add_default_cylinder(struct dive *dive);
extern int first_hidden_cylinder(const struct dive *d);
@ -101,44 +91,34 @@ extern void dump_cylinders(struct dive *dive, bool verbose);
#endif
/* Weightsystem table functions */
extern void clear_weightsystem_table(struct weightsystem_table *);
extern void add_to_weightsystem_table(struct weightsystem_table *, int idx, weightsystem_t ws);
extern void add_to_weightsystem_table(weightsystem_table *, int idx, weightsystem_t ws);
/* Cylinder table functions */
extern void clear_cylinder_table(struct cylinder_table *);
extern void add_cylinder(struct cylinder_table *, int idx, cylinder_t cyl);
extern void append_cylinder(struct cylinder_table *, cylinder_t cyl);
void get_gas_string(struct gasmix gasmix, char *text, int len);
const char *gasname(struct gasmix gasmix);
typedef struct tank_info {
const char *name;
struct ws_info {
std::string name;
weight_t weight;
};
extern std::vector<ws_info> ws_info_table;
extern weight_t get_weightsystem_weight(const std::string &name); // returns 0 if not found
extern void add_weightsystem_description(const weightsystem_t &);
struct tank_info {
std::string name;
int cuft, ml, psi, bar;
} tank_info_t;
struct tank_info_table {
int nr, allocated;
struct tank_info *infos;
};
extern struct tank_info_table tank_info_table;
extern void reset_tank_info_table(struct tank_info_table *table);
extern void clear_tank_info_table(struct tank_info_table *table);
extern void add_tank_info_metric(struct tank_info_table *table, const char *name, int ml, int bar);
extern void add_tank_info_imperial(struct tank_info_table *table, const char *name, int cuft, int psi);
extern void extract_tank_info(const struct tank_info *info, volume_t *size, pressure_t *working_pressure);
extern bool get_tank_info_data(struct tank_info_table *table, const char *name, volume_t *size, pressure_t *pressure);
extern void set_tank_info_data(struct tank_info_table *table, const char *name, volume_t size, pressure_t working_pressure);
struct ws_info_t {
const char *name;
int grams;
};
extern struct ws_info_t ws_info[MAX_WS_INFO];
extern struct ws_info_t *get_weightsystem_description(const char *name);
#ifdef __cplusplus
}
#endif
extern std::vector<tank_info> tank_info_table;
extern struct tank_info *get_tank_info(std::vector<tank_info> &table, const std::string &name);
extern void set_tank_info_data(std::vector<tank_info> &table, const std::string &name, volume_t size, pressure_t working_pressure);
extern std::pair<volume_t, pressure_t> extract_tank_info(const struct tank_info &info);
extern std::pair<volume_t, pressure_t> get_tank_info_data(const std::vector<tank_info> &table, const std::string &name);
extern void add_cylinder_description(const cylinder_type_t &);
extern void reset_tank_info_table(std::vector<tank_info> &table);
#endif // EQUIPMENT_H

View File

@ -1,50 +1,44 @@
// SPDX-License-Identifier: GPL-2.0
#ifdef __clang__
// Clang has a bug on zero-initialization of C structs.
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
#endif
#include <stdarg.h>
#include "errorhelper.h"
#include "membuffer.h"
#include "format.h"
#include <stdarg.h>
#if !defined(Q_OS_ANDROID) && !defined(__ANDROID__)
#define LOG_MSG(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__)
#define LOG_MSG(fmt, s) fprintf(stderr, fmt, s)
#else
#include <android/log.h>
#define LOG_MSG(fmt, ...) __android_log_print(ANDROID_LOG_INFO, "Subsurface", fmt, ##__VA_ARGS__);
#define LOG_MSG(fmt, s) __android_log_print(ANDROID_LOG_INFO, "Subsurface", fmt, s);
#endif
#define VA_BUF(b, fmt) do { va_list args; va_start(args, fmt); put_vformat(b, fmt, args); va_end(args); } while (0)
int verbose;
void report_info(const char *fmt, ...)
{
struct membufferpp buf;
VA_BUF(&buf, fmt);
strip_mb(&buf);
LOG_MSG("INFO: %s\n", mb_cstring(&buf));
va_list args;
va_start(args, fmt);
std::string s = vformat_string_std(fmt, args);
va_end(args);
LOG_MSG("INFO: %s\n", s.c_str());
}
static void (*error_cb)(char *) = NULL;
static void (*error_cb)(std::string) = NULL;
int report_error(const char *fmt, ...)
{
struct membufferpp buf;
VA_BUF(&buf, fmt);
strip_mb(&buf);
LOG_MSG("ERROR: %s\n", mb_cstring(&buf));
va_list args;
va_start(args, fmt);
std::string s = vformat_string_std(fmt, args);
va_end(args);
LOG_MSG("ERROR: %s\n", s.c_str());
/* if there is no error callback registered, don't produce errors */
if (!error_cb)
return -1;
error_cb(detach_cstring(&buf));
if (error_cb)
error_cb(std::move(s));
return -1;
}
void set_error_cb(void(*cb)(char *))
void set_error_cb(void(*cb)(std::string))
{
error_cb = cb;
}

View File

@ -4,9 +4,7 @@
// error reporting functions
#ifdef __cplusplus
extern "C" {
#endif
#include <string>
#ifdef __GNUC__
#define __printf(x, y) __attribute__((__format__(__printf__, x, y)))
@ -17,11 +15,6 @@ extern "C" {
extern int verbose;
extern int __printf(1, 2) report_error(const char *fmt, ...);
extern void __printf(1, 2) report_info(const char *fmt, ...);
extern void set_error_cb(void(*cb)(char *)); // Callback takes ownership of passed string
#ifdef __cplusplus
}
#endif
extern void set_error_cb(void(*cb)(std::string s)); // Callback takes ownership of passed string
#endif

View File

@ -1,118 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
#include "event.h"
#include "eventtype.h"
#include "subsurface-string.h"
#include <string.h>
#include <stdlib.h>
int event_is_gaschange(const struct event *ev)
{
return ev->type == SAMPLE_EVENT_GASCHANGE ||
ev->type == SAMPLE_EVENT_GASCHANGE2;
}
bool event_is_divemodechange(const struct event *ev)
{
return same_string(ev->name, "modechange");
}
struct event *clone_event(const struct event *src_ev)
{
struct event *ev;
if (!src_ev)
return NULL;
size_t size = sizeof(*src_ev) + strlen(src_ev->name) + 1;
ev = (struct event*) malloc(size);
if (!ev)
exit(1);
memcpy(ev, src_ev, size);
ev->next = NULL;
return ev;
}
void free_events(struct event *ev)
{
while (ev) {
struct event *next = ev->next;
free(ev);
ev = next;
}
}
struct event *create_event(unsigned int time, int type, int flags, int value, const char *name)
{
int gas_index = -1;
struct event *ev;
unsigned int size, len = strlen(name);
size = sizeof(*ev) + len + 1;
ev = malloc(size);
if (!ev)
return NULL;
memset(ev, 0, size);
memcpy(ev->name, name, len);
ev->time.seconds = time;
ev->type = type;
ev->flags = flags;
ev->value = value;
/*
* Expand the events into a sane format. Currently
* just gas switches
*/
switch (type) {
case SAMPLE_EVENT_GASCHANGE2:
/* High 16 bits are He percentage */
ev->gas.mix.he.permille = (value >> 16) * 10;
/* Extension to the GASCHANGE2 format: cylinder index in 'flags' */
/* TODO: verify that gas_index < num_cylinders. */
if (flags > 0)
gas_index = flags-1;
/* Fallthrough */
case SAMPLE_EVENT_GASCHANGE:
/* Low 16 bits are O2 percentage */
ev->gas.mix.o2.permille = (value & 0xffff) * 10;
ev->gas.index = gas_index;
break;
}
remember_event_type(ev);
return ev;
}
struct event *clone_event_rename(const struct event *ev, const char *name)
{
return create_event(ev->time.seconds, ev->type, ev->flags, ev->value, name);
}
bool same_event(const struct event *a, const struct event *b)
{
if (a->time.seconds != b->time.seconds)
return 0;
if (a->type != b->type)
return 0;
if (a->flags != b->flags)
return 0;
if (a->value != b->value)
return 0;
return !strcmp(a->name, b->name);
}
extern enum event_severity get_event_severity(const struct event *ev)
{
switch (ev->flags & SAMPLE_FLAGS_SEVERITY_MASK) {
case SAMPLE_FLAGS_SEVERITY_INFO:
return EVENT_SEVERITY_INFO;
case SAMPLE_FLAGS_SEVERITY_WARN:
return EVENT_SEVERITY_WARN;
case SAMPLE_FLAGS_SEVERITY_ALARM:
return EVENT_SEVERITY_ALARM;
default:
return EVENT_SEVERITY_NONE;
}
}

112
core/event.cpp Normal file
View File

@ -0,0 +1,112 @@
// SPDX-License-Identifier: GPL-2.0
#include "event.h"
#include "divecomputer.h"
#include "eventtype.h"
#include "subsurface-string.h"
event::event() : type(SAMPLE_EVENT_NONE), flags(0), value(0),
divemode(OC), hidden(false)
{
/* That overwrites divemode. Is this a smart thing to do? */
gas.index = -1;
gas.mix = gasmix_air;
}
event::event(unsigned int time, int type, int flags, int value, const std::string &name) :
type(type), flags(flags), value(value), divemode(OC),
hidden(false), name(name)
{
int gas_index = -1;
fraction_t he;
this->time.seconds = time;
/*
* Expand the events into a sane format. Currently
* just gas switches
*/
switch (type) {
case SAMPLE_EVENT_GASCHANGE2:
/* High 16 bits are He percentage */
he.permille = (value >> 16) * 10;
/* Extension to the GASCHANGE2 format: cylinder index in 'flags' */
/* TODO: verify that gas_index < num_cylinders. */
if (flags > 0)
gas_index = flags-1;
/* Fallthrough */
case SAMPLE_EVENT_GASCHANGE:
/* Low 16 bits are O2 percentage */
gas.mix.he = he;
gas.mix.o2.permille = (value & 0xffff) * 10;
gas.index = gas_index;
break;
}
remember_event_type(this);
}
event::~event()
{
}
bool event::is_gaschange() const
{
return type == SAMPLE_EVENT_GASCHANGE || type == SAMPLE_EVENT_GASCHANGE2;
}
bool event::is_divemodechange() const
{
return name == "modechange";
}
bool event::operator==(const event &b) const
{
return std::tie(time.seconds, type, flags, value, name) ==
std::tie(b.time.seconds, b.type, b.flags, b.value, b.name);
}
enum event_severity event::get_severity() const
{
switch (flags & SAMPLE_FLAGS_SEVERITY_MASK) {
case SAMPLE_FLAGS_SEVERITY_INFO:
return EVENT_SEVERITY_INFO;
case SAMPLE_FLAGS_SEVERITY_WARN:
return EVENT_SEVERITY_WARN;
case SAMPLE_FLAGS_SEVERITY_ALARM:
return EVENT_SEVERITY_ALARM;
default:
return EVENT_SEVERITY_NONE;
}
}
event_loop::event_loop(const char *name) : name(name), idx(0)
{
}
struct event *event_loop::next(struct divecomputer &dc)
{
if (name.empty())
return nullptr;
while (idx < dc.events.size()) {
struct event &ev = dc.events[idx++];
if (ev.name == name)
return &ev;
}
return nullptr;
}
const struct event *event_loop::next(const struct divecomputer &dc)
{
return next(const_cast<divecomputer &>(dc));
}
struct event *get_first_event(struct divecomputer &dc, const std::string &name)
{
auto it = std::find_if(dc.events.begin(), dc.events.end(), [name](auto &ev) { return ev.name == name; });
return it != dc.events.end() ? &*it : nullptr;
}
const struct event *get_first_event(const struct divecomputer &dc, const std::string &name)
{
return get_first_event(const_cast<divecomputer &>(dc), name);
}

View File

@ -6,11 +6,10 @@
#include "gas.h"
#include "units.h"
#include <string>
#include <libdivecomputer/parser.h>
#ifdef __cplusplus
extern "C" {
#endif
struct divecomputer;
enum event_severity {
EVENT_SEVERITY_NONE = 0,
@ -23,8 +22,8 @@ enum event_severity {
* Events are currently based straight on what libdivecomputer gives us.
* We need to wrap these into our own events at some point to remove some of the limitations.
*/
struct event {
struct event *next;
duration_t time;
int type;
/* This is the annoying libdivecomputer format. */
@ -42,26 +41,53 @@ struct event {
struct gasmix mix;
} gas;
};
bool deleted; // used internally in the parser and in fixup_dive().
bool hidden;
char name[];
std::string name;
event();
event(unsigned int time, int type, int flags, int value, const std::string &name);
~event();
bool operator==(const event &b2) const;
bool is_gaschange() const;
bool is_divemodechange() const;
event_severity get_severity() const;
};
extern int event_is_gaschange(const struct event *ev);
extern bool event_is_divemodechange(const struct event *ev);
extern struct event *clone_event(const struct event *src_ev);
extern void free_events(struct event *ev);
extern struct event *create_event(unsigned int time, int type, int flags, int value, const char *name);
extern struct event *clone_event_rename(const struct event *ev, const char *name);
extern bool same_event(const struct event *a, const struct event *b);
extern enum event_severity get_event_severity(const struct event *ev);
class event_loop
{
std::string name;
size_t idx;
public:
event_loop(const char *name);
struct event *next(struct divecomputer &dc); // nullptr -> end
const struct event *next(const struct divecomputer &dc); // nullptr -> end
};
/* Since C doesn't have parameter-based overloading, two versions of get_next_event. */
extern const struct event *get_next_event(const struct event *event, const char *name);
extern struct event *get_next_event_mutable(struct event *event, const char *name);
/* Get gasmixes at increasing timestamps. */
class gasmix_loop {
const struct dive &dive;
const struct divecomputer &dc;
struct gasmix last;
event_loop loop;
const struct event *ev;
public:
gasmix_loop(const struct dive &dive, const struct divecomputer &dc);
gasmix next(int time);
};
#ifdef __cplusplus
}
#endif
/* Get divemodes at increasing timestamps. */
class divemode_loop {
const struct divecomputer &dc;
divemode_t last;
event_loop loop;
const struct event *ev;
public:
divemode_loop(const struct divecomputer &dc);
divemode_t next(int time);
};
extern const struct event *get_first_event(const struct divecomputer &dc, const std::string &name);
extern struct event *get_first_event(struct divecomputer &dc, const std::string &name);
#endif

View File

@ -14,7 +14,7 @@ struct event_type {
bool plot;
event_type(const struct event *ev) :
name(ev->name),
severity(get_event_severity(ev)),
severity(ev->get_severity()),
plot(true)
{
}
@ -27,14 +27,14 @@ static bool operator==(const event_type &en1, const event_type &en2)
return en1.name == en2.name && en1.severity == en2.severity;
}
extern "C" void clear_event_types()
void clear_event_types()
{
event_types.clear();
}
extern "C" void remember_event_type(const struct event *ev)
void remember_event_type(const struct event *ev)
{
if (empty_string(ev->name))
if (ev->name.empty())
return;
event_type type(ev);
if (std::find(event_types.begin(), event_types.end(), type) != event_types.end())
@ -42,33 +42,33 @@ extern "C" void remember_event_type(const struct event *ev)
event_types.push_back(std::move(type));
}
extern "C" bool is_event_type_hidden(const struct event *ev)
bool is_event_type_hidden(const struct event *ev)
{
auto it = std::find(event_types.begin(), event_types.end(), ev);
return it != event_types.end() && !it->plot;
}
extern "C" void hide_event_type(const struct event *ev)
void hide_event_type(const struct event *ev)
{
auto it = std::find(event_types.begin(), event_types.end(), ev);
if (it != event_types.end())
it->plot = false;
}
extern "C" void show_all_event_types()
void show_all_event_types()
{
for (event_type &e: event_types)
e.plot = true;
}
extern "C" void show_event_type(int idx)
void show_event_type(int idx)
{
if (idx < 0 || idx >= (int)event_types.size())
return;
event_types[idx].plot = true;
}
extern "C" bool any_event_types_hidden()
bool any_event_types_hidden()
{
return std::any_of(event_types.begin(), event_types.end(),
[] (const event_type &e) { return !e.plot; });
@ -102,13 +102,13 @@ static QString event_type_name(QString name, event_severity severity)
return QStringLiteral("%1 (%2)").arg(name, severity_name);
}
QString event_type_name(const event *ev)
QString event_type_name(const event &ev)
{
if (!ev || empty_string(ev->name))
if (ev.name.empty())
return QString();
QString name = QString::fromUtf8(ev->name);
return event_type_name(std::move(name), get_event_severity(ev));
QString name = QString::fromStdString(ev.name);
return event_type_name(std::move(name), ev.get_severity());
}
QString event_type_name(int idx)

View File

@ -3,29 +3,18 @@
#ifndef EVENTNAME_H
#define EVENTNAME_H
#ifdef __cplusplus
extern "C" {
#endif
#include <vector>
#include <QString>
extern void clear_event_types(void);
extern void clear_event_types();
extern void remember_event_type(const struct event *ev);
extern bool is_event_type_hidden(const struct event *ev);
extern void hide_event_type(const struct event *ev);
extern void show_all_event_types();
extern void show_event_type(int idx);
extern bool any_event_types_hidden();
#ifdef __cplusplus
}
// C++-only functions
#include <vector>
#include <QString>
extern std::vector<int> hidden_event_types();
QString event_type_name(const event *ev);
QString event_type_name(int idx);
#endif
extern QString event_type_name(const event &ev);
extern QString event_type_name(int idx);
#endif

View File

@ -2,10 +2,11 @@
#ifndef EXTRADATA_H
#define EXTRADATA_H
#include <string>
struct extra_data {
const char *key;
const char *value;
struct extra_data *next;
std::string key;
std::string value;
};
#endif

View File

@ -1,5 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
#include "ssrf.h"
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
@ -78,7 +77,7 @@ static void zip_read(struct zip_file *file, const char *filename, struct divelog
(void) parse_xml_buffer(filename, mem.data(), read, log, NULL);
}
extern "C" int try_to_open_zip(const char *filename, struct divelog *log)
int try_to_open_zip(const char *filename, struct divelog *log)
{
int success = 0;
/* Grr. libzip needs to re-open the file, it can't take a buffer */
@ -268,7 +267,7 @@ bool remote_repo_uptodate(const char *filename, struct git_info *info)
return false;
}
extern "C" int parse_file(const char *filename, struct divelog *log)
int parse_file(const char *filename, struct divelog *log)
{
struct git_info info;
const char *fmt;

View File

@ -6,13 +6,12 @@
#include <sys/stat.h>
#include <stdio.h>
#include <vector>
#include <utility>
struct divelog;
struct zip;
#ifdef __cplusplus
extern "C" {
#endif
extern void ostctools_import(const char *file, struct divelog *log);
extern int parse_file(const char *filename, struct divelog *log);
@ -28,22 +27,9 @@ extern int subsurface_access(const char *path, int mode);
extern int subsurface_stat(const char *path, struct stat *buf);
extern struct zip *subsurface_zip_open_readonly(const char *path, int flags, int *errorp);
extern int subsurface_zip_close(struct zip *zip);
#ifdef __cplusplus
}
// C++ only functions
#include <vector>
#include <utility>
// return data, errorcode pair.
extern std::pair<std::string, int> readfile(const char *filename);
extern std::pair<std::string, int> readfile(const char *filename); // return data, errorcode pair.
extern int try_to_open_cochran(const char *filename, std::string &mem, struct divelog *log);
extern int try_to_open_liquivision(const char *filename, std::string &mem, struct divelog *log);
extern int datatrak_import(std::string &mem, std::string &wl_mem, struct divelog *log);
#endif
#endif // FILE_H

View File

@ -134,87 +134,87 @@ static const range_mode_description *get_range_mode_description(enum filter_cons
return nullptr;
}
static enum filter_constraint_type filter_constraint_type_from_string(const char *s)
static enum filter_constraint_type filter_constraint_type_from_string(const std::string &s)
{
for (const auto &desc: type_descriptions) {
if (same_string(desc.token, s))
if (desc.token == s)
return desc.type;
}
report_error("unknown filter constraint type: %s", s);
report_error("unknown filter constraint type: %s", s.c_str());
return FILTER_CONSTRAINT_DATE;
}
static enum filter_constraint_string_mode filter_constraint_string_mode_from_string(const char *s)
static enum filter_constraint_string_mode filter_constraint_string_mode_from_string(const std::string &s)
{
for (const auto &desc: string_mode_descriptions) {
if (same_string(desc.token, s))
if (desc.token == s)
return desc.mode;
}
report_error("unknown filter constraint string mode: %s", s);
report_error("unknown filter constraint string mode: %s", s.c_str());
return FILTER_CONSTRAINT_EXACT;
}
static enum filter_constraint_range_mode filter_constraint_range_mode_from_string(const char *s)
static enum filter_constraint_range_mode filter_constraint_range_mode_from_string(const std::string &s)
{
for (const auto &desc: range_mode_descriptions) {
if (same_string(desc.token, s))
if (desc.token == s)
return desc.mode;
}
report_error("unknown filter constraint range mode: %s", s);
report_error("unknown filter constraint range mode: %s", s.c_str());
return FILTER_CONSTRAINT_EQUAL;
}
extern "C" const char *filter_constraint_type_to_string(enum filter_constraint_type type)
const char *filter_constraint_type_to_string(enum filter_constraint_type type)
{
const type_description *desc = get_type_description(type);
return desc ? desc->token : "unknown";
}
extern "C" const char *filter_constraint_string_mode_to_string(enum filter_constraint_string_mode mode)
const char *filter_constraint_string_mode_to_string(enum filter_constraint_string_mode mode)
{
const string_mode_description *desc = get_string_mode_description(mode);
return desc ? desc->token : "unknown";
}
extern "C" const char *filter_constraint_range_mode_to_string(enum filter_constraint_range_mode mode)
const char *filter_constraint_range_mode_to_string(enum filter_constraint_range_mode mode)
{
const range_mode_description *desc = get_range_mode_description(mode);
return desc ? desc->token : "unknown";
}
extern "C" int filter_constraint_type_to_index(enum filter_constraint_type type)
int filter_constraint_type_to_index(enum filter_constraint_type type)
{
const type_description *desc = get_type_description(type);
return desc ? desc - type_descriptions : -1;
}
extern "C" int filter_constraint_string_mode_to_index(enum filter_constraint_string_mode mode)
int filter_constraint_string_mode_to_index(enum filter_constraint_string_mode mode)
{
const string_mode_description *desc = get_string_mode_description(mode);
return desc ? desc - string_mode_descriptions : -1;
}
extern "C" int filter_constraint_range_mode_to_index(enum filter_constraint_range_mode mode)
int filter_constraint_range_mode_to_index(enum filter_constraint_range_mode mode)
{
const range_mode_description *desc = get_range_mode_description(mode);
return desc ? desc - range_mode_descriptions : -1;
}
extern "C" enum filter_constraint_type filter_constraint_type_from_index(int index)
enum filter_constraint_type filter_constraint_type_from_index(int index)
{
if (index >= 0 && index < (int)std::size(type_descriptions))
return type_descriptions[index].type;
return (enum filter_constraint_type)-1;
}
extern "C" enum filter_constraint_string_mode filter_constraint_string_mode_from_index(int index)
enum filter_constraint_string_mode filter_constraint_string_mode_from_index(int index)
{
if (index >= 0 && index < (int)std::size(string_mode_descriptions))
return string_mode_descriptions[index].mode;
return (enum filter_constraint_string_mode)-1;
}
extern "C" enum filter_constraint_range_mode filter_constraint_range_mode_from_index(int index)
enum filter_constraint_range_mode filter_constraint_range_mode_from_index(int index)
{
if (index >= 0 && index < (int)std::size(range_mode_descriptions))
return range_mode_descriptions[index].mode;
@ -390,7 +390,7 @@ QStringList filter_contraint_multiple_choice_translated(enum filter_constraint_t
return QStringList();
}
extern "C" bool filter_constraint_is_string(filter_constraint_type type)
bool filter_constraint_is_string(filter_constraint_type type)
{
// Currently a constraint is filter based if and only if it has a string
// mode (i.e. starts with, substring, exact). In the future we might also
@ -398,7 +398,7 @@ extern "C" bool filter_constraint_is_string(filter_constraint_type type)
return filter_constraint_has_string_mode(type);
}
extern "C" bool filter_constraint_is_timestamp(filter_constraint_type type)
bool filter_constraint_is_timestamp(filter_constraint_type type)
{
return type == FILTER_CONSTRAINT_DATE || type == FILTER_CONSTRAINT_DATE_TIME;
}
@ -408,37 +408,37 @@ static bool is_numerical_constraint(filter_constraint_type type)
return !filter_constraint_is_string(type) && !filter_constraint_is_timestamp(type);
}
extern "C" bool filter_constraint_has_string_mode(enum filter_constraint_type type)
bool filter_constraint_has_string_mode(enum filter_constraint_type type)
{
const type_description *desc = get_type_description(type);
return desc && desc->has_string_mode;
}
extern "C" bool filter_constraint_has_range_mode(enum filter_constraint_type type)
bool filter_constraint_has_range_mode(enum filter_constraint_type type)
{
const type_description *desc = get_type_description(type);
return desc && desc->has_range_mode;
}
extern "C" bool filter_constraint_is_star(filter_constraint_type type)
bool filter_constraint_is_star(filter_constraint_type type)
{
const type_description *desc = get_type_description(type);
return desc && desc->is_star_widget;
}
extern "C" bool filter_constraint_has_date_widget(filter_constraint_type type)
bool filter_constraint_has_date_widget(filter_constraint_type type)
{
const type_description *desc = get_type_description(type);
return desc && desc->has_date;
}
extern "C" bool filter_constraint_has_time_widget(filter_constraint_type type)
bool filter_constraint_has_time_widget(filter_constraint_type type)
{
const type_description *desc = get_type_description(type);
return desc && desc->has_time;
}
extern "C" int filter_constraint_num_decimals(enum filter_constraint_type type)
int filter_constraint_num_decimals(enum filter_constraint_type type)
{
const type_description *desc = get_type_description(type);
return desc ? desc->decimal_places : 1;
@ -446,7 +446,7 @@ extern "C" int filter_constraint_num_decimals(enum filter_constraint_type type)
// String constraints are valid if there is at least one term.
// Other constraints are always valid.
extern "C" bool filter_constraint_is_valid(const struct filter_constraint *constraint)
bool filter_constraint_is_valid(const struct filter_constraint *constraint)
{
if (!filter_constraint_is_string(constraint->type))
return true;
@ -551,14 +551,14 @@ filter_constraint::filter_constraint(const filter_constraint &c) :
data.numerical_range = c.data.numerical_range;
}
filter_constraint::filter_constraint(const char *type_in, const char *string_mode_in,
const char *range_mode_in, bool negate_in, const char *s_in) :
filter_constraint::filter_constraint(const std::string &type_in, const std::string &string_mode_in,
const std::string &range_mode_in, bool negate_in, const std::string &s_in) :
type(filter_constraint_type_from_string(type_in)),
string_mode(FILTER_CONSTRAINT_STARTS_WITH),
range_mode(FILTER_CONSTRAINT_GREATER),
negate(negate_in)
{
QString s(s_in);
QString s = QString::fromStdString(s_in);
if (filter_constraint_has_string_mode(type))
string_mode = filter_constraint_string_mode_from_string(string_mode_in);
if (filter_constraint_has_range_mode(type))
@ -622,22 +622,22 @@ filter_constraint::~filter_constraint()
delete data.string_list;
}
std::string filter_constraint_data_to_string(const filter_constraint *c)
std::string filter_constraint_data_to_string(const filter_constraint &c)
{
if (filter_constraint_is_timestamp(c->type)) {
std::string from_s = format_datetime(c->data.timestamp_range.from);
std::string to_s = format_datetime(c->data.timestamp_range.to);
if (filter_constraint_is_timestamp(c.type)) {
std::string from_s = format_datetime(c.data.timestamp_range.from);
std::string to_s = format_datetime(c.data.timestamp_range.to);
return from_s + ',' + to_s;
} else if (filter_constraint_is_string(c->type)) {
} else if (filter_constraint_is_string(c.type)) {
// TODO: this obviously breaks if the strings contain ",".
// That is currently not supported by the UI, but one day we might
// have to escape the strings.
return c->data.string_list->join(",").toStdString();
} else if (filter_constraint_is_multiple_choice(c->type)) {
return std::to_string(c->data.multiple_choice);
return c.data.string_list->join(",").toStdString();
} else if (filter_constraint_is_multiple_choice(c.type)) {
return std::to_string(c.data.multiple_choice);
} else {
return std::to_string(c->data.numerical_range.from) + ',' +
std::to_string(c->data.numerical_range.to);
return std::to_string(c.data.numerical_range.from) + ',' +
std::to_string(c.data.numerical_range.to);
}
}
@ -818,18 +818,18 @@ static bool check(const filter_constraint &c, const QStringList &list)
static bool has_tags(const filter_constraint &c, const struct dive *d)
{
QStringList dive_tags;
for (const tag_entry *tag = d->tag_list; tag; tag = tag->next)
dive_tags.push_back(QString::fromStdString(tag->tag->name).trimmed());
dive_tags.append(gettextFromC::tr(divemode_text_ui[d->dc.divemode]).trimmed());
for (const divetag *tag: d->tags)
dive_tags.push_back(QString::fromStdString(tag->name).trimmed());
dive_tags.append(gettextFromC::tr(divemode_text_ui[d->dcs[0].divemode]).trimmed());
return check(c, dive_tags);
}
static bool has_people(const filter_constraint &c, const struct dive *d)
{
QStringList dive_people;
for (const QString &s: QString(d->buddy).split(",", SKIP_EMPTY))
for (const QString &s: QString::fromStdString(d->buddy).split(",", SKIP_EMPTY))
dive_people.push_back(s.trimmed());
for (const QString &s: QString(d->diveguide).split(",", SKIP_EMPTY))
for (const QString &s: QString::fromStdString(d->diveguide).split(",", SKIP_EMPTY))
dive_people.push_back(s.trimmed());
return check(c, dive_people);
}
@ -838,10 +838,10 @@ static bool has_locations(const filter_constraint &c, const struct dive *d)
{
QStringList diveLocations;
if (d->divetrip)
diveLocations.push_back(QString(d->divetrip->location).trimmed());
diveLocations.push_back(QString::fromStdString(d->divetrip->location).trimmed());
if (d->dive_site)
diveLocations.push_back(QString(d->dive_site->name).trimmed());
diveLocations.push_back(QString::fromStdString(d->dive_site->name).trimmed());
return check(c, diveLocations);
}
@ -849,8 +849,8 @@ static bool has_locations(const filter_constraint &c, const struct dive *d)
static bool has_weight_type(const filter_constraint &c, const struct dive *d)
{
QStringList weightsystemTypes;
for (int i = 0; i < d->weightsystems.nr; ++i)
weightsystemTypes.push_back(d->weightsystems.weightsystems[i].description);
for (auto &ws: d->weightsystems)
weightsystemTypes.push_back(QString::fromStdString(ws.description));
return check(c, weightsystemTypes);
}
@ -858,8 +858,8 @@ static bool has_weight_type(const filter_constraint &c, const struct dive *d)
static bool has_cylinder_type(const filter_constraint &c, const struct dive *d)
{
QStringList cylinderTypes;
for (int i = 0; i < d->cylinders.nr; ++i)
cylinderTypes.push_back(d->cylinders.cylinders[i].type.description);
for (const cylinder_t &cyl: d->cylinders)
cylinderTypes.push_back(QString::fromStdString(cyl.type.description));
return check(c, cylinderTypes);
}
@ -867,16 +867,16 @@ static bool has_cylinder_type(const filter_constraint &c, const struct dive *d)
static bool has_suits(const filter_constraint &c, const struct dive *d)
{
QStringList diveSuits;
if (d->suit)
diveSuits.push_back(QString(d->suit));
if (!d->suit.empty())
diveSuits.push_back(QString::fromStdString(d->suit));
return check(c, diveSuits);
}
static bool has_notes(const filter_constraint &c, const struct dive *d)
{
QStringList diveNotes;
if (d->notes)
diveNotes.push_back(QString(d->notes));
if (!d->notes.empty())
diveNotes.push_back(QString::fromStdString(d->notes));
return check(c, diveNotes);
}
@ -905,22 +905,15 @@ static bool check_numerical_range_non_zero(const filter_constraint &c, int v)
static bool check_cylinder_size(const filter_constraint &c, const struct dive *d)
{
for (int i = 0; i < d->cylinders.nr; ++i) {
const cylinder_t &cyl = d->cylinders.cylinders[i];
if (cyl.type.size.mliter && check_numerical_range(c, cyl.type.size.mliter))
return true;
}
return false;
return std::any_of(d->cylinders.begin(), d->cylinders.end(), [&c](auto &cyl)
{ return cyl.type.size.mliter &&
check_numerical_range(c, cyl.type.size.mliter); });
}
static bool check_gas_range(const filter_constraint &c, const struct dive *d, gas_component component)
{
for (int i = 0; i < d->cylinders.nr; ++i) {
const cylinder_t &cyl = d->cylinders.cylinders[i];
if (check_numerical_range(c, get_gas_component_fraction(cyl.gasmix, component).permille))
return true;
}
return false;
return std::any_of(d->cylinders.begin(), d->cylinders.end(), [&c, &component](auto &cyl)
{ return check_numerical_range(c, get_gas_component_fraction(cyl.gasmix, component).permille); });
}
static long days_since_epoch(timestamp_t timestamp)
@ -959,12 +952,12 @@ static bool check_datetime_range(const filter_constraint &c, const struct dive *
// where the given timestamp is during that dive.
return time_during_dive_with_offset(d, c.data.timestamp_range.from, 0) != c.negate;
case FILTER_CONSTRAINT_LESS:
return (dive_endtime(d) <= c.data.timestamp_range.to) != c.negate;
return (d->endtime() <= c.data.timestamp_range.to) != c.negate;
case FILTER_CONSTRAINT_GREATER:
return (d->when >= c.data.timestamp_range.from) != c.negate;
case FILTER_CONSTRAINT_RANGE:
return (d->when >= c.data.timestamp_range.from &&
dive_endtime(d) <= c.data.timestamp_range.to) != c.negate;
d->endtime() <= c.data.timestamp_range.to) != c.negate;
}
return false;
}
@ -979,14 +972,14 @@ static bool check_time_of_day_internal(const dive *d, enum filter_constraint_ran
// where the given timestamp is during that dive. Note: this will fail for dives
// that run past midnight. We might want to special case that.
return (seconds_since_midnight(d->when) <= from &&
seconds_since_midnight(dive_endtime(d)) >= from) != negate;
seconds_since_midnight(d->endtime()) >= from) != negate;
case FILTER_CONSTRAINT_LESS:
return (seconds_since_midnight(dive_endtime(d)) <= to) != negate;
return (seconds_since_midnight(d->endtime()) <= to) != negate;
case FILTER_CONSTRAINT_GREATER:
return (seconds_since_midnight(d->when) >= from) != negate;
case FILTER_CONSTRAINT_RANGE:
return (seconds_since_midnight(d->when) >= from &&
seconds_since_midnight(dive_endtime(d)) <= to) != negate;
seconds_since_midnight(d->endtime()) <= to) != negate;
}
return false;
}
@ -1074,11 +1067,11 @@ bool filter_constraint_match_dive(const filter_constraint &c, const struct dive
case FILTER_CONSTRAINT_SAC:
return check_numerical_range_non_zero(c, d->sac);
case FILTER_CONSTRAINT_LOGGED:
return is_logged(d) != c.negate;
return d->is_logged() != c.negate;
case FILTER_CONSTRAINT_PLANNED:
return is_planned(d) != c.negate;
return d->is_planned() != c.negate;
case FILTER_CONSTRAINT_DIVE_MODE:
return check_multiple_choice(c, (int)d->dc.divemode); // should we be smarter and check all DCs?
return check_multiple_choice(c, (int)d->dcs[0].divemode); // should we be smarter and check all DCs?
case FILTER_CONSTRAINT_TAGS:
return has_tags(c, d);
case FILTER_CONSTRAINT_PEOPLE:

View File

@ -5,16 +5,10 @@
#define FILTER_CONSTRAINT_H
#include "units.h"
#include <QStringList>
struct dive;
#ifdef __cplusplus
#include <QStringList>
extern "C" {
#else
typedef void QStringList;
#endif
enum filter_constraint_type {
FILTER_CONSTRAINT_DATE,
FILTER_CONSTRAINT_DATE_TIME,
@ -82,16 +76,14 @@ struct filter_constraint {
QStringList *string_list;
uint64_t multiple_choice; // bit-field for multiple choice lists. currently, we support 64 items, extend if needed.
} data;
#ifdef __cplusplus
// For C++, define constructors, assignment operators and destructor to make our lives easier.
filter_constraint(filter_constraint_type type);
filter_constraint(const char *type, const char *string_mode,
const char *range_mode, bool negate, const char *data); // from parser data
filter_constraint(const std::string &type, const std::string &string_mode,
const std::string &range_mode, bool negate, const std::string &data); // from parser data
filter_constraint(const filter_constraint &);
filter_constraint &operator=(const filter_constraint &);
~filter_constraint();
bool operator==(const filter_constraint &f2) const;
#endif
};
extern const char *filter_constraint_type_to_string(enum filter_constraint_type);
@ -117,12 +109,6 @@ extern bool filter_constraint_has_time_widget(enum filter_constraint_type);
extern int filter_constraint_num_decimals(enum filter_constraint_type);
extern bool filter_constraint_is_valid(const struct filter_constraint *constraint);
#ifdef __cplusplus
}
#endif
// C++ only functions
#ifdef __cplusplus
QString filter_constraint_type_to_string_translated(enum filter_constraint_type);
QString filter_constraint_negate_to_string_translated(bool negate);
QString filter_constraint_string_mode_to_string_translated(enum filter_constraint_string_mode);
@ -151,8 +137,6 @@ void filter_constraint_set_timestamp_from(filter_constraint &c, timestamp_t from
void filter_constraint_set_timestamp_to(filter_constraint &c, timestamp_t to); // convert according to current units (metric or imperial)
void filter_constraint_set_multiple_choice(filter_constraint &c, uint64_t);
bool filter_constraint_match_dive(const filter_constraint &c, const struct dive *d);
std::string filter_constraint_data_to_string(const struct filter_constraint *constraint); // caller takes ownership of returned string
#endif
std::string filter_constraint_data_to_string(const struct filter_constraint &constraint); // caller takes ownership of returned string
#endif

View File

@ -4,24 +4,14 @@
#include "qthelper.h"
#include "subsurface-string.h"
static filter_preset_table &global_table()
std::string filter_preset::fulltext_query() const
{
return *divelog.filter_presets;
return data.fullText.originalQuery.toStdString();
}
extern "C" int filter_presets_count(void)
const char *filter_preset::fulltext_mode() const
{
return (int)global_table().size();
}
extern std::string filter_preset_fulltext_query(int preset)
{
return global_table()[preset].data.fullText.originalQuery.toStdString();
}
extern "C" const char *filter_preset_fulltext_mode(int preset)
{
switch (global_table()[preset].data.fulltextStringMode) {
switch (data.fulltextStringMode) {
default:
case StringFilterMode::SUBSTRING:
return "substring";
@ -32,98 +22,19 @@ extern "C" const char *filter_preset_fulltext_mode(int preset)
}
}
extern "C" void filter_preset_set_fulltext(struct filter_preset *preset, const char *fulltext, const char *fulltext_string_mode)
void filter_preset::set_fulltext(const std::string fulltext, const std::string &fulltext_string_mode)
{
if (same_string(fulltext_string_mode, "substring"))
preset->data.fulltextStringMode = StringFilterMode::SUBSTRING;
else if (same_string(fulltext_string_mode, "startswith"))
preset->data.fulltextStringMode = StringFilterMode::STARTSWITH;
else // if (same_string(fulltext_string_mode, "exact"))
preset->data.fulltextStringMode = StringFilterMode::EXACT;
preset->data.fullText = fulltext;
if (fulltext_string_mode == "substring")
data.fulltextStringMode = StringFilterMode::SUBSTRING;
else if (fulltext_string_mode == "startswith")
data.fulltextStringMode = StringFilterMode::STARTSWITH;
else // if (fulltext_string_mode == "exact"))
data.fulltextStringMode = StringFilterMode::EXACT;
data.fullText = QString::fromStdString(std::move(fulltext));
}
extern "C" int filter_preset_constraint_count(int preset)
void filter_preset::add_constraint(const std::string &type, const std::string &string_mode,
const std::string &range_mode, bool negate, const std::string &constraint_data)
{
return (int)global_table()[preset].data.constraints.size();
}
extern "C" const filter_constraint *filter_preset_constraint(int preset, int constraint)
{
return &global_table()[preset].data.constraints[constraint];
}
extern "C" void filter_preset_set_name(struct filter_preset *preset, const char *name)
{
preset->name = name;
}
static int filter_preset_add_to_table(const std::string name, const FilterData &d, struct filter_preset_table &table)
{
// std::lower_bound does a binary search - the vector must be sorted.
filter_preset newEntry { name, d };
auto it = std::lower_bound(table.begin(), table.end(), newEntry,
[](const filter_preset &p1, const filter_preset &p2)
{ return p1.name < p2.name; });
it = table.insert(it, newEntry);
return it - table.begin();
}
// Take care that the name doesn't already exist by adding numbers
static std::string get_unique_preset_name(const std::string &orig, const struct filter_preset_table &table)
{
std::string res = orig;
int count = 2;
while (std::find_if(table.begin(), table.end(),
[&res](const filter_preset &preset)
{ return preset.name == res; }) != table.end()) {
res = orig + "#" + std::to_string(count);
++count;
}
return res;
}
extern "C" void add_filter_preset_to_table(const struct filter_preset *preset, struct filter_preset_table *table)
{
std::string name = get_unique_preset_name(preset->name, *table);
filter_preset_add_to_table(name, preset->data, *table);
}
extern "C" void filter_preset_add_constraint(struct filter_preset *preset, const char *type, const char *string_mode,
const char *range_mode, bool negate, const char *data)
{
preset->data.constraints.emplace_back(type, string_mode, range_mode, negate, data);
}
int filter_preset_id(const std::string &name)
{
auto it = std::find_if(global_table().begin(), global_table().end(),
[&name] (filter_preset &p) { return p.name == name; });
return it != global_table().end() ? it - global_table().begin() : -1;
}
std::string filter_preset_name(int preset)
{
return global_table()[preset].name;
}
void filter_preset_set(int preset, const FilterData &data)
{
global_table()[preset].data = data;
}
FilterData filter_preset_get(int preset)
{
return global_table()[preset].data;
}
int filter_preset_add(const std::string &nameIn, const FilterData &d)
{
std::string name = get_unique_preset_name(nameIn, global_table());
return filter_preset_add_to_table(name, d, global_table());
}
void filter_preset_delete(int preset)
{
global_table().erase(global_table().begin() + preset);
data.constraints.emplace_back(type, string_mode, range_mode, negate, constraint_data);
}

View File

@ -1,75 +1,23 @@
// SPDX-License-Identifier: GPL-2.0
// A list of filter settings with names. Every name is unique, which
// means that saving to an old name will overwrite the old preset.
//
// Even though the filter data itself is a C++/Qt class to simplify
// string manipulation and memory management, the data is accessible
// via pure C functions so that it can be written to the log (git or xml).
#ifndef FILTER_PRESETS_H
#define FILTER_PRESETS_H
#include "divefilter.h"
#include <string>
struct dive;
struct filter_constraint;
struct FilterData;
// So that we can pass filter preset table between C and C++ we define
// it as an opaque type in C. Thus we can easily create the table in C++
// without having to do our own memory management and pass pointers to
// void through C.
#ifdef __cplusplus
#include "divefilter.h"
#include <vector>
#include <string>
struct filter_preset {
std::string name;
FilterData data;
std::string fulltext_query() const; // Fulltext query of filter preset.
const char *fulltext_mode() const; // String mode of fulltext query. Ownership is *not* passed to caller.
void set_fulltext(const std::string fulltext, const std::string &fulltext_string_mode); // First argument is consumed.
void add_constraint(const std::string &type, const std::string &string_mode,
const std::string &range_mode, bool negate, const std::string &data); // called by the parser, therefore data passed as strings.
};
// Subclassing standard library containers is generally
// not recommended. However, this makes interaction with
// C-code easier and since we don't add any member functions,
// this is not a problem.
struct filter_preset_table : public std::vector<filter_preset>
{
};
#else
struct filter_preset;
struct filter_preset_table;
#endif
#ifdef __cplusplus
extern "C" {
#endif
// The C IO code accesses the filter presets via integer indices.
extern int filter_presets_count(void);
extern const char *filter_preset_fulltext_mode(int preset); // string mode of fulltext query. ownership is *not* passed to caller.
extern int filter_preset_constraint_count(int preset); // number of constraints in the filter preset.
extern const struct filter_constraint *filter_preset_constraint(int preset, int constraint); // get constraint. ownership is *not* passed to caller.
extern void filter_preset_set_name(struct filter_preset *preset, const char *name);
extern void filter_preset_set_fulltext(struct filter_preset *preset, const char *fulltext, const char *fulltext_string_mode);
extern void add_filter_preset_to_table(const struct filter_preset *preset, struct filter_preset_table *table);
extern void filter_preset_add_constraint(struct filter_preset *preset, const char *type, const char *string_mode,
const char *range_mode, bool negate, const char *data); // called by the parser, therefore data passed as strings.
#ifdef __cplusplus
}
#endif
// C++ only functions
#ifdef __cplusplus
struct FilterData;
int filter_preset_id(const std::string &s); // for now, we assume that names are unique. returns -1 if no preset with that name.
void filter_preset_set(int preset, const FilterData &d); // this will override a preset if the name already exists.
FilterData filter_preset_get(int preset);
int filter_preset_add(const std::string &name, const FilterData &d); // returns point of insertion
void filter_preset_delete(int preset);
std::string filter_preset_name(int preset); // name of filter preset - caller must free the result.
std::string filter_preset_fulltext_query(int preset); // fulltext query of filter preset - caller must free the result.
#endif
#endif

View File

@ -0,0 +1,53 @@
// SPDX-License-Identifier: GPL-2.0
#include "filterpresettable.h"
#include "filterpreset.h"
#include <algorithm>
int filter_preset_table::preset_id(const std::string &name) const
{
auto it = std::find_if(begin(), end(), [&name] (const filter_preset &p) { return p.name == name; });
return it != end() ? it - begin() : -1;
}
// Take care that the name doesn't already exist by adding numbers
static std::string get_unique_preset_name(const std::string &orig, const struct filter_preset_table &table)
{
std::string res = orig;
int count = 2;
while (std::find_if(table.begin(), table.end(),
[&res](const filter_preset &preset)
{ return preset.name == res; }) != table.end()) {
res = orig + "#" + std::to_string(count);
++count;
}
return res;
}
static int filter_preset_add_to_table(const std::string name, const FilterData &d, struct filter_preset_table &table)
{
// std::lower_bound does a binary search - the vector must be sorted.
filter_preset newEntry { name, d };
auto it = std::lower_bound(table.begin(), table.end(), newEntry,
[](const filter_preset &p1, const filter_preset &p2)
{ return p1.name < p2.name; });
it = table.insert(it, newEntry);
return it - table.begin();
}
void filter_preset_table::add(const filter_preset &preset)
{
std::string name = get_unique_preset_name(preset.name, *this);
filter_preset_add_to_table(name, preset.data, *this);
}
int filter_preset_table::add(const std::string &nameIn, const FilterData &d)
{
std::string name = get_unique_preset_name(nameIn, *this);
return filter_preset_add_to_table(name, d, *this);
}
void filter_preset_table::remove(int preset)
{
erase(begin() + preset);
}

25
core/filterpresettable.h Normal file
View File

@ -0,0 +1,25 @@
// SPDX-License-Identifier: GPL-2.0
// A list of filter settings with names. Every name is unique, which
// means that saving to an old name will overwrite the old preset.
#ifndef FILTER_PRESETSTABLE_H
#define FILTER_PRESETSTABLE_H
#include <string>
#include <vector>
struct filter_preset;
struct FilterData;
// Subclassing standard library containers is generally
// not recommended. However, this makes interaction with
// C-code easier and since we don't add any member functions,
// this is not a problem.
struct filter_preset_table : public std::vector<filter_preset>
{
void add(const filter_preset &preset);
int add(const std::string &name, const FilterData &d); // returns point of insertion
void remove(int iox);
int preset_id(const std::string &s) const; // for now, we assume that names are unique. returns -1 if no preset with that name
};
#endif

View File

@ -353,20 +353,27 @@ std::string casprintf_loc(const char *cformat, ...)
return std::string(utf8.constData(), utf8.size());
}
std::string __printf(1, 2) format_string_std(const char *fmt, ...)
std::string format_string_std(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
size_t stringsize = vsnprintf(NULL, 0, fmt, ap);
std::string res = vformat_string_std(fmt, ap);
va_end(ap);
return res;
}
std::string vformat_string_std(const char *fmt, va_list ap)
{
va_list ap2;
va_copy(ap2, ap);
size_t stringsize = vsnprintf(NULL, 0, fmt, ap2);
va_end(ap2);
if (stringsize == 0)
return std::string();
std::string res;
res.resize(stringsize); // Pointless clearing, oh my.
// This overwrites the terminal null-byte of std::string.
// That's probably "undefined behavior". Oh my.
va_start(ap, fmt);
vsnprintf(res.data(), stringsize + 1, fmt, ap);
va_end(ap);
return res;
}

View File

@ -7,12 +7,11 @@
#define __printf(x, y)
#endif
#ifdef __cplusplus
#include <QString>
__printf(1, 2) QString qasprintf_loc(const char *cformat, ...);
__printf(1, 0) QString vqasprintf_loc(const char *cformat, va_list ap);
__printf(1, 2) std::string casprintf_loc(const char *cformat, ...);
__printf(1, 0) std::string vformat_string_std(const char *fmt, va_list ap);
__printf(1, 2) std::string format_string_std(const char *fmt, ...);
#endif
#endif

View File

@ -10,11 +10,6 @@
#include <QLocale>
#include <map>
// This class caches each dives words, so that we can unregister a dive from the full text search
struct full_text_cache {
std::vector<QString> words;
};
// The FullText-search class
class FullText {
std::map<QString, std::vector<dive *>> words; // Dives that belong to each word
@ -35,8 +30,6 @@ static FullText self;
// C-interface functions
extern "C" {
void fulltext_register(struct dive *d)
{
self.registerDive(d);
@ -57,8 +50,6 @@ void fulltext_populate()
self.populate();
}
} // extern "C"
// C++-only interface functions
FullTextResult fulltext_find_dives(const FullTextQuery &q, StringFilterMode mode)
{
@ -123,31 +114,27 @@ static void tokenize(QString s, std::vector<QString> &res)
static std::vector<QString> getWords(const dive *d)
{
std::vector<QString> res;
tokenize(QString(d->notes), res);
tokenize(QString(d->diveguide), res);
tokenize(QString(d->buddy), res);
tokenize(QString(d->suit), res);
for (const tag_entry *tag = d->tag_list; tag; tag = tag->next)
tokenize(QString::fromStdString(tag->tag->name), res);
for (int i = 0; i < d->cylinders.nr; ++i) {
const cylinder_t &cyl = *get_cylinder(d, i);
tokenize(QString(cyl.type.description), res);
}
for (int i = 0; i < d->weightsystems.nr; ++i) {
const weightsystem_t &ws = d->weightsystems.weightsystems[i];
tokenize(QString(ws.description), res);
}
tokenize(QString::fromStdString(d->notes), res);
tokenize(QString::fromStdString(d->diveguide), res);
tokenize(QString::fromStdString(d->buddy), res);
tokenize(QString::fromStdString(d->suit), res);
for (const divetag *tag: d->tags)
tokenize(QString::fromStdString(tag->name), res);
for (auto &cyl: d->cylinders)
tokenize(QString::fromStdString(cyl.type.description), res);
for (auto &ws: d->weightsystems)
tokenize(QString::fromStdString(ws.description), res);
// TODO: We should tokenize all dive-sites and trips first and then
// take the tokens from a cache.
if (d->dive_site) {
tokenize(d->dive_site->name, res);
const char *country = taxonomy_get_country(&d->dive_site->taxonomy);
if (country)
tokenize(country, res);
tokenize(QString::fromStdString(d->dive_site->name), res);
std::string country = taxonomy_get_country(d->dive_site->taxonomy);
if (!country.empty())
tokenize(country.c_str(), res);
}
// TODO: We should index trips separately!
if (d->divetrip)
tokenize(d->divetrip->location, res);
tokenize(QString::fromStdString(d->divetrip->location), res);
return res;
}
@ -156,11 +143,9 @@ void FullText::populate()
// we want this to be two calls as the second text is overwritten below by the lines starting with "\r"
uiNotification(QObject::tr("Create full text index"));
uiNotification(QObject::tr("start processing"));
int i;
dive *d;
for_each_dive(i, d)
registerDive(d);
uiNotification(QObject::tr("%1 dives processed").arg(divelog.dives->nr));
for (auto &d: divelog.dives)
registerDive(d.get());
uiNotification(QObject::tr("%1 dives processed").arg(divelog.dives.size()));
}
void FullText::registerDive(struct dive *d)
@ -168,7 +153,7 @@ void FullText::registerDive(struct dive *d)
if (d->full_text)
unregisterWords(d, d->full_text->words);
else
d->full_text = new full_text_cache;
d->full_text = std::make_unique<full_text_cache>();
d->full_text->words = getWords(d);
registerWords(d, d->full_text->words);
}
@ -178,18 +163,13 @@ void FullText::unregisterDive(struct dive *d)
if (!d->full_text)
return;
unregisterWords(d, d->full_text->words);
delete d->full_text;
d->full_text = nullptr;
d->full_text.reset();
}
void FullText::unregisterAll()
{
int i;
dive *d;
for_each_dive(i, d) {
delete d->full_text;
d->full_text = nullptr;
}
for (auto &d: divelog.dives)
d->full_text.reset();
words.clear();
}

View File

@ -5,43 +5,30 @@
// issues such as COW semantics and UTF-16 encoding, it provides
// platform independence and reasonable performance. Therefore,
// this is based in QString instead of std::string.
//
// To make this accessible from C, this does manual memory management:
// Every dive is associated with a cache of words. Thus, when deleting
// a dive, a function freeing that data has to be called.
#ifndef FULLTEXT_H
#define FULLTEXT_H
// 1) The C-accessible interface
#include <QString>
#include <vector>
#ifdef __cplusplus
extern "C" {
#endif
struct full_text_cache;
struct dive;
void fulltext_register(struct dive *d); // Note: can be called repeatedly
void fulltext_unregister(struct dive *d); // Note: can be called repeatedly
void fulltext_unregister_all(); // Unregisters all dives in the dive table
void fulltext_populate(); // Registers all dives in the dive table
#ifdef __cplusplus
}
#endif
// 2) The C++-only interface
#ifdef __cplusplus
#include <QString>
#include <vector>
enum class StringFilterMode {
SUBSTRING = 0,
STARTSWITH = 1,
EXACT = 2
};
// This class caches each dives words, so that we can unregister a dive from the full text search
struct full_text_cache {
std::vector<QString> words;
};
// A fulltext query. Basically a list of normalized words we search for
struct FullTextQuery {
std::vector<QString> words;
@ -63,4 +50,3 @@ FullTextResult fulltext_find_dives(const FullTextQuery &q, StringFilterMode);
bool fulltext_dive_matches(const struct dive *d, const FullTextQuery &q, StringFilterMode);
#endif
#endif

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/* gas-model.c */
/* gas-model.cpp */
/* gas compressibility model */
#include <algorithm> // std::clamp
#include <stdio.h>
#include <stdlib.h>
#include "dive.h"
@ -46,23 +47,20 @@ double gas_compressibility_factor(struct gasmix gas, double bar)
-8.83632921053e-08,
+5.33304543646e-11
};
int o2, he;
double Z;
/*
* The curve fitting range is only [0,500] bar.
* Anything else is way out of range for cylinder
* pressures.
*/
if (bar < 0) bar = 0;
if (bar > 500) bar = 500;
bar = std::clamp(bar, 0.0, 500.0);
o2 = get_o2(gas);
he = get_he(gas);
int o2 = get_o2(gas);
int he = get_he(gas);
Z = virial_m1(o2_coefficients, bar) * o2 +
virial_m1(he_coefficients, bar) * he +
virial_m1(n2_coefficients, bar) * (1000 - o2 - he);
double Z = virial_m1(o2_coefficients, bar) * o2 +
virial_m1(he_coefficients, bar) * he +
virial_m1(n2_coefficients, bar) * (1000 - o2 - he);
/*
* We add the 1.0 at the very end - the linear mixing of the

View File

@ -5,6 +5,7 @@
#include "gettext.h"
#include <stdio.h>
#include <string.h>
#include <QtGlobal> // for QT_TRANSLATE_NOOP
/* Perform isobaric counterdiffusion calculations for gas changes in trimix dives.
* Here we use the rule-of-fifths where, during a change involving trimix gas, the increase in nitrogen
@ -39,20 +40,20 @@ int same_gasmix(struct gasmix a, struct gasmix b)
return get_o2(a) == get_o2(b) && get_he(a) == get_he(b);
}
void sanitize_gasmix(struct gasmix *mix)
void sanitize_gasmix(struct gasmix &mix)
{
unsigned int o2, he;
o2 = get_o2(*mix);
he = get_he(*mix);
o2 = get_o2(mix);
he = get_he(mix);
/* Regular air: leave empty */
if (!he) {
if (!o2)
return;
/* 20.8% to 21% O2 is just air */
if (gasmix_is_air(*mix)) {
mix->o2.permille = 0;
if (gasmix_is_air(mix)) {
mix.o2.permille = 0;
return;
}
}
@ -61,7 +62,7 @@ void sanitize_gasmix(struct gasmix *mix)
if (o2 <= 1000 && he <= 1000 && o2 + he <= 1000)
return;
report_info("Odd gasmix: %u O2 %u He", o2, he);
memset(mix, 0, sizeof(*mix));
mix = gasmix_air;
}
int gasmix_distance(struct gasmix a, struct gasmix b)
@ -115,42 +116,43 @@ int pscr_o2(const double amb_pressure, struct gasmix mix)
* The structure "pressures" is used to return calculated gas pressures to the calling software.
* Call parameters: po2 = po2 value applicable to the record in calling function
* amb_pressure = ambient pressure applicable to the record in calling function
* *pressures = structure for communicating o2 sensor values from and gas pressures to the calling function.
* *mix = structure containing cylinder gas mixture information.
* divemode = the dive mode pertaining to this point in the dive profile.
* This function called by: calculate_gas_information_new() in profile.cpp; add_segment() in deco.cpp.
*/
void fill_pressures(struct gas_pressures *pressures, const double amb_pressure, struct gasmix mix, double po2, enum divemode_t divemode)
gas_pressures fill_pressures(const double amb_pressure, struct gasmix mix, double po2, enum divemode_t divemode)
{
if ((divemode != OC) && po2) { // This is a rebreather dive where pressures->o2 is defined
struct gas_pressures pressures;
if ((divemode != OC) && po2) { // This is a rebreather dive where pressures.o2 is defined
if (po2 >= amb_pressure) {
pressures->o2 = amb_pressure;
pressures->n2 = pressures->he = 0.0;
pressures.o2 = amb_pressure;
pressures.n2 = pressures.he = 0.0;
} else {
pressures->o2 = po2;
pressures.o2 = po2;
if (get_o2(mix) == 1000) {
pressures->he = pressures->n2 = 0;
pressures.he = pressures.n2 = 0;
} else {
pressures->he = (amb_pressure - pressures->o2) * (double)get_he(mix) / (1000 - get_o2(mix));
pressures->n2 = amb_pressure - pressures->o2 - pressures->he;
pressures.he = (amb_pressure - pressures.o2) * (double)get_he(mix) / (1000 - get_o2(mix));
pressures.n2 = amb_pressure - pressures.o2 - pressures.he;
}
}
} else {
if (divemode == PSCR) { /* The steady state approximation should be good enough */
pressures->o2 = pscr_o2(amb_pressure, mix) / 1000.0;
pressures.o2 = pscr_o2(amb_pressure, mix) / 1000.0;
if (get_o2(mix) != 1000) {
pressures->he = (amb_pressure - pressures->o2) * get_he(mix) / (1000.0 - get_o2(mix));
pressures->n2 = (amb_pressure - pressures->o2) * get_n2(mix) / (1000.0 - get_o2(mix));
pressures.he = (amb_pressure - pressures.o2) * get_he(mix) / (1000.0 - get_o2(mix));
pressures.n2 = (amb_pressure - pressures.o2) * get_n2(mix) / (1000.0 - get_o2(mix));
} else {
pressures->he = pressures->n2 = 0;
pressures.he = pressures.n2 = 0;
}
} else {
// Open circuit dives: no gas pressure values available, they need to be calculated
pressures->o2 = get_o2(mix) / 1000.0 * amb_pressure; // These calculations are also used if the CCR calculation above..
pressures->he = get_he(mix) / 1000.0 * amb_pressure; // ..returned a po2 of zero (i.e. o2 sensor data not resolvable)
pressures->n2 = get_n2(mix) / 1000.0 * amb_pressure;
pressures.o2 = get_o2(mix) / 1000.0 * amb_pressure; // These calculations are also used if the CCR calculation above..
pressures.he = get_he(mix) / 1000.0 * amb_pressure; // ..returned a po2 of zero (i.e. o2 sensor data not resolvable)
pressures.n2 = get_n2(mix) / 1000.0 * amb_pressure;
}
}
return pressures;
}
enum gastype gasmix_to_type(struct gasmix mix)

View File

@ -5,12 +5,6 @@
#include "divemode.h"
#include "units.h"
#ifdef __cplusplus
extern "C" {
#else
#include <stdbool.h>
#endif
enum gas_component { N2, HE, O2 };
// o2 == 0 && he == 0 -> air
@ -61,13 +55,13 @@ static inline int get_n2(struct gasmix mix)
int pscr_o2(const double amb_pressure, struct gasmix mix);
struct gas_pressures {
double o2, n2, he;
double o2 = 0.0, n2 = 0.0, he = 0.0;
};
extern void sanitize_gasmix(struct gasmix *mix);
extern void sanitize_gasmix(struct gasmix &mix);
extern int gasmix_distance(struct gasmix a, struct gasmix b);
extern fraction_t get_gas_component_fraction(struct gasmix mix, enum gas_component component);
extern void fill_pressures(struct gas_pressures *pressures, double amb_pressure, struct gasmix mix, double po2, enum divemode_t dctype);
extern gas_pressures fill_pressures(double amb_pressure, struct gasmix mix, double po2, enum divemode_t dctype);
extern bool gasmix_is_air(struct gasmix gasmix);
extern bool gasmix_is_invalid(struct gasmix mix);
@ -75,8 +69,4 @@ extern enum gastype gasmix_to_type(struct gasmix mix);
extern const char *gastype_name(enum gastype type);
extern fraction_t make_fraction(int f);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/* gaspressures.c
* ---------------
/* gaspressures.cpp
* ----------------
* This file contains the routines to calculate the gas pressures in the cylinders.
* The functions below support the code in profile.cpp.
* The high-level function is populate_pressure_information(), called by function
@ -10,15 +10,8 @@
* populate_pressure_information() -> calc_pressure_time()
* -> fill_missing_tank_pressures() -> fill_missing_segment_pressures()
* -> get_pr_interpolate_data()
*
* The pr_track_t related functions below implement a linked list that is used by
* the majority of the functions below. The linked list covers a part of the dive profile
* for which there are no cylinder pressure data. Each element in the linked list
* represents a segment between two consecutive points on the dive profile.
* pr_track_t is defined in gaspressures.h
*/
#include "ssrf.h"
#include "dive.h"
#include "event.h"
#include "profile.h"
@ -26,23 +19,29 @@
#include "pref.h"
#include <stdlib.h>
#include <vector>
/*
* simple structure to track the beginning and end tank pressure as
* well as the integral of depth over time spent while we have no
* pressure reading from the tank */
typedef struct pr_track_struct pr_track_t;
struct pr_track_struct {
struct pr_track_t {
int start;
int end;
int t_start;
int t_end;
int pressure_time;
pr_track_t *next;
pr_track_t(int start, int t_start) :
start(start),
end(0),
t_start(t_start),
t_end(t_start),
pressure_time(0)
{
}
};
typedef struct pr_interpolate_struct pr_interpolate_t;
struct pr_interpolate_struct {
struct pr_interpolate_t {
int start;
int end;
int pressure_time;
@ -51,61 +50,17 @@ struct pr_interpolate_struct {
enum interpolation_strategy {SAC, TIME, CONSTANT};
static pr_track_t *pr_track_alloc(int start, int t_start)
{
pr_track_t *pt = malloc(sizeof(pr_track_t));
pt->start = start;
pt->end = 0;
pt->t_start = pt->t_end = t_start;
pt->pressure_time = 0;
pt->next = NULL;
return pt;
}
/* poor man's linked list */
static pr_track_t *list_last(pr_track_t *list)
{
pr_track_t *tail = list;
if (!tail)
return NULL;
while (tail->next) {
tail = tail->next;
}
return tail;
}
static pr_track_t *list_add(pr_track_t *list, pr_track_t *element)
{
pr_track_t *tail = list_last(list);
if (!tail)
return element;
tail->next = element;
return list;
}
static void list_free(pr_track_t *list)
{
if (!list)
return;
list_free(list->next);
free(list);
}
#ifdef DEBUG_PR_TRACK
static void dump_pr_track(int cyl, pr_track_t *track_pr)
static void dump_pr_track(int cyl, std::vector<pr_track_t> &track_pr)
{
pr_track_t *list;
printf("cyl%d:\n", cyl);
list = track_pr;
while (list) {
for (const auto &item: track_pr) {
printf(" start %f end %f t_start %d:%02d t_end %d:%02d pt %d\n",
mbar_to_PSI(list->start),
mbar_to_PSI(list->end),
FRACTION_TUPLE(list->t_start, 60),
FRACTION_TUPLE(list->t_end, 60),
list->pressure_time);
list = list->next;
mbar_to_PSI(item.start),
mbar_to_PSI(item.end),
FRACTION_TUPLE(item.t_start, 60),
FRACTION_TUPLE(item.t_end, 60),
item.pressure_time);
}
}
#endif
@ -128,24 +83,22 @@ static void dump_pr_track(int cyl, pr_track_t *track_pr)
* segments according to how big of a time_pressure area
* they have.
*/
static void fill_missing_segment_pressures(pr_track_t *list, enum interpolation_strategy strategy)
static void fill_missing_segment_pressures(std::vector<pr_track_t> &list, enum interpolation_strategy strategy)
{
double magic;
while (list) {
int start = list->start, end;
pr_track_t *tmp = list;
for (auto it = list.begin(); it != list.end(); ++it) {
int start = it->start, end;
int pt_sum = 0, pt = 0;
auto tmp = it;
for (;;) {
pt_sum += tmp->pressure_time;
end = tmp->end;
if (end)
break;
end = start;
if (!tmp->next)
if (std::next(tmp) == list.end())
break;
tmp = tmp->next;
++tmp;
}
if (!start)
@ -159,37 +112,34 @@ static void fill_missing_segment_pressures(pr_track_t *list, enum interpolation_
*
* Now dole out the pressures relative to pressure-time.
*/
list->start = start;
it->start = start;
tmp->end = end;
switch (strategy) {
case SAC:
for (;;) {
int pressure;
pt += list->pressure_time;
pt += it->pressure_time;
pressure = start;
if (pt_sum)
pressure -= lrint((start - end) * (double)pt / pt_sum);
list->end = pressure;
if (list == tmp)
it->end = pressure;
if (it == tmp)
break;
list = list->next;
list->start = pressure;
++it;
it->start = pressure;
}
break;
case TIME:
if (list->t_end && (tmp->t_start - tmp->t_end)) {
magic = (list->t_start - tmp->t_end) / (tmp->t_start - tmp->t_end);
list->end = lrint(start - (start - end) * magic);
if (it->t_end && (tmp->t_start - tmp->t_end)) {
double magic = (it->t_start - tmp->t_end) / (tmp->t_start - tmp->t_end);
it->end = lrint(start - (start - end) * magic);
} else {
list->end = start;
it->end = start;
}
break;
case CONSTANT:
list->end = start;
it->end = start;
}
/* Ok, we've done that set of segments */
list = list->next;
}
}
@ -202,42 +152,39 @@ void dump_pr_interpolate(int i, pr_interpolate_t interpolate_pr)
#endif
static struct pr_interpolate_struct get_pr_interpolate_data(pr_track_t *segment, struct plot_info *pi, int cur)
{ // cur = index to pi->entry corresponding to t_end of segment;
struct pr_interpolate_struct interpolate;
static pr_interpolate_t get_pr_interpolate_data(const pr_track_t &segment, struct plot_info &pi, int cur)
{ // cur = index to pi.entry corresponding to t_end of segment;
pr_interpolate_t interpolate;
int i;
struct plot_data *entry;
interpolate.start = segment->start;
interpolate.end = segment->end;
interpolate.start = segment.start;
interpolate.end = segment.end;
interpolate.acc_pressure_time = 0;
interpolate.pressure_time = 0;
for (i = 0; i < pi->nr; i++) {
entry = pi->entry + i;
for (i = 0; i < pi.nr; i++) {
const plot_data &entry = pi.entry[i];
if (entry->sec < segment->t_start)
if (entry.sec < segment.t_start)
continue;
interpolate.pressure_time += entry->pressure_time;
if (entry->sec >= segment->t_end)
interpolate.pressure_time += entry.pressure_time;
if (entry.sec >= segment.t_end)
break;
if (i <= cur)
interpolate.acc_pressure_time += entry->pressure_time;
interpolate.acc_pressure_time += entry.pressure_time;
}
return interpolate;
}
static void fill_missing_tank_pressures(const struct dive *dive, struct plot_info *pi, pr_track_t *track_pr, int cyl)
static void fill_missing_tank_pressures(const struct dive *dive, struct plot_info &pi, std::vector<pr_track_t> &track_pr, int cyl)
{
int i;
struct plot_data *entry;
pr_interpolate_t interpolate = { 0, 0, 0, 0 };
pr_track_t *last_segment = NULL;
int cur_pr;
enum interpolation_strategy strategy;
/* no segment where this cylinder is used */
if (!track_pr)
if (track_pr.empty())
return;
if (get_cylinder(dive, cyl)->cylinder_use == OC_GAS)
@ -245,7 +192,7 @@ static void fill_missing_tank_pressures(const struct dive *dive, struct plot_inf
else
strategy = TIME;
fill_missing_segment_pressures(track_pr, strategy); // Interpolate the missing tank pressure values ..
cur_pr = track_pr->start; // in the pr_track_t lists of structures
cur_pr = track_pr[0].start; // in the pr_track_t lists of structures
// and keep the starting pressure for each cylinder.
#ifdef DEBUG_PR_TRACK
dump_pr_track(cyl, track_pr);
@ -261,47 +208,44 @@ static void fill_missing_tank_pressures(const struct dive *dive, struct plot_inf
*
* The first two pi structures are "fillers", but in case we don't have a sample
* at time 0 we need to process the second of them here, therefore i=1 */
for (i = 1; i < pi->nr; i++) { // For each point on the profile:
double magic;
pr_track_t *segment;
int pressure;
auto last_segment = track_pr.end();
for (i = 1; i < pi.nr; i++) { // For each point on the profile:
const struct plot_data &entry = pi.entry[i];
entry = pi->entry + i;
int pressure = get_plot_pressure(pi, i, cyl);
pressure = get_plot_pressure(pi, i, cyl);
if (pressure) { // If there is a valid pressure value,
last_segment = NULL; // get rid of interpolation data,
cur_pr = pressure; // set current pressure
continue; // and skip to next point.
if (pressure) { // If there is a valid pressure value,
last_segment = track_pr.end(); // get rid of interpolation data,
cur_pr = pressure; // set current pressure
continue; // and skip to next point.
}
// If there is NO valid pressure value..
// Find the pressure segment corresponding to this entry..
segment = track_pr;
while (segment && segment->t_end < entry->sec) // Find the track_pr with end time..
segment = segment->next; // ..that matches the plot_info time (entry->sec)
auto it = track_pr.begin();
while (it != track_pr.end() && it->t_end < entry.sec) // Find the track_pr with end time..
++it; // ..that matches the plot_info time (entry.sec)
// After last segment? All done.
if (!segment)
if (it == track_pr.end())
break;
// Before first segment, or between segments.. Go on, no interpolation.
if (segment->t_start > entry->sec)
if (it->t_start > entry.sec)
continue;
if (!segment->pressure_time) { // Empty segment?
if (!it->pressure_time) { // Empty segment?
set_plot_pressure_data(pi, i, SENSOR_PR, cyl, cur_pr);
// Just use our current pressure
continue; // and skip to next point.
}
// If there is a valid segment but no tank pressure ..
if (segment == last_segment) {
interpolate.acc_pressure_time += entry->pressure_time;
if (it == last_segment) {
interpolate.acc_pressure_time += entry.pressure_time;
} else {
// Set up an interpolation structure
interpolate = get_pr_interpolate_data(segment, pi, i);
last_segment = segment;
interpolate = get_pr_interpolate_data(*it, pi, i);
last_segment = it;
}
if(get_cylinder(dive, cyl)->cylinder_use == OC_GAS) {
@ -309,20 +253,19 @@ static void fill_missing_tank_pressures(const struct dive *dive, struct plot_inf
/* if this segment has pressure_time, then calculate a new interpolated pressure */
if (interpolate.pressure_time) {
/* Overall pressure change over total pressure-time for this segment*/
magic = (interpolate.end - interpolate.start) / (double)interpolate.pressure_time;
double magic = (interpolate.end - interpolate.start) / (double)interpolate.pressure_time;
/* Use that overall pressure change to update the current pressure */
cur_pr = lrint(interpolate.start + magic * interpolate.acc_pressure_time);
}
} else {
magic = (interpolate.end - interpolate.start) / (segment->t_end - segment->t_start);
cur_pr = lrint(segment->start + magic * (entry->sec - segment->t_start));
double magic = (interpolate.end - interpolate.start) / (it->t_end - it->t_start);
cur_pr = lrint(it->start + magic * (entry.sec - it->t_start));
}
set_plot_pressure_data(pi, i, INTERPOLATED_PR, cyl, cur_pr); // and store the interpolated data in plot_info
}
}
/*
* What's the pressure-time between two plot data entries?
* We're calculating the integral of pressure over time by
@ -334,10 +277,10 @@ static void fill_missing_tank_pressures(const struct dive *dive, struct plot_inf
* scale pressures, so it ends up being a unitless scaling
* factor.
*/
static inline int calc_pressure_time(const struct dive *dive, struct plot_data *a, struct plot_data *b)
static inline int calc_pressure_time(const struct dive *dive, const struct plot_data &a, const struct plot_data &b)
{
int time = b->sec - a->sec;
int depth = (a->depth + b->depth) / 2;
int time = b.sec - a.sec;
int depth = (a.depth + b.depth) / 2;
if (depth <= SURFACE_THRESHOLD)
return 0;
@ -347,36 +290,33 @@ static inline int calc_pressure_time(const struct dive *dive, struct plot_data *
#ifdef PRINT_PRESSURES_DEBUG
// A CCR debugging tool that prints the gas pressures in cylinder 0 and in the diluent cylinder, used in populate_pressure_information():
static void debug_print_pressures(struct plot_info *pi)
static void debug_print_pressures(struct plot_info &pi)
{
int i;
for (i = 0; i < pi->nr; i++)
for (i = 0; i < pi.nr; i++)
printf("%5d |%9d | %9d |\n", i, get_plot_sensor_pressure(pi, i), get_plot_interpolated_pressure(pi, i));
}
#endif
/* This function goes through the list of tank pressures, of structure plot_info for the dive profile where each
* item in the list corresponds to one point (node) of the profile. It finds values for which there are no tank
* pressures (pressure==0). For each missing item (node) of tank pressure it creates a pr_track_alloc structure
* pressures (pressure==0). For each missing item (node) of tank pressure it creates a pr_track_t structure
* that represents a segment on the dive profile and that contains tank pressures. There is a linked list of
* pr_track_alloc structures for each cylinder. These pr_track_alloc structures ultimately allow for filling
* pr_track_t structures for each cylinder. These pr_track_t structures ultimately allow for filling
* the missing tank pressure values on the dive profile using the depth_pressure of the dive. To do this, it
* calculates the summed pressure-time value for the duration of the dive and stores these * in the pr_track_alloc
* calculates the summed pressure-time value for the duration of the dive and stores these in the pr_track_t
* structures. This function is called by create_plot_info_new() in profile.cpp
*/
void populate_pressure_information(const struct dive *dive, const struct divecomputer *dc, struct plot_info *pi, int sensor)
void populate_pressure_information(const struct dive *dive, const struct divecomputer *dc, struct plot_info &pi, int sensor)
{
UNUSED(dc);
int first, last, cyl;
cylinder_t *cylinder = get_cylinder(dive, sensor);
pr_track_t *track = NULL;
pr_track_t *current = NULL;
const struct event *ev, *b_ev;
const cylinder_t *cylinder = get_cylinder(dive, sensor);
std::vector<pr_track_t> track;
size_t current = std::string::npos;
int missing_pr = 0, dense = 1;
enum divemode_t dmode = dc->divemode;
const double gasfactor[5] = {1.0, 0.0, prefs.pscr_ratio/1000.0, 1.0, 1.0 };
if (sensor < 0 || sensor >= dive->cylinders.nr)
if (sensor < 0 || static_cast<size_t>(sensor) >= dive->cylinders.size())
return;
/* if we have no pressure data whatsoever, this is pointless, so let's just return */
@ -386,7 +326,7 @@ void populate_pressure_information(const struct dive *dive, const struct divecom
/* Get a rough range of where we have any pressures at all */
first = last = -1;
for (int i = 0; i < pi->nr; i++) {
for (int i = 0; i < pi.nr; i++) {
int pressure = get_plot_sensor_pressure(pi, i, sensor);
if (!pressure)
@ -409,34 +349,31 @@ void populate_pressure_information(const struct dive *dive, const struct divecom
* itself has a gas change event.
*/
cyl = sensor;
ev = NULL;
if (has_gaschange_event(dive, dc, sensor))
ev = get_next_event(dc->events, "gaschange");
b_ev = get_next_event(dc->events, "modechange");
event_loop loop_gas("gaschange");
const struct event *ev = has_gaschange_event(dive, dc, sensor) ?
loop_gas.next(*dc) : nullptr;
divemode_loop loop_mode(*dc);
for (int i = first; i <= last; i++) {
struct plot_data *entry = pi->entry + i;
struct plot_data &entry = pi.entry[i];
int pressure = get_plot_sensor_pressure(pi, i, sensor);
int time = entry->sec;
int time = entry.sec;
while (ev && ev->time.seconds <= time) { // Find 1st gaschange event after
cyl = get_cylinder_index(dive, ev); // the current gas change.
cyl = get_cylinder_index(dive, *ev); // the current gas change.
if (cyl < 0)
cyl = sensor;
ev = get_next_event(ev->next, "gaschange");
ev = loop_gas.next(*dc);
}
while (b_ev && b_ev->time.seconds <= time) { // Keep existing divemode, then
dmode = b_ev->value; // find 1st divemode change event after the current
b_ev = get_next_event(b_ev->next, "modechange"); // divemode change.
}
divemode_t dmode = loop_mode.next(time);
if (current) { // calculate pressure-time, taking into account the dive mode for this specific segment.
entry->pressure_time = (int)(calc_pressure_time(dive, entry - 1, entry) * gasfactor[dmode] + 0.5);
current->pressure_time += entry->pressure_time;
current->t_end = entry->sec;
if (current != std::string::npos) { // calculate pressure-time, taking into account the dive mode for this specific segment.
entry.pressure_time = (int)(calc_pressure_time(dive, pi.entry[i - 1], entry) * gasfactor[dmode] + 0.5);
track[current].pressure_time += entry.pressure_time;
track[current].t_end = entry.sec;
if (pressure)
current->end = pressure;
track[current].end = pressure;
}
// We have a final pressure for 'current'
@ -444,7 +381,7 @@ void populate_pressure_information(const struct dive *dive, const struct divecom
// current pressure track entry and continue
// until we get back to this cylinder.
if (cyl != sensor) {
current = NULL;
current = std::string::npos;
set_plot_pressure_data(pi, i, SENSOR_PR, sensor, 0);
continue;
}
@ -453,7 +390,7 @@ void populate_pressure_information(const struct dive *dive, const struct divecom
// continue with or without a tracking entry. Mark any
// existing tracking entry as non-dense, and remember
// to fill in interpolated data.
if (current && !pressure) {
if (current != std::string::npos && !pressure) {
missing_pr = 1;
dense = 0;
continue;
@ -462,7 +399,7 @@ void populate_pressure_information(const struct dive *dive, const struct divecom
// If we already have a pressure tracking entry, and
// it has not had any missing samples, just continue
// using it - there's nothing to interpolate yet.
if (current && dense)
if (current != std::string::npos && dense)
continue;
// We need to start a new tracking entry, either
@ -471,18 +408,15 @@ void populate_pressure_information(const struct dive *dive, const struct divecom
// missing entries that need to be interpolated.
// Or maybe we didn't have a previous one at all,
// and this is the first pressure entry.
current = pr_track_alloc(pressure, entry->sec);
track = list_add(track, current);
track.emplace_back(pressure, entry.sec);
current = track.size() - 1;
dense = 1;
}
if (missing_pr) {
if (missing_pr)
fill_missing_tank_pressures(dive, pi, track, sensor);
}
#ifdef PRINT_PRESSURES_DEBUG
debug_print_pressures(pi);
#endif
list_free(track);
}

View File

@ -2,13 +2,6 @@
#ifndef GASPRESSURES_H
#define GASPRESSURES_H
#ifdef __cplusplus
extern "C" {
#endif
void populate_pressure_information(const struct dive *, const struct divecomputer *, struct plot_info &, int);
void populate_pressure_information(const struct dive *, const struct divecomputer *, struct plot_info *, int);
#ifdef __cplusplus
}
#endif
#endif // GASPRESSURES_H

View File

@ -2,22 +2,10 @@
#ifndef MYGETTEXT_H
#define MYGETTEXT_H
#ifdef __cplusplus
extern "C" const char *trGettext(const char *);
const char *trGettext(const char *);
static inline const char *translate(const char *, const char *arg)
{
return trGettext(arg);
}
#else
/* this is for the Qt based translations */
extern const char *trGettext(const char *);
#define translate(_context, arg) trGettext(arg)
#define QT_TRANSLATE_NOOP(_context, arg) arg
#define QT_TRANSLATE_NOOP3(_context, arg, _comment) arg
#endif
#endif // MYGETTEXT_H

View File

@ -6,7 +6,7 @@
static QHash<QByteArray, QByteArray> translationCache;
static QMutex lock;
extern "C" const char *trGettext(const char *text)
const char *trGettext(const char *text)
{
QByteArray key(text);
QMutexLocker l(&lock);

View File

@ -4,7 +4,7 @@
#include <QCoreApplication>
extern "C" const char *trGettext(const char *text);
const char *trGettext(const char *text);
class gettextFromC {
Q_DECLARE_TR_FUNCTIONS(gettextFromC)

View File

@ -4,7 +4,6 @@
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
#endif
#include "ssrf.h"
#include <stdio.h>
#include <ctype.h>
#include <string.h>
@ -58,7 +57,7 @@ static bool includes_string_caseinsensitive(const char *haystack, const char *ne
return 0;
}
extern "C" void set_git_update_cb(int(*cb)(const char *))
void set_git_update_cb(int(*cb)(const char *))
{
update_progress_cb = cb;
}
@ -69,7 +68,7 @@ extern "C" void set_git_update_cb(int(*cb)(const char *))
// proportional - some parts are based on compute performance, some on network speed)
// they also provide information where in the process we are so we can analyze the log
// to understand which parts of the process take how much time.
extern "C" int git_storage_update_progress(const char *text)
int git_storage_update_progress(const char *text)
{
int ret = 0;
if (update_progress_cb)
@ -135,9 +134,6 @@ std::string normalize_cloud_name(const std::string &remote_in)
std::string get_local_dir(const std::string &url, const std::string &branch)
{
SHA_CTX ctx;
unsigned char hash[20];
// this optimization could in theory lead to odd things happening if the
// cloud backend servers ever get out of sync - but when a user switches
// between those servers (either because one is down, or because the algorithm
@ -148,11 +144,11 @@ std::string get_local_dir(const std::string &url, const std::string &branch)
// That zero-byte update is so that we don't get hash
// collisions for "repo1 branch" vs "repo 1branch".
SHA1_Init(&ctx);
SHA1_Update(&ctx, remote.c_str(), remote.size());
SHA1_Update(&ctx, "", 1);
SHA1_Update(&ctx, branch.c_str(), branch.size());
SHA1_Final(hash, &ctx);
SHA1 sha;
sha.update(remote);
sha.update("", 1);
sha.update(branch);
auto hash = sha.hash();
return format_string_std("%s/cloudstorage/%02x%02x%02x%02x%02x%02x%02x%02x",
system_default_directory(),
hash[0], hash[1], hash[2], hash[3],
@ -244,7 +240,7 @@ static bool exceeded_auth_attempts()
return false;
}
extern "C" int credential_ssh_cb(git_cred **out,
int credential_ssh_cb(git_cred **out,
const char *,
const char *,
unsigned int allowed_types,
@ -276,7 +272,7 @@ extern "C" int credential_ssh_cb(git_cred **out,
return GIT_EUSER;
}
extern "C" int credential_https_cb(git_cred **out,
int credential_https_cb(git_cred **out,
const char *,
const char *,
unsigned int,
@ -291,7 +287,7 @@ extern "C" int credential_https_cb(git_cred **out,
return git_cred_userpass_plaintext_new(out, username, password);
}
extern "C" int certificate_check_cb(git_cert *cert, int valid, const char *host, void *)
int certificate_check_cb(git_cert *cert, int valid, const char *host, void *)
{
if (verbose)
report_info("git storage: certificate callback for host %s with validity %d\n", host, valid);
@ -342,7 +338,7 @@ static int update_remote(struct git_info *info, git_remote *origin, git_referenc
return 0;
}
extern "C" int update_git_checkout(git_repository *repo, git_object *parent, git_tree *tree);
int update_git_checkout(git_repository *repo, git_object *parent, git_tree *tree);
static int try_to_git_merge(struct git_info *info, git_reference **local_p, git_reference *, git_oid *base, const git_oid *local_id, const git_oid *remote_id)
{
@ -350,7 +346,7 @@ static int try_to_git_merge(struct git_info *info, git_reference **local_p, git_
git_commit *local_commit, *remote_commit, *base_commit;
git_index *merged_index;
git_merge_options merge_options;
struct membufferpp msg;
membuffer msg;
if (verbose) {
char outlocal[41], outremote[41];

View File

@ -4,14 +4,12 @@
#include "git2.h"
#include "filterpreset.h"
#include <string>
struct dive_log;
#ifdef __cplusplus
extern "C" {
#else
#include <stdbool.h>
#endif
struct git_oid;
struct git_repository;
struct divelog;
#define CLOUD_HOST_US "ssrf-cloud-us.subsurface-divelog.org" // preferred (faster/bigger) server in the US
#define CLOUD_HOST_U2 "ssrf-cloud-u2.subsurface-divelog.org" // secondary (older) server in the US
@ -24,21 +22,12 @@ enum remote_transport { RT_LOCAL, RT_HTTPS, RT_SSH, RT_OTHER };
extern bool git_local_only;
extern bool git_remote_sync_successful;
extern void clear_git_id(void);
extern void clear_git_id();
extern void set_git_id(const struct git_oid *);
void set_git_update_cb(int(*)(const char *));
int git_storage_update_progress(const char *text);
int get_authorship(git_repository *repo, git_signature **authorp);
#ifdef __cplusplus
}
#include <string>
struct git_oid;
struct git_repository;
struct divelog;
struct git_info {
std::string url;
std::string branch;
@ -63,6 +52,4 @@ extern int git_load_dives(struct git_info *, struct divelog *log);
extern int do_git_save(struct git_info *, bool select_only, bool create_empty);
extern int git_create_local_repo(const std::string &filename);
#endif
#endif // GITACCESS_H

View File

@ -96,7 +96,7 @@ Thumbnailer::Thumbnail Thumbnailer::fetchImage(const QString &urlfilename, const
// For io error or video, return early with the appropriate dummy-icon.
if (type == MEDIATYPE_IO_ERROR)
return { failImage, MEDIATYPE_IO_ERROR, zero_duration };
return { failImage, MEDIATYPE_IO_ERROR, duration_t() };
else if (type == MEDIATYPE_VIDEO)
return fetchVideoThumbnail(filename, originalFilename, md.duration);
@ -112,7 +112,7 @@ Thumbnailer::Thumbnail Thumbnailer::fetchImage(const QString &urlfilename, const
// Try to check for a video-file extension. Since we couldn't parse the video file,
// we pass 0 as the duration.
if (hasVideoFileExtension(filename))
return fetchVideoThumbnail(filename, originalFilename, zero_duration);
return fetchVideoThumbnail(filename, originalFilename, duration_t());
// Give up: we simply couldn't determine what this thing is.
// But since we managed to read this file, mark this file in the cache as unknown.
@ -122,9 +122,9 @@ Thumbnailer::Thumbnail Thumbnailer::fetchImage(const QString &urlfilename, const
// to treat requests from other threads. invokeMethod() is Qt's way of calling a
// function in a different thread, namely the thread the called object is associated to.
QMetaObject::invokeMethod(ImageDownloader::instance(), "load", Qt::AutoConnection, Q_ARG(QUrl, url), Q_ARG(QString, originalFilename));
return { QImage(), MEDIATYPE_STILL_LOADING, zero_duration };
return { QImage(), MEDIATYPE_STILL_LOADING, duration_t() };
}
return { QImage(), MEDIATYPE_IO_ERROR, zero_duration };
return { QImage(), MEDIATYPE_IO_ERROR, duration_t() };
}
// Fetch a picture based on its original filename. If there is a translated filename (obtained either
@ -140,7 +140,7 @@ Thumbnailer::Thumbnail Thumbnailer::getHashedImage(const QString &filename, bool
// If there is a translated filename, try that first.
// Note that we set the default type to io-error, so that if we didn't try
// the local filename first, we will load the file from the canonical filename.
Thumbnail thumbnail { QImage(), MEDIATYPE_IO_ERROR, zero_duration };
Thumbnail thumbnail { QImage(), MEDIATYPE_IO_ERROR, duration_t() };
if (localFilename != filename)
thumbnail = fetchImage(localFilename, filename, tryDownload);
@ -187,7 +187,7 @@ Thumbnailer::Thumbnail Thumbnailer::getPictureThumbnailFromStream(QDataStream &s
{
QImage res;
stream >> res;
return { std::move(res), MEDIATYPE_PICTURE, zero_duration };
return { std::move(res), MEDIATYPE_PICTURE, duration_t() };
}
void Thumbnailer::markVideoThumbnail(QImage &img)
@ -210,7 +210,7 @@ Thumbnailer::Thumbnail Thumbnailer::getVideoThumbnailFromStream(QDataStream &str
// Likewise test the duration and number of pictures for sanity (no videos longer than 10 h,
// no more than 10000 pictures).
if (stream.status() != QDataStream::Ok || duration > 36000 || numPics > 10000)
return { QImage(), MEDIATYPE_VIDEO, zero_duration };
return { QImage(), MEDIATYPE_VIDEO, duration_t() };
// If the file didn't contain an image, but user turned on thumbnail extraction, schedule thumbnail
// for extraction. TODO: save failure to extract thumbnails to disk so that thumbnailing
@ -240,7 +240,7 @@ Thumbnailer::Thumbnail Thumbnailer::getThumbnailFromCache(const QString &picture
{
QString filename = thumbnailFileName(picture_filename);
if (filename.isEmpty())
return { QImage(), MEDIATYPE_UNKNOWN, zero_duration };
return { QImage(), MEDIATYPE_UNKNOWN, duration_t() };
QFile file(filename);
if (prefs.auto_recalculate_thumbnails) {
@ -254,13 +254,13 @@ Thumbnailer::Thumbnail Thumbnailer::getThumbnailFromCache(const QString &picture
if (pictureTime.isValid() && thumbnailTime.isValid() && thumbnailTime < pictureTime) {
// Both files exist, have valid timestamps and thumbnail was calculated before picture.
// Return an empty thumbnail to signal recalculation of the thumbnail
return { QImage(), MEDIATYPE_UNKNOWN, zero_duration };
return { QImage(), MEDIATYPE_UNKNOWN, duration_t() };
}
}
}
if (!file.open(QIODevice::ReadOnly))
return { QImage(), MEDIATYPE_UNKNOWN, zero_duration };
return { QImage(), MEDIATYPE_UNKNOWN, duration_t() };
QDataStream stream(&file);
// Each thumbnail file is composed of a media-type and an image file.
@ -271,8 +271,8 @@ Thumbnailer::Thumbnail Thumbnailer::getThumbnailFromCache(const QString &picture
switch (type) {
case MEDIATYPE_PICTURE: return getPictureThumbnailFromStream(stream);
case MEDIATYPE_VIDEO: return getVideoThumbnailFromStream(stream, picture_filename);
case MEDIATYPE_UNKNOWN: return { unknownImage, MEDIATYPE_UNKNOWN, zero_duration };
default: return { QImage(), MEDIATYPE_UNKNOWN, zero_duration };
case MEDIATYPE_UNKNOWN: return { unknownImage, MEDIATYPE_UNKNOWN, duration_t() };
default: return { QImage(), MEDIATYPE_UNKNOWN, duration_t() };
}
}
@ -319,7 +319,7 @@ Thumbnailer::Thumbnail Thumbnailer::fetchVideoThumbnail(const QString &filename,
return { videoImage, MEDIATYPE_VIDEO, duration };
} else {
// Video-thumbnailing is disabled. Write a thumbnail without picture.
return addVideoThumbnailToCache(originalFilename, duration, QImage(), zero_duration);
return addVideoThumbnailToCache(originalFilename, duration, QImage(), duration_t());
}
}
@ -337,7 +337,7 @@ Thumbnailer::Thumbnail Thumbnailer::addPictureThumbnailToCache(const QString &pi
stream << thumbnail;
file.commit();
}
return { thumbnail, MEDIATYPE_PICTURE, zero_duration };
return { thumbnail, MEDIATYPE_PICTURE, duration_t() };
}
Thumbnailer::Thumbnail Thumbnailer::addUnknownThumbnailToCache(const QString &picture_filename)
@ -348,7 +348,7 @@ Thumbnailer::Thumbnail Thumbnailer::addUnknownThumbnailToCache(const QString &pi
QDataStream stream(&file);
stream << (quint32)MEDIATYPE_UNKNOWN;
}
return { unknownImage, MEDIATYPE_UNKNOWN, zero_duration };
return { unknownImage, MEDIATYPE_UNKNOWN, duration_t() };
}
void Thumbnailer::frameExtracted(QString filename, QImage thumbnail, duration_t duration, duration_t offset)
@ -374,7 +374,7 @@ void Thumbnailer::frameExtractionFailed(QString filename, duration_t duration)
{
// Frame extraction failed, but this was due to ffmpeg not starting
// add to the thumbnail cache as a video image with unknown thumbnail.
addVideoThumbnailToCache(filename, duration, QImage(), zero_duration);
addVideoThumbnailToCache(filename, duration, QImage(), duration_t());
QMutexLocker l(&lock);
workingOn.remove(filename);
}
@ -435,7 +435,7 @@ void Thumbnailer::imageDownloaded(QString filename)
void Thumbnailer::imageDownloadFailed(QString filename)
{
emit thumbnailChanged(filename, failImage, zero_duration);
emit thumbnailChanged(filename, failImage, duration_t());
QMutexLocker l(&lock);
workingOn.remove(filename);
}

View File

@ -5,7 +5,6 @@
#endif
#include <stdlib.h>
#include "ssrf.h"
#include "dive.h"
#include "divesite.h"
#include "errorhelper.h"
@ -29,7 +28,7 @@ static int cobalt_profile_sample(void *param, int, char **data, char **)
if (data[1])
state->cur_sample->depth.mm = atoi(data[1]);
if (data[2])
state->cur_sample->temperature.mkelvin = state->metric ? C_to_mkelvin(strtod_flags(data[2], NULL, 0)) : F_to_mkelvin(strtod_flags(data[2], NULL, 0));
state->cur_sample->temperature.mkelvin = state->metric ? C_to_mkelvin(permissive_strtod(data[2], NULL)) : F_to_mkelvin(permissive_strtod(data[2], NULL));
sample_end(state);
return 0;
@ -64,7 +63,7 @@ static int cobalt_buddies(void *param, int, char **data, char **)
struct parser_state *state = (struct parser_state *)param;
if (data[0])
utf8_string(data[0], &state->cur_dive->buddy);
utf8_string_std(data[0], &state->cur_dive->buddy);
return 0;
}
@ -81,8 +80,8 @@ static int cobalt_visibility(void *, int, char **, char **)
static int cobalt_location(void *param, int, char **data, char **)
{
char **location = (char **)param;
*location = data[0] ? strdup(data[0]) : NULL;
std::string *location = (std::string *)param;
*location = data[0] ? data[0] : NULL;
return 0;
}
@ -92,7 +91,7 @@ static int cobalt_dive(void *param, int, char **data, char **)
int retval = 0;
struct parser_state *state = (struct parser_state *)param;
sqlite3 *handle = state->sql_handle;
char *location, *location_site;
std::string location, location_site;
char get_profile_template[] = "select runtime*60,(DepthPressure*10000/SurfacePressure)-10000,p.Temperature from Dive AS d JOIN TrackPoints AS p ON d.Id=p.DiveId where d.Id=%d";
char get_cylinder_template[] = "select FO2,FHe,StartingPressure,EndingPressure,TankSize,TankPressure,TotalConsumption from GasMixes where DiveID=%d and StartingPressure>0 and EndingPressure > 0 group by FO2,FHe";
char get_buddy_template[] = "select l.Data from Items AS i, List AS l ON i.Value1=l.Id where i.DiveId=%d and l.Type=4";
@ -107,7 +106,7 @@ static int cobalt_dive(void *param, int, char **data, char **)
state->cur_dive->when = (time_t)(atol(data[1]));
if (data[4])
utf8_string(data[4], &state->cur_dive->notes);
utf8_string_std(data[4], &state->cur_dive->notes);
/* data[5] should have information on Units used, but I cannot
* parse it at all based on the sample log I have received. The
@ -119,13 +118,13 @@ static int cobalt_dive(void *param, int, char **data, char **)
/* Cobalt stores the pressures, not the depth */
if (data[6])
state->cur_dive->dc.maxdepth.mm = atoi(data[6]);
state->cur_dive->dcs[0].maxdepth.mm = atoi(data[6]);
if (data[7])
state->cur_dive->dc.duration.seconds = atoi(data[7]);
state->cur_dive->dcs[0].duration.seconds = atoi(data[7]);
if (data[8])
state->cur_dive->dc.surface_pressure.mbar = atoi(data[8]);
state->cur_dive->dcs[0].surface_pressure.mbar = atoi(data[8]);
/*
* TODO: the deviceid hash should be calculated here.
*/
@ -134,15 +133,15 @@ static int cobalt_dive(void *param, int, char **data, char **)
if (data[9]) {
utf8_string_std(data[9], &state->cur_settings.dc.serial_nr);
state->cur_settings.dc.deviceid = atoi(data[9]);
state->cur_settings.dc.model = strdup("Cobalt import");
state->cur_settings.dc.model = "Cobalt import";
}
dc_settings_end(state);
settings_end(state);
if (data[9]) {
state->cur_dive->dc.deviceid = atoi(data[9]);
state->cur_dive->dc.model = strdup("Cobalt import");
state->cur_dive->dcs[0].deviceid = atoi(data[9]);
state->cur_dive->dcs[0].model = "Cobalt import";
}
snprintf(get_buffer, sizeof(get_buffer) - 1, get_cylinder_template, state->cur_dive->number);
@ -180,19 +179,10 @@ static int cobalt_dive(void *param, int, char **data, char **)
return 1;
}
if (location && location_site) {
char *tmp = (char *)malloc(strlen(location) + strlen(location_site) + 4);
if (!tmp) {
free(location);
free(location_site);
return 1;
}
sprintf(tmp, "%s / %s", location, location_site);
add_dive_to_dive_site(state->cur_dive, find_or_create_dive_site_with_name(tmp, state->log->sites));
free(tmp);
if (!location.empty() && !location_site.empty()) {
std::string tmp = location + " / " + location_site;
state->log->sites.find_or_create(tmp)->add_dive(state->cur_dive.get());
}
free(location);
free(location_site);
snprintf(get_buffer, sizeof(get_buffer) - 1, get_profile_template, state->cur_dive->number);
retval = sqlite3_exec(handle, get_buffer, &cobalt_profile_sample, state, NULL);
@ -206,8 +196,7 @@ static int cobalt_dive(void *param, int, char **data, char **)
return SQLITE_OK;
}
extern "C" int parse_cobalt_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
int parse_cobalt_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
{
int retval;
struct parser_state state;

View File

@ -5,7 +5,6 @@
#include "dive.h"
#include "errorhelper.h"
#include "ssrf.h"
#include "subsurface-string.h"
#include "divelist.h"
#include "divelog.h"
@ -270,7 +269,7 @@ static int parse_dan_format(const char *filename, struct xml_params *params, str
return ret;
}
extern "C" int parse_csv_file(const char *filename, struct xml_params *params, const char *csvtemplate, struct divelog *log)
int parse_csv_file(const char *filename, struct xml_params *params, const char *csvtemplate, struct divelog *log)
{
int ret;
std::string mem;
@ -402,7 +401,6 @@ int try_to_open_csv(std::string &mem, enum csv_format type, struct divelog *log)
char *header[8];
int i, time;
timestamp_t date;
struct dive *dive;
struct divecomputer *dc;
for (i = 0; i < 8; i++) {
@ -417,10 +415,10 @@ int try_to_open_csv(std::string &mem, enum csv_format type, struct divelog *log)
if (!date)
return 0;
dive = alloc_dive();
auto dive = std::make_unique<struct dive>();
dive->when = date;
dive->number = atoi(header[1]);
dc = &dive->dc;
dc = &dive->dcs[0];
time = 0;
for (;;) {
@ -438,7 +436,6 @@ int try_to_open_csv(std::string &mem, enum csv_format type, struct divelog *log)
sample = prepare_sample(dc);
sample->time.seconds = time;
add_sample_data(sample, type, val);
finish_sample(dc);
time++;
dc->duration.seconds = time;
@ -446,7 +443,7 @@ int try_to_open_csv(std::string &mem, enum csv_format type, struct divelog *log)
break;
p = end + 1;
}
record_dive_to_table(dive, log->dives);
log->dives.record_dive(std::move(dive));
return 1;
}
@ -497,9 +494,7 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log)
bool has_depth = false, has_setpoint = false, has_ndl = false;
char *lineptr;
int prev_time = 0;
cylinder_t cyl = empty_cylinder;
struct dive *dive;
struct divecomputer *dc;
struct tm cur_tm;
@ -513,34 +508,40 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log)
cur_tm.tm_min = mm;
cur_tm.tm_sec = ss;
dive = alloc_dive();
auto dive = std::make_unique<struct dive>();
dive->when = utc_mktime(&cur_tm);;
dive->dc.model = strdup("Poseidon MkVI Discovery");
dive->dcs[0].model = "Poseidon MkVI Discovery";
value = parse_mkvi_value(memtxt.data(), "Rig Serial number");
dive->dc.deviceid = atoi(value.c_str());
dive->dc.divemode = CCR;
dive->dc.no_o2sensors = 2;
dive->dcs[0].deviceid = atoi(value.c_str());
dive->dcs[0].divemode = CCR;
dive->dcs[0].no_o2sensors = 2;
cyl.cylinder_use = OXYGEN;
cyl.type.size.mliter = 3000;
cyl.type.workingpressure.mbar = 200000;
cyl.type.description = "3l Mk6";
cyl.gasmix.o2.permille = 1000;
cyl.manually_added = true;
cyl.bestmix_o2 = 0;
cyl.bestmix_he = 0;
add_cloned_cylinder(&dive->cylinders, cyl);
{
cylinder_t cyl;
cyl.cylinder_use = OXYGEN;
cyl.type.size.mliter = 3000;
cyl.type.workingpressure.mbar = 200000;
cyl.type.description = "3l Mk6";
cyl.gasmix.o2.permille = 1000;
cyl.manually_added = true;
cyl.bestmix_o2 = 0;
cyl.bestmix_he = 0;
dive->cylinders.push_back(std::move(cyl));
}
cyl.cylinder_use = DILUENT;
cyl.type.size.mliter = 3000;
cyl.type.workingpressure.mbar = 200000;
cyl.type.description = "3l Mk6";
value = parse_mkvi_value(memtxt.data(), "Helium percentage");
he = atoi(value.c_str());
value = parse_mkvi_value(memtxt.data(), "Nitrogen percentage");
cyl.gasmix.o2.permille = (100 - atoi(value.c_str()) - he) * 10;
cyl.gasmix.he.permille = he * 10;
add_cloned_cylinder(&dive->cylinders, cyl);
{
cylinder_t cyl;
cyl.cylinder_use = DILUENT;
cyl.type.size.mliter = 3000;
cyl.type.workingpressure.mbar = 200000;
cyl.type.description = "3l Mk6";
value = parse_mkvi_value(memtxt.data(), "Helium percentage");
he = atoi(value.c_str());
value = parse_mkvi_value(memtxt.data(), "Nitrogen percentage");
cyl.gasmix.o2.permille = (100 - atoi(value.c_str()) - he) * 10;
cyl.gasmix.he.permille = he * 10;
dive->cylinders.push_back(std::move(cyl));
}
lineptr = strstr(memtxt.data(), "Dive started at");
while (!empty_string(lineptr) && (lineptr = strchr(lineptr, '\n'))) {
@ -551,9 +552,9 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log)
std::string value = parse_mkvi_value(lineptr, key.c_str());
if (value.empty())
break;
add_extra_data(&dive->dc, key.c_str(), value.c_str());
add_extra_data(&dive->dcs[0], key, value);
}
dc = &dive->dc;
dc = &dive->dcs[0];
/*
* Read samples from the CSV file. A sample contains all the lines with same timestamp. The CSV file has
@ -573,10 +574,8 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log)
*/
auto [memcsv, err] = readfile(csv);
if (err < 0) {
free_dive(dive);
if (err < 0)
return report_error(translate("gettextFromC", "Poseidon import failed: unable to read '%s'"), csv);
}
lineptr = memcsv.data();
for (;;) {
struct sample *sample;
@ -746,12 +745,11 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log)
add_sample_data(sample, POSEIDON_SETPOINT, prev_setpoint);
if (!has_ndl && prev_ndl >= 0)
add_sample_data(sample, POSEIDON_NDL, prev_ndl);
finish_sample(dc);
if (!lineptr || !*lineptr)
break;
}
record_dive_to_table(dive, log->dives);
log->dives.record_dive(std::move(dive));
return 1;
} else {
return 0;

View File

@ -21,10 +21,6 @@ enum csv_format {
#define MAXCOLDIGITS 10
#ifdef __cplusplus
extern "C" {
#endif
int parse_csv_file(const char *filename, struct xml_params *params, const char *csvtemplate, struct divelog *log);
int try_to_open_csv(std::string &mem, enum csv_format type, struct divelog *log);
int parse_txt_file(const char *filename, const char *csv, struct divelog *log);
@ -32,8 +28,4 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log);
int parse_seabear_log(const char *filename, struct divelog *log);
int parse_manual_file(const char *filename, struct xml_params *params, struct divelog *log);
#ifdef __cplusplus
}
#endif
#endif // IMPORTCSV_H

View File

@ -4,7 +4,6 @@
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
#endif
#include "ssrf.h"
#include "dive.h"
#include "divesite.h"
#include "sample.h"
@ -56,6 +55,8 @@ static int divinglog_cylinder(void *param, int, char **data, char **)
static int divinglog_profile(void *param, int, char **data, char **)
{
using namespace std::string_literals;
struct parser_state *state = (struct parser_state *)param;
int sinterval = 0;
@ -129,14 +130,14 @@ static int divinglog_profile(void *param, int, char **data, char **)
state->cur_sample->temperature.mkelvin = C_to_mkelvin(temp / 10.0f);
state->cur_sample->pressure[0].mbar = pressure * 100;
state->cur_sample->rbt.seconds = rbt;
if (oldcyl != tank && tank >= 0 && tank < state->cur_dive->cylinders.nr) {
struct gasmix mix = get_cylinder(state->cur_dive, tank)->gasmix;
if (oldcyl != tank && tank >= 0 && static_cast<size_t>(tank) < state->cur_dive->cylinders.size()) {
struct gasmix mix = get_cylinder(state->cur_dive.get(), tank)->gasmix;
int o2 = get_o2(mix);
int he = get_he(mix);
event_start(state);
state->cur_event.time.seconds = time;
strcpy(state->cur_event.name, "gaschange");
state->cur_event.name = "gaschange"s;
o2 = (o2 + 5) / 10;
he = (he + 5) / 10;
@ -211,8 +212,8 @@ static int divinglog_profile(void *param, int, char **data, char **)
* Count the number of o2 sensors
*/
if (!state->cur_dive->dc.no_o2sensors && (state->cur_sample->o2sensor[0].mbar || state->cur_sample->o2sensor[1].mbar || state->cur_sample->o2sensor[2].mbar)) {
state->cur_dive->dc.no_o2sensors = state->cur_sample->o2sensor[0].mbar ? 1 : 0 +
if (!state->cur_dive->dcs[0].no_o2sensors && (state->cur_sample->o2sensor[0].mbar || state->cur_sample->o2sensor[1].mbar || state->cur_sample->o2sensor[2].mbar)) {
state->cur_dive->dcs[0].no_o2sensors = state->cur_sample->o2sensor[0].mbar ? 1 : 0 +
state->cur_sample->o2sensor[1].mbar ? 1 : 0 +
state->cur_sample->o2sensor[2].mbar ? 1 : 0;
}
@ -223,7 +224,7 @@ static int divinglog_profile(void *param, int, char **data, char **)
if (ptr1[6] - '0') {
event_start(state);
state->cur_event.time.seconds = time;
strcpy(state->cur_event.name, "rbt");
state->cur_event.name = "rbt"s;
event_end(state);
}
@ -231,7 +232,7 @@ static int divinglog_profile(void *param, int, char **data, char **)
if (ptr1[7] - '0') {
event_start(state);
state->cur_event.time.seconds = time;
strcpy(state->cur_event.name, "ascent");
state->cur_event.name = "ascent"s;
event_end(state);
}
@ -239,7 +240,7 @@ static int divinglog_profile(void *param, int, char **data, char **)
if (ptr1[8] - '0') {
event_start(state);
state->cur_event.time.seconds = time;
strcpy(state->cur_event.name, "violation");
state->cur_event.name = "violation"s;
event_end(state);
}
@ -247,7 +248,7 @@ static int divinglog_profile(void *param, int, char **data, char **)
if (ptr1[9] - '0') {
event_start(state);
state->cur_event.time.seconds = time;
strcpy(state->cur_event.name, "workload");
state->cur_event.name = "workload"s;
event_end(state);
}
@ -275,22 +276,22 @@ static int divinglog_dive(void *param, int, char **data, char **)
state->cur_dive->when = (time_t)(atol(data[1]));
if (data[2])
add_dive_to_dive_site(state->cur_dive, find_or_create_dive_site_with_name(data[2], state->log->sites));
state->log->sites.find_or_create(std::string(data[2]))->add_dive(state->cur_dive.get());
if (data[3])
utf8_string(data[3], &state->cur_dive->buddy);
utf8_string_std(data[3], &state->cur_dive->buddy);
if (data[4])
utf8_string(data[4], &state->cur_dive->notes);
utf8_string_std(data[4], &state->cur_dive->notes);
if (data[5])
state->cur_dive->dc.maxdepth.mm = lrint(strtod_flags(data[5], NULL, 0) * 1000);
state->cur_dive->dcs[0].maxdepth.mm = lrint(permissive_strtod(data[5], NULL) * 1000);
if (data[6])
state->cur_dive->dc.duration.seconds = atoi(data[6]) * 60;
state->cur_dive->dcs[0].duration.seconds = atoi(data[6]) * 60;
if (data[7])
utf8_string(data[7], &state->cur_dive->diveguide);
utf8_string_std(data[7], &state->cur_dive->diveguide);
if (data[8])
state->cur_dive->airtemp.mkelvin = C_to_mkelvin(atol(data[8]));
@ -300,11 +301,11 @@ static int divinglog_dive(void *param, int, char **data, char **)
if (data[10]) {
weightsystem_t ws = { { atoi(data[10]) * 1000 }, translate("gettextFromC", "unknown"), false };
add_cloned_weightsystem(&state->cur_dive->weightsystems, ws);
state->cur_dive->weightsystems.push_back(std::move(ws));
}
if (data[11])
state->cur_dive->suit = strdup(data[11]);
state->cur_dive->suit = data[11];
/* Divinglog has following visibility options: good, medium, bad */
if (data[14]) {
@ -329,9 +330,9 @@ static int divinglog_dive(void *param, int, char **data, char **)
dc_settings_start(state);
if (data[12]) {
state->cur_dive->dc.model = strdup(data[12]);
state->cur_dive->dcs[0].model = data[12];
} else {
state->cur_settings.dc.model = strdup("Divinglog import");
state->cur_settings.dc.model = "Divinglog import";
}
snprintf(get_buffer, sizeof(get_buffer) - 1, get_cylinder0_template, diveid);
@ -354,10 +355,10 @@ static int divinglog_dive(void *param, int, char **data, char **)
case '0':
break;
case '1':
state->cur_dive->dc.divemode = PSCR;
state->cur_dive->dcs[0].divemode = PSCR;
break;
case '2':
state->cur_dive->dc.divemode = CCR;
state->cur_dive->dcs[0].divemode = CCR;
break;
}
}
@ -366,9 +367,9 @@ static int divinglog_dive(void *param, int, char **data, char **)
settings_end(state);
if (data[12]) {
state->cur_dive->dc.model = strdup(data[12]);
state->cur_dive->dcs[0].model = data[12];
} else {
state->cur_dive->dc.model = strdup("Divinglog import");
state->cur_dive->dcs[0].model = "Divinglog import";
}
snprintf(get_buffer, sizeof(get_buffer) - 1, get_profile_template, diveid);
@ -384,7 +385,7 @@ static int divinglog_dive(void *param, int, char **data, char **)
}
extern "C" int parse_divinglog_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
int parse_divinglog_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
{
int retval;
struct parser_state state;

View File

@ -6,7 +6,6 @@
#include <stdlib.h>
#include "qthelper.h"
#include "ssrf.h"
#include "dive.h"
#include "sample.h"
#include "subsurface-string.h"
@ -27,11 +26,12 @@
*/
static int seac_gaschange(void *param, sqlite3_stmt *sqlstmt)
{
using namespace std::string_literals;
struct parser_state *state = (struct parser_state *)param;
event_start(state);
state->cur_event.time.seconds = sqlite3_column_int(sqlstmt, 1);
strcpy(state->cur_event.name, "gaschange");
state->cur_event.name = "gaschange"s;
state->cur_event.gas.mix.o2.permille = 10 * sqlite3_column_int(sqlstmt, 4);
event_end(state);
@ -70,7 +70,7 @@ static int seac_dive(void *param, int, char **data, char **)
state->cur_dive->number = atoi(data[0]);
// Create first cylinder
cylinder_t *curcyl = get_or_create_cylinder(state->cur_dive, 0);
cylinder_t *curcyl = get_or_create_cylinder(state->cur_dive.get(), 0);
// Get time and date
sscanf(data[2], "%d/%d/%2d", &day, &month, &year);
@ -132,14 +132,14 @@ static int seac_dive(void *param, int, char **data, char **)
if (data[6]) {
switch (atoi(data[6])) {
case 1:
state->cur_dive->dc.divemode = OC;
state->cur_dive->dcs[0].divemode = OC;
break;
// Gauge Mode
case 2:
state->cur_dive->dc.divemode = UNDEF_COMP_TYPE;
state->cur_dive->dcs[0].divemode = UNDEF_COMP_TYPE;
break;
case 3:
state->cur_dive->dc.divemode = FREEDIVE;
state->cur_dive->dcs[0].divemode = FREEDIVE;
break;
default:
if (verbose) {
@ -150,12 +150,12 @@ static int seac_dive(void *param, int, char **data, char **)
// 9 = comments from seac app
if (data[9]) {
utf8_string(data[9], &state->cur_dive->notes);
utf8_string_std(data[9], &state->cur_dive->notes);
}
// 10 = dive duration
if (data[10]) {
state->cur_dive->dc.duration.seconds = atoi(data[10]);
state->cur_dive->dcs[0].duration.seconds = atoi(data[10]);
}
// 8 = water_type
@ -181,7 +181,7 @@ static int seac_dive(void *param, int, char **data, char **)
if (data[11]) {
state->cur_dive->dc.maxdepth.mm = 10 * atoi(data[11]);
state->cur_dive->dcs[0].maxdepth.mm = 10 * atoi(data[11]);
}
// Create sql_stmt type to query DB
@ -205,27 +205,24 @@ static int seac_dive(void *param, int, char **data, char **)
settings_start(state);
dc_settings_start(state);
// These dc values are const char *, therefore we have to cast.
// Will be fixed by converting to std::string
utf8_string(data[1], (char **)&state->cur_dive->dc.serial);
utf8_string(data[12], (char **)&state->cur_dive->dc.fw_version);
state->cur_dive->dc.model = strdup("Seac Action");
utf8_string_std(data[1], &state->cur_dive->dcs[0].serial);
utf8_string_std(data[12],&state->cur_dive->dcs[0].fw_version);
state->cur_dive->dcs[0].model = "Seac Action";
state->cur_dive->dc.deviceid = calculate_string_hash(data[1]);
state->cur_dive->dcs[0].deviceid = calculate_string_hash(data[1]);
add_extra_data(&state->cur_dive->dc, "GF-Lo", (const char*)sqlite3_column_text(sqlstmt, 9));
add_extra_data(&state->cur_dive->dc, "GF-Hi", (const char*)sqlite3_column_text(sqlstmt, 10));
add_extra_data(&state->cur_dive->dcs[0], "GF-Lo", (const char*)sqlite3_column_text(sqlstmt, 9));
add_extra_data(&state->cur_dive->dcs[0], "GF-Hi", (const char*)sqlite3_column_text(sqlstmt, 10));
dc_settings_end(state);
settings_end(state);
if (data[11]) {
state->cur_dive->dc.maxdepth.mm = 10 * atoi(data[11]);
state->cur_dive->dcs[0].maxdepth.mm = 10 * atoi(data[11]);
}
curcyl->gasmix.o2.permille = 10 * sqlite3_column_int(sqlstmt, 4);
// Track gasses to tell when switch occurs
lastgas = curcyl->gasmix;
curgas = curcyl->gasmix;
@ -241,7 +238,7 @@ static int seac_dive(void *param, int, char **data, char **)
seac_gaschange(state, sqlstmt);
lastgas = curgas;
cylnum ^= 1; // Only need to toggle between two cylinders
curcyl = get_or_create_cylinder(state->cur_dive, cylnum);
curcyl = get_or_create_cylinder(state->cur_dive.get(), cylnum);
curcyl->gasmix.o2.permille = 10 * sqlite3_column_int(sqlstmt, 4);
}
state->cur_sample->stopdepth.mm = 10 * sqlite3_column_int(sqlstmt, 5);
@ -264,7 +261,7 @@ static int seac_dive(void *param, int, char **data, char **)
* The callback function performs another SQL query on the other
* table, to read in the sample values.
*/
extern "C" int parse_seac_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
int parse_seac_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
{
int retval;
char *err = NULL;

View File

@ -4,7 +4,6 @@
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
#endif
#include "ssrf.h"
#include "dive.h"
#include "sample.h"
#include "subsurface-string.h"
@ -23,8 +22,8 @@ static int shearwater_cylinders(void *param, int, char **data, char **)
struct parser_state *state = (struct parser_state *)param;
cylinder_t *cyl;
int o2 = lrint(strtod_flags(data[0], NULL, 0) * 1000);
int he = lrint(strtod_flags(data[1], NULL, 0) * 1000);
int o2 = lrint(permissive_strtod(data[0], NULL) * 1000);
int he = lrint(permissive_strtod(data[1], NULL) * 1000);
/* Shearwater allows entering only 99%, not 100%
* so assume 99% to be pure oxygen */
@ -50,8 +49,8 @@ static int shearwater_changes(void *param, int columns, char **data, char **)
if (!data[0] || !data[1] || !data[2]) {
return 2;
}
int o2 = lrint(strtod_flags(data[1], NULL, 0) * 1000);
int he = lrint(strtod_flags(data[2], NULL, 0) * 1000);
int o2 = lrint(permissive_strtod(data[1], NULL) * 1000);
int he = lrint(permissive_strtod(data[2], NULL) * 1000);
/* Shearwater allows entering only 99%, not 100%
* so assume 99% to be pure oxygen */
@ -59,24 +58,20 @@ static int shearwater_changes(void *param, int columns, char **data, char **)
o2 = 1000;
// Find the cylinder index
int index;
bool found = false;
for (index = 0; index < state->cur_dive->cylinders.nr; ++index) {
const cylinder_t *cyl = get_cylinder(state->cur_dive, index);
if (cyl->gasmix.o2.permille == o2 && cyl->gasmix.he.permille == he) {
found = true;
break;
}
}
if (!found) {
auto it = std::find_if(state->cur_dive->cylinders.begin(), state->cur_dive->cylinders.end(),
[o2, he](auto &cyl)
{ return cyl.gasmix.o2.permille == o2 && cyl.gasmix.he.permille == he; });
if (it == state->cur_dive->cylinders.end()) {
// Cylinder not found, creating a new one
cyl = cylinder_start(state);
cyl->gasmix.o2.permille = o2;
cyl->gasmix.he.permille = he;
cylinder_end(state);
it = std::prev(state->cur_dive->cylinders.end());
}
add_gas_switch_event(state->cur_dive, get_dc(state), state->sample_rate ? atoi(data[0]) / state->sample_rate * 10 : atoi(data[0]), index);
add_gas_switch_event(state->cur_dive.get(), get_dc(state), state->sample_rate ? atoi(data[0]) / state->sample_rate * 10 : atoi(data[0]),
it - state->cur_dive->cylinders.begin());
return 0;
}
@ -101,12 +96,11 @@ static int shearwater_profile_sample(void *param, int, char **data, char **)
if (data[1])
state->cur_sample->depth.mm = state->metric ? lrint(strtod_flags(data[1], NULL, 0) * 1000) : feet_to_mm(strtod_flags(data[1], NULL, 0));
state->cur_sample->depth.mm = state->metric ? lrint(permissive_strtod(data[1], NULL) * 1000) : feet_to_mm(permissive_strtod(data[1], NULL));
if (data[2])
state->cur_sample->temperature.mkelvin = state->metric ? C_to_mkelvin(strtod_flags(data[2], NULL, 0)) : F_to_mkelvin(strtod_flags(data[2], NULL, 0));
if (data[3]) {
state->cur_sample->setpoint.mbar = lrint(strtod_flags(data[3], NULL, 0) * 1000);
}
state->cur_sample->temperature.mkelvin = state->metric ? C_to_mkelvin(permissive_strtod(data[2], NULL)) : F_to_mkelvin(permissive_strtod(data[2], NULL));
if (data[3])
state->cur_sample->setpoint.mbar = lrint(permissive_strtod(data[3], NULL) * 1000);
if (data[4])
state->cur_sample->ndl.seconds = atoi(data[4]) * 60;
if (data[5])
@ -161,11 +155,11 @@ static int shearwater_ai_profile_sample(void *param, int, char **data, char **)
state->cur_sample->time.seconds = atoi(data[0]);
if (data[1])
state->cur_sample->depth.mm = state->metric ? lrint(strtod_flags(data[1], NULL, 0) * 1000) : feet_to_mm(strtod_flags(data[1], NULL, 0));
state->cur_sample->depth.mm = state->metric ? lrint(permissive_strtod(data[1], NULL) * 1000) : feet_to_mm(permissive_strtod(data[1], NULL));
if (data[2])
state->cur_sample->temperature.mkelvin = state->metric ? C_to_mkelvin(strtod_flags(data[2], NULL, 0)) : F_to_mkelvin(strtod_flags(data[2], NULL, 0));
state->cur_sample->temperature.mkelvin = state->metric ? C_to_mkelvin(permissive_strtod(data[2], NULL)) : F_to_mkelvin(permissive_strtod(data[2], NULL));
if (data[3]) {
state->cur_sample->setpoint.mbar = lrint(strtod_flags(data[3], NULL, 0) * 1000);
state->cur_sample->setpoint.mbar = lrint(permissive_strtod(data[3], NULL) * 1000);
}
if (data[4])
state->cur_sample->ndl.seconds = atoi(data[4]) * 60;
@ -215,7 +209,7 @@ static int shearwater_mode(void *param, int, char **data, char **)
struct parser_state *state = (struct parser_state *)param;
if (data[0])
state->cur_dive->dc.divemode = atoi(data[0]) == 0 ? CCR : OC;
state->cur_dive->dcs[0].divemode = atoi(data[0]) == 0 ? CCR : OC;
return 0;
}
@ -240,23 +234,23 @@ static int shearwater_dive(void *param, int, char **data, char **)
long int dive_id = atol(data[11]);
if (data[2])
add_dive_site(data[2], state->cur_dive, state);
add_dive_site(data[2], state->cur_dive.get(), state);
if (data[3])
utf8_string(data[3], &state->cur_dive->buddy);
utf8_string_std(data[3], &state->cur_dive->buddy);
if (data[4])
utf8_string(data[4], &state->cur_dive->notes);
utf8_string_std(data[4], &state->cur_dive->notes);
state->metric = atoi(data[5]) == 1 ? 0 : 1;
/* TODO: verify that metric calculation is correct */
if (data[6])
state->cur_dive->dc.maxdepth.mm = state->metric ? lrint(strtod_flags(data[6], NULL, 0) * 1000) : feet_to_mm(strtod_flags(data[6], NULL, 0));
state->cur_dive->dcs[0].maxdepth.mm = state->metric ? lrint(permissive_strtod(data[6], NULL) * 1000) : feet_to_mm(permissive_strtod(data[6], NULL));
if (data[7])
state->cur_dive->dc.duration.seconds = atoi(data[7]) * 60;
state->cur_dive->dcs[0].duration.seconds = atoi(data[7]) * 60;
if (data[8])
state->cur_dive->dc.surface_pressure.mbar = atoi(data[8]);
state->cur_dive->dcs[0].surface_pressure.mbar = atoi(data[8]);
/*
* TODO: the deviceid hash should be calculated here.
*/
@ -267,13 +261,13 @@ static int shearwater_dive(void *param, int, char **data, char **)
if (data[10]) {
switch (atoi(data[10])) {
case 2:
state->cur_settings.dc.model = strdup("Shearwater Petrel/Perdix");
state->cur_settings.dc.model = "Shearwater Petrel/Perdix";
break;
case 4:
state->cur_settings.dc.model = strdup("Shearwater Predator");
state->cur_settings.dc.model = "Shearwater Predator";
break;
default:
state->cur_settings.dc.model = strdup("Shearwater import");
state->cur_settings.dc.model = "Shearwater import";
break;
}
}
@ -286,13 +280,13 @@ static int shearwater_dive(void *param, int, char **data, char **)
if (data[10]) {
switch (atoi(data[10])) {
case 2:
state->cur_dive->dc.model = strdup("Shearwater Petrel/Perdix");
state->cur_dive->dcs[0].model = "Shearwater Petrel/Perdix";
break;
case 4:
state->cur_dive->dc.model = strdup("Shearwater Predator");
state->cur_dive->dcs[0].model = "Shearwater Predator";
break;
default:
state->cur_dive->dc.model = strdup("Shearwater import");
state->cur_dive->dcs[0].model = "Shearwater import";
break;
}
}
@ -370,23 +364,23 @@ static int shearwater_cloud_dive(void *param, int, char **data, char **)
state->sample_rate = 0;
if (data[2])
add_dive_site(data[2], state->cur_dive, state);
add_dive_site(data[2], state->cur_dive.get(), state);
if (data[3])
utf8_string(data[3], &state->cur_dive->buddy);
utf8_string_std(data[3], &state->cur_dive->buddy);
if (data[4])
utf8_string(data[4], &state->cur_dive->notes);
utf8_string_std(data[4], &state->cur_dive->notes);
state->metric = atoi(data[5]) == 1 ? 0 : 1;
/* TODO: verify that metric calculation is correct */
if (data[6])
state->cur_dive->dc.maxdepth.mm = state->metric ? lrint(strtod_flags(data[6], NULL, 0) * 1000) : feet_to_mm(strtod_flags(data[6], NULL, 0));
state->cur_dive->dcs[0].maxdepth.mm = state->metric ? lrint(permissive_strtod(data[6], NULL) * 1000) : feet_to_mm(permissive_strtod(data[6], NULL));
if (data[7])
state->cur_dive->dc.duration.seconds = atoi(data[7]);
state->cur_dive->dcs[0].duration.seconds = atoi(data[7]);
if (data[8])
state->cur_dive->dc.surface_pressure.mbar = atoi(data[8]);
state->cur_dive->dcs[0].surface_pressure.mbar = atoi(data[8]);
/*
* TODO: the deviceid hash should be calculated here.
*/
@ -397,13 +391,13 @@ static int shearwater_cloud_dive(void *param, int, char **data, char **)
if (data[10]) {
switch (atoi(data[10])) {
case 2:
state->cur_settings.dc.model = strdup("Shearwater Petrel/Perdix");
state->cur_settings.dc.model = "Shearwater Petrel/Perdix";
break;
case 4:
state->cur_settings.dc.model = strdup("Shearwater Predator");
state->cur_settings.dc.model = "Shearwater Predator";
break;
default:
state->cur_settings.dc.model = strdup("Shearwater import");
state->cur_settings.dc.model = "Shearwater import";
break;
}
}
@ -416,13 +410,13 @@ static int shearwater_cloud_dive(void *param, int, char **data, char **)
if (data[10]) {
switch (atoi(data[10])) {
case 2:
state->cur_dive->dc.model = strdup("Shearwater Petrel/Perdix");
state->cur_dive->dcs[0].model = "Shearwater Petrel/Perdix";
break;
case 4:
state->cur_dive->dc.model = strdup("Shearwater Predator");
state->cur_dive->dcs[0].model = "Shearwater Predator";
break;
default:
state->cur_dive->dc.model = strdup("Shearwater import");
state->cur_dive->dcs[0].model = "Shearwater import";
break;
}
}
@ -473,7 +467,7 @@ static int shearwater_cloud_dive(void *param, int, char **data, char **)
return SQLITE_OK;
}
extern "C" int parse_shearwater_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
int parse_shearwater_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
{
int retval;
struct parser_state state;
@ -496,7 +490,7 @@ extern "C" int parse_shearwater_buffer(sqlite3 *handle, const char *url, const c
return 0;
}
extern "C" int parse_shearwater_cloud_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
int parse_shearwater_cloud_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
{
int retval;
struct parser_state state;

View File

@ -4,7 +4,6 @@
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
#endif
#include "ssrf.h"
#include "dive.h"
#include "parse.h"
#include "sample.h"
@ -21,6 +20,7 @@
static int dm4_events(void *param, int, char **data, char **)
{
using namespace std::string_literals;
struct parser_state *state = (struct parser_state *)param;
event_start(state);
@ -31,108 +31,108 @@ static int dm4_events(void *param, int, char **data, char **)
switch (atoi(data[2])) {
case 1:
/* 1 Mandatory Safety Stop */
strcpy(state->cur_event.name, "safety stop (mandatory)");
state->cur_event.name = "safety stop (mandatory)"s;
break;
case 3:
/* 3 Deco */
/* What is Subsurface's term for going to
* deco? */
strcpy(state->cur_event.name, "deco");
state->cur_event.name = "deco"s;
break;
case 4:
/* 4 Ascent warning */
strcpy(state->cur_event.name, "ascent");
state->cur_event.name = "ascent"s;
break;
case 5:
/* 5 Ceiling broken */
strcpy(state->cur_event.name, "violation");
state->cur_event.name = "violation"s;
break;
case 6:
/* 6 Mandatory safety stop ceiling error */
strcpy(state->cur_event.name, "violation");
state->cur_event.name = "violation"s;
break;
case 7:
/* 7 Below deco floor */
strcpy(state->cur_event.name, "below floor");
state->cur_event.name = "below floor"s;
break;
case 8:
/* 8 Dive time alarm */
strcpy(state->cur_event.name, "divetime");
state->cur_event.name = "divetime"s;
break;
case 9:
/* 9 Depth alarm */
strcpy(state->cur_event.name, "maxdepth");
state->cur_event.name = "maxdepth"s;
break;
case 10:
/* 10 OLF 80% */
case 11:
/* 11 OLF 100% */
strcpy(state->cur_event.name, "OLF");
state->cur_event.name = "OLF"s;
break;
case 12:
/* 12 High pO₂ */
strcpy(state->cur_event.name, "PO2");
state->cur_event.name = "PO2"s;
break;
case 13:
/* 13 Air time */
strcpy(state->cur_event.name, "airtime");
state->cur_event.name = "airtime"s;
break;
case 17:
/* 17 Ascent warning */
strcpy(state->cur_event.name, "ascent");
state->cur_event.name = "ascent"s;
break;
case 18:
/* 18 Ceiling error */
strcpy(state->cur_event.name, "ceiling");
state->cur_event.name = "ceiling"s;
break;
case 19:
/* 19 Surfaced */
strcpy(state->cur_event.name, "surface");
state->cur_event.name = "surface"s;
break;
case 20:
/* 20 Deco */
strcpy(state->cur_event.name, "deco");
state->cur_event.name = "deco"s;
break;
case 22:
case 32:
/* 22 Mandatory safety stop violation */
/* 32 Deep stop violation */
strcpy(state->cur_event.name, "violation");
state->cur_event.name = "violation"s;
break;
case 30:
/* Tissue level warning */
strcpy(state->cur_event.name, "tissue warning");
state->cur_event.name = "tissue warning"s;
break;
case 37:
/* Tank pressure alarm */
strcpy(state->cur_event.name, "tank pressure");
state->cur_event.name = "tank pressure"s;
break;
case 257:
/* 257 Dive active */
/* This seems to be given after surface when
* descending again. */
strcpy(state->cur_event.name, "surface");
state->cur_event.name = "surface"s;
break;
case 258:
/* 258 Bookmark */
if (data[3]) {
strcpy(state->cur_event.name, "heading");
state->cur_event.name = "heading"s;
state->cur_event.value = atoi(data[3]);
} else {
strcpy(state->cur_event.name, "bookmark");
state->cur_event.name = "bookmark"s;
}
break;
case 259:
/* Deep stop */
strcpy(state->cur_event.name, "Deep stop");
state->cur_event.name = "Deep stop"s;
break;
case 260:
/* Deep stop */
strcpy(state->cur_event.name, "Deep stop cleared");
state->cur_event.name = "Deep stop cleared"s;
break;
case 266:
/* Mandatory safety stop activated */
strcpy(state->cur_event.name, "safety stop (mandatory)");
state->cur_event.name = "safety stop (mandatory)"s;
break;
case 267:
/* Mandatory safety stop deactivated */
@ -140,7 +140,7 @@ static int dm4_events(void *param, int, char **data, char **)
* profile so skipping as well for now */
break;
default:
strcpy(state->cur_event.name, "unknown");
state->cur_event.name = "unknown"s;
state->cur_event.value = atoi(data[2]);
break;
}
@ -155,7 +155,7 @@ static int dm4_tags(void *param, int, char **data, char **)
struct parser_state *state = (struct parser_state *)param;
if (data[0])
taglist_add_tag(&state->cur_dive->tag_list, data[0]);
taglist_add_tag(state->cur_dive->tags, data[0]);
return 0;
}
@ -179,7 +179,7 @@ static int dm4_dive(void *param, int, char **data, char **)
state->cur_dive->when = (time_t)(atol(data[1]));
if (data[2])
utf8_string(data[2], &state->cur_dive->notes);
utf8_string_std(data[2], &state->cur_dive->notes);
/*
* DM4 stores Duration and DiveTime. It looks like DiveTime is
@ -191,7 +191,7 @@ static int dm4_dive(void *param, int, char **data, char **)
if (data[3])
state->cur_dive->duration.seconds = atoi(data[3]);
if (data[15])
state->cur_dive->dc.duration.seconds = atoi(data[15]);
state->cur_dive->dcs[0].duration.seconds = atoi(data[15]);
/*
* TODO: the deviceid hash should be calculated here.
@ -208,11 +208,11 @@ static int dm4_dive(void *param, int, char **data, char **)
settings_end(state);
if (data[6])
state->cur_dive->dc.maxdepth.mm = lrint(strtod_flags(data[6], NULL, 0) * 1000);
state->cur_dive->dcs[0].maxdepth.mm = lrint(permissive_strtod(data[6], NULL) * 1000);
if (data[8])
state->cur_dive->dc.airtemp.mkelvin = C_to_mkelvin(atoi(data[8]));
state->cur_dive->dcs[0].airtemp.mkelvin = C_to_mkelvin(atoi(data[8]));
if (data[9])
state->cur_dive->dc.watertemp.mkelvin = C_to_mkelvin(atoi(data[9]));
state->cur_dive->dcs[0].watertemp.mkelvin = C_to_mkelvin(atoi(data[9]));
/*
* TODO: handle multiple cylinders
@ -227,7 +227,7 @@ static int dm4_dive(void *param, int, char **data, char **)
if (data[11] && atoi(data[11]) > 0)
cyl->end.mbar = (atoi(data[11]));
if (data[12])
cyl->type.size.mliter = lrint((strtod_flags(data[12], NULL, 0)) * 1000);
cyl->type.size.mliter = lrint((permissive_strtod(data[12], NULL)) * 1000);
if (data[13])
cyl->type.workingpressure.mbar = (atoi(data[13]));
if (data[20])
@ -237,7 +237,7 @@ static int dm4_dive(void *param, int, char **data, char **)
cylinder_end(state);
if (data[14])
state->cur_dive->dc.surface_pressure.mbar = (atoi(data[14]) * 1000);
state->cur_dive->dcs[0].surface_pressure.mbar = (atoi(data[14]) * 1000);
interval = data[16] ? atoi(data[16]) : 0;
profileBlob = (float *)data[17];
@ -249,7 +249,7 @@ static int dm4_dive(void *param, int, char **data, char **)
if (profileBlob)
state->cur_sample->depth.mm = lrintf(profileBlob[i] * 1000.0f);
else
state->cur_sample->depth.mm = state->cur_dive->dc.maxdepth.mm;
state->cur_sample->depth.mm = state->cur_dive->dcs[0].maxdepth.mm;
if (data[18] && data[18][0])
state->cur_sample->temperature.mkelvin = C_to_mkelvin(tempBlob[i]);
@ -277,7 +277,7 @@ static int dm4_dive(void *param, int, char **data, char **)
return SQLITE_OK;
}
extern "C" int parse_dm4_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
int parse_dm4_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
{
int retval;
char *err = NULL;
@ -314,10 +314,10 @@ static int dm5_cylinders(void *param, int, char **data, char **)
/* DM5 shows tank size of 12 liters when the actual
* value is 0 (and using metric units). So we just use
* the same 12 liters when size is not available */
if (strtod_flags(data[6], NULL, 0) == 0.0 && cyl->start.mbar)
if (permissive_strtod(data[6], NULL) == 0.0 && cyl->start.mbar)
cyl->type.size.mliter = 12000;
else
cyl->type.size.mliter = lrint((strtod_flags(data[6], NULL, 0)) * 1000);
cyl->type.size.mliter = lrint((permissive_strtod(data[6], NULL)) * 1000);
}
if (data[2])
cyl->gasmix.o2.permille = atoi(data[2]) * 10;
@ -329,19 +329,20 @@ static int dm5_cylinders(void *param, int, char **data, char **)
static int dm5_gaschange(void *param, int, char **data, char **)
{
using namespace std::string_literals;
struct parser_state *state = (struct parser_state *)param;
event_start(state);
if (data[0])
state->cur_event.time.seconds = atoi(data[0]);
if (data[1]) {
strcpy(state->cur_event.name, "gaschange");
state->cur_event.value = lrint(strtod_flags(data[1], NULL, 0));
state->cur_event.name = "gaschange"s;
state->cur_event.value = lrint(permissive_strtod(data[1], NULL));
}
/* He part of the mix */
if (data[2])
state->cur_event.value += lrint(strtod_flags(data[2], NULL, 0)) << 16;
state->cur_event.value += lrint(permissive_strtod(data[2], NULL)) << 16;
event_end(state);
return 0;
@ -366,12 +367,12 @@ static int dm5_dive(void *param, int, char **data, char **)
state->cur_dive->when = (time_t)(atol(data[1]));
if (data[2])
utf8_string(data[2], &state->cur_dive->notes);
utf8_string_std(data[2], &state->cur_dive->notes);
if (data[3])
state->cur_dive->duration.seconds = atoi(data[3]);
if (data[15])
state->cur_dive->dc.duration.seconds = atoi(data[15]);
state->cur_dive->dcs[0].duration.seconds = atoi(data[15]);
/*
* TODO: the deviceid hash should be calculated here.
@ -389,30 +390,28 @@ static int dm5_dive(void *param, int, char **data, char **)
settings_end(state);
if (data[6])
state->cur_dive->dc.maxdepth.mm = lrint(strtod_flags(data[6], NULL, 0) * 1000);
state->cur_dive->dcs[0].maxdepth.mm = lrint(permissive_strtod(data[6], NULL) * 1000);
if (data[8])
state->cur_dive->dc.airtemp.mkelvin = C_to_mkelvin(atoi(data[8]));
state->cur_dive->dcs[0].airtemp.mkelvin = C_to_mkelvin(atoi(data[8]));
if (data[9])
state->cur_dive->dc.watertemp.mkelvin = C_to_mkelvin(atoi(data[9]));
state->cur_dive->dcs[0].watertemp.mkelvin = C_to_mkelvin(atoi(data[9]));
if (data[4]) {
state->cur_dive->dc.deviceid = atoi(data[4]);
state->cur_dive->dcs[0].deviceid = atoi(data[4]);
}
// Ugh. dc.model is const char * -> we are not supposed to write into it. This will
// change when we convert to std::string.
if (data[5])
utf8_string(data[5], (char **)&state->cur_dive->dc.model);
utf8_string_std(data[5], &state->cur_dive->dcs[0].model);
if (data[25]) {
switch(atoi(data[25])) {
case 1:
state->cur_dive->dc.divemode = OC;
state->cur_dive->dcs[0].divemode = OC;
break;
case 5:
state->cur_dive->dc.divemode = CCR;
state->cur_dive->dcs[0].divemode = CCR;
break;
default:
state->cur_dive->dc.divemode = OC;
state->cur_dive->dcs[0].divemode = OC;
break;
}
}
@ -425,7 +424,7 @@ static int dm5_dive(void *param, int, char **data, char **)
}
if (data[14])
state->cur_dive->dc.surface_pressure.mbar = (atoi(data[14]) / 100);
state->cur_dive->dcs[0].surface_pressure.mbar = (atoi(data[14]) / 100);
interval = data[16] ? atoi(data[16]) : 0;
@ -513,7 +512,7 @@ static int dm5_dive(void *param, int, char **data, char **)
if (profileBlob)
state->cur_sample->depth.mm = lrintf(profileBlob[i] * 1000.0f);
else
state->cur_sample->depth.mm = state->cur_dive->dc.maxdepth.mm;
state->cur_sample->depth.mm = state->cur_dive->dcs[0].maxdepth.mm;
if (data[18] && data[18][0])
state->cur_sample->temperature.mkelvin = C_to_mkelvin(tempBlob[i]);
@ -549,7 +548,7 @@ static int dm5_dive(void *param, int, char **data, char **)
return SQLITE_OK;
}
extern "C" int parse_dm5_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
int parse_dm5_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
{
int retval;
char *err = NULL;

View File

@ -32,13 +32,11 @@ static std::string make_default_filename()
return system_default_path() + "/subsurface.xml";
}
extern "C" {
const char mac_system_divelist_default_font[] = "Arial";
const char *system_divelist_default_font = mac_system_divelist_default_font;
double system_divelist_default_font_size = -1.0;
void subsurface_OS_pref_setup(void)
void subsurface_OS_pref_setup()
{
// nothing
}
@ -49,13 +47,13 @@ bool subsurface_ignore_font(const char*)
return false;
}
const char *system_default_directory(void)
const char *system_default_directory()
{
static const std::string path = system_default_path();
return path.c_str();
}
const char *system_default_filename(void)
const char *system_default_filename()
{
static const std::string fn = make_default_filename();
return fn.c_str();
@ -109,12 +107,12 @@ int subsurface_zip_close(struct zip *zip)
}
/* win32 console */
void subsurface_console_init(void)
void subsurface_console_init()
{
/* NOP */
}
void subsurface_console_exit(void)
void subsurface_console_exit()
{
/* NOP */
}
@ -123,4 +121,3 @@ bool subsurface_user_is_root()
{
return false;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,7 @@
#include <stdint.h>
#include <stdio.h>
#include <string>
/* libdivecomputer */
@ -20,47 +21,42 @@
#define dc_usb_storage_open(stream, context, devname) (DC_STATUS_UNSUPPORTED)
#endif
#ifdef __cplusplus
extern "C" {
#else
#include <stdbool.h>
#endif
struct dive;
struct divelog;
struct devices;
typedef struct {
dc_descriptor_t *descriptor;
const char *vendor, *product, *devname;
const char *model, *btname;
unsigned char *fingerprint;
unsigned int fsize, fdeviceid, fdiveid;
struct dc_event_devinfo_t devinfo;
uint32_t diveid;
dc_device_t *device;
dc_context_t *context;
dc_iostream_t *iostream;
bool force_download;
bool libdc_log;
bool libdc_dump;
bool bluetooth_mode;
bool sync_time;
FILE *libdc_logfile;
struct divelog *log;
void *androidUsbDeviceDescriptor;
} device_data_t;
struct device_data_t {
dc_descriptor_t *descriptor = nullptr;
std::string vendor, product, devname;
std::string model, btname;
unsigned char *fingerprint = nullptr;
unsigned int fsize = 0, fdeviceid = 0, fdiveid = 0;
struct dc_event_devinfo_t devinfo = { };
uint32_t diveid = 0;
dc_device_t *device = nullptr;
dc_context_t *context = nullptr;
dc_iostream_t *iostream = nullptr;
bool force_download = false;
bool libdc_log = false;
bool libdc_dump = false;
bool bluetooth_mode = false;
bool sync_time = false;
FILE *libdc_logfile = nullptr;
struct divelog *log = nullptr;
void *androidUsbDeviceDescriptor = nullptr;
device_data_t();
~device_data_t();
};
const char *errmsg (dc_status_t rc);
const char *do_libdivecomputer_import(device_data_t *data);
const char *do_uemis_import(device_data_t *data);
std::string do_libdivecomputer_import(device_data_t *data);
dc_status_t libdc_buffer_parser(struct dive *dive, device_data_t *data, unsigned char *buffer, int size);
void logfunc(dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, const char *msg, void *userdata);
dc_descriptor_t *get_descriptor(dc_family_t type, unsigned int model);
extern int import_thread_cancelled;
extern const char *progress_bar_text;
extern void (*progress_callback)(const char *text);
extern std::string progress_bar_text;
extern void (*progress_callback)(const std::string &text);
extern double progress_bar_fraction;
dc_status_t ble_packet_open(dc_iostream_t **iostream, dc_context_t *context, const char* devaddr, void *userdata);
@ -72,13 +68,7 @@ dc_status_t divecomputer_device_open(device_data_t *data);
unsigned int get_supported_transports(device_data_t *data);
#ifdef __cplusplus
}
#include <string>
extern std::string logfile_name;
extern std::string dumpfile_name;
#endif
#endif // LIBDIVECOMPUTER_H

View File

@ -2,7 +2,6 @@
#include <string.h>
#include <stdlib.h>
#include "ssrf.h"
#include "divesite.h"
#include "dive.h"
#include "divelog.h"
@ -132,25 +131,23 @@ static int handle_event_ver3(int code, const unsigned char *ps, unsigned int ps_
return skip;
}
static void parse_dives(int log_version, const unsigned char *buf, unsigned int buf_size, struct dive_table *table, struct dive_site_table *sites)
static void parse_dives(int log_version, const unsigned char *buf, unsigned int buf_size, struct dive_table &table, dive_site_table &sites)
{
unsigned int ptr = 0;
unsigned char model;
struct dive *dive;
struct divecomputer *dc;
struct sample *sample;
while (ptr < buf_size) {
int i;
dive = alloc_dive();
auto dive = std::make_unique<struct dive>();
memset(&sensor_ids, 0, sizeof(sensor_ids));
dc = &dive->dc;
dc = &dive->dcs[0];
/* Just the main cylinder until we can handle the buddy cylinder porperly */
for (i = 0; i < 1; i++) {
cylinder_t cyl = empty_cylinder;
fill_default_cylinder(dive, &cyl);
cylinder_t cyl = default_cylinder(dive.get());
add_cylinder(&dive->cylinders, i, cyl);
}
@ -158,17 +155,17 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int
model = *(buf + ptr);
switch (model) {
case 0:
dc->model = strdup("Xen");
dc->model = "Xen";
break;
case 1:
case 2:
dc->model = strdup("Xeo");
dc->model = "Xeo";
break;
case 4:
dc->model = strdup("Lynx");
dc->model = "Lynx";
break;
default:
dc->model = strdup("Liquivision");
dc->model = "Liquivision";
break;
}
ptr++;
@ -191,7 +188,7 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int
/* Store the location only if we have one */
if (!location.empty())
add_dive_to_dive_site(dive, find_or_create_dive_site_with_name(location.c_str(), sites));
sites.find_or_create(location)->add_dive(dive.get());
ptr += len + 4 + place_len;
@ -202,7 +199,7 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int
// Blank notes are better than the default text
std::string notes((char *)buf + ptr, len);
if (!starts_with(notes, "Comment ..."))
dive->notes = strdup(notes.c_str());
dive->notes = notes;
ptr += len;
dive->id = array_uint32_le(buf + ptr);
@ -336,7 +333,6 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int
sample->depth.mm = array_uint16_le(ds + (d - 1) * 2) * 10; // cm->mm
sample->temperature.mkelvin = C_to_mkelvin((float) array_uint16_le(ts + (d - 1) * 2) / 10); // dC->mK
add_sample_pressure(sample, event.pressure.sensor, event.pressure.mbar);
finish_sample(dc);
break;
} else if (event.time > sample_time) {
@ -344,7 +340,6 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int
sample->time.seconds = sample_time;
sample->depth.mm = depth_mm;
sample->temperature.mkelvin = temp_mk;
finish_sample(dc);
d++;
continue;
@ -353,7 +348,6 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int
sample->depth.mm = depth_mm;
sample->temperature.mkelvin = temp_mk;
add_sample_pressure(sample, event.pressure.sensor, event.pressure.mbar);
finish_sample(dc);
d++;
break;
@ -372,7 +366,6 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int
sample->temperature.mkelvin = last_temp + (temp_mk - last_temp)
* ((int)event.time - (int)last_time) / sample_interval;
}
finish_sample(dc);
break;
}
@ -387,7 +380,6 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int
sample->depth.mm = array_uint16_le(ds + d * 2) * 10; // cm->mm
sample->temperature.mkelvin =
C_to_mkelvin((float)array_uint16_le(ts + d * 2) / 10);
finish_sample(dc);
}
if (log_version == 3 && model == 4) {
@ -415,17 +407,11 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int
}
// End dive
record_dive_to_table(dive, table);
dive = NULL;
table.record_dive(std::move(dive));
// Advance ptr for next dive
ptr += ps_ptr + 4;
} // while
//DEBUG save_dives("/tmp/test.xml");
// if we bailed out of the loop, the dive hasn't been recorded and dive hasn't been set to NULL
free_dive(dive);
}
int try_to_open_liquivision(const char *, std::string &mem, struct divelog *log)

View File

@ -1,5 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
#include "ssrf.h"
#include <stdio.h>
#include <ctype.h>
#include <string.h>
@ -38,8 +37,8 @@ std::string saved_git_id;
struct git_parser_state {
git_repository *repo = nullptr;
struct divecomputer *active_dc = nullptr;
struct dive *active_dive = nullptr;
dive_trip_t *active_trip = nullptr;
std::unique_ptr<dive> active_dive;
std::unique_ptr<dive_trip> active_trip;
std::string fulltext_mode;
std::string fulltext_query;
std::string filter_constraint_type;
@ -47,7 +46,7 @@ struct git_parser_state {
std::string filter_constraint_range_mode;
bool filter_constraint_negate = false;
std::string filter_constraint_data;
struct picture active_pic = { 0 };
struct picture active_pic;
struct dive_site *active_site = nullptr;
std::unique_ptr<filter_preset> active_filter;
struct divelog *log = nullptr;
@ -172,16 +171,16 @@ static int get_hex(const char *line)
static void parse_dive_gps(char *line, struct git_parser_state *state)
{
location_t location;
struct dive_site *ds = get_dive_site_for_dive(state->active_dive);
struct dive_site *ds = get_dive_site_for_dive(state->active_dive.get());
parse_location(line, &location);
if (!ds) {
ds = get_dive_site_by_gps(&location, state->log->sites);
ds = state->log->sites.get_by_gps(&location);
if (!ds)
ds = create_dive_site_with_gps("", &location, state->log->sites);
add_dive_to_dive_site(state->active_dive, ds);
ds = state->log->sites.create(std::string(), location);
ds->add_dive(state->active_dive.get());
} else {
if (dive_site_has_gps_location(ds) && !same_location(&ds->location, &location)) {
if (dive_site_has_gps_location(ds) && ds->location != location) {
std::string coords = printGPSCoordsC(&location);
// we have a dive site that already has GPS coordinates
// note 1: there will be much less copying once the core
@ -189,10 +188,8 @@ static void parse_dive_gps(char *line, struct git_parser_state *state)
// note 2: we could include the first newline in the
// translation string, but that would be weird and cause
// a new string.
std::string new_text = std::string(ds->notes) + '\n' +
format_string_std(translate("gettextFromC", "multiple GPS locations for this dive site; also %s\n"), coords.c_str());
free(ds->notes);
ds->notes = strdup(new_text.c_str());
ds->notes += '\n';
ds->notes += format_string_std(translate("gettextFromC", "multiple GPS locations for this dive site; also %s\n"), coords.c_str());
}
ds->location = location;
}
@ -210,54 +207,43 @@ static std::string get_first_converted_string(struct git_parser_state *state)
return std::move(state->converted_strings.front());
}
// This is a dummy function that converts the first
// converted string to a newly allocated C-string.
// Will be removed when the core data structures are
// converted to std::string.
static char *get_first_converted_string_c(struct git_parser_state *state)
{
return strdup(get_first_converted_string(state).c_str());
}
static void parse_dive_location(char *, struct git_parser_state *state)
{
std::string name = get_first_converted_string(state);
struct dive_site *ds = get_dive_site_for_dive(state->active_dive);
struct dive_site *ds = get_dive_site_for_dive(state->active_dive.get());
if (!ds) {
ds = get_dive_site_by_name(name.c_str(), state->log->sites);
ds = state->log->sites.get_by_name(name);
if (!ds)
ds = create_dive_site(name.c_str(), state->log->sites);
add_dive_to_dive_site(state->active_dive, ds);
ds = state->log->sites.create(name);
ds->add_dive(state->active_dive.get());
} else {
// we already had a dive site linked to the dive
if (empty_string(ds->name)) {
free(ds->name); // empty_string could mean pointer to a 0-byte!
ds->name = strdup(name.c_str());
if (ds->name.empty()) {
ds->name = name.c_str();
} else {
// and that dive site had a name. that's weird - if our name is different, add it to the notes
if (!same_string(ds->name, name.c_str())) {
std::string new_string = std::string(ds->notes) + '\n' +
format_string_std(translate("gettextFromC", "additional name for site: %s\n"), name.c_str());
ds->notes = strdup(new_string.c_str());
if (ds->name == name) {
ds->notes += '\n';
ds->notes += format_string_std(translate("gettextFromC", "additional name for site: %s\n"), name.c_str());
}
}
}
}
static void parse_dive_diveguide(char *, struct git_parser_state *state)
{ state->active_dive->diveguide = get_first_converted_string_c(state); }
{ state->active_dive->diveguide = get_first_converted_string(state); }
static void parse_dive_buddy(char *, struct git_parser_state *state)
{ state->active_dive->buddy = get_first_converted_string_c(state); }
{ state->active_dive->buddy = get_first_converted_string(state); }
static void parse_dive_suit(char *, struct git_parser_state *state)
{ state->active_dive->suit = get_first_converted_string_c(state); }
{ state->active_dive->suit = get_first_converted_string(state); }
static void parse_dive_notes(char *, struct git_parser_state *state)
{ state->active_dive->notes = get_first_converted_string_c(state); }
{ state->active_dive->notes = get_first_converted_string(state); }
static void parse_dive_divesiteid(char *line, struct git_parser_state *state)
{ add_dive_to_dive_site(state->active_dive, get_dive_site_by_uuid(get_hex(line), state->log->sites)); }
{ state->log->sites.get_by_uuid(get_hex(line))->add_dive(state->active_dive.get()); }
/*
* We can have multiple tags.
@ -266,7 +252,7 @@ static void parse_dive_tags(char *, struct git_parser_state *state)
{
for (const std::string &tag: state->converted_strings) {
if (!tag.empty())
taglist_add_tag(&state->active_dive->tag_list, tag.c_str());
taglist_add_tag(state->active_dive->tags, tag.c_str());
}
}
@ -314,13 +300,13 @@ static void parse_dive_invalid(char *, struct git_parser_state *state)
}
static void parse_site_description(char *, struct git_parser_state *state)
{ state->active_site->description = get_first_converted_string_c(state); }
{ state->active_site->description = get_first_converted_string(state); }
static void parse_site_name(char *, struct git_parser_state *state)
{ state->active_site->name = get_first_converted_string_c(state); }
{ state->active_site->name = get_first_converted_string(state); }
static void parse_site_notes(char *, struct git_parser_state *state)
{ state->active_site->notes = get_first_converted_string_c(state); }
{ state->active_site->notes = get_first_converted_string(state); }
static void parse_site_gps(char *line, struct git_parser_state *state)
{
@ -332,8 +318,8 @@ static void parse_site_geo(char *line, struct git_parser_state *state)
int origin;
int category;
sscanf(line, "cat %d origin %d \"", &category, &origin);
taxonomy_set_category(&state->active_site->taxonomy, (taxonomy_category)category,
get_first_converted_string(state).c_str(), (taxonomy_origin)origin);
taxonomy_set_category(state->active_site->taxonomy, (taxonomy_category)category,
get_first_converted_string(state), (taxonomy_origin)origin);
}
static std::string pop_cstring(struct git_parser_state *state, const char *err)
@ -390,7 +376,7 @@ static void parse_cylinder_keyvalue(void *_cylinder, const char *key, const std:
return;
}
if (!strcmp(key, "description")) {
cylinder->type.description = strdup(value.c_str());
cylinder->type.description = value;
return;
}
if (!strcmp(key, "o2")) {
@ -434,7 +420,7 @@ static void parse_cylinder_keyvalue(void *_cylinder, const char *key, const std:
static void parse_dive_cylinder(char *line, struct git_parser_state *state)
{
cylinder_t cylinder = empty_cylinder;
cylinder_t cylinder;
for (;;) {
char c;
@ -445,9 +431,9 @@ static void parse_dive_cylinder(char *line, struct git_parser_state *state)
line = parse_keyvalue_entry(parse_cylinder_keyvalue, &cylinder, line, state);
}
if (cylinder.cylinder_use == OXYGEN)
state->o2pressure_sensor = state->active_dive->cylinders.nr;
state->o2pressure_sensor = static_cast<int>(state->active_dive->cylinders.size());
add_cylinder(&state->active_dive->cylinders, state->active_dive->cylinders.nr, cylinder);
state->active_dive->cylinders.push_back(std::move(cylinder));
}
static void parse_weightsystem_keyvalue(void *_ws, const char *key, const std::string &value)
@ -458,7 +444,7 @@ static void parse_weightsystem_keyvalue(void *_ws, const char *key, const std::s
return;
}
if (!strcmp(key, "description")) {
ws->description = strdup(value.c_str());
ws->description = value;
return;
}
report_error("Unknown weightsystem key/value pair (%s/%s)", key, value.c_str());
@ -466,7 +452,7 @@ static void parse_weightsystem_keyvalue(void *_ws, const char *key, const std::s
static void parse_dive_weightsystem(char *line, struct git_parser_state *state)
{
weightsystem_t ws = empty_weightsystem;
weightsystem_t ws;
for (;;) {
char c;
@ -477,7 +463,7 @@ static void parse_dive_weightsystem(char *line, struct git_parser_state *state)
line = parse_keyvalue_entry(parse_weightsystem_keyvalue, &ws, line, state);
}
add_to_weightsystem_table(&state->active_dive->weightsystems, state->active_dive->weightsystems.nr, ws);
state->active_dive->weightsystems.push_back(std::move(ws));
}
static int match_action(char *line, void *data,
@ -656,7 +642,7 @@ static char *parse_sample_unit(struct sample *sample, double val, char *unit)
*/
static int sanitize_sensor_id(const struct dive *d, int nr)
{
return d && nr >= 0 && nr < d->cylinders.nr ? nr : NO_SENSOR;
return d && nr >= 0 && static_cast<size_t>(nr) < d->cylinders.size() ? nr : NO_SENSOR;
}
/*
@ -680,13 +666,14 @@ static int sanitize_sensor_id(const struct dive *d, int nr)
static struct sample *new_sample(struct git_parser_state *state)
{
struct sample *sample = prepare_sample(state->active_dc);
if (sample != state->active_dc->sample) {
memcpy(sample, sample - 1, sizeof(struct sample));
size_t num_samples = state->active_dc->samples.size();
if (num_samples >= 2) {
*sample = state->active_dc->samples[num_samples - 2];
sample->pressure[0].mbar = 0;
sample->pressure[1].mbar = 0;
} else {
sample->sensor[0] = sanitize_sensor_id(state->active_dive, !state->o2pressure_sensor);
sample->sensor[1] = sanitize_sensor_id(state->active_dive, state->o2pressure_sensor);
sample->sensor[0] = sanitize_sensor_id(state->active_dive.get(), !state->o2pressure_sensor);
sample->sensor[1] = sanitize_sensor_id(state->active_dive.get(), state->o2pressure_sensor);
}
return sample;
}
@ -722,7 +709,6 @@ static void sample_parser(char *line, struct git_parser_state *state)
line = parse_sample_unit(sample, val, line);
}
}
finish_sample(state->active_dc);
}
static void parse_dc_airtemp(char *line, struct git_parser_state *state)
@ -755,7 +741,7 @@ static void parse_dc_meandepth(char *line, struct git_parser_state *state)
{ state->active_dc->meandepth = get_depth(line); }
static void parse_dc_model(char *, struct git_parser_state *state)
{ state->active_dc->model = get_first_converted_string_c(state); }
{ state->active_dc->model = get_first_converted_string(state); }
static void parse_dc_numberofoxygensensors(char *line, struct git_parser_state *state)
{ state->active_dc->no_o2sensors = get_index(line); }
@ -795,7 +781,7 @@ static int get_divemode(const char *divemodestring) {
struct parse_event {
std::string name;
int has_divemode = false;
struct event ev = { 0 };
struct event ev;
};
static void parse_event_keyvalue(void *_parse, const char *key, const std::string &value)
@ -833,14 +819,13 @@ static void parse_dc_keyvalue(char *line, struct git_parser_state *state)
if (state->converted_strings.size() != 2)
return;
add_extra_data(state->active_dc, state->converted_strings[0].c_str(), state->converted_strings[1].c_str());
add_extra_data(state->active_dc, state->converted_strings[0], state->converted_strings[1]);
}
static void parse_dc_event(char *line, struct git_parser_state *state)
{
int m, s = 0;
struct parse_event p;
struct event *ev;
m = strtol(line, &line, 10);
if (*line == ':')
@ -860,17 +845,17 @@ static void parse_dc_event(char *line, struct git_parser_state *state)
if (p.has_divemode && p.name != "modechange")
p.name = "modechange";
ev = add_event(state->active_dc, p.ev.time.seconds, p.ev.type, p.ev.flags, p.ev.value, p.name.c_str());
struct event *ev = add_event(state->active_dc, p.ev.time.seconds, p.ev.type, p.ev.flags, p.ev.value, p.name.c_str());
/*
* Older logs might mark the dive to be CCR by having an "SP change" event at time 0:00.
* Better to mark them being CCR on import so no need for special treatments elsewhere on
* the code.
*/
if (ev && p.ev.time.seconds == 0 && p.ev.type == SAMPLE_EVENT_PO2 && p.ev.value && state->active_dc->divemode==OC)
if (p.ev.time.seconds == 0 && p.ev.type == SAMPLE_EVENT_PO2 && p.ev.value && state->active_dc->divemode==OC)
state->active_dc->divemode = CCR;
if (ev && event_is_gaschange(ev)) {
if (ev->is_gaschange()) {
/*
* We subtract one here because "0" is "no index",
* and the parsing will add one for actual cylinder
@ -891,10 +876,10 @@ static void parse_trip_time(char *, struct git_parser_state *)
{ }
static void parse_trip_location(char *, struct git_parser_state *state)
{ state->active_trip->location = get_first_converted_string_c(state); }
{ state->active_trip->location = get_first_converted_string(state); }
static void parse_trip_notes(char *, struct git_parser_state *state)
{ state->active_trip->notes = get_first_converted_string_c(state); }
{ state->active_trip->notes = get_first_converted_string(state); }
static void parse_settings_autogroup(char *, struct git_parser_state *state)
{
@ -1044,13 +1029,13 @@ static void parse_settings_fingerprint(char *line, struct git_parser_state *stat
}
if (verbose > 1)
report_info("fingerprint %08x %08x %08x %08x %s\n", fph.model, fph.serial, fph.fdeviceid, fph.fdiveid, fph.hex_data.c_str());
create_fingerprint_node_from_hex(&fingerprint_table, fph.model, fph.serial,
fph.hex_data.c_str(), fph.fdeviceid, fph.fdiveid);
create_fingerprint_node_from_hex(fingerprints, fph.model, fph.serial,
fph.hex_data, fph.fdeviceid, fph.fdiveid);
}
static void parse_picture_filename(char *, struct git_parser_state *state)
{
state->active_pic.filename = get_first_converted_string_c(state);
state->active_pic.filename = get_first_converted_string(state);
}
static void parse_picture_gps(char *line, struct git_parser_state *state)
@ -1185,10 +1170,10 @@ static void parse_filter_preset_constraint(char *line, struct git_parser_state *
line = parse_keyvalue_entry(parse_filter_preset_constraint_keyvalue, state, line, state);
}
filter_preset_add_constraint(state->active_filter.get(), state->filter_constraint_type.c_str(),
state->filter_constraint_string_mode.c_str(),
state->filter_constraint_range_mode.c_str(),
state->filter_constraint_negate, state->filter_constraint_data.c_str());
state->active_filter->add_constraint(state->filter_constraint_type,
state->filter_constraint_string_mode,
state->filter_constraint_range_mode,
state->filter_constraint_negate, state->filter_constraint_data);
state->filter_constraint_type.clear();
state->filter_constraint_string_mode.clear();
state->filter_constraint_range_mode.clear();
@ -1222,14 +1207,14 @@ static void parse_filter_preset_fulltext(char *line, struct git_parser_state *st
line = parse_keyvalue_entry(parse_filter_preset_fulltext_keyvalue, state, line, state);
}
filter_preset_set_fulltext(state->active_filter.get(), state->fulltext_query.c_str(), state->fulltext_mode.c_str());
state->active_filter.get()->set_fulltext(std::move(state->fulltext_query), state->fulltext_mode);
state->fulltext_mode.clear();
state->fulltext_query.clear();
}
static void parse_filter_preset_name(char *, struct git_parser_state *state)
{
filter_preset_set_name(state->active_filter.get(), get_first_converted_string_c(state));
state->active_filter->name = get_first_converted_string(state);
}
/* These need to be sorted! */
@ -1383,33 +1368,27 @@ static void for_each_line(git_blob *blob, line_fn_t *fn, struct git_parser_state
static void finish_active_trip(struct git_parser_state *state)
{
dive_trip_t *trip = state->active_trip;
auto &trip = state->active_trip;
if (trip) {
state->active_trip = NULL;
insert_trip(trip, state->log->trips);
}
if (trip)
state->log->trips.put(std::move(trip));
}
static void finish_active_dive(struct git_parser_state *state)
{
struct dive *dive = state->active_dive;
if (dive) {
state->active_dive = NULL;
record_dive_to_table(dive, state->log->dives);
}
if (state->active_dive)
state->log->dives.record_dive(std::move(state->active_dive));
}
static void create_new_dive(timestamp_t when, struct git_parser_state *state)
{
state->active_dive = alloc_dive();
state->active_dive = std::make_unique<dive>();
/* We'll fill in more data from the dive file */
state->active_dive->when = when;
if (state->active_trip)
add_dive_to_trip(state->active_dive, state->active_trip);
state->active_trip->add_dive(state->active_dive.get());
}
static bool validate_date(int yyyy, int mm, int dd)
@ -1439,7 +1418,7 @@ static int dive_trip_directory(const char *root, const char *name, struct git_pa
if (!validate_date(yyyy, mm, dd))
return GIT_WALK_SKIP;
finish_active_trip(state);
state->active_trip = alloc_trip();
state->active_trip = std::make_unique<dive_trip>();
return GIT_WALK_OK;
}
@ -1649,17 +1628,12 @@ static git_blob *git_tree_entry_blob(git_repository *repo, const git_tree_entry
static struct divecomputer *create_new_dc(struct dive *dive)
{
struct divecomputer *dc = &dive->dc;
struct divecomputer *dc = &dive->dcs.back();
while (dc->next)
dc = dc->next;
/* Did we already fill that in? */
if (dc->samples || dc->model || dc->when) {
struct divecomputer *newdc = (divecomputer *)calloc(1, sizeof(*newdc));
if (!newdc)
return NULL;
dc->next = newdc;
dc = newdc;
if (!dc->samples.empty() || !dc->model.empty() || dc->when) {
dive->dcs.emplace_back();
dc = &dive->dcs.back();
}
dc->when = dive->when;
dc->duration = dive->duration;
@ -1679,7 +1653,7 @@ static int parse_divecomputer_entry(struct git_parser_state *state, const git_tr
if (!blob)
return report_error("Unable to read divecomputer file");
state->active_dc = create_new_dc(state->active_dive);
state->active_dc = create_new_dc(state->active_dive.get());
for_each_line(blob, divecomputer_parser, state);
git_blob_free(blob);
state->active_dc = NULL;
@ -1694,13 +1668,12 @@ static int parse_divecomputer_entry(struct git_parser_state *state, const git_tr
*/
static int parse_dive_entry(struct git_parser_state *state, const git_tree_entry *entry, const char *suffix)
{
struct dive *dive = state->active_dive;
git_blob *blob = git_tree_entry_blob(state->repo, entry);
if (!blob)
return report_error("Unable to read dive file");
if (*suffix)
dive->number = atoi(suffix + 1);
clear_weightsystem_table(&state->active_dive->weightsystems);
state->active_dive->number = atoi(suffix + 1);
state->active_dive->weightsystems.clear();
state->o2pressure_sensor = 1;
for_each_line(blob, dive_parser, state);
git_blob_free(blob);
@ -1712,7 +1685,7 @@ static int parse_site_entry(struct git_parser_state *state, const git_tree_entry
if (*suffix == '\0')
return report_error("Dive site without uuid");
uint32_t uuid = strtoul(suffix, NULL, 16);
state->active_site = alloc_or_get_dive_site(uuid, state->log->sites);
state->active_site = state->log->sites.alloc_or_get(uuid);
git_blob *blob = git_tree_entry_blob(state->repo, entry);
if (!blob)
return report_error("Unable to read dive site file");
@ -1768,12 +1741,12 @@ static int parse_picture_entry(struct git_parser_state *state, const git_tree_en
state->active_pic.offset.seconds = offset;
for_each_line(blob, picture_parser, state);
add_picture(&state->active_dive->pictures, state->active_pic);
add_picture(state->active_dive->pictures, std::move(state->active_pic));
git_blob_free(blob);
/* add_picture took ownership of the data -
* clear out our copy just to be sure. */
state->active_pic = empty_picture;
state->active_pic = picture();
return 0;
}
@ -1788,7 +1761,7 @@ static int parse_filter_preset(struct git_parser_state *state, const git_tree_en
git_blob_free(blob);
add_filter_preset_to_table(state->active_filter.get(), state->log->filter_presets);
state->log->filter_presets.add(*state->active_filter);
state->active_filter.reset();
return 0;
@ -1796,8 +1769,8 @@ static int parse_filter_preset(struct git_parser_state *state, const git_tree_en
static int walk_tree_file(const char *root, const git_tree_entry *entry, struct git_parser_state *state)
{
struct dive *dive = state->active_dive;
dive_trip_t *trip = state->active_trip;
auto &dive = state->active_dive;
auto &trip = state->active_trip;
const char *name = git_tree_entry_name(entry);
if (verbose > 1)
report_info("git load handling file %s\n", name);
@ -1827,7 +1800,7 @@ static int walk_tree_file(const char *root, const git_tree_entry *entry, struct
return parse_settings_entry(state, entry);
break;
}
report_error("Unknown file %s%s (%p %p)", root, name, dive, trip);
report_error("Unknown file %s%s (%p %p)", root, name, dive.get(), trip.get());
return GIT_WALK_SKIP;
}
@ -1850,12 +1823,12 @@ static int load_dives_from_tree(git_repository *repo, git_tree *tree, struct git
return 0;
}
extern "C" void clear_git_id(void)
void clear_git_id()
{
saved_git_id.clear();
}
extern "C" void set_git_id(const struct git_oid *id)
void set_git_id(const struct git_oid *id)
{
char git_id_buffer[GIT_OID_HEXSZ + 1];

View File

@ -1,7 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/* macos.c */
/* implements Mac OS X specific functions */
#include "ssrf.h"
#include <stdlib.h>
#include <dirent.h>
#include <fnmatch.h>
@ -48,13 +47,11 @@ static std::string make_default_filename()
return system_default_path() + "/" + user + ".xml";
}
extern "C" {
const char mac_system_divelist_default_font[] = "Arial";
const char *system_divelist_default_font = mac_system_divelist_default_font;
double system_divelist_default_font_size = -1.0;
void subsurface_OS_pref_setup(void)
void subsurface_OS_pref_setup()
{
// nothing
}
@ -65,13 +62,13 @@ bool subsurface_ignore_font(const char *)
return false;
}
const char *system_default_directory(void)
const char *system_default_directory()
{
static const std::string path = system_default_path();
return path.c_str();
}
const char *system_default_filename(void)
const char *system_default_filename()
{
static const std::string fn = make_default_filename();
return fn.c_str();
@ -185,12 +182,12 @@ int subsurface_zip_close(struct zip *zip)
}
/* win32 console */
void subsurface_console_init(void)
void subsurface_console_init()
{
/* NOP */
}
void subsurface_console_exit(void)
void subsurface_console_exit()
{
/* NOP */
}
@ -199,5 +196,3 @@ bool subsurface_user_is_root()
{
return geteuid() == 0;
}
}

Some files were not shown because too many files have changed in this diff Show More