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
277 changed files with 10436 additions and 13735 deletions

View File

@ -10,7 +10,7 @@ ColumnLimit: 0
ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 8 ConstructorInitializerIndentWidth: 8
ContinuationIndentWidth: 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 IndentFunctionDeclarationAfterType: false #personal taste, good for long methods
IndentWidth: 8 IndentWidth: 8
MaxEmptyLinesToKeep: 2 MaxEmptyLinesToKeep: 2

View File

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

View File

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

View File

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

View File

@ -134,9 +134,9 @@ void addDiveSite(const QString &name)
execute(new AddDiveSite(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) 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)); 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) void addGasSwitch(struct dive *d, int dcNr, int seconds, int tank)

View File

@ -4,7 +4,7 @@
#include "core/divelog.h" #include "core/divelog.h"
#include "core/equipment.h" #include "core/equipment.h"
#include "core/pictureobj.h" #include "core/picture.h"
#include "core/taxonomy.h" #include "core/taxonomy.h"
#include <QVector> #include <QVector>
#include <QAction> #include <QAction>
@ -68,7 +68,7 @@ void editDiveSiteCountry(dive_site *ds, const QString &value);
void editDiveSiteLocation(dive_site *ds, location_t 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 editDiveSiteTaxonomy(dive_site *ds, taxonomy_data &value); // value is consumed (i.e. will be erased after call)!
void addDiveSite(const QString &name); 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 mergeDiveSites(dive_site *ds, const QVector<dive_site *> &sites);
void purgeUnusedDiveSites(); void purgeUnusedDiveSites();
@ -132,8 +132,8 @@ void editTripNotes(dive_trip *trip, const QString &s);
void addEventBookmark(struct dive *d, int dcNr, int seconds); void addEventBookmark(struct dive *d, int dcNr, int seconds);
void addEventDivemodeSwitch(struct dive *d, int dcNr, int seconds, int divemode); void addEventDivemodeSwitch(struct dive *d, int dcNr, int seconds, int divemode);
void addEventSetpointChange(struct dive *d, int dcNr, int seconds, pressure_t pO2); 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 renameEvent(struct dive *d, int dcNr, int idx, std::string name);
void removeEvent(struct dive *d, int dcNr, struct event *ev); void removeEvent(struct dive *d, int dcNr, int idx);
void addGasSwitch(struct dive *d, int dcNr, int seconds, int tank); void addGasSwitch(struct dive *d, int dcNr, int seconds, int tank);
// 7) Picture (media) commands // 7) Picture (media) commands
@ -144,7 +144,7 @@ struct PictureListForDeletion {
}; };
struct PictureListForAddition { struct PictureListForAddition {
dive *d; dive *d;
std::vector<PictureObj> pics; std::vector<picture> pics;
}; };
void setPictureOffset(dive *d, const QString &filename, offset_t offset); void setPictureOffset(dive *d, const QString &filename, offset_t offset);
void removePictures(const std::vector<PictureListForDeletion> &pictures); 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 getListOfDives(const std::vector<struct dive*> &dives)
{ {
QString listOfDives; QString listOfDives;
if ((int)dives.size() == divelog.dives->nr) if (dives.size() == divelog.dives.size())
return Base::tr("all dives"); return Base::tr("all dives");
int i = 0; int i = 0;
for (dive *d: dives) { for (dive *d: dives) {

View File

@ -7,7 +7,6 @@
#include "core/divesite.h" #include "core/divesite.h"
#include "core/trip.h" #include "core/trip.h"
#include "core/dive.h" #include "core/dive.h"
#include "core/owning_ptrs.h"
#include <QUndoCommand> #include <QUndoCommand>
#include <QCoreApplication> // For Q_DECLARE_TR_FUNCTIONS #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. // 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. // 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 // To take ownership of dives/trips, std::unique_ptr<>s are used.
// are simply derived from std::unique_ptr and therefore use well-established semantics. // Expressed in C-terms: std::unique_ptr<T> is the same as T* with the following
// Expressed in C-terms: std::unique_ptr<T> is exactly the same as T* with the following
// twists: // twists:
// 1) default-initialized to NULL. // 1) default-initialized to NULL.
// 2) if it goes out of scope (local scope or containing object destroyed), it does: // 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. // move-semantics and Qt's containers are incompatible, owing to COW semantics.
// //
// Usage: // Usage:
// OwningDivePtr dPtr; // Initialize to null-state: not owning any dive. // std::unique_ptr<dive> 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(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(). // // 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 = 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. // 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. // dPtr.reset(); // Delete currently owned dive and reset to null.
// dPtr2 = dPtr1; // Fails to compile. // dPtr2 = dPtr1; // Fails to compile.
// dPtr2 = std::move(dPtr1); // dPtr2 takes ownership, dPtr1 is reset to null. // 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 // dPtr1 = fun(); // Compiles. Simply put: the compiler knows that the result of fun() will
// // be trashed and therefore can be moved-from. // // 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 // 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(). // // 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, // 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) if (index == -1)
return; 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() bool EditDeviceNickname::workToBeDone()
{ {
return get_device(divelog.devices, index) != nullptr; return index >= 0;
} }
void EditDeviceNickname::redo() void EditDeviceNickname::redo()
{ {
device *dev = get_device_mutable(divelog.devices, index); if (index < 0 || static_cast<size_t>(index) >= divelog.devices.size())
if (!dev)
return; return;
std::swap(dev->nickName, nickname); std::swap(divelog.devices[index].nickName, nickname);
emit diveListNotifier.deviceEdited(); emit diveListNotifier.deviceEdited();
} }

View File

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

View File

@ -15,16 +15,16 @@ namespace Command {
// This helper structure describes a dive that we want to add. // This helper structure describes a dive that we want to add.
struct DiveToAdd { struct DiveToAdd {
OwningDivePtr dive; // Dive to add std::unique_ptr<struct dive> dive; // Dive to add
dive_trip *trip; // Trip the dive belongs to, may be null dive_trip *trip; // Trip the dive belongs to, may be null
dive_site *site; // Site the dive is associated with, 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 // Multiple trips, dives and dive sites that have to be added for a command
struct DivesAndTripsToAdd { struct DivesAndTripsToAdd {
std::vector<DiveToAdd> dives; std::vector<DiveToAdd> dives;
std::vector<OwningTripPtr> trips; std::vector<std::unique_ptr<dive_trip>> trips;
std::vector<OwningDiveSitePtr> sites; std::vector<std::unique_ptr<dive_site>> sites;
}; };
// Dives and sites that have to be removed for a command // Dives and sites that have to be removed for a command
@ -48,7 +48,7 @@ struct DiveToTrip
struct DivesToTrip struct DivesToTrip
{ {
std::vector<DiveToTrip> divesToMove; // If dive_trip is null, remove from trip 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 // All divelist commands derive from a common base class. It keeps track
@ -58,7 +58,7 @@ struct DivesToTrip
class DiveListBase : public Base { class DiveListBase : public Base {
protected: protected:
// These are helper functions to add / remove dive from the C-core structures. // 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); dive *addDive(DiveToAdd &d);
DivesAndTripsToAdd removeDives(DivesAndSitesToRemove &divesAndSitesToDelete); DivesAndTripsToAdd removeDives(DivesAndSitesToRemove &divesAndSitesToDelete);
DivesAndSitesToRemove addDives(DivesAndTripsToAdd &toAdd); DivesAndSitesToRemove addDives(DivesAndTripsToAdd &toAdd);
@ -108,10 +108,10 @@ private:
// For redo and undo // For redo and undo
DivesAndTripsToAdd divesToAdd; DivesAndTripsToAdd divesToAdd;
DivesAndSitesToRemove divesAndSitesToRemove; DivesAndSitesToRemove divesAndSitesToRemove;
struct device_table devicesToAddAndRemove; device_table devicesToAddAndRemove;
// For redo // For redo
std::vector<OwningDiveSitePtr> sitesToAdd; std::vector<std::unique_ptr<dive_site>> sitesToAdd;
std::vector<std::pair<std::string,FilterData>> std::vector<std::pair<std::string,FilterData>>
filterPresetsToAdd; filterPresetsToAdd;
@ -133,7 +133,7 @@ private:
// For redo // For redo
DivesAndSitesToRemove divesToDelete; DivesAndSitesToRemove divesToDelete;
std::vector<OwningTripPtr> tripsToAdd; std::vector<std::unique_ptr<dive_trip>> tripsToAdd;
DivesAndTripsToAdd divesToAdd; DivesAndTripsToAdd divesToAdd;
}; };
@ -196,7 +196,7 @@ struct MergeTrips : public TripBase {
class SplitDivesBase : public DiveListBase { class SplitDivesBase : public DiveListBase {
protected: protected:
SplitDivesBase(dive *old, std::array<dive *, 2> newDives); SplitDivesBase(dive *old, std::array<std::unique_ptr<dive>, 2> newDives);
private: private:
void undoit() override; void undoit() override;
void redoit() override; void redoit() override;
@ -237,7 +237,7 @@ class DiveComputerBase : public DiveListBase {
protected: protected:
// old_dive must be a dive known to the core. // old_dive must be a dive known to the core.
// new_dive must be new dive whose ownership is taken. // 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: private:
void undoit() override; void undoit() override;
void redoit() 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 // 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. // 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; std::vector<dive_site *> res;
QVector<dive *> changedDives; QVector<dive *> changedDives;
res.reserve(sites.size()); res.reserve(sites.size());
for (OwningDiveSitePtr &ds: sites) { for (std::unique_ptr<dive_site> &ds: sites) {
// Readd the dives that belonged to this site // 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 // TODO: send dive site changed signal
struct dive *d = ds->dives.dives[i];
d->dive_site = ds.get(); d->dive_site = ds.get();
changedDives.push_back(d); changedDives.push_back(d);
} }
// Add dive site to core, but remember a non-owning pointer first. // Add dive site to core, but remember a non-owning pointer first.
res.push_back(ds.get()); auto add_res = divelog.sites.put(std::move(ds)); // Return ownership to backend.
int idx = register_dive_site(ds.release()); // Return ownership to backend. res.push_back(add_res.ptr);
emit diveListNotifier.diveSiteAdded(res.back(), idx); // Inform frontend of new dive site. emit diveListNotifier.diveSiteAdded(res.back(), add_res.idx); // Inform frontend of new dive site.
} }
emit diveListNotifier.divesChanged(changedDives, DiveField::DIVESITE); 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 // 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 // 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. // 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; QVector<dive *> changedDives;
res.reserve(sites.size()); res.reserve(sites.size());
for (dive_site *ds: sites) { for (dive_site *ds: sites) {
// Reset the dive_site field of the affected dives // Reset the dive_site field of the affected dives
for (int i = 0; i < ds->dives.nr; ++i) { for (dive *d: ds->dives) {
struct dive *d = ds->dives.dives[i];
d->dive_site = nullptr; d->dive_site = nullptr;
changedDives.push_back(d); changedDives.push_back(d);
} }
// Remove dive site from core and take ownership. // Remove dive site from core and take ownership.
int idx = unregister_dive_site(ds); auto pull_res = divelog.sites.pull(ds);
res.emplace_back(ds); res.push_back(std::move(pull_res.ptr));
emit diveListNotifier.diveSiteDeleted(ds, idx); // Inform frontend of removed dive site. emit diveListNotifier.diveSiteDeleted(ds, pull_res.idx); // Inform frontend of removed dive site.
} }
emit diveListNotifier.divesChanged(changedDives, DiveField::DIVESITE); emit diveListNotifier.divesChanged(changedDives, DiveField::DIVESITE);
@ -77,8 +75,8 @@ static std::vector<OwningDiveSitePtr> removeDiveSites(std::vector<dive_site *> &
AddDiveSite::AddDiveSite(const QString &name) AddDiveSite::AddDiveSite(const QString &name)
{ {
setText(Command::Base::tr("add dive site")); setText(Command::Base::tr("add dive site"));
sitesToAdd.emplace_back(alloc_dive_site()); sitesToAdd.push_back(std::make_unique<dive_site>());
sitesToAdd.back()->name = copy_qstring(name); sitesToAdd.back()->name = name.toStdString();
} }
bool AddDiveSite::workToBeDone() bool AddDiveSite::workToBeDone()
@ -96,25 +94,17 @@ void AddDiveSite::undo()
sitesToAdd = removeDiveSites(sitesToRemove); 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)); setText(Command::Base::tr("import dive sites from %1").arg(source));
for (int i = 0; i < sites->nr; ++i) { for (auto &new_ds: sites) {
struct dive_site *new_ds = sites->dive_sites[i]; // Don't import dive sites that already exist.
// We might want to be smarter here and merge dive site data, etc.
// Don't import dive sites that already exist. Currently we only check for if (divelog.sites.get_same(*new_ds))
// 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);
continue; continue;
} sitesToAdd.push_back(std::move(new_ds));
sitesToAdd.emplace_back(new_ds);
} }
// All site have been consumed
sites->nr = 0;
} }
bool ImportDiveSites::workToBeDone() bool ImportDiveSites::workToBeDone()
@ -155,10 +145,9 @@ void DeleteDiveSites::undo()
PurgeUnusedDiveSites::PurgeUnusedDiveSites() PurgeUnusedDiveSites::PurgeUnusedDiveSites()
{ {
setText(Command::Base::tr("purge unused dive sites")); setText(Command::Base::tr("purge unused dive sites"));
for (int i = 0; i < divelog.sites->nr; ++i) { for (const auto &ds: divelog.sites) {
dive_site *ds = divelog.sites->dive_sites[i]; if (ds->dives.empty())
if (ds->dives.nr == 0) sitesToRemove.push_back(ds.get());
sitesToRemove.push_back(ds);
} }
} }
@ -177,24 +166,15 @@ void PurgeUnusedDiveSites::undo()
sitesToRemove = addDiveSites(sitesToAdd); 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), EditDiveSiteName::EditDiveSiteName(dive_site *dsIn, const QString &name) : ds(dsIn),
value(name) value(name.toStdString())
{ {
setText(Command::Base::tr("Edit dive site name")); setText(Command::Base::tr("Edit dive site name"));
} }
bool EditDiveSiteName::workToBeDone() bool EditDiveSiteName::workToBeDone()
{ {
return value != QString(ds->name); return value != ds->name;
} }
void EditDiveSiteName::redo() void EditDiveSiteName::redo()
@ -210,14 +190,14 @@ void EditDiveSiteName::undo()
} }
EditDiveSiteDescription::EditDiveSiteDescription(dive_site *dsIn, const QString &description) : ds(dsIn), EditDiveSiteDescription::EditDiveSiteDescription(dive_site *dsIn, const QString &description) : ds(dsIn),
value(description) value(description.toStdString())
{ {
setText(Command::Base::tr("Edit dive site description")); setText(Command::Base::tr("Edit dive site description"));
} }
bool EditDiveSiteDescription::workToBeDone() bool EditDiveSiteDescription::workToBeDone()
{ {
return value != QString(ds->description); return value != ds->description;
} }
void EditDiveSiteDescription::redo() void EditDiveSiteDescription::redo()
@ -233,14 +213,14 @@ void EditDiveSiteDescription::undo()
} }
EditDiveSiteNotes::EditDiveSiteNotes(dive_site *dsIn, const QString &notes) : ds(dsIn), EditDiveSiteNotes::EditDiveSiteNotes(dive_site *dsIn, const QString &notes) : ds(dsIn),
value(notes) value(notes.toStdString())
{ {
setText(Command::Base::tr("Edit dive site notes")); setText(Command::Base::tr("Edit dive site notes"));
} }
bool EditDiveSiteNotes::workToBeDone() bool EditDiveSiteNotes::workToBeDone()
{ {
return value != QString(ds->notes); return value != ds->notes;
} }
void EditDiveSiteNotes::redo() void EditDiveSiteNotes::redo()
@ -256,20 +236,20 @@ void EditDiveSiteNotes::undo()
} }
EditDiveSiteCountry::EditDiveSiteCountry(dive_site *dsIn, const QString &country) : ds(dsIn), EditDiveSiteCountry::EditDiveSiteCountry(dive_site *dsIn, const QString &country) : ds(dsIn),
value(country) value(country.toStdString())
{ {
setText(Command::Base::tr("Edit dive site country")); setText(Command::Base::tr("Edit dive site country"));
} }
bool EditDiveSiteCountry::workToBeDone() bool EditDiveSiteCountry::workToBeDone()
{ {
return !same_string(qPrintable(value), taxonomy_get_country(&ds->taxonomy)); return value == taxonomy_get_country(ds->taxonomy);
} }
void EditDiveSiteCountry::redo() void EditDiveSiteCountry::redo()
{ {
QString old = taxonomy_get_country(&ds->taxonomy); std::string old = taxonomy_get_country(ds->taxonomy);
taxonomy_set_country(&ds->taxonomy, qPrintable(value), taxonomy_origin::GEOMANUAL); taxonomy_set_country(ds->taxonomy, value, taxonomy_origin::GEOMANUAL);
value = old; value = old;
emit diveListNotifier.diveSiteChanged(ds, LocationInformationModel::TAXONOMY); // Inform frontend of changed dive site. 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); bool old_ok = has_location(&ds->location);
if (ok != old_ok) if (ok != old_ok)
return true; return true;
return ok && !same_location(&value, &ds->location); return ok && value != ds->location;
} }
void EditDiveSiteLocation::redo() void EditDiveSiteLocation::redo()
@ -310,14 +290,11 @@ void EditDiveSiteLocation::undo()
EditDiveSiteTaxonomy::EditDiveSiteTaxonomy(dive_site *dsIn, taxonomy_data &taxonomy) : ds(dsIn), EditDiveSiteTaxonomy::EditDiveSiteTaxonomy(dive_site *dsIn, taxonomy_data &taxonomy) : ds(dsIn),
value(taxonomy) 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")); setText(Command::Base::tr("Edit dive site taxonomy"));
} }
EditDiveSiteTaxonomy::~EditDiveSiteTaxonomy() EditDiveSiteTaxonomy::~EditDiveSiteTaxonomy()
{ {
free_taxonomy(&value);
} }
bool EditDiveSiteTaxonomy::workToBeDone() bool EditDiveSiteTaxonomy::workToBeDone()
@ -364,10 +341,10 @@ void MergeDiveSites::redo()
// The dives of the above dive sites were reset to no dive sites. // The dives of the above dive sites were reset to no dive sites.
// Add them to the merged-into dive site. Thankfully, we remember // Add them to the merged-into dive site. Thankfully, we remember
// the dives in the sitesToAdd vector. // the dives in the sitesToAdd vector.
for (const OwningDiveSitePtr &site: sitesToAdd) { for (const std::unique_ptr<dive_site> &site: sitesToAdd) {
for (int i = 0; i < site->dives.nr; ++i) { for (dive *d: site->dives) {
add_dive_to_dive_site(site->dives.dives[i], ds); ds->add_dive(d);
divesChanged.push_back(site->dives.dives[i]); divesChanged.push_back(d);
} }
} }
emit diveListNotifier.divesChanged(divesChanged, DiveField::DIVESITE); 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 // Before readding the dive sites, unregister the corresponding dives so that they can be
// readded to their old dive sites. // readded to their old dive sites.
for (const OwningDiveSitePtr &site: sitesToAdd) { for (const std::unique_ptr<dive_site> &site: sitesToAdd) {
for (int i = 0; i < site->dives.nr; ++i) { for (dive *d: site->dives) {
unregister_dive_from_dive_site(site->dives.dives[i]); unregister_dive_from_dive_site(d);
divesChanged.push_back(site->dives.dives[i]); divesChanged.push_back(d);
} }
} }
@ -405,9 +382,9 @@ ApplyGPSFixes::ApplyGPSFixes(const std::vector<DiveAndLocation> &fixes)
siteLocations.push_back({ ds, dl.location }); siteLocations.push_back({ ds, dl.location });
} }
} else { } else {
ds = create_dive_site(qPrintable(dl.name), divelog.sites); ds = divelog.sites.create(dl.name.toStdString());
ds->location = dl.location; 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() dl.d->dive_site = nullptr; // This will be set on redo()
sitesToAdd.emplace_back(ds); sitesToAdd.emplace_back(ds);
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,15 +6,12 @@
#include "command_base.h" #include "command_base.h"
#include "core/divemode.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 // We put everything in a namespace, so that we can shorten names without polluting the global namespace
namespace Command { namespace Command {
// Events are a strange thing: they contain there own description which means // Pointers to events are not stable, so we always store indexes.
// 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
class EventBase : public Base { class EventBase : public Base {
protected: protected:
@ -25,8 +22,7 @@ protected:
virtual void undoit() = 0; virtual void undoit() = 0;
// Note: we store dive and the divecomputer-number instead of a pointer to the divecomputer. // 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 // Pointers to divecomputers are not stable.
// are probably not stable.
struct dive *d; struct dive *d;
int dcNr; int dcNr;
private: private:
@ -35,15 +31,15 @@ private:
class AddEventBase : public EventBase { class AddEventBase : public EventBase {
public: 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: protected:
void undoit() override; void undoit() override;
void redoit() override; void redoit() override;
private: private:
bool workToBeDone() override; bool workToBeDone() override;
OwningEventPtr eventToAdd; // for redo struct event ev; // for redo
event *eventToRemove; // for undo int idx; // for undo
}; };
class AddEventBookmark : public AddEventBase { class AddEventBookmark : public AddEventBase {
@ -67,28 +63,28 @@ private:
class RenameEvent : public EventBase { class RenameEvent : public EventBase {
public: 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: private:
bool workToBeDone() override; bool workToBeDone() override;
void undoit() override; void undoit() override;
void redoit() override; void redoit() override;
OwningEventPtr eventToAdd; // for undo and redo int idx; // for undo and redo
event *eventToRemove; // for undo and redo std::string name; // for undo and redo
}; };
class RemoveEvent : public EventBase { class RemoveEvent : public EventBase {
public: public:
RemoveEvent(struct dive *d, int dcNr, struct event *ev); RemoveEvent(struct dive *d, int dcNr, int idx);
private: private:
bool workToBeDone() override; bool workToBeDone() override;
void undoit() override; void undoit() override;
void redoit() override; void redoit() override;
void post() const; // Called to fix up dives should a gas-change have happened. void post() const; // Called to fix up dives should a gas-change have happened.
OwningEventPtr eventToAdd; // for undo event ev; // for undo
event *eventToRemove; // for redo int idx; // for redo
int cylinder; // affected cylinder (if removing gas switch). <0: not a gas switch. int cylinder; // affected cylinder (if removing gas switch). <0: not a gas switch.
}; };
class AddGasSwitch : public EventBase { class AddGasSwitch : public EventBase {
@ -100,8 +96,8 @@ private:
void redoit() override; void redoit() override;
std::vector<int> cylinders; // cylinders that are modified std::vector<int> cylinders; // cylinders that are modified
std::vector<OwningEventPtr> eventsToAdd; std::vector<event> eventsToAdd;
std::vector<event *> eventsToRemove; std::vector<int> eventsToRemove;
}; };
} // namespace Command } // namespace Command

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -200,7 +200,7 @@ void CheckCloudConnection::gotContinent(QNetworkReply *reply)
} }
// helper to be used from C code // helper to be used from C code
extern "C" bool canReachCloudServer(struct git_info *info) bool canReachCloudServer(struct git_info *info)
{ {
if (verbose) if (verbose)
qWarning() << "Cloud storage: checking connection to cloud server" << info->url.c_str(); 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" #pragma clang diagnostic ignored "-Wmissing-field-initializers"
#endif #endif
#include "ssrf.h"
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.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 ndl = 0;
unsigned int in_deco = 0, deco_ceiling = 0, deco_time = 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; struct sample *sample;
// Initialize stat variables // 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) { while (offset + config.sample_size < size) {
s = samples + offset; s = samples + offset;
// Start with an empty sample
sample = prepare_sample(dc);
sample->time.seconds = sample_cnt * profile_period;
// Check for event // Check for event
if (s[0] & 0x80) { if (s[0] & 0x80) {
cochran_dive_event(dc, s, sample_cnt * profile_period, &in_deco, &deco_ceiling, &deco_time); 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; continue;
} }
// Start with an empty sample
sample = prepare_sample(dc);
sample->time.seconds = sample_cnt * profile_period;
// Depth is in every sample // Depth is in every sample
depth_sample = (double)(s[0] & 0x3F) / 4 * (s[0] & 0x40 ? -1 : 1); depth_sample = (double)(s[0] & 0x3F) / 4 * (s[0] & 0x40 ? -1 : 1);
depth += depth_sample; depth += depth_sample;
@ -592,8 +591,6 @@ static void cochran_parse_samples(struct dive *dive, const unsigned char *log,
sample->sensor[0] = 0; sample->sensor[0] = 0;
sample->pressure[0].mbar = lrint(psi * PSI / 100); sample->pressure[0].mbar = lrint(psi * PSI / 100);
finish_sample(dc);
offset += config.sample_size; offset += config.sample_size;
sample_cnt++; 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, static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
const unsigned char *in, unsigned size, const unsigned char *in, unsigned size,
struct dive_table *table) struct dive_table &table)
{ {
unsigned char *buf = (unsigned char *)malloc(size); unsigned char *buf = (unsigned char *)malloc(size);
struct dive *dive;
struct divecomputer *dc; struct divecomputer *dc;
struct tm tm = {0}; struct tm tm = {0};
uint32_t csum[5];
double max_depth, avg_depth, min_temp; double max_depth, avg_depth, min_temp;
unsigned int duration = 0, corrupt_dive = 0; 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"); puts("\nSample Data\n");
#endif #endif
dive = alloc_dive(); auto dive = std::make_unique<struct dive>();
dc = &dive->dc; dc = &dive->dcs[0];
unsigned char *log = (buf + 0x4914); 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_GEMINI:
case TYPE_COMMANDER: case TYPE_COMMANDER:
if (config.type == TYPE_GEMINI) { if (config.type == TYPE_GEMINI) {
cylinder_t cyl = empty_cylinder;
dc->model = "Gemini"; dc->model = "Gemini";
dc->deviceid = buf[0x18c] * 256 + buf[0x18d]; // serial no 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 cyl.gasmix.o2.permille = (log[CMD_O2_PERCENT] / 256
+ log[CMD_O2_PERCENT + 1]) * 10; + log[CMD_O2_PERCENT + 1]) * 10;
cyl.gasmix.he.permille = 0; cyl.gasmix.he.permille = 0;
add_cylinder(&dive->cylinders, 0, cyl); add_cylinder(&dive->cylinders, 0, std::move(cyl));
} else { } else {
dc->model = "Commander"; dc->model = "Commander";
dc->deviceid = array_uint32_le(buf + 0x31e); // serial no dc->deviceid = array_uint32_le(buf + 0x31e); // serial no
for (g = 0; g < 2; g++) { for (g = 0; g < 2; g++) {
cylinder_t cyl = empty_cylinder; cylinder_t cyl = default_cylinder(dive.get());
fill_default_cylinder(dive, &cyl);
cyl.gasmix.o2.permille = (log[CMD_O2_PERCENT + g * 2] / 256 cyl.gasmix.o2.permille = (log[CMD_O2_PERCENT + g * 2] / 256
+ log[CMD_O2_PERCENT + g * 2 + 1]) * 10; + log[CMD_O2_PERCENT + g * 2 + 1]) * 10;
cyl.gasmix.he.permille = 0; 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); * (double) log[CMD_ALTITUDE] * 250 * FEET, 5.25588) * 1000);
dc->salinity = 10000 + 150 * log[CMD_WATER_CONDUCTIVITY]; dc->salinity = 10000 + 150 * log[CMD_WATER_CONDUCTIVITY];
SHA1(log + CMD_NUMBER, 2, (unsigned char *)csum); dc->diveid = SHA1_uint32(log + CMD_NUMBER, 2);
dc->diveid = csum[0];
if (log[CMD_MAX_DEPTH] == 0xff && log[CMD_MAX_DEPTH + 1] == 0xff) if (log[CMD_MAX_DEPTH] == 0xff && log[CMD_MAX_DEPTH + 1] == 0xff)
corrupt_dive = 1; corrupt_dive = 1;
@ -733,15 +725,14 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
dc->model = "EMC"; dc->model = "EMC";
dc->deviceid = array_uint32_le(buf + 0x31e); // serial no dc->deviceid = array_uint32_le(buf + 0x31e); // serial no
for (g = 0; g < 4; g++) { for (g = 0; g < 4; g++) {
cylinder_t cyl = empty_cylinder; cylinder_t cyl = default_cylinder(dive.get());
fill_default_cylinder(dive, &cyl);
cyl.gasmix.o2.permille = cyl.gasmix.o2.permille =
(log[EMC_O2_PERCENT + g * 2] / 256 (log[EMC_O2_PERCENT + g * 2] / 256
+ log[EMC_O2_PERCENT + g * 2 + 1]) * 10; + log[EMC_O2_PERCENT + g * 2 + 1]) * 10;
cyl.gasmix.he.permille = cyl.gasmix.he.permille =
(log[EMC_HE_PERCENT + g * 2] / 256 (log[EMC_HE_PERCENT + g * 2] / 256
+ log[EMC_HE_PERCENT + g * 2 + 1]) * 10; + 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]; 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); * (double) log[EMC_ALTITUDE] * 250 * FEET, 5.25588) * 1000);
dc->salinity = 10000 + 150 * (log[EMC_WATER_CONDUCTIVITY] & 0x3); dc->salinity = 10000 + 150 * (log[EMC_WATER_CONDUCTIVITY] & 0x3);
SHA1(log + EMC_NUMBER, 2, (unsigned char *)csum); dc->diveid = SHA1_uint32(log + EMC_NUMBER, 2);
dc->diveid = csum[0];
if (log[EMC_MAX_DEPTH] == 0xff && log[EMC_MAX_DEPTH + 1] == 0xff) if (log[EMC_MAX_DEPTH] == 0xff && log[EMC_MAX_DEPTH + 1] == 0xff)
corrupt_dive = 1; 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) if (sample_pre_offset < sample_end_offset && sample_end_offset != 0xffffffff)
sample_size = sample_end_offset - sample_pre_offset; 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, + config.logbook_size, sample_size,
&duration, &max_depth, &avg_depth, &min_temp); &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; dc->duration.seconds = duration;
} }
record_dive_to_table(dive, table); table.record_dive(std::move(dive));
free(buf); 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_START_IDX VELO_STABLE
#define VELOCITY_COLORS 5 #define VELOCITY_COLORS 5
typedef enum { enum color_index_t {
/* SAC colors. Order is important, the SAC_COLORS_START_IDX define above. */ /* SAC colors. Order is important, the SAC_COLORS_START_IDX define above. */
SAC_1, SAC_1,
SAC_2, SAC_2,
@ -145,7 +145,7 @@ typedef enum {
CALC_CEILING_DEEP, CALC_CEILING_DEEP,
TISSUE_PERCENTAGE, TISSUE_PERCENTAGE,
DURATION_LINE DURATION_LINE
} color_index_t; };
QColor getColor(const color_index_t i, bool isGrayscale = false); QColor getColor(const color_index_t i, bool isGrayscale = false);
QColor getSacColor(int sac, int diveSac); 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) bool ConfigureDiveComputer::saveXMLBackup(const QString &fileName, const DeviceDetails &details, device_data_t *data)
{ {
QString xml = ""; QString xml = "";
QString vendor = data->vendor;
QString product = data->product;
QXmlStreamWriter writer(&xml); QXmlStreamWriter writer(&xml);
writer.setAutoFormatting(true); writer.setAutoFormatting(true);
writer.writeStartDocument(); writer.writeStartDocument();
writer.writeStartElement("DiveComputerSettingsBackup"); writer.writeStartElement("DiveComputerSettingsBackup");
writer.writeStartElement("DiveComputer"); writer.writeStartElement("DiveComputer");
writer.writeTextElement("Vendor", vendor); writer.writeTextElement("Vendor", QString::fromStdString(data->vendor));
writer.writeTextElement("Product", product); writer.writeTextElement("Product", QString::fromStdString(data->product));
writer.writeEndElement(); writer.writeEndElement();
writer.writeStartElement("Settings"); writer.writeStartElement("Settings");
writer.writeTextElement("CustomText", details.customText); writer.writeTextElement("CustomText", details.customText);

View File

@ -15,11 +15,11 @@
#include "units.h" #include "units.h"
#include "device.h" #include "device.h"
#include "file.h" #include "file.h"
#include "format.h"
#include "divesite.h" #include "divesite.h"
#include "dive.h" #include "dive.h"
#include "divelog.h" #include "divelog.h"
#include "errorhelper.h" #include "errorhelper.h"
#include "ssrf.h"
#include "tag.h" #include "tag.h"
static unsigned int two_bytes_to_int(unsigned char x, unsigned char y) 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 * Returns libdc's equivalent model number (also from g_models) or zero if
* this a manual dive. * 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; dc_descriptor_t *d = NULL;
int i = 0; int i = 0;
while (model != g_models[i].model_num && g_models[i].model_num != 0xEE) while (model != g_models[i].model_num && g_models[i].model_num != 0xEE)
i++; i++;
dev_data->model = copy_string(g_models[i].name); dev_data.model = g_models[i].name;
dev_data->vendor = (const char *)malloc(strlen(g_models[i].name) + 1); 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.vendor.clear();
dev_data->product = copy_string(strchr(g_models[i].name, ' ') + 1); 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); d = get_descriptor(g_models[i].type, g_models[i].libdc_num);
if (d) if (d)
dev_data->descriptor = d; dev_data.descriptor = d;
else else
return 0; return 0;
return g_models[i].libdc_num; 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. * 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. * 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) { auto it = std::find_if(tank_info_table.begin(), tank_info_table.end(),
const struct tank_info *ti = &tank_info_table.infos[i]; [size] (const tank_info &info) { return info.ml == size; });
if (ti->ml == size) return it != tank_info_table.end() ? it->name : std::string();
return ti->name;
}
return "";
} }
/* /*
@ -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) static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct divelog *log, char *maxbuf)
{ {
int rc, profile_length, libdc_model; int rc, profile_length, libdc_model;
char *tmp_notes_str = NULL;
unsigned char *tmp_string1 = NULL, unsigned char *tmp_string1 = NULL,
*locality = NULL, *locality = NULL,
*dive_point = NULL, *dive_point = NULL,
*compl_buffer, *compl_buffer,
*membuf = runner; *membuf = runner;
char buffer[1024];
unsigned char tmp_1byte; unsigned char tmp_1byte;
unsigned int tmp_2bytes; unsigned int tmp_2bytes;
unsigned long tmp_4bytes; unsigned long tmp_4bytes;
struct dive_site *ds; std::string tmp_notes_str;
char is_nitrox = 0, is_O2 = 0, is_SCR = 0; char is_nitrox = 0, is_O2 = 0, is_SCR = 0;
device_data_t *devdata = (device_data_t *)calloc(1, sizeof(device_data_t)); device_data_t devdata;
devdata->log = log; devdata.log = log;
/* /*
* Parse byte to byte till next dive entry * 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 * Next, Time in minutes since 00:00
*/ */
read_bytes(2); 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 * 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 * Subsurface only have a location variable, so we have to merge DTrak's
* Locality and Dive points. * Locality and Dive points.
*/ */
snprintf(buffer, sizeof(buffer), "%s, %s", locality, dive_point); {
ds = get_dive_site_by_name(buffer, log->sites); std::string buffer2 = std::string((char *)locality) + " " + (char *)dive_point;
if (!ds) struct dive_site *ds = log->sites.get_by_name(buffer2);
ds = create_dive_site(buffer, log->sites); if (!ds)
add_dive_to_dive_site(dt_dive, ds); ds = log->sites.create(buffer2);
ds->add_dive(dt_dive);
}
free(locality); free(locality);
locality = NULL; locality = NULL;
free(dive_point); free(dive_point);
@ -241,19 +240,19 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
read_bytes(1); read_bytes(1);
switch (tmp_1byte) { switch (tmp_1byte) {
case 1: case 1:
dt_dive->dc.surface_pressure.mbar = 1013; dt_dive->dcs[0].surface_pressure.mbar = 1013;
break; break;
case 2: case 2:
dt_dive->dc.surface_pressure.mbar = 932; dt_dive->dcs[0].surface_pressure.mbar = 932;
break; break;
case 3: case 3:
dt_dive->dc.surface_pressure.mbar = 828; dt_dive->dcs[0].surface_pressure.mbar = 828;
break; break;
case 4: case 4:
dt_dive->dc.surface_pressure.mbar = 735; dt_dive->dcs[0].surface_pressure.mbar = 735;
break; break;
default: 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); read_bytes(2);
if (tmp_2bytes != 0x7FFF) 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 * Weather, values table, 0 to 6
* Subsurface don't have this record but we can use tags * Subsurface don't have this record but we can use tags
*/ */
dt_dive->tag_list = NULL; dt_dive->tags.clear();
read_bytes(1); read_bytes(1);
switch (tmp_1byte) { switch (tmp_1byte) {
case 1: 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; break;
case 2: 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; break;
case 3: 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; break;
case 4: 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; break;
case 5: 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; break;
case 6: 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; break;
default: default:
// unknown, do nothing // unknown, do nothing
@ -298,7 +297,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
*/ */
read_bytes(2); read_bytes(2);
if (tmp_2bytes != 0x7FFF) 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 * 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); read_bytes(1);
switch (tmp_1byte) { switch (tmp_1byte) {
case 1: case 1:
dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "No suit")); dt_dive->suit = "No suit";
break; break;
case 2: case 2:
dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Shorty")); dt_dive->suit = "Shorty";
break; break;
case 3: case 3:
dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Combi")); dt_dive->suit = "Combi";
break; break;
case 4: case 4:
dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Wet suit")); dt_dive->suit = "Wet suit";
break; break;
case 5: case 5:
dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Semidry suit")); dt_dive->suit = "Semidry suit";
break; break;
case 6: case 6:
dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Dry suit")); dt_dive->suit = "Dry suit";
break; break;
default: default:
// unknown, do nothing // unknown, do nothing
@ -335,14 +334,14 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
*/ */
read_bytes(2); read_bytes(2);
if (tmp_2bytes != 0x7FFF) { if (tmp_2bytes != 0x7FFF) {
cylinder_t cyl = empty_cylinder; cylinder_t cyl;
cyl.type.size.mliter = tmp_2bytes * 10; cyl.type.size.mliter = tmp_2bytes * 10;
cyl.type.description = cyl_type_by_size(tmp_2bytes * 10); cyl.type.description = cyl_type_by_size(tmp_2bytes * 10);
cyl.start.mbar = 200000; cyl.start.mbar = 200000;
cyl.gasmix.he.permille = 0; cyl.gasmix.he.permille = 0;
cyl.gasmix.o2.permille = 210; cyl.gasmix.o2.permille = 210;
cyl.manually_added = true; 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); read_bytes(2);
if (tmp_2bytes != 0x7FFF) 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. * Dive time in minutes.
*/ */
read_bytes(2); read_bytes(2);
if (tmp_2bytes != 0x7FFF) 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 * 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); read_bytes(2);
if (tmp_2bytes != 0x7fff) 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 else
dt_dive->watertemp.mkelvin = 0; 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. * Air used in bar*100.
*/ */
read_bytes(2); 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)); 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); read_bytes(1);
if (bit_set(tmp_1byte, 2)) 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)) 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)) 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)) 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)) 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)) 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 * Dive Type 2 - Bit table, use tags again
*/ */
read_bytes(1); read_bytes(1);
if (bit_set(tmp_1byte, 0)) { 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; is_nitrox = 1;
} }
if (bit_set(tmp_1byte, 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; 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); read_bytes(1);
if (bit_set(tmp_1byte, 0)) 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)) 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)) 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)) 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)) 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)) 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)) 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)) 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 * Dive Activity 2 - Bit table, use tags again
*/ */
read_bytes(1); read_bytes(1);
if (bit_set(tmp_1byte, 0)) 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)) 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)) 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)) 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)) 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 * 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); read_bytes(1);
if (tmp_1byte != 0) { if (tmp_1byte != 0) {
read_string(tmp_string1); read_string(tmp_string1);
snprintf(buffer, sizeof(buffer), "%s: %s\n", tmp_notes_str= format_string_std("%s: %s\n",
QT_TRANSLATE_NOOP("gettextFromC", "Other activities"), translate("gettextFromC", "Other activities"),
tmp_string1); tmp_string1);
tmp_notes_str = strdup(buffer);
free(tmp_string1); free(tmp_string1);
} }
@ -464,7 +462,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
read_bytes(1); read_bytes(1);
if (tmp_1byte != 0) { if (tmp_1byte != 0) {
read_string(tmp_string1); read_string(tmp_string1);
dt_dive->buddy = strdup((char *)tmp_string1); dt_dive->buddy = (const char *)tmp_string1;
free(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); read_bytes(1);
if (tmp_1byte != 0) { if (tmp_1byte != 0) {
read_string(tmp_string1); read_string(tmp_string1);
int len = snprintf(buffer, sizeof(buffer), "%s%s:\n%s", dt_dive->notes = format_string_std("%s%s:\n%s",
tmp_notes_str ? tmp_notes_str : "", tmp_notes_str.c_str(),
QT_TRANSLATE_NOOP("gettextFromC", "Datatrak/Wlog notes"), translate("gettextFromC", "Datatrak/Wlog notes"),
tmp_string1); tmp_string1);
dt_dive->notes = (char *)calloc((len +1), 1);
memcpy(dt_dive->notes, buffer, len);
free(tmp_string1); free(tmp_string1);
} }
free(tmp_notes_str);
/* /*
* Alarms 1 and Alarms2 - Bit tables - Not in Subsurface, we use the profile * 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); libdc_model = dtrak_prepare_data(tmp_1byte, devdata);
if (!libdc_model) if (!libdc_model)
report_error(translate("gettextFromC", "[Warning] Manual dive # %d\n"), dt_dive->number); 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 * 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); compl_buffer = (unsigned char *) calloc(18 + profile_length, 1);
rc = dt_libdc_buffer(membuf, profile_length, libdc_model, compl_buffer); rc = dt_libdc_buffer(membuf, profile_length, libdc_model, compl_buffer);
if (rc == DC_STATUS_SUCCESS) { 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 { } else {
report_error(translate("gettextFromC", "[Error] Out of memory for dive %d. Abort parsing."), dt_dive->number); report_error(translate("gettextFromC", "[Error] Out of memory for dive %d. Abort parsing."), dt_dive->number);
free(compl_buffer); free(compl_buffer);
goto bail; 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 = get_cylinder(dt_dive, 0)->gasmix.o2.permille =
lrint(membuf[23] & 0x0F ? 20.0 + 2 * (membuf[23] & 0x0F) : 21.0) * 10; 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; get_cylinder(dt_dive, 0)->gasmix.o2.permille = membuf[23] * 10;
free(compl_buffer); 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 * Initialize some dive data not supported by Datatrak/WLog
*/ */
if (!libdc_model) if (!libdc_model)
dt_dive->dc.deviceid = 0; dt_dive->dcs[0].deviceid = 0;
else else
dt_dive->dc.deviceid = 0xffffffff; dt_dive->dcs[0].deviceid = 0xffffffff;
dt_dive->dc.next = NULL; if (!is_SCR && dt_dive->cylinders.size() > 0) {
if (!is_SCR && dt_dive->cylinders.nr > 0) {
get_cylinder(dt_dive, 0)->end.mbar = get_cylinder(dt_dive, 0)->start.mbar - 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); ((get_cylinder(dt_dive, 0)->gas_used.mliter / get_cylinder(dt_dive, 0)->type.size.mliter) * 1000);
} }
free(devdata);
return (char *)membuf; return (char *)membuf;
bail: bail:
free(locality); free(locality);
free(devdata);
return NULL; 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_viz = offset + 258,
pos_tank_init = offset + 266, pos_tank_init = offset + 266,
pos_suit = offset + 268; 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(); 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); (void)memcpy(wlog_notes_temp, runner + offset, NOTES_LENGTH);
wlog_notes = to_utf8((unsigned char *) wlog_notes_temp); wlog_notes = to_utf8((unsigned char *) wlog_notes_temp);
} }
if (dt_dive->notes && wlog_notes) { if (wlog_notes)
buffer = (char *)calloc (strlen(dt_dive->notes) + strlen(wlog_notes) + 1, 1); dt_dive->notes += wlog_notes;
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);
/* /*
* Weight in Kg * 100 * Weight in Kg * 100
*/ */
tmp = (int) two_bytes_to_int(runner[pos_weight + 1], runner[pos_weight]); tmp = (int) two_bytes_to_int(runner[pos_weight + 1], runner[pos_weight]);
if (tmp != 0x7fff) { if (tmp != 0x7fff) {
weightsystem_t ws = { {tmp * 10}, QT_TRANSLATE_NOOP("gettextFromC", "unknown"), false }; weightsystem_t ws = { {tmp * 10}, translate("gettextFromC", "unknown"), false };
add_cloned_weightsystem(&dt_dive->weightsystems, ws); 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); wlog_suit = to_utf8((unsigned char *) wlog_suit_temp);
} }
if (wlog_suit) if (wlog_suit)
dt_dive->suit = copy_string(wlog_suit); dt_dive->suit = wlog_suit;
free(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(); runner = mem.data();
JUMP(runner, 12); 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)) { 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()) if (!wl_mem.empty())
wlog_compl_parser(wl_mem, ptdive, i); wlog_compl_parser(wl_mem, ptdive.get(), i);
if (runner == NULL) { if (runner == NULL) {
report_error("%s", translate("gettextFromC", "Error: no dive")); report_error("%s", translate("gettextFromC", "Error: no dive"));
free(ptdive);
rc = 1; rc = 1;
goto out; goto out;
} else { } else {
record_dive_to_table(ptdive, log->dives); log->dives.record_dive(std::move(ptdive));
} }
i++; i++;
} }
out: out:
sort_dive_table(log->dives); log->dives.sort();
return rc; return rc;
bail: bail:
return 1; return 1;

View File

@ -21,7 +21,6 @@
#include <assert.h> #include <assert.h>
#include "deco.h" #include "deco.h"
#include "ssrf.h"
#include "dive.h" #include "dive.h"
#include "gas.h" #include "gas.h"
#include "subsurface-string.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; 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; int ci = -1;
double ret_tolerance_limit_ambient_pressure = 0.0; 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; return 0;
} }
extern "C" void vpmb_start_gradient(struct deco_state *ds) void vpmb_start_gradient(struct deco_state *ds)
{ {
int ci; 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; int ci;
double n2_b, n2_c; 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; time /= 60.0;
int ci; 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 // 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; int ci;
double gradient; 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 */ /* 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; int ci;
struct gas_pressures pressures;
bool icd = false; 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); gasmix, (double) ccpo2 / 1000.0, divemode);
for (ci = 0; ci < 16; ci++) { 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 #if DECO_CALC_DEBUG
extern "C" void dump_tissues(struct deco_state *ds) void dump_tissues(struct deco_state *ds)
{ {
int ci; int ci;
printf("N2 tissues:"); printf("N2 tissues:");
@ -489,7 +487,7 @@ extern "C" void dump_tissues(struct deco_state *ds)
} }
#endif #endif
extern "C" void clear_vpmb_state(struct deco_state *ds) void clear_vpmb_state(struct deco_state *ds)
{ {
int ci; int ci;
for (ci = 0; ci < 16; 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; 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; int ci;
memset(ds, 0, sizeof(*ds)); *ds = deco_state();
clear_vpmb_state(ds); clear_vpmb_state(ds);
for (ci = 0; ci < 16; ci++) { 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; 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; *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; int depth;
double pressure_delta; double pressure_delta;
@ -564,7 +562,7 @@ extern "C" int deco_allowed_depth(double tissues_tolerance, double surface_press
return depth; return depth;
} }
extern "C" void set_gf(short gflow, short gfhigh) void set_gf(short gflow, short gfhigh)
{ {
if (gflow != -1) if (gflow != -1)
buehlmann_config.gf_low = (double)gflow / 100.0; 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; 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) if (conservatism < 0)
vpmb_config.conservatism = 0; vpmb_config.conservatism = 0;
@ -582,7 +580,7 @@ extern "C" void set_vpmb_conservatism(short conservatism)
vpmb_config.conservatism = 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 surface_pressure_bar = get_surface_pressure_in_mbar(dive, true) / 1000.0;
double gf_low = buehlmann_config.gf_low; 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; return gf;
} }
extern "C" double regressiona(const struct deco_state *ds) double regressiona(const struct deco_state *ds)
{ {
if (ds->sum1 > 1) { if (ds->sum1 > 1) {
double avxy = ds->sumxy / ds->sum1; double avxy = ds->sumxy / ds->sum1;
@ -609,7 +607,7 @@ extern "C" double regressiona(const struct deco_state *ds)
return 0.0; return 0.0;
} }
extern "C" double regressionb(const struct deco_state *ds) double regressionb(const struct deco_state *ds)
{ {
if (ds->sum1) if (ds->sum1)
return ds->sumy / ds->sum1 - ds->sumx * regressiona(ds) / 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; return 0.0;
} }
extern "C" void reset_regression(struct deco_state *ds) void reset_regression(struct deco_state *ds)
{ {
ds->sum1 = 0; ds->sum1 = 0;
ds->sumxx = ds->sumx = 0L; ds->sumxx = ds->sumx = 0L;
ds->sumy = ds->sumxy = 0.0; 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) if (!ds->plot_depth)
return; return;

View File

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

View File

@ -8,7 +8,7 @@
#include "selection.h" #include "selection.h"
#include "core/settings/qPrefDiveComputer.h" #include "core/settings/qPrefDiveComputer.h"
struct fingerprint_table fingerprint_table; fingerprint_table fingerprints;
static bool same_device(const device &dev1, const device &dev2) 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 bool device::operator<(const device &a) const
{ {
int diff; return std::tie(model, serialNumber) < std::tie(a.model, a.serialNumber);
diff = model.compare(a.model);
if (diff)
return diff < 0;
return serialNumber < 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; return NULL;
const std::vector<device> &dcs = table->devices;
device dev { dc->model, dc->serial }; device dev { dc->model, dc->serial };
auto it = std::lower_bound(dcs.begin(), dcs.end(), dev); auto it = std::lower_bound(table.begin(), table.end(), dev);
return it != dcs.end() && same_device(*it, dev) ? &*it : NULL; 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; return -1;
const struct device *dev = get_device_for_dc(table, dc); const struct device *dev = get_device_for_dc(table, dc);
if (dev) { if (dev) {
auto it = std::lower_bound(table->devices.begin(), table->devices.end(), *dev); auto it = std::lower_bound(table.begin(), table.end(), *dev);
return it - table->devices.begin(); 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); auto it = std::lower_bound(table.begin(), table.end(), dev);
return it != device_table->devices.end() && same_device(*it, *dev); return it != table.end() && same_device(*it, dev);
} }
void device::showchanges(const std::string &n) const 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()) if (m.empty() || s.empty())
return -1; 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()); auto it = std::lower_bound(table.begin(), table.end(), dev);
} if (it != table.end() && same_device(*it, dev)) {
int idx = it - table.begin();
extern "C" int remove_device(struct device_table *device_table, const struct device *dev) table.erase(it);
{
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);
return idx; return idx;
} else { } else {
return -1; 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; return;
device_table->devices.erase(device_table->devices.begin() + idx); table.erase(table.begin() + idx);
}
extern "C" void clear_device_table(struct device_table *device_table)
{
device_table->devices.clear();
} }
/* Returns whether the given device is used by a selected dive. */ /* 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()) { for (dive *d: getDiveSelection()) {
struct divecomputer *dc; for (auto &dc: d->dcs) {
for_each_dc (d, dc) { if (dc.deviceid == dev.deviceId)
if (dc->deviceid == dev->deviceId)
return true; return true;
} }
} }
return false; 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; 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); const device *existNode = get_device_for_dc(divelog.devices, dc);
if (existNode && !existNode->nickName.empty()) if (existNode && !existNode->nickName.empty())
return existNode->nickName.c_str(); return existNode->nickName;
else else
return dc->model; 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 // managing fingerprint data
bool fingerprint_record::operator<(const fingerprint_record &a) const bool fingerprint_record::operator<(const fingerprint_record &a) const
{ {
if (model == a.model) return std::tie(model, serial) < std::tie(a.model, a.serial);
return serial < a.serial;
return model < a.model;
} }
// annoyingly, the Cressi Edy doesn't support a serial number (it's always 0), but still uses fingerprints // 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 // 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) if (model == 0)
return 0; return { 0, nullptr };
struct fingerprint_record fpr = { model, serial }; struct fingerprint_record fpr = { model, serial };
auto it = std::lower_bound(table->fingerprints.begin(), table->fingerprints.end(), fpr); auto it = std::lower_bound(table.begin(), table.end(), fpr);
if (it != table->fingerprints.end() && it->model == model && it->serial == serial) { 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 // 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 // for - so if one is found, we still need to check for equality
if (has_dive(it->fdeviceid, it->fdiveid)) { if (has_dive(it->fdeviceid, it->fdiveid))
*fp_out = it->raw_data; return { it->fsize, it->raw_data.get() };
return it->fsize;
}
} }
return 0; return { 0, nullptr };
} }
extern "C" void create_fingerprint_node(struct fingerprint_table *table, uint32_t model, uint32_t serial, 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) 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 // since raw data can contain \0 we copy this manually, not as string
unsigned char *raw_data = (unsigned char *)malloc(fsize); auto raw_data = std::make_unique<unsigned char []>(fsize);
if (!raw_data) std::copy(raw_data_in, raw_data_in + fsize, raw_data.get());
return;
memcpy(raw_data, raw_data_in, fsize);
struct fingerprint_record fpr = { model, serial, raw_data, fsize, fdeviceid, fdiveid }; struct fingerprint_record fpr = { model, serial, std::move(raw_data), fsize, fdeviceid, fdiveid };
auto it = std::lower_bound(table->fingerprints.begin(), table->fingerprints.end(), fpr); auto it = std::lower_bound(table.begin(), table.end(), fpr);
if (it != table->fingerprints.end() && it->model == model && it->serial == serial) { 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 // 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 // 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 // can update the existing entry; first we free the memory for the stored raw data
free(it->raw_data);
it->fdeviceid = fdeviceid; it->fdeviceid = fdeviceid;
it->fdiveid = fdiveid; it->fdiveid = fdiveid;
it->raw_data = raw_data; it->raw_data = std::move(fpr.raw_data);
it->fsize = fsize; it->fsize = fsize;
} else { } else {
// insert a new one // 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, void create_fingerprint_node_from_hex(fingerprint_table &table, uint32_t model, uint32_t serial,
const char *hex_data, uint32_t fdeviceid, uint32_t fdiveid) 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, create_fingerprint_node(table, model, serial,
(const unsigned char *)raw.constData(), raw.size(), fdeviceid, fdiveid); (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) static char to_hex_digit(unsigned char d)
{ {
return d <= 9 ? d + '0' : d - 10 + 'a'; 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()) std::string res(fsize * 2, ' ');
return std::string(); for (unsigned int i = 0; i < fsize; ++i) {
struct fingerprint_record *fpr = &table->fingerprints[i]; res[2 * i] = to_hex_digit((raw_data[i] >> 4) & 0xf);
std::string res(fpr->fsize * 2, ' '); res[2 * i + 1] = to_hex_digit(raw_data[i] & 0xf);
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);
} }
return res; return res;
} }

View File

@ -3,73 +3,13 @@
#define DEVICE_H #define DEVICE_H
#include <stdint.h> #include <stdint.h>
#include <memory>
#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 <string> #include <string>
#include <vector> #include <vector>
struct divecomputer;
struct dive_table;
struct device { struct device {
bool operator<(const device &a) const; bool operator<(const device &a) const;
void showchanges(const std::string &n) const; void showchanges(const std::string &n) const;
@ -79,28 +19,48 @@ struct device {
uint32_t deviceId; // Always the string hash of the serialNumber 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 { struct fingerprint_record {
bool operator<(const fingerprint_record &a) const; bool operator<(const fingerprint_record &a) const;
uint32_t model; // model and libdivecomputer serial number to uint32_t model; // model and libdivecomputer serial number to
uint32_t serial; // look up the fingerprint 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 fsize; // size of raw fingerprint data
unsigned int fdeviceid; // corresponding deviceid unsigned int fdeviceid; // corresponding deviceid
unsigned int fdiveid; // corresponding diveid unsigned int fdiveid; // corresponding diveid
std::string get_data() const; // As hex-string
}; };
struct device_table { using fingerprint_table = std::vector<fingerprint_record>;
// Keep the dive computers in a vector sorted by (model, serial)
std::vector<device> devices;
};
struct fingerprint_table { // global device table
// Keep the fingerprint records in a vector sorted by (model, serial) - these are uint32_t here extern fingerprint_table fingerprints;
std::vector<fingerprint_record> 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 #endif // DEVICE_H

File diff suppressed because it is too large Load Diff

View File

@ -7,14 +7,13 @@
#include "divemode.h" #include "divemode.h"
#include "divecomputer.h" #include "divecomputer.h"
#include "equipment.h" #include "equipment.h"
#include "picture.h" #include "picture.h" // TODO: remove
#include "tag.h"
#include <stdio.h> #include <array>
#include <stdlib.h> #include <memory>
#include <string>
#ifdef __cplusplus #include <vector>
extern "C" {
#endif
extern int last_xml_version; extern int last_xml_version;
@ -22,44 +21,72 @@ extern const char *divemode_text_ui[];
extern const char *divemode_text[]; extern const char *divemode_text[];
struct dive_site; struct dive_site;
struct dive_site_table;
struct dive_table; struct dive_table;
struct dive_trip; struct dive_trip;
struct full_text_cache; struct full_text_cache;
struct event; struct event;
struct trip_table; 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 {
struct dive_trip *divetrip; struct dive_trip *divetrip = nullptr;
timestamp_t when; timestamp_t when = 0;
struct dive_site *dive_site; struct dive_site *dive_site = nullptr;
char *notes; std::string notes;
char *diveguide, *buddy; std::string diveguide, buddy;
struct cylinder_table cylinders; std::string suit;
struct weightsystem_table weightsystems; cylinder_table cylinders;
char *suit; weightsystem_table weightsystems;
int number; int number = 0;
int rating; int rating = 0;
int wavesize, current, visibility, surge, chill; /* 0 - 5 star ratings */ int wavesize = 0, current = 0, visibility = 0, surge = 0, chill = 0; /* 0 - 5 star ratings */
int sac, otu, cns, maxcns; int sac = 0, otu = 0, cns = 0, maxcns = 0;
/* Calculated based on dive computer data */ /* Calculated based on dive computer data */
temperature_t mintemp, maxtemp, watertemp, airtemp; temperature_t mintemp, maxtemp, watertemp, airtemp;
depth_t maxdepth, meandepth; depth_t maxdepth, meandepth;
pressure_t surface_pressure; pressure_t surface_pressure;
duration_t duration; duration_t duration;
int salinity; // kg per 10000 l int salinity = 0; // kg per 10000 l
int user_salinity; // water density reflecting a user-specified type 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 */ /* 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 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 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 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 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? */ /* when selectively copying dive information, which parts should be copied? */
struct dive_components { 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 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(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 struct dive_site *get_dive_site_for_dive(const struct dive *dive);
extern const char *get_dive_country(const struct dive *dive); extern std::string get_dive_country(const struct dive *dive);
extern const char *get_dive_location(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 unsigned int number_of_computers(const struct dive *dive);
extern struct divecomputer *get_dive_dc(struct dive *dive, int nr); 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 const struct divecomputer *get_dive_dc(const struct dive *dive, int nr);
extern timestamp_t dive_endtime(const struct dive *dive);
extern void set_git_prefs(const char *prefs); extern void set_git_prefs(const char *prefs);
extern struct dive *make_first_dc(const struct dive *d, int dc_number); extern std::unique_ptr<dive> clone_make_first_dc(const struct dive &d, int dc_number);
extern struct dive *clone_delete_divecomputer(const struct dive *d, int dc_number); extern std::unique_ptr<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::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 bool dive_site_has_gps_location(const struct dive_site *ds);
extern int dive_has_gps_location(const struct dive *dive); extern int dive_has_gps_location(const struct dive *dive);
extern location_t dive_get_gps_location(const struct dive *d); 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(const char *filename);
extern int save_dives_logic(const char *filename, bool select_only, bool anonymize); 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 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); extern int save_dive_sites_logic(const char *filename, const struct dive_site *sites[], int nr_sites, bool anonymize);
struct membuffer; 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_init();
extern void subsurface_console_exit(void); extern void subsurface_console_exit();
extern bool subsurface_user_is_root(void); 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 clear_dive(struct dive *dive);
extern void copy_dive(const struct dive *s, struct dive *d); 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 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 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 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 struct dive *fixup_dive(struct dive *dive);
extern pressure_t calculate_surface_pressure(const 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 pressure_t un_fixup_surface_pressure(const struct dive *d);
extern int get_dive_salinity(const struct dive *dive); extern int get_dive_salinity(const struct dive *dive);
extern int dive_getUniqID(); extern int dive_getUniqID();
extern int split_dive(const struct dive *dive, struct dive **new1, struct dive **new2); extern std::array<std::unique_ptr<dive>, 2> split_dive(const struct dive &dive);
extern int split_dive_at_time(const struct dive *dive, duration_t time, struct dive **new1, struct dive **new2); extern std::array<std::unique_ptr<dive>, 2> split_dive_at_time(const struct dive &dive, duration_t time);
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); 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_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 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_used(const struct dive *dive, int idx);
extern bool is_cylinder_prot(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 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 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 void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, int *mean, int *duration); 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 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 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 bool cylinder_with_sensor_sample(const struct dive *dive, int cylinder_id); extern bool cylinder_with_sensor_sample(const struct dive *dive, int cylinder_id);
/* UI related protopypes */ /* UI related protopypes */
@ -206,32 +215,17 @@ extern void invalidate_dive_cache(struct dive *dc);
extern int total_weight(const struct dive *); 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 */ /* 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); 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 /* Make pointers to dive and dive_trip "Qt metatypes" so that they can be passed through
* QVariants and through QML. * QVariants and through QML.
*/ */
#include <QObject> #include <QObject>
#include <string>
Q_DECLARE_METATYPE(struct dive *); Q_DECLARE_METATYPE(struct dive *);
extern std::string existing_filename; extern std::string existing_filename;
#endif
#endif // DIVE_H #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 "divemode.h"
#include "units.h" #include "units.h"
#include <string>
#ifdef __cplusplus #include <vector>
extern "C" {
#endif
struct extra_data; struct extra_data;
struct event;
struct sample; struct sample;
/* Is this header the correct place? */ /* Is this header the correct place? */
@ -29,44 +28,40 @@ struct sample;
* A deviceid or diveid of zero is assumed to be "no ID". * A deviceid or diveid of zero is assumed to be "no ID".
*/ */
struct divecomputer { struct divecomputer {
timestamp_t when; timestamp_t when = 0;
duration_t duration, surfacetime, last_manual_time; duration_t duration, surfacetime, last_manual_time;
depth_t maxdepth, meandepth; depth_t maxdepth, meandepth;
temperature_t airtemp, watertemp; temperature_t airtemp, watertemp;
pressure_t surface_pressure; pressure_t surface_pressure;
enum divemode_t divemode; // dive computer type: OC(default) or CCR enum divemode_t divemode = OC; // dive computer type: OC(default) or CCR
uint8_t no_o2sensors; // rebreathers: number of O2 sensors used uint8_t no_o2sensors = 0; // rebreathers: number of O2 sensors used
int salinity; // kg per 10000 l int salinity = 0; // kg per 10000 l
const char *model, *serial, *fw_version; std::string model, serial, fw_version;
uint32_t deviceid, diveid; uint32_t deviceid = 0, diveid = 0;
int samples, alloc_samples; // Note: ve store samples, events and extra_data in std::vector<>s.
struct sample *sample; // This means that pointers to these items are *not* stable.
struct event *events; std::vector<struct sample> samples;
struct extra_data *extra_data; std::vector<struct event> events;
struct divecomputer *next; 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 fake_dc(struct divecomputer *dc);
extern void free_dc(struct divecomputer *dc);
extern void free_dc_contents(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 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 struct sample *prepare_sample(struct divecomputer *dc);
extern void finish_sample(struct divecomputer *dc); extern void append_sample(const struct sample &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 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 unsigned int dc_airtemp(const struct divecomputer *dc); extern struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const std::string &name);
extern unsigned int dc_watertemp(const struct divecomputer *dc); extern struct event remove_event_from_dc(struct divecomputer *dc, int idx);
extern void copy_events(const struct divecomputer *s, struct divecomputer *d); struct event *get_event(struct divecomputer *dc, int idx);
extern void swap_event(struct divecomputer *dc, struct event *from, struct event *to); extern void add_extra_data(struct divecomputer *dc, const std::string &key, const std::string &value);
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 uint32_t calculate_string_hash(const char *str); extern uint32_t calculate_string_hash(const char *str);
extern bool is_dc_planner(const struct divecomputer *dc); extern bool is_dc_planner(const struct divecomputer *dc);
extern void make_planner_dc(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); 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) */ /* 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); extern int match_one_dc(const struct divecomputer &a, const struct divecomputer &b);
#ifdef __cplusplus
}
#endif
#endif #endif

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -2,29 +2,29 @@
#ifndef DIVELIST_H #ifndef DIVELIST_H
#define DIVELIST_H #define DIVELIST_H
#include "triptable.h"
#include "divesitetable.h"
#include "units.h" #include "units.h"
#include <memory>
#ifdef __cplusplus #include <vector>
extern "C" {
#endif
struct dive; struct dive;
struct divelog; struct divelog;
struct trip_table; struct device;
struct dive_site_table;
struct device_table;
struct deco_state; struct deco_state;
struct dive_table { int comp_dives(const struct dive &a, const struct dive &b);
int nr, allocated; int comp_dives_ptr(const struct dive *a, const struct dive *b);
struct dive **dives;
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 */ /* this is used for both git and xml format */
#define DATAFORMAT_VERSION 3 #define DATAFORMAT_VERSION 3
extern void sort_dive_table(struct dive_table *table);
extern void update_cylinder_related_info(struct dive *); extern void update_cylinder_related_info(struct dive *);
extern int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_planner); 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_IS_DOWNLOADED (1 << 1)
#define IMPORT_MERGE_ALL_TRIPS (1 << 2) #define IMPORT_MERGE_ALL_TRIPS (1 << 2)
#define IMPORT_ADD_TO_NEW_TRIP (1 << 3) #define IMPORT_ADD_TO_NEW_TRIP (1 << 3)
extern void add_imported_dives(struct divelog *log, int flags); 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 process_imported_dives_result {
struct trip_table *trips_to_add, struct dive_site_table *sites_to_add, dive_table dives_to_add;
struct device_table *devices_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 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 int get_dive_nr_at_idx(int idx);
extern timestamp_t get_surface_interval(timestamp_t when); 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 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(); int get_min_datafile_version();
void reset_min_datafile_version();
void report_datafile_version(int version); void report_datafile_version(int version);
int get_dive_id_closest_to(timestamp_t when); int get_dive_id_closest_to(timestamp_t when);
void clear_dive_file_data(); void clear_dive_file_data();
void clear_dive_table(struct dive_table *table); void clear_dive_table(struct dive_table *table);
void move_dive_table(struct dive_table *src, struct dive_table *dst); struct dive *register_dive(std::unique_ptr<dive> d);
struct dive *unregister_dive(int idx);
extern bool has_dive(unsigned int deviceid, unsigned int diveid); extern bool has_dive(unsigned int deviceid, unsigned int diveid);
#ifdef __cplusplus
}
#endif
#endif // DIVELIST_H #endif // DIVELIST_H

View File

@ -3,96 +3,89 @@
#include "divelist.h" #include "divelist.h"
#include "divesite.h" #include "divesite.h"
#include "device.h" #include "device.h"
#include "dive.h"
#include "errorhelper.h" #include "errorhelper.h"
#include "filterpreset.h" #include "filterpreset.h"
#include "filterpresettable.h"
#include "trip.h" #include "trip.h"
struct divelog divelog; struct divelog divelog;
// We can't use smart pointers, since this is used from C divelog::divelog() = default;
// and it would be bold to presume that std::unique_ptr<> divelog::~divelog() = default;
// and a plain pointer have the same memory layout. divelog::divelog(divelog &&) = default;
divelog::divelog() : struct divelog &divelog::operator=(divelog &&) = default;
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;
}
/* this implements the mechanics of removing the dive from the /* this implements the mechanics of removing the dive from the
* dive log and the trip, but doesn't deal with updating dive trips, etc */ * 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) { if (idx < 0 || static_cast<size_t>(idx) >= dives.size()) {
report_info("Warning: deleting unexisting dive with index %d", idx); report_info("Warning: deleting non-existing dive with index %d", idx);
return; return;
} }
struct dive *dive = log->dives->dives[idx]; struct dive *dive = dives[idx].get();
remove_dive_from_trip(dive, log->trips); 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); 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() void divelog::clear()
{ {
while (dives->nr > 0) dives.clear();
delete_single_dive(this, dives->nr - 1); sites.clear();
while (sites->nr) trips.clear();
delete_dive_site(get_dive_site(0, sites), sites); devices.clear();
if (trips->nr != 0) { filter_presets.clear();
report_info("Warning: trip table not empty in divelog::clear()!");
trips->nr = 0;
}
clear_device_table(devices);
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 // 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 #ifndef DIVELOG_H
#define DIVELOG_H #define DIVELOG_H
struct dive_table; #include "divelist.h"
struct trip_table; #include "divesitetable.h"
struct dive_site_table; #include "filterpresettable.h"
struct device_table; #include "triptable.h"
struct filter_preset_table;
#include <stdbool.h> #include <vector>
struct device;
struct divelog { struct divelog {
struct dive_table *dives; dive_table dives;
struct trip_table *trips; trip_table trips;
struct dive_site_table *sites; dive_site_table sites;
struct device_table *devices; std::vector<device> devices;
struct filter_preset_table *filter_presets; filter_preset_table filter_presets;
bool autogroup; bool autogroup = false;
#ifdef __cplusplus
void clear();
divelog(); divelog();
~divelog(); ~divelog();
divelog(divelog &&log); // move constructor (argument is consumed). divelog(divelog &&); // move constructor (argument is consumed).
divelog &operator=(divelog &&log); // move assignment (argument is consumed). divelog &operator=(divelog &&); // move assignment (argument is consumed).
#endif
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; 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 #endif

View File

@ -75,45 +75,42 @@ static void exportHTMLstatistics(const QString filename, struct htmlExportSettin
QFile file(filename); QFile file(filename);
file.open(QIODevice::WriteOnly | QIODevice::Text); file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream out(&file); QTextStream out(&file);
stats_summary_auto_free stats;
stats_t total_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.selection_size = 0;
total_stats.total_time.seconds = 0; total_stats.total_time.seconds = 0;
int i = 0;
out << "divestat=["; out << "divestat=[";
if (hes.yearlyStatistics) { if (hes.yearlyStatistics) {
while (stats.stats_yearly != NULL && stats.stats_yearly[i].period) { for (const auto &s: stats.stats_yearly) {
out << "{"; out << "{";
out << "\"YEAR\":\"" << stats.stats_yearly[i].period << "\","; out << "\"YEAR\":\"" << s.period << "\",";
out << "\"DIVES\":\"" << stats.stats_yearly[i].selection_size << "\","; out << "\"DIVES\":\"" << s.selection_size << "\",";
out << "\"TOTAL_TIME\":\"" << get_dive_duration_string(stats.stats_yearly[i].total_time.seconds, out << "\"TOTAL_TIME\":\"" << get_dive_duration_string(s.total_time.seconds,
gettextFromC::tr("h"), gettextFromC::tr("min"), gettextFromC::tr("sec"), " ") << "\","; 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 << "\"AVERAGE_TIME\":\"" << formatMinutes(s.total_time.seconds / s.selection_size) << "\",";
out << "\"SHORTEST_TIME\":\"" << formatMinutes(stats.stats_yearly[i].shortest_time.seconds) << "\","; out << "\"SHORTEST_TIME\":\"" << formatMinutes(s.shortest_time.seconds) << "\",";
out << "\"LONGEST_TIME\":\"" << formatMinutes(stats.stats_yearly[i].longest_time.seconds) << "\","; out << "\"LONGEST_TIME\":\"" << formatMinutes(s.longest_time.seconds) << "\",";
out << "\"AVG_DEPTH\":\"" << get_depth_string(stats.stats_yearly[i].avg_depth) << "\","; out << "\"AVG_DEPTH\":\"" << get_depth_string(s.avg_depth) << "\",";
out << "\"MIN_DEPTH\":\"" << get_depth_string(stats.stats_yearly[i].min_depth) << "\","; out << "\"MIN_DEPTH\":\"" << get_depth_string(s.min_depth) << "\",";
out << "\"MAX_DEPTH\":\"" << get_depth_string(stats.stats_yearly[i].max_depth) << "\","; out << "\"MAX_DEPTH\":\"" << get_depth_string(s.max_depth) << "\",";
out << "\"AVG_SAC\":\"" << get_volume_string(stats.stats_yearly[i].avg_sac) << "\","; out << "\"AVG_SAC\":\"" << get_volume_string(s.avg_sac) << "\",";
out << "\"MIN_SAC\":\"" << get_volume_string(stats.stats_yearly[i].min_sac) << "\","; out << "\"MIN_SAC\":\"" << get_volume_string(s.min_sac) << "\",";
out << "\"MAX_SAC\":\"" << get_volume_string(stats.stats_yearly[i].max_sac) << "\","; out << "\"MAX_SAC\":\"" << get_volume_string(s.max_sac) << "\",";
if (stats.stats_yearly[i].combined_count) { if (s.combined_count) {
temperature_t avg_temp; 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) << "\","; out << "\"AVG_TEMP\":\"" << get_temperature_string(avg_temp) << "\",";
} else { } else {
out << "\"AVG_TEMP\":\"0.0\","; 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 << "\"MIN_TEMP\":\"" << (s.min_temp.mkelvin == 0 ? 0 : get_temperature_string(s.min_temp)) << "\",";
out << "\"MAX_TEMP\":\"" << (stats.stats_yearly[i].max_temp.mkelvin == 0 ? 0 : get_temperature_string(stats.stats_yearly[i].max_temp)) << "\","; out << "\"MAX_TEMP\":\"" << (s.max_temp.mkelvin == 0 ? 0 : get_temperature_string(s.max_temp)) << "\",";
out << "},"; out << "},";
total_stats.selection_size += stats.stats_yearly[i].selection_size; total_stats.selection_size += s.selection_size;
total_stats.total_time.seconds += stats.stats_yearly[i].total_time.seconds; total_stats.total_time.seconds += s.total_time.seconds;
i++;
} }
exportHTMLstatisticsTotal(out, &total_stats); 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 #ifndef DIVESITE_H
#define DIVESITE_H #define DIVESITE_H
#include "units.h"
#include "taxonomy.h"
#include "divelist.h" #include "divelist.h"
#include "taxonomy.h"
#include "units.h"
#include <stdlib.h> #include <stdlib.h>
#ifdef __cplusplus
#include <QString>
#include <QObject>
extern "C" {
#else
#include <stdbool.h>
#endif
struct dive_site struct dive_site
{ {
uint32_t uuid; uint32_t uuid = 0;
char *name; std::string name;
struct dive_table dives; std::vector<dive *> dives;
location_t location; location_t location;
char *description; std::string description;
char *notes; std::string notes;
struct taxonomy_data taxonomy; 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); struct dive_site *unregister_dive_from_dive_site(struct dive *d);
int divesite_comp_uuid(const dive_site &ds1, const dive_site &ds2);
#ifdef __cplusplus
}
QString constructLocationTags(struct taxonomy_data *taxonomy, bool for_maintab);
/* Make pointer-to-dive_site a "Qt metatype" so that we can pass it through QVariants */ /* Make pointer-to-dive_site a "Qt metatype" so that we can pass it through QVariants */
#include <QObject>
Q_DECLARE_METATYPE(dive_site *); Q_DECLARE_METATYPE(dive_site *);
#endif
#endif // DIVESITE_H #endif // DIVESITE_H

View File

@ -84,14 +84,14 @@ taxonomy_data reverseGeoLookup(degrees_t latitude, degrees_t longitude)
QString url; QString url;
QJsonObject obj; QJsonObject obj;
taxonomy_data taxonomy = { 0, 0 }; taxonomy_data taxonomy;
// check the oceans API to figure out the body of water // 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); 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 obj = doAsyncRESTGetRequest(url, 5000); // 5 secs. timeout
QVariantMap oceanName = obj.value("ocean").toVariant().toMap(); QVariantMap oceanName = obj.value("ocean").toVariant().toMap();
if (oceanName["name"].isValid()) 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 // 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); 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++) { for (int idx = TC_COUNTRY; idx < TC_NR_CATEGORIES; idx++) {
if (firstData[taxonomy_api_names[idx]].isValid()) { if (firstData[taxonomy_api_names[idx]].isValid()) {
QString value = firstData[taxonomy_api_names[idx]].toString(); 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); std::string l3 = taxonomy_get_value(taxonomy, TC_ADMIN_L3);
const char *lt = taxonomy_get_value(&taxonomy, TC_LOCALNAME); std::string lt = taxonomy_get_value(taxonomy, TC_LOCALNAME);
if (empty_string(l3) && !empty_string(lt)) { if (!l3.empty() && !lt.empty()) {
// basically this means we did get a local name (what we call town), but just like most places // 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, // 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 // 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 { } else {
report_error("geonames.org did not provide reverse lookup information"); 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 "downloadfromdcthread.h"
#include "core/errorhelper.h" #include "core/errorhelper.h"
#include "core/format.h"
#include "core/libdivecomputer.h" #include "core/libdivecomputer.h"
#include "core/qthelper.h" #include "core/qthelper.h"
#include "core/range.h" #include "core/range.h"
#include "core/uemis.h"
#include "core/settings/qPrefDiveComputer.h" #include "core/settings/qPrefDiveComputer.h"
#include "core/divelist.h" #include "core/divelist.h"
#if defined(Q_OS_ANDROID) #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; QMap<QString, dc_descriptor_t *> descriptorLookup;
ConnectionListModel connectionListModel; 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() static void updateRememberedDCs()
{ {
QString current = qPrefDiveComputer::vendor() + " - " + qPrefDiveComputer::product() + " - " + qPrefDiveComputer::device(); QString current = qPrefDiveComputer::vendor() + " - " + qPrefDiveComputer::product() + " - " + qPrefDiveComputer::device();
@ -89,7 +81,7 @@ void DownloadThread::run()
auto internalData = m_data->internalData(); auto internalData = m_data->internalData();
internalData->descriptor = descriptorLookup[m_data->vendor().toLower() + m_data->product().toLower()]; internalData->descriptor = descriptorLookup[m_data->vendor().toLower() + m_data->product().toLower()];
internalData->log = &log; internalData->log = &log;
internalData->btname = strdup(m_data->devBluetoothName().toUtf8()); internalData->btname = m_data->devBluetoothName().toStdString();
if (!internalData->descriptor) { if (!internalData->descriptor) {
report_info("No download possible when DC type is unknown"); report_info("No download possible when DC type is unknown");
return; return;
@ -105,27 +97,28 @@ void DownloadThread::run()
report_info("Starting download from %s", qPrintable(getTransportString(transports))); report_info("Starting download from %s", qPrintable(getTransportString(transports)));
report_info("downloading %s dives", internalData->force_download ? "all" : "only new"); report_info("downloading %s dives", internalData->force_download ? "all" : "only new");
clear_divelog(&log); log.clear();
Q_ASSERT(internalData->log != nullptr); Q_ASSERT(internalData->log != nullptr);
const char *errorText; std::string errorText;
import_thread_cancelled = false; import_thread_cancelled = false;
error.clear(); error.clear();
if (!strcmp(internalData->vendor, "Uemis")) if (internalData->vendor == "Uemis")
errorText = do_uemis_import(internalData); errorText = do_uemis_import(internalData);
else else
errorText = do_libdivecomputer_import(internalData); errorText = do_libdivecomputer_import(internalData);
if (errorText) { if (!errorText.empty()) {
error = str_error(errorText, internalData->devname, internalData->vendor, internalData->product); error = format_string_std(errorText.c_str(), internalData->devname.c_str(),
report_info("Finishing download thread: %s", qPrintable(error)); internalData->vendor.c_str(), internalData->product.c_str());
report_info("Finishing download thread: %s", error.c_str());
} else { } else {
if (!log.dives->nr) if (log.dives.empty())
error = tr("No new dives downloaded from dive computer"); error = tr("No new dives downloaded from dive computer").toStdString();
report_info("Finishing download thread: %d dives downloaded", log.dives->nr); report_info("Finishing download thread: %d dives downloaded", static_cast<int>(log.dives.size()));
} }
qPrefDiveComputer::set_vendor(internalData->vendor); qPrefDiveComputer::set_vendor(internalData->vendor.c_str());
qPrefDiveComputer::set_product(internalData->product); qPrefDiveComputer::set_product(internalData->product.c_str());
qPrefDiveComputer::set_device(internalData->devname); qPrefDiveComputer::set_device(internalData->devname.c_str());
qPrefDiveComputer::set_device_name(m_data->devBluetoothName()); qPrefDiveComputer::set_device_name(m_data->devBluetoothName());
updateRememberedDCs(); updateRememberedDCs();
@ -209,7 +202,6 @@ void show_computer_list()
DCDeviceData::DCDeviceData() DCDeviceData::DCDeviceData()
{ {
memset(&data, 0, sizeof(data));
data.log = nullptr; data.log = nullptr;
data.diveid = 0; data.diveid = 0;
#if defined(BT_SUPPORT) #if defined(BT_SUPPORT)
@ -241,9 +233,8 @@ QStringList DCDeviceData::getProductListFromVendor(const QString &vendor)
return productList[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); return connectionListModel.indexOf(product);
} }
@ -254,17 +245,17 @@ DCDeviceData *DownloadThread::data()
QString DCDeviceData::vendor() const QString DCDeviceData::vendor() const
{ {
return data.vendor; return QString::fromStdString(data.vendor);
} }
QString DCDeviceData::product() const QString DCDeviceData::product() const
{ {
return data.product; return QString::fromStdString(data.product);
} }
QString DCDeviceData::devName() const QString DCDeviceData::devName() const
{ {
return data.devname; return QString::fromStdString(data.devname);
} }
QString DCDeviceData::devBluetoothName() const QString DCDeviceData::devBluetoothName() const
@ -299,12 +290,12 @@ bool DCDeviceData::syncTime() const
void DCDeviceData::setVendor(const QString &vendor) void DCDeviceData::setVendor(const QString &vendor)
{ {
data.vendor = copy_qstring(vendor); data.vendor = vendor.toStdString();
} }
void DCDeviceData::setProduct(const QString &product) void DCDeviceData::setProduct(const QString &product)
{ {
data.product = copy_qstring(product); data.product = product.toStdString();
} }
void DCDeviceData::setDevName(const QString &devName) 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 back = devName.mid(idx1 + 1, idx2 - idx1 - 1);
QString newDevName = back.indexOf(':') >= 0 ? back : front; QString newDevName = back.indexOf(':') >= 0 ? back : front;
qWarning() << "Found invalid bluetooth device" << devName << "corrected to" << newDevName << "."; qWarning() << "Found invalid bluetooth device" << devName << "corrected to" << newDevName << ".";
data.devname = copy_qstring(newDevName); data.devname = newDevName.toStdString();
return; return;
} }
} }
data.devname = copy_qstring(devName); data.devname = devName.toStdString();
} }
#if defined(Q_OS_ANDROID) #if defined(Q_OS_ANDROID)

View File

@ -74,7 +74,7 @@ public:
void run() override; void run() override;
DCDeviceData *data(); DCDeviceData *data();
QString error; std::string error;
struct divelog log; struct divelog log;
private: 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" #include "gas.h"
#ifdef __cplusplus #include <memory>
extern "C" { #include <string>
#endif #include <vector>
struct dive; struct dive;
enum cylinderuse {OC_GAS, DILUENT, OXYGEN, NOT_USED, NUM_GAS_USE}; // The different uses for cylinders enum cylinderuse {OC_GAS, DILUENT, OXYGEN, NOT_USED, NUM_GAS_USE}; // The different uses for cylinders
extern const char *cylinderuse_text[NUM_GAS_USE]; extern const char *cylinderuse_text[NUM_GAS_USE];
typedef struct struct cylinder_type_t
{ {
volume_t size; volume_t size;
pressure_t workingpressure; pressure_t workingpressure;
const char *description; /* "LP85", "AL72", "AL80", "HP100+" or whatever */ std::string description; /* "LP85", "AL72", "AL80", "HP100+" or whatever */
} cylinder_type_t; };
typedef struct struct cylinder_t
{ {
cylinder_type_t type; cylinder_type_t type;
struct gasmix gasmix; struct gasmix gasmix = gasmix_air;
pressure_t start, end, sample_start, sample_end; pressure_t start, end, sample_start, sample_end;
depth_t depth; depth_t depth;
bool manually_added; bool manually_added = false;
volume_t gas_used; volume_t gas_used;
volume_t deco_gas_used; volume_t deco_gas_used;
enum cylinderuse cylinder_use; enum cylinderuse cylinder_use = OC_GAS;
bool bestmix_o2; bool bestmix_o2 = false;
bool bestmix_he; bool bestmix_he = false;
} cylinder_t;
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 }; cylinder_t();
~cylinder_t();
/* 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;
}; };
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; weight_t weight;
const char *description; /* "integrated", "belt", "ankle" */ std::string description; /* "integrated", "belt", "ankle" */
bool auto_filled; /* weight was automatically derived from the type */ bool auto_filled = false; /* weight was automatically derived from the type */
} weightsystem_t;
static const weightsystem_t empty_weightsystem = { { 0 }, 0, false }; weightsystem_t();
weightsystem_t(weight_t w, std::string desc, bool auto_filled);
/* Table of weightsystems. Attention: this stores weightsystems, ~weightsystem_t();
* *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;
}; };
#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 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 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 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(struct dive *d, int idx);
extern cylinder_t *get_cylinder(const 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 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 bool same_weightsystem(weightsystem_t w1, weightsystem_t w2);
extern void remove_cylinder(struct dive *dive, int idx); extern void remove_cylinder(struct dive *dive, int idx);
extern void remove_weightsystem(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 set_weightsystem(struct dive *dive, int idx, weightsystem_t ws);
extern void reset_cylinders(struct dive *dive, bool track_gas); 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 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 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 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 void add_default_cylinder(struct dive *dive);
extern int first_hidden_cylinder(const struct dive *d); extern int first_hidden_cylinder(const struct dive *d);
@ -101,44 +91,34 @@ extern void dump_cylinders(struct dive *dive, bool verbose);
#endif #endif
/* Weightsystem table functions */ /* Weightsystem table functions */
extern void clear_weightsystem_table(struct weightsystem_table *); extern void add_to_weightsystem_table(weightsystem_table *, int idx, weightsystem_t ws);
extern void add_to_weightsystem_table(struct weightsystem_table *, int idx, weightsystem_t ws);
/* Cylinder table functions */ /* 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 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); void get_gas_string(struct gasmix gasmix, char *text, int len);
const char *gasname(struct gasmix gasmix); const char *gasname(struct gasmix gasmix);
typedef struct tank_info { struct ws_info {
const char *name; 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; 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 std::vector<tank_info> tank_info_table;
extern void reset_tank_info_table(struct tank_info_table *table); extern struct tank_info *get_tank_info(std::vector<tank_info> &table, const std::string &name);
extern void clear_tank_info_table(struct tank_info_table *table); extern void set_tank_info_data(std::vector<tank_info> &table, const std::string &name, volume_t size, pressure_t working_pressure);
extern void add_tank_info_metric(struct tank_info_table *table, const char *name, int ml, int bar); extern std::pair<volume_t, pressure_t> extract_tank_info(const struct tank_info &info);
extern void add_tank_info_imperial(struct tank_info_table *table, const char *name, int cuft, int psi); extern std::pair<volume_t, pressure_t> get_tank_info_data(const std::vector<tank_info> &table, const std::string &name);
extern void extract_tank_info(const struct tank_info *info, volume_t *size, pressure_t *working_pressure); extern void add_cylinder_description(const cylinder_type_t &);
extern bool get_tank_info_data(struct tank_info_table *table, const char *name, volume_t *size, pressure_t *pressure); extern void reset_tank_info_table(std::vector<tank_info> &table);
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
#endif // EQUIPMENT_H #endif // EQUIPMENT_H

View File

@ -1,50 +1,44 @@
// SPDX-License-Identifier: GPL-2.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
#include <stdarg.h>
#include "errorhelper.h" #include "errorhelper.h"
#include "membuffer.h" #include "format.h"
#include <stdarg.h>
#if !defined(Q_OS_ANDROID) && !defined(__ANDROID__) #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 #else
#include <android/log.h> #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 #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; int verbose;
void report_info(const char *fmt, ...) void report_info(const char *fmt, ...)
{ {
struct membufferpp buf; va_list args;
va_start(args, fmt);
VA_BUF(&buf, fmt); std::string s = vformat_string_std(fmt, args);
strip_mb(&buf); va_end(args);
LOG_MSG("INFO: %s\n", mb_cstring(&buf)); 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, ...) int report_error(const char *fmt, ...)
{ {
struct membufferpp buf; va_list args;
va_start(args, fmt);
VA_BUF(&buf, fmt); std::string s = vformat_string_std(fmt, args);
strip_mb(&buf); va_end(args);
LOG_MSG("ERROR: %s\n", mb_cstring(&buf)); LOG_MSG("ERROR: %s\n", s.c_str());
/* if there is no error callback registered, don't produce errors */ /* if there is no error callback registered, don't produce errors */
if (!error_cb) if (error_cb)
return -1; error_cb(std::move(s));
error_cb(detach_cstring(&buf));
return -1; return -1;
} }
void set_error_cb(void(*cb)(char *)) void set_error_cb(void(*cb)(std::string))
{ {
error_cb = cb; error_cb = cb;
} }

View File

@ -4,9 +4,7 @@
// error reporting functions // error reporting functions
#ifdef __cplusplus #include <string>
extern "C" {
#endif
#ifdef __GNUC__ #ifdef __GNUC__
#define __printf(x, y) __attribute__((__format__(__printf__, x, y))) #define __printf(x, y) __attribute__((__format__(__printf__, x, y)))
@ -17,11 +15,6 @@ extern "C" {
extern int verbose; extern int verbose;
extern int __printf(1, 2) report_error(const char *fmt, ...); extern int __printf(1, 2) report_error(const char *fmt, ...);
extern void __printf(1, 2) report_info(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 extern void set_error_cb(void(*cb)(std::string s)); // Callback takes ownership of passed string
#ifdef __cplusplus
}
#endif
#endif #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 "gas.h"
#include "units.h" #include "units.h"
#include <string>
#include <libdivecomputer/parser.h> #include <libdivecomputer/parser.h>
#ifdef __cplusplus struct divecomputer;
extern "C" {
#endif
enum event_severity { enum event_severity {
EVENT_SEVERITY_NONE = 0, EVENT_SEVERITY_NONE = 0,
@ -23,8 +22,8 @@ enum event_severity {
* Events are currently based straight on what libdivecomputer gives us. * 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. * We need to wrap these into our own events at some point to remove some of the limitations.
*/ */
struct event { struct event {
struct event *next;
duration_t time; duration_t time;
int type; int type;
/* This is the annoying libdivecomputer format. */ /* This is the annoying libdivecomputer format. */
@ -42,26 +41,53 @@ struct event {
struct gasmix mix; struct gasmix mix;
} gas; } gas;
}; };
bool deleted; // used internally in the parser and in fixup_dive().
bool hidden; 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); class event_loop
extern bool event_is_divemodechange(const struct event *ev); {
extern struct event *clone_event(const struct event *src_ev); std::string name;
extern void free_events(struct event *ev); size_t idx;
extern struct event *create_event(unsigned int time, int type, int flags, int value, const char *name); public:
extern struct event *clone_event_rename(const struct event *ev, const char *name); event_loop(const char *name);
extern bool same_event(const struct event *a, const struct event *b); struct event *next(struct divecomputer &dc); // nullptr -> end
extern enum event_severity get_event_severity(const struct event *ev); const struct event *next(const struct divecomputer &dc); // nullptr -> end
};
/* Since C doesn't have parameter-based overloading, two versions of get_next_event. */ /* Get gasmixes at increasing timestamps. */
extern const struct event *get_next_event(const struct event *event, const char *name); class gasmix_loop {
extern struct event *get_next_event_mutable(struct event *event, const char *name); 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 /* Get divemodes at increasing timestamps. */
} class divemode_loop {
#endif 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 #endif

View File

@ -14,7 +14,7 @@ struct event_type {
bool plot; bool plot;
event_type(const struct event *ev) : event_type(const struct event *ev) :
name(ev->name), name(ev->name),
severity(get_event_severity(ev)), severity(ev->get_severity()),
plot(true) 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; return en1.name == en2.name && en1.severity == en2.severity;
} }
extern "C" void clear_event_types() void clear_event_types()
{ {
event_types.clear(); 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; return;
event_type type(ev); event_type type(ev);
if (std::find(event_types.begin(), event_types.end(), type) != event_types.end()) 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)); 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); auto it = std::find(event_types.begin(), event_types.end(), ev);
return it != event_types.end() && !it->plot; 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); auto it = std::find(event_types.begin(), event_types.end(), ev);
if (it != event_types.end()) if (it != event_types.end())
it->plot = false; it->plot = false;
} }
extern "C" void show_all_event_types() void show_all_event_types()
{ {
for (event_type &e: event_types) for (event_type &e: event_types)
e.plot = true; 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()) if (idx < 0 || idx >= (int)event_types.size())
return; return;
event_types[idx].plot = true; 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(), return std::any_of(event_types.begin(), event_types.end(),
[] (const event_type &e) { return !e.plot; }); [] (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); 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(); return QString();
QString name = QString::fromUtf8(ev->name); QString name = QString::fromStdString(ev.name);
return event_type_name(std::move(name), get_event_severity(ev)); return event_type_name(std::move(name), ev.get_severity());
} }
QString event_type_name(int idx) QString event_type_name(int idx)

View File

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

View File

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

View File

@ -1,5 +1,4 @@
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
#include "ssrf.h"
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <sys/stat.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); (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; int success = 0;
/* Grr. libzip needs to re-open the file, it can't take a buffer */ /* 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; 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; struct git_info info;
const char *fmt; const char *fmt;

View File

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

View File

@ -134,87 +134,87 @@ static const range_mode_description *get_range_mode_description(enum filter_cons
return nullptr; 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) { for (const auto &desc: type_descriptions) {
if (same_string(desc.token, s)) if (desc.token == s)
return desc.type; 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; 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) { for (const auto &desc: string_mode_descriptions) {
if (same_string(desc.token, s)) if (desc.token == s)
return desc.mode; 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; 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) { for (const auto &desc: range_mode_descriptions) {
if (same_string(desc.token, s)) if (desc.token == s)
return desc.mode; 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; 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); const type_description *desc = get_type_description(type);
return desc ? desc->token : "unknown"; 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); const string_mode_description *desc = get_string_mode_description(mode);
return desc ? desc->token : "unknown"; 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); const range_mode_description *desc = get_range_mode_description(mode);
return desc ? desc->token : "unknown"; 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); const type_description *desc = get_type_description(type);
return desc ? desc - type_descriptions : -1; 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); const string_mode_description *desc = get_string_mode_description(mode);
return desc ? desc - string_mode_descriptions : -1; 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); const range_mode_description *desc = get_range_mode_description(mode);
return desc ? desc - range_mode_descriptions : -1; 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)) if (index >= 0 && index < (int)std::size(type_descriptions))
return type_descriptions[index].type; return type_descriptions[index].type;
return (enum filter_constraint_type)-1; 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)) if (index >= 0 && index < (int)std::size(string_mode_descriptions))
return string_mode_descriptions[index].mode; return string_mode_descriptions[index].mode;
return (enum filter_constraint_string_mode)-1; 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)) if (index >= 0 && index < (int)std::size(range_mode_descriptions))
return range_mode_descriptions[index].mode; return range_mode_descriptions[index].mode;
@ -390,7 +390,7 @@ QStringList filter_contraint_multiple_choice_translated(enum filter_constraint_t
return QStringList(); 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 // 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 // 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); 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; 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); 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); const type_description *desc = get_type_description(type);
return desc && desc->has_string_mode; 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); const type_description *desc = get_type_description(type);
return desc && desc->has_range_mode; 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); const type_description *desc = get_type_description(type);
return desc && desc->is_star_widget; 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); const type_description *desc = get_type_description(type);
return desc && desc->has_date; 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); const type_description *desc = get_type_description(type);
return desc && desc->has_time; 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); const type_description *desc = get_type_description(type);
return desc ? desc->decimal_places : 1; 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. // String constraints are valid if there is at least one term.
// Other constraints are always valid. // 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)) if (!filter_constraint_is_string(constraint->type))
return true; return true;
@ -551,14 +551,14 @@ filter_constraint::filter_constraint(const filter_constraint &c) :
data.numerical_range = c.data.numerical_range; data.numerical_range = c.data.numerical_range;
} }
filter_constraint::filter_constraint(const char *type_in, const char *string_mode_in, filter_constraint::filter_constraint(const std::string &type_in, const std::string &string_mode_in,
const char *range_mode_in, bool negate_in, const char *s_in) : const std::string &range_mode_in, bool negate_in, const std::string &s_in) :
type(filter_constraint_type_from_string(type_in)), type(filter_constraint_type_from_string(type_in)),
string_mode(FILTER_CONSTRAINT_STARTS_WITH), string_mode(FILTER_CONSTRAINT_STARTS_WITH),
range_mode(FILTER_CONSTRAINT_GREATER), range_mode(FILTER_CONSTRAINT_GREATER),
negate(negate_in) negate(negate_in)
{ {
QString s(s_in); QString s = QString::fromStdString(s_in);
if (filter_constraint_has_string_mode(type)) if (filter_constraint_has_string_mode(type))
string_mode = filter_constraint_string_mode_from_string(string_mode_in); string_mode = filter_constraint_string_mode_from_string(string_mode_in);
if (filter_constraint_has_range_mode(type)) if (filter_constraint_has_range_mode(type))
@ -622,22 +622,22 @@ filter_constraint::~filter_constraint()
delete data.string_list; 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)) { if (filter_constraint_is_timestamp(c.type)) {
std::string from_s = format_datetime(c->data.timestamp_range.from); std::string from_s = format_datetime(c.data.timestamp_range.from);
std::string to_s = format_datetime(c->data.timestamp_range.to); std::string to_s = format_datetime(c.data.timestamp_range.to);
return from_s + ',' + to_s; 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 ",". // TODO: this obviously breaks if the strings contain ",".
// That is currently not supported by the UI, but one day we might // That is currently not supported by the UI, but one day we might
// have to escape the strings. // have to escape the strings.
return c->data.string_list->join(",").toStdString(); return c.data.string_list->join(",").toStdString();
} else if (filter_constraint_is_multiple_choice(c->type)) { } else if (filter_constraint_is_multiple_choice(c.type)) {
return std::to_string(c->data.multiple_choice); return std::to_string(c.data.multiple_choice);
} else { } else {
return std::to_string(c->data.numerical_range.from) + ',' + return std::to_string(c.data.numerical_range.from) + ',' +
std::to_string(c->data.numerical_range.to); 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) static bool has_tags(const filter_constraint &c, const struct dive *d)
{ {
QStringList dive_tags; QStringList dive_tags;
for (const tag_entry *tag = d->tag_list; tag; tag = tag->next) for (const divetag *tag: d->tags)
dive_tags.push_back(QString::fromStdString(tag->tag->name).trimmed()); dive_tags.push_back(QString::fromStdString(tag->name).trimmed());
dive_tags.append(gettextFromC::tr(divemode_text_ui[d->dc.divemode]).trimmed()); dive_tags.append(gettextFromC::tr(divemode_text_ui[d->dcs[0].divemode]).trimmed());
return check(c, dive_tags); return check(c, dive_tags);
} }
static bool has_people(const filter_constraint &c, const struct dive *d) static bool has_people(const filter_constraint &c, const struct dive *d)
{ {
QStringList dive_people; 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()); 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()); dive_people.push_back(s.trimmed());
return check(c, dive_people); return check(c, dive_people);
} }
@ -838,10 +838,10 @@ static bool has_locations(const filter_constraint &c, const struct dive *d)
{ {
QStringList diveLocations; QStringList diveLocations;
if (d->divetrip) if (d->divetrip)
diveLocations.push_back(QString(d->divetrip->location).trimmed()); diveLocations.push_back(QString::fromStdString(d->divetrip->location).trimmed());
if (d->dive_site) 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); 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) static bool has_weight_type(const filter_constraint &c, const struct dive *d)
{ {
QStringList weightsystemTypes; QStringList weightsystemTypes;
for (int i = 0; i < d->weightsystems.nr; ++i) for (auto &ws: d->weightsystems)
weightsystemTypes.push_back(d->weightsystems.weightsystems[i].description); weightsystemTypes.push_back(QString::fromStdString(ws.description));
return check(c, weightsystemTypes); 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) static bool has_cylinder_type(const filter_constraint &c, const struct dive *d)
{ {
QStringList cylinderTypes; QStringList cylinderTypes;
for (int i = 0; i < d->cylinders.nr; ++i) for (const cylinder_t &cyl: d->cylinders)
cylinderTypes.push_back(d->cylinders.cylinders[i].type.description); cylinderTypes.push_back(QString::fromStdString(cyl.type.description));
return check(c, cylinderTypes); 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) static bool has_suits(const filter_constraint &c, const struct dive *d)
{ {
QStringList diveSuits; QStringList diveSuits;
if (d->suit) if (!d->suit.empty())
diveSuits.push_back(QString(d->suit)); diveSuits.push_back(QString::fromStdString(d->suit));
return check(c, diveSuits); return check(c, diveSuits);
} }
static bool has_notes(const filter_constraint &c, const struct dive *d) static bool has_notes(const filter_constraint &c, const struct dive *d)
{ {
QStringList diveNotes; QStringList diveNotes;
if (d->notes) if (!d->notes.empty())
diveNotes.push_back(QString(d->notes)); diveNotes.push_back(QString::fromStdString(d->notes));
return check(c, diveNotes); 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) static bool check_cylinder_size(const filter_constraint &c, const struct dive *d)
{ {
for (int i = 0; i < d->cylinders.nr; ++i) { return std::any_of(d->cylinders.begin(), d->cylinders.end(), [&c](auto &cyl)
const cylinder_t &cyl = d->cylinders.cylinders[i]; { return cyl.type.size.mliter &&
if (cyl.type.size.mliter && check_numerical_range(c, cyl.type.size.mliter)) check_numerical_range(c, cyl.type.size.mliter); });
return true;
}
return false;
} }
static bool check_gas_range(const filter_constraint &c, const struct dive *d, gas_component component) 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) { return std::any_of(d->cylinders.begin(), d->cylinders.end(), [&c, &component](auto &cyl)
const cylinder_t &cyl = d->cylinders.cylinders[i]; { return check_numerical_range(c, get_gas_component_fraction(cyl.gasmix, component).permille); });
if (check_numerical_range(c, get_gas_component_fraction(cyl.gasmix, component).permille))
return true;
}
return false;
} }
static long days_since_epoch(timestamp_t timestamp) 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. // where the given timestamp is during that dive.
return time_during_dive_with_offset(d, c.data.timestamp_range.from, 0) != c.negate; return time_during_dive_with_offset(d, c.data.timestamp_range.from, 0) != c.negate;
case FILTER_CONSTRAINT_LESS: 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: case FILTER_CONSTRAINT_GREATER:
return (d->when >= c.data.timestamp_range.from) != c.negate; return (d->when >= c.data.timestamp_range.from) != c.negate;
case FILTER_CONSTRAINT_RANGE: case FILTER_CONSTRAINT_RANGE:
return (d->when >= c.data.timestamp_range.from && 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; 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 // 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. // that run past midnight. We might want to special case that.
return (seconds_since_midnight(d->when) <= from && 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: 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: case FILTER_CONSTRAINT_GREATER:
return (seconds_since_midnight(d->when) >= from) != negate; return (seconds_since_midnight(d->when) >= from) != negate;
case FILTER_CONSTRAINT_RANGE: case FILTER_CONSTRAINT_RANGE:
return (seconds_since_midnight(d->when) >= from && return (seconds_since_midnight(d->when) >= from &&
seconds_since_midnight(dive_endtime(d)) <= to) != negate; seconds_since_midnight(d->endtime()) <= to) != negate;
} }
return false; return false;
} }
@ -1074,11 +1067,11 @@ bool filter_constraint_match_dive(const filter_constraint &c, const struct dive
case FILTER_CONSTRAINT_SAC: case FILTER_CONSTRAINT_SAC:
return check_numerical_range_non_zero(c, d->sac); return check_numerical_range_non_zero(c, d->sac);
case FILTER_CONSTRAINT_LOGGED: case FILTER_CONSTRAINT_LOGGED:
return is_logged(d) != c.negate; return d->is_logged() != c.negate;
case FILTER_CONSTRAINT_PLANNED: case FILTER_CONSTRAINT_PLANNED:
return is_planned(d) != c.negate; return d->is_planned() != c.negate;
case FILTER_CONSTRAINT_DIVE_MODE: 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: case FILTER_CONSTRAINT_TAGS:
return has_tags(c, d); return has_tags(c, d);
case FILTER_CONSTRAINT_PEOPLE: case FILTER_CONSTRAINT_PEOPLE:

View File

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

View File

@ -4,24 +4,14 @@
#include "qthelper.h" #include "qthelper.h"
#include "subsurface-string.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(); switch (data.fulltextStringMode) {
}
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) {
default: default:
case StringFilterMode::SUBSTRING: case StringFilterMode::SUBSTRING:
return "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")) if (fulltext_string_mode == "substring")
preset->data.fulltextStringMode = StringFilterMode::SUBSTRING; data.fulltextStringMode = StringFilterMode::SUBSTRING;
else if (same_string(fulltext_string_mode, "startswith")) else if (fulltext_string_mode == "startswith")
preset->data.fulltextStringMode = StringFilterMode::STARTSWITH; data.fulltextStringMode = StringFilterMode::STARTSWITH;
else // if (same_string(fulltext_string_mode, "exact")) else // if (fulltext_string_mode == "exact"))
preset->data.fulltextStringMode = StringFilterMode::EXACT; data.fulltextStringMode = StringFilterMode::EXACT;
preset->data.fullText = fulltext; 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(); data.constraints.emplace_back(type, string_mode, range_mode, negate, constraint_data);
}
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);
} }

View File

@ -1,75 +1,23 @@
// SPDX-License-Identifier: GPL-2.0 // 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 #ifndef FILTER_PRESETS_H
#define FILTER_PRESETS_H #define FILTER_PRESETS_H
#include "divefilter.h"
#include <string>
struct dive; struct dive;
struct filter_constraint; 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 { struct filter_preset {
std::string name; std::string name;
FilterData data; 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 #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()); 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_list ap;
va_start(ap, fmt); va_start(ap, fmt);
size_t stringsize = vsnprintf(NULL, 0, fmt, ap); std::string res = vformat_string_std(fmt, ap);
va_end(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) if (stringsize == 0)
return std::string(); return std::string();
std::string res; std::string res;
res.resize(stringsize); // Pointless clearing, oh my. res.resize(stringsize); // Pointless clearing, oh my.
// This overwrites the terminal null-byte of std::string. // This overwrites the terminal null-byte of std::string.
// That's probably "undefined behavior". Oh my. // That's probably "undefined behavior". Oh my.
va_start(ap, fmt);
vsnprintf(res.data(), stringsize + 1, fmt, ap); vsnprintf(res.data(), stringsize + 1, fmt, ap);
va_end(ap);
return res; return res;
} }

View File

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

View File

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

View File

@ -5,43 +5,30 @@
// issues such as COW semantics and UTF-16 encoding, it provides // issues such as COW semantics and UTF-16 encoding, it provides
// platform independence and reasonable performance. Therefore, // platform independence and reasonable performance. Therefore,
// this is based in QString instead of std::string. // 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 #ifndef FULLTEXT_H
#define 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; struct dive;
void fulltext_register(struct dive *d); // Note: can be called repeatedly 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(struct dive *d); // Note: can be called repeatedly
void fulltext_unregister_all(); // Unregisters all dives in the dive table void fulltext_unregister_all(); // Unregisters all dives in the dive table
void fulltext_populate(); // Registers 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 { enum class StringFilterMode {
SUBSTRING = 0, SUBSTRING = 0,
STARTSWITH = 1, STARTSWITH = 1,
EXACT = 2 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 // A fulltext query. Basically a list of normalized words we search for
struct FullTextQuery { struct FullTextQuery {
std::vector<QString> words; 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); bool fulltext_dive_matches(const struct dive *d, const FullTextQuery &q, StringFilterMode);
#endif #endif
#endif

View File

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

View File

@ -5,6 +5,7 @@
#include "gettext.h" #include "gettext.h"
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <QtGlobal> // for QT_TRANSLATE_NOOP
/* Perform isobaric counterdiffusion calculations for gas changes in trimix dives. /* 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 * 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); 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; unsigned int o2, he;
o2 = get_o2(*mix); o2 = get_o2(mix);
he = get_he(*mix); he = get_he(mix);
/* Regular air: leave empty */ /* Regular air: leave empty */
if (!he) { if (!he) {
if (!o2) if (!o2)
return; return;
/* 20.8% to 21% O2 is just air */ /* 20.8% to 21% O2 is just air */
if (gasmix_is_air(*mix)) { if (gasmix_is_air(mix)) {
mix->o2.permille = 0; mix.o2.permille = 0;
return; return;
} }
} }
@ -61,7 +62,7 @@ void sanitize_gasmix(struct gasmix *mix)
if (o2 <= 1000 && he <= 1000 && o2 + he <= 1000) if (o2 <= 1000 && he <= 1000 && o2 + he <= 1000)
return; return;
report_info("Odd gasmix: %u O2 %u He", o2, he); 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) 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. * 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 * Call parameters: po2 = po2 value applicable to the record in calling function
* amb_pressure = ambient pressure 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. * *mix = structure containing cylinder gas mixture information.
* divemode = the dive mode pertaining to this point in the dive profile. * 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. * 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) { if (po2 >= amb_pressure) {
pressures->o2 = amb_pressure; pressures.o2 = amb_pressure;
pressures->n2 = pressures->he = 0.0; pressures.n2 = pressures.he = 0.0;
} else { } else {
pressures->o2 = po2; pressures.o2 = po2;
if (get_o2(mix) == 1000) { if (get_o2(mix) == 1000) {
pressures->he = pressures->n2 = 0; pressures.he = pressures.n2 = 0;
} else { } else {
pressures->he = (amb_pressure - pressures->o2) * (double)get_he(mix) / (1000 - get_o2(mix)); pressures.he = (amb_pressure - pressures.o2) * (double)get_he(mix) / (1000 - get_o2(mix));
pressures->n2 = amb_pressure - pressures->o2 - pressures->he; pressures.n2 = amb_pressure - pressures.o2 - pressures.he;
} }
} }
} else { } else {
if (divemode == PSCR) { /* The steady state approximation should be good enough */ 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) { if (get_o2(mix) != 1000) {
pressures->he = (amb_pressure - pressures->o2) * get_he(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)); pressures.n2 = (amb_pressure - pressures.o2) * get_n2(mix) / (1000.0 - get_o2(mix));
} else { } else {
pressures->he = pressures->n2 = 0; pressures.he = pressures.n2 = 0;
} }
} else { } else {
// Open circuit dives: no gas pressure values available, they need to be calculated // 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.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.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.n2 = get_n2(mix) / 1000.0 * amb_pressure;
} }
} }
return pressures;
} }
enum gastype gasmix_to_type(struct gasmix mix) enum gastype gasmix_to_type(struct gasmix mix)

View File

@ -5,12 +5,6 @@
#include "divemode.h" #include "divemode.h"
#include "units.h" #include "units.h"
#ifdef __cplusplus
extern "C" {
#else
#include <stdbool.h>
#endif
enum gas_component { N2, HE, O2 }; enum gas_component { N2, HE, O2 };
// o2 == 0 && he == 0 -> air // 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); int pscr_o2(const double amb_pressure, struct gasmix mix);
struct gas_pressures { 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 int gasmix_distance(struct gasmix a, struct gasmix b);
extern fraction_t get_gas_component_fraction(struct gasmix mix, enum gas_component component); 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_air(struct gasmix gasmix);
extern bool gasmix_is_invalid(struct gasmix mix); 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 const char *gastype_name(enum gastype type);
extern fraction_t make_fraction(int f); extern fraction_t make_fraction(int f);
#ifdef __cplusplus
}
#endif
#endif #endif

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
/* gaspressures.c /* gaspressures.cpp
* --------------- * ----------------
* This file contains the routines to calculate the gas pressures in the cylinders. * This file contains the routines to calculate the gas pressures in the cylinders.
* The functions below support the code in profile.cpp. * The functions below support the code in profile.cpp.
* The high-level function is populate_pressure_information(), called by function * The high-level function is populate_pressure_information(), called by function
@ -10,15 +10,8 @@
* populate_pressure_information() -> calc_pressure_time() * populate_pressure_information() -> calc_pressure_time()
* -> fill_missing_tank_pressures() -> fill_missing_segment_pressures() * -> fill_missing_tank_pressures() -> fill_missing_segment_pressures()
* -> get_pr_interpolate_data() * -> 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 "dive.h"
#include "event.h" #include "event.h"
#include "profile.h" #include "profile.h"
@ -26,23 +19,29 @@
#include "pref.h" #include "pref.h"
#include <stdlib.h> #include <stdlib.h>
#include <vector>
/* /*
* simple structure to track the beginning and end tank pressure as * simple structure to track the beginning and end tank pressure as
* well as the integral of depth over time spent while we have no * well as the integral of depth over time spent while we have no
* pressure reading from the tank */ * pressure reading from the tank */
typedef struct pr_track_struct pr_track_t; struct pr_track_t {
struct pr_track_struct {
int start; int start;
int end; int end;
int t_start; int t_start;
int t_end; int t_end;
int pressure_time; 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_t {
struct pr_interpolate_struct {
int start; int start;
int end; int end;
int pressure_time; int pressure_time;
@ -51,61 +50,17 @@ struct pr_interpolate_struct {
enum interpolation_strategy {SAC, TIME, CONSTANT}; 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 #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); printf("cyl%d:\n", cyl);
list = track_pr; for (const auto &item: track_pr) {
while (list) {
printf(" start %f end %f t_start %d:%02d t_end %d:%02d pt %d\n", printf(" start %f end %f t_start %d:%02d t_end %d:%02d pt %d\n",
mbar_to_PSI(list->start), mbar_to_PSI(item.start),
mbar_to_PSI(list->end), mbar_to_PSI(item.end),
FRACTION_TUPLE(list->t_start, 60), FRACTION_TUPLE(item.t_start, 60),
FRACTION_TUPLE(list->t_end, 60), FRACTION_TUPLE(item.t_end, 60),
list->pressure_time); item.pressure_time);
list = list->next;
} }
} }
#endif #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 * segments according to how big of a time_pressure area
* they have. * 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; for (auto it = list.begin(); it != list.end(); ++it) {
int start = it->start, end;
while (list) {
int start = list->start, end;
pr_track_t *tmp = list;
int pt_sum = 0, pt = 0; int pt_sum = 0, pt = 0;
auto tmp = it;
for (;;) { for (;;) {
pt_sum += tmp->pressure_time; pt_sum += tmp->pressure_time;
end = tmp->end; end = tmp->end;
if (end) if (end)
break; break;
end = start; end = start;
if (!tmp->next) if (std::next(tmp) == list.end())
break; break;
tmp = tmp->next; ++tmp;
} }
if (!start) 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. * Now dole out the pressures relative to pressure-time.
*/ */
list->start = start; it->start = start;
tmp->end = end; tmp->end = end;
switch (strategy) { switch (strategy) {
case SAC: case SAC:
for (;;) { for (;;) {
int pressure; int pressure;
pt += list->pressure_time; pt += it->pressure_time;
pressure = start; pressure = start;
if (pt_sum) if (pt_sum)
pressure -= lrint((start - end) * (double)pt / pt_sum); pressure -= lrint((start - end) * (double)pt / pt_sum);
list->end = pressure; it->end = pressure;
if (list == tmp) if (it == tmp)
break; break;
list = list->next; ++it;
list->start = pressure; it->start = pressure;
} }
break; break;
case TIME: case TIME:
if (list->t_end && (tmp->t_start - tmp->t_end)) { if (it->t_end && (tmp->t_start - tmp->t_end)) {
magic = (list->t_start - tmp->t_end) / (tmp->t_start - tmp->t_end); double magic = (it->t_start - tmp->t_end) / (tmp->t_start - tmp->t_end);
list->end = lrint(start - (start - end) * magic); it->end = lrint(start - (start - end) * magic);
} else { } else {
list->end = start; it->end = start;
} }
break; break;
case CONSTANT: 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 #endif
static struct pr_interpolate_struct get_pr_interpolate_data(pr_track_t *segment, struct plot_info *pi, int cur) 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; { // cur = index to pi.entry corresponding to t_end of segment;
struct pr_interpolate_struct interpolate; pr_interpolate_t interpolate;
int i; int i;
struct plot_data *entry;
interpolate.start = segment->start; interpolate.start = segment.start;
interpolate.end = segment->end; interpolate.end = segment.end;
interpolate.acc_pressure_time = 0; interpolate.acc_pressure_time = 0;
interpolate.pressure_time = 0; interpolate.pressure_time = 0;
for (i = 0; i < pi->nr; i++) { for (i = 0; i < pi.nr; i++) {
entry = pi->entry + i; const plot_data &entry = pi.entry[i];
if (entry->sec < segment->t_start) if (entry.sec < segment.t_start)
continue; continue;
interpolate.pressure_time += entry->pressure_time; interpolate.pressure_time += entry.pressure_time;
if (entry->sec >= segment->t_end) if (entry.sec >= segment.t_end)
break; break;
if (i <= cur) if (i <= cur)
interpolate.acc_pressure_time += entry->pressure_time; interpolate.acc_pressure_time += entry.pressure_time;
} }
return interpolate; 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; int i;
struct plot_data *entry;
pr_interpolate_t interpolate = { 0, 0, 0, 0 }; pr_interpolate_t interpolate = { 0, 0, 0, 0 };
pr_track_t *last_segment = NULL;
int cur_pr; int cur_pr;
enum interpolation_strategy strategy; enum interpolation_strategy strategy;
/* no segment where this cylinder is used */ /* no segment where this cylinder is used */
if (!track_pr) if (track_pr.empty())
return; return;
if (get_cylinder(dive, cyl)->cylinder_use == OC_GAS) 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 else
strategy = TIME; strategy = TIME;
fill_missing_segment_pressures(track_pr, strategy); // Interpolate the missing tank pressure values .. 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. // and keep the starting pressure for each cylinder.
#ifdef DEBUG_PR_TRACK #ifdef DEBUG_PR_TRACK
dump_pr_track(cyl, track_pr); 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 * 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 */ * 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: auto last_segment = track_pr.end();
double magic; for (i = 1; i < pi.nr; i++) { // For each point on the profile:
pr_track_t *segment; const struct plot_data &entry = pi.entry[i];
int pressure;
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 = track_pr.end(); // get rid of interpolation data,
if (pressure) { // If there is a valid pressure value, cur_pr = pressure; // set current pressure
last_segment = NULL; // get rid of interpolation data, continue; // and skip to next point.
cur_pr = pressure; // set current pressure
continue; // and skip to next point.
} }
// If there is NO valid pressure value.. // If there is NO valid pressure value..
// Find the pressure segment corresponding to this entry.. // Find the pressure segment corresponding to this entry..
segment = track_pr; auto it = track_pr.begin();
while (segment && segment->t_end < entry->sec) // Find the track_pr with end time.. while (it != track_pr.end() && it->t_end < entry.sec) // Find the track_pr with end time..
segment = segment->next; // ..that matches the plot_info time (entry->sec) ++it; // ..that matches the plot_info time (entry.sec)
// After last segment? All done. // After last segment? All done.
if (!segment) if (it == track_pr.end())
break; break;
// Before first segment, or between segments.. Go on, no interpolation. // Before first segment, or between segments.. Go on, no interpolation.
if (segment->t_start > entry->sec) if (it->t_start > entry.sec)
continue; continue;
if (!segment->pressure_time) { // Empty segment? if (!it->pressure_time) { // Empty segment?
set_plot_pressure_data(pi, i, SENSOR_PR, cyl, cur_pr); set_plot_pressure_data(pi, i, SENSOR_PR, cyl, cur_pr);
// Just use our current pressure // Just use our current pressure
continue; // and skip to next point. continue; // and skip to next point.
} }
// If there is a valid segment but no tank pressure .. // If there is a valid segment but no tank pressure ..
if (segment == last_segment) { if (it == last_segment) {
interpolate.acc_pressure_time += entry->pressure_time; interpolate.acc_pressure_time += entry.pressure_time;
} else { } else {
// Set up an interpolation structure // Set up an interpolation structure
interpolate = get_pr_interpolate_data(segment, pi, i); interpolate = get_pr_interpolate_data(*it, pi, i);
last_segment = segment; last_segment = it;
} }
if(get_cylinder(dive, cyl)->cylinder_use == OC_GAS) { 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 this segment has pressure_time, then calculate a new interpolated pressure */
if (interpolate.pressure_time) { if (interpolate.pressure_time) {
/* Overall pressure change over total pressure-time for this segment*/ /* 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 */ /* Use that overall pressure change to update the current pressure */
cur_pr = lrint(interpolate.start + magic * interpolate.acc_pressure_time); cur_pr = lrint(interpolate.start + magic * interpolate.acc_pressure_time);
} }
} else { } else {
magic = (interpolate.end - interpolate.start) / (segment->t_end - segment->t_start); double magic = (interpolate.end - interpolate.start) / (it->t_end - it->t_start);
cur_pr = lrint(segment->start + magic * (entry->sec - segment->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 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? * What's the pressure-time between two plot data entries?
* We're calculating the integral of pressure over time by * 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 * scale pressures, so it ends up being a unitless scaling
* factor. * 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 time = b.sec - a.sec;
int depth = (a->depth + b->depth) / 2; int depth = (a.depth + b.depth) / 2;
if (depth <= SURFACE_THRESHOLD) if (depth <= SURFACE_THRESHOLD)
return 0; return 0;
@ -347,36 +290,33 @@ static inline int calc_pressure_time(const struct dive *dive, struct plot_data *
#ifdef PRINT_PRESSURES_DEBUG #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(): // 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; 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)); printf("%5d |%9d | %9d |\n", i, get_plot_sensor_pressure(pi, i), get_plot_interpolated_pressure(pi, i));
} }
#endif #endif
/* This function goes through the list of tank pressures, of structure plot_info for the dive profile where each /* 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 * 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 * 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 * 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 * 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; int first, last, cyl;
cylinder_t *cylinder = get_cylinder(dive, sensor); const cylinder_t *cylinder = get_cylinder(dive, sensor);
pr_track_t *track = NULL; std::vector<pr_track_t> track;
pr_track_t *current = NULL; size_t current = std::string::npos;
const struct event *ev, *b_ev;
int missing_pr = 0, dense = 1; 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 }; 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; return;
/* if we have no pressure data whatsoever, this is pointless, so let's just 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 */ /* Get a rough range of where we have any pressures at all */
first = last = -1; 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); int pressure = get_plot_sensor_pressure(pi, i, sensor);
if (!pressure) if (!pressure)
@ -409,34 +349,31 @@ void populate_pressure_information(const struct dive *dive, const struct divecom
* itself has a gas change event. * itself has a gas change event.
*/ */
cyl = sensor; cyl = sensor;
ev = NULL; event_loop loop_gas("gaschange");
if (has_gaschange_event(dive, dc, sensor)) const struct event *ev = has_gaschange_event(dive, dc, sensor) ?
ev = get_next_event(dc->events, "gaschange"); loop_gas.next(*dc) : nullptr;
b_ev = get_next_event(dc->events, "modechange"); divemode_loop loop_mode(*dc);
for (int i = first; i <= last; i++) { 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 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 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) if (cyl < 0)
cyl = sensor; 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 divemode_t dmode = loop_mode.next(time);
dmode = b_ev->value; // find 1st divemode change event after the current
b_ev = get_next_event(b_ev->next, "modechange"); // divemode change.
}
if (current) { // calculate pressure-time, taking into account the dive mode for this specific segment. 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, entry - 1, entry) * gasfactor[dmode] + 0.5); entry.pressure_time = (int)(calc_pressure_time(dive, pi.entry[i - 1], entry) * gasfactor[dmode] + 0.5);
current->pressure_time += entry->pressure_time; track[current].pressure_time += entry.pressure_time;
current->t_end = entry->sec; track[current].t_end = entry.sec;
if (pressure) if (pressure)
current->end = pressure; track[current].end = pressure;
} }
// We have a final pressure for 'current' // 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 // current pressure track entry and continue
// until we get back to this cylinder. // until we get back to this cylinder.
if (cyl != sensor) { if (cyl != sensor) {
current = NULL; current = std::string::npos;
set_plot_pressure_data(pi, i, SENSOR_PR, sensor, 0); set_plot_pressure_data(pi, i, SENSOR_PR, sensor, 0);
continue; 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 // continue with or without a tracking entry. Mark any
// existing tracking entry as non-dense, and remember // existing tracking entry as non-dense, and remember
// to fill in interpolated data. // to fill in interpolated data.
if (current && !pressure) { if (current != std::string::npos && !pressure) {
missing_pr = 1; missing_pr = 1;
dense = 0; dense = 0;
continue; 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 // If we already have a pressure tracking entry, and
// it has not had any missing samples, just continue // it has not had any missing samples, just continue
// using it - there's nothing to interpolate yet. // using it - there's nothing to interpolate yet.
if (current && dense) if (current != std::string::npos && dense)
continue; continue;
// We need to start a new tracking entry, either // 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. // missing entries that need to be interpolated.
// Or maybe we didn't have a previous one at all, // Or maybe we didn't have a previous one at all,
// and this is the first pressure entry. // and this is the first pressure entry.
current = pr_track_alloc(pressure, entry->sec); track.emplace_back(pressure, entry.sec);
track = list_add(track, current); current = track.size() - 1;
dense = 1; dense = 1;
} }
if (missing_pr) { if (missing_pr)
fill_missing_tank_pressures(dive, pi, track, sensor); fill_missing_tank_pressures(dive, pi, track, sensor);
}
#ifdef PRINT_PRESSURES_DEBUG #ifdef PRINT_PRESSURES_DEBUG
debug_print_pressures(pi); debug_print_pressures(pi);
#endif #endif
list_free(track);
} }

View File

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

View File

@ -2,22 +2,10 @@
#ifndef MYGETTEXT_H #ifndef MYGETTEXT_H
#define MYGETTEXT_H #define MYGETTEXT_H
#ifdef __cplusplus const char *trGettext(const char *);
extern "C" const char *trGettext(const char *);
static inline const char *translate(const char *, const char *arg) static inline const char *translate(const char *, const char *arg)
{ {
return trGettext(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 #endif // MYGETTEXT_H

View File

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

View File

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

View File

@ -4,7 +4,6 @@
#pragma clang diagnostic ignored "-Wmissing-field-initializers" #pragma clang diagnostic ignored "-Wmissing-field-initializers"
#endif #endif
#include "ssrf.h"
#include <stdio.h> #include <stdio.h>
#include <ctype.h> #include <ctype.h>
#include <string.h> #include <string.h>
@ -58,7 +57,7 @@ static bool includes_string_caseinsensitive(const char *haystack, const char *ne
return 0; 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; 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) // 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 // 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. // 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; int ret = 0;
if (update_progress_cb) 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) 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 // 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 // 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 // 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 // That zero-byte update is so that we don't get hash
// collisions for "repo1 branch" vs "repo 1branch". // collisions for "repo1 branch" vs "repo 1branch".
SHA1_Init(&ctx); SHA1 sha;
SHA1_Update(&ctx, remote.c_str(), remote.size()); sha.update(remote);
SHA1_Update(&ctx, "", 1); sha.update("", 1);
SHA1_Update(&ctx, branch.c_str(), branch.size()); sha.update(branch);
SHA1_Final(hash, &ctx); auto hash = sha.hash();
return format_string_std("%s/cloudstorage/%02x%02x%02x%02x%02x%02x%02x%02x", return format_string_std("%s/cloudstorage/%02x%02x%02x%02x%02x%02x%02x%02x",
system_default_directory(), system_default_directory(),
hash[0], hash[1], hash[2], hash[3], hash[0], hash[1], hash[2], hash[3],
@ -244,7 +240,7 @@ static bool exceeded_auth_attempts()
return false; return false;
} }
extern "C" int credential_ssh_cb(git_cred **out, int credential_ssh_cb(git_cred **out,
const char *, const char *,
const char *, const char *,
unsigned int allowed_types, unsigned int allowed_types,
@ -276,7 +272,7 @@ extern "C" int credential_ssh_cb(git_cred **out,
return GIT_EUSER; return GIT_EUSER;
} }
extern "C" int credential_https_cb(git_cred **out, int credential_https_cb(git_cred **out,
const char *, const char *,
const char *, const char *,
unsigned int, unsigned int,
@ -291,7 +287,7 @@ extern "C" int credential_https_cb(git_cred **out,
return git_cred_userpass_plaintext_new(out, username, password); 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) if (verbose)
report_info("git storage: certificate callback for host %s with validity %d\n", host, valid); 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; 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) 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_commit *local_commit, *remote_commit, *base_commit;
git_index *merged_index; git_index *merged_index;
git_merge_options merge_options; git_merge_options merge_options;
struct membufferpp msg; membuffer msg;
if (verbose) { if (verbose) {
char outlocal[41], outremote[41]; char outlocal[41], outremote[41];

View File

@ -4,14 +4,12 @@
#include "git2.h" #include "git2.h"
#include "filterpreset.h" #include "filterpreset.h"
#include <string>
struct dive_log; struct dive_log;
struct git_oid;
#ifdef __cplusplus struct git_repository;
extern "C" { struct divelog;
#else
#include <stdbool.h>
#endif
#define CLOUD_HOST_US "ssrf-cloud-us.subsurface-divelog.org" // preferred (faster/bigger) server in the US #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 #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_local_only;
extern bool git_remote_sync_successful; 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 *); extern void set_git_id(const struct git_oid *);
void set_git_update_cb(int(*)(const char *)); void set_git_update_cb(int(*)(const char *));
int git_storage_update_progress(const char *text); int git_storage_update_progress(const char *text);
int get_authorship(git_repository *repo, git_signature **authorp); int get_authorship(git_repository *repo, git_signature **authorp);
#ifdef __cplusplus
}
#include <string>
struct git_oid;
struct git_repository;
struct divelog;
struct git_info { struct git_info {
std::string url; std::string url;
std::string branch; 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 do_git_save(struct git_info *, bool select_only, bool create_empty);
extern int git_create_local_repo(const std::string &filename); extern int git_create_local_repo(const std::string &filename);
#endif
#endif // GITACCESS_H #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. // For io error or video, return early with the appropriate dummy-icon.
if (type == MEDIATYPE_IO_ERROR) if (type == MEDIATYPE_IO_ERROR)
return { failImage, MEDIATYPE_IO_ERROR, zero_duration }; return { failImage, MEDIATYPE_IO_ERROR, duration_t() };
else if (type == MEDIATYPE_VIDEO) else if (type == MEDIATYPE_VIDEO)
return fetchVideoThumbnail(filename, originalFilename, md.duration); 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, // Try to check for a video-file extension. Since we couldn't parse the video file,
// we pass 0 as the duration. // we pass 0 as the duration.
if (hasVideoFileExtension(filename)) 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. // 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. // 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 // 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. // 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)); 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 // 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. // 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 // 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. // 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) if (localFilename != filename)
thumbnail = fetchImage(localFilename, filename, tryDownload); thumbnail = fetchImage(localFilename, filename, tryDownload);
@ -187,7 +187,7 @@ Thumbnailer::Thumbnail Thumbnailer::getPictureThumbnailFromStream(QDataStream &s
{ {
QImage res; QImage res;
stream >> res; stream >> res;
return { std::move(res), MEDIATYPE_PICTURE, zero_duration }; return { std::move(res), MEDIATYPE_PICTURE, duration_t() };
} }
void Thumbnailer::markVideoThumbnail(QImage &img) 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, // Likewise test the duration and number of pictures for sanity (no videos longer than 10 h,
// no more than 10000 pictures). // no more than 10000 pictures).
if (stream.status() != QDataStream::Ok || duration > 36000 || numPics > 10000) 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 // 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 // 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); QString filename = thumbnailFileName(picture_filename);
if (filename.isEmpty()) if (filename.isEmpty())
return { QImage(), MEDIATYPE_UNKNOWN, zero_duration }; return { QImage(), MEDIATYPE_UNKNOWN, duration_t() };
QFile file(filename); QFile file(filename);
if (prefs.auto_recalculate_thumbnails) { if (prefs.auto_recalculate_thumbnails) {
@ -254,13 +254,13 @@ Thumbnailer::Thumbnail Thumbnailer::getThumbnailFromCache(const QString &picture
if (pictureTime.isValid() && thumbnailTime.isValid() && thumbnailTime < pictureTime) { if (pictureTime.isValid() && thumbnailTime.isValid() && thumbnailTime < pictureTime) {
// Both files exist, have valid timestamps and thumbnail was calculated before picture. // Both files exist, have valid timestamps and thumbnail was calculated before picture.
// Return an empty thumbnail to signal recalculation of the thumbnail // 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)) if (!file.open(QIODevice::ReadOnly))
return { QImage(), MEDIATYPE_UNKNOWN, zero_duration }; return { QImage(), MEDIATYPE_UNKNOWN, duration_t() };
QDataStream stream(&file); QDataStream stream(&file);
// Each thumbnail file is composed of a media-type and an image 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) { switch (type) {
case MEDIATYPE_PICTURE: return getPictureThumbnailFromStream(stream); case MEDIATYPE_PICTURE: return getPictureThumbnailFromStream(stream);
case MEDIATYPE_VIDEO: return getVideoThumbnailFromStream(stream, picture_filename); case MEDIATYPE_VIDEO: return getVideoThumbnailFromStream(stream, picture_filename);
case MEDIATYPE_UNKNOWN: return { unknownImage, MEDIATYPE_UNKNOWN, zero_duration }; case MEDIATYPE_UNKNOWN: return { unknownImage, MEDIATYPE_UNKNOWN, duration_t() };
default: return { QImage(), MEDIATYPE_UNKNOWN, zero_duration }; default: return { QImage(), MEDIATYPE_UNKNOWN, duration_t() };
} }
} }
@ -319,7 +319,7 @@ Thumbnailer::Thumbnail Thumbnailer::fetchVideoThumbnail(const QString &filename,
return { videoImage, MEDIATYPE_VIDEO, duration }; return { videoImage, MEDIATYPE_VIDEO, duration };
} else { } else {
// Video-thumbnailing is disabled. Write a thumbnail without picture. // 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; stream << thumbnail;
file.commit(); file.commit();
} }
return { thumbnail, MEDIATYPE_PICTURE, zero_duration }; return { thumbnail, MEDIATYPE_PICTURE, duration_t() };
} }
Thumbnailer::Thumbnail Thumbnailer::addUnknownThumbnailToCache(const QString &picture_filename) Thumbnailer::Thumbnail Thumbnailer::addUnknownThumbnailToCache(const QString &picture_filename)
@ -348,7 +348,7 @@ Thumbnailer::Thumbnail Thumbnailer::addUnknownThumbnailToCache(const QString &pi
QDataStream stream(&file); QDataStream stream(&file);
stream << (quint32)MEDIATYPE_UNKNOWN; 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) 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 // Frame extraction failed, but this was due to ffmpeg not starting
// add to the thumbnail cache as a video image with unknown thumbnail. // 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); QMutexLocker l(&lock);
workingOn.remove(filename); workingOn.remove(filename);
} }
@ -435,7 +435,7 @@ void Thumbnailer::imageDownloaded(QString filename)
void Thumbnailer::imageDownloadFailed(QString filename) void Thumbnailer::imageDownloadFailed(QString filename)
{ {
emit thumbnailChanged(filename, failImage, zero_duration); emit thumbnailChanged(filename, failImage, duration_t());
QMutexLocker l(&lock); QMutexLocker l(&lock);
workingOn.remove(filename); workingOn.remove(filename);
} }

View File

@ -5,7 +5,6 @@
#endif #endif
#include <stdlib.h> #include <stdlib.h>
#include "ssrf.h"
#include "dive.h" #include "dive.h"
#include "divesite.h" #include "divesite.h"
#include "errorhelper.h" #include "errorhelper.h"
@ -29,7 +28,7 @@ static int cobalt_profile_sample(void *param, int, char **data, char **)
if (data[1]) if (data[1])
state->cur_sample->depth.mm = atoi(data[1]); state->cur_sample->depth.mm = atoi(data[1]);
if (data[2]) 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); sample_end(state);
return 0; return 0;
@ -64,7 +63,7 @@ static int cobalt_buddies(void *param, int, char **data, char **)
struct parser_state *state = (struct parser_state *)param; struct parser_state *state = (struct parser_state *)param;
if (data[0]) if (data[0])
utf8_string(data[0], &state->cur_dive->buddy); utf8_string_std(data[0], &state->cur_dive->buddy);
return 0; return 0;
} }
@ -81,8 +80,8 @@ static int cobalt_visibility(void *, int, char **, char **)
static int cobalt_location(void *param, int, char **data, char **) static int cobalt_location(void *param, int, char **data, char **)
{ {
char **location = (char **)param; std::string *location = (std::string *)param;
*location = data[0] ? strdup(data[0]) : NULL; *location = data[0] ? data[0] : NULL;
return 0; return 0;
} }
@ -92,7 +91,7 @@ static int cobalt_dive(void *param, int, char **data, char **)
int retval = 0; int retval = 0;
struct parser_state *state = (struct parser_state *)param; struct parser_state *state = (struct parser_state *)param;
sqlite3 *handle = state->sql_handle; 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_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_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"; 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])); state->cur_dive->when = (time_t)(atol(data[1]));
if (data[4]) 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 /* data[5] should have information on Units used, but I cannot
* parse it at all based on the sample log I have received. The * 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 */ /* Cobalt stores the pressures, not the depth */
if (data[6]) 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]) 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]) 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. * 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]) { if (data[9]) {
utf8_string_std(data[9], &state->cur_settings.dc.serial_nr); utf8_string_std(data[9], &state->cur_settings.dc.serial_nr);
state->cur_settings.dc.deviceid = atoi(data[9]); 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); dc_settings_end(state);
settings_end(state); settings_end(state);
if (data[9]) { if (data[9]) {
state->cur_dive->dc.deviceid = atoi(data[9]); state->cur_dive->dcs[0].deviceid = atoi(data[9]);
state->cur_dive->dc.model = strdup("Cobalt import"); state->cur_dive->dcs[0].model = "Cobalt import";
} }
snprintf(get_buffer, sizeof(get_buffer) - 1, get_cylinder_template, state->cur_dive->number); 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; return 1;
} }
if (location && location_site) { if (!location.empty() && !location_site.empty()) {
char *tmp = (char *)malloc(strlen(location) + strlen(location_site) + 4); std::string tmp = location + " / " + location_site;
if (!tmp) { state->log->sites.find_or_create(tmp)->add_dive(state->cur_dive.get());
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);
} }
free(location);
free(location_site);
snprintf(get_buffer, sizeof(get_buffer) - 1, get_profile_template, state->cur_dive->number); 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); 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; return SQLITE_OK;
} }
int parse_cobalt_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
extern "C" int parse_cobalt_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
{ {
int retval; int retval;
struct parser_state state; struct parser_state state;

View File

@ -5,7 +5,6 @@
#include "dive.h" #include "dive.h"
#include "errorhelper.h" #include "errorhelper.h"
#include "ssrf.h"
#include "subsurface-string.h" #include "subsurface-string.h"
#include "divelist.h" #include "divelist.h"
#include "divelog.h" #include "divelog.h"
@ -270,7 +269,7 @@ static int parse_dan_format(const char *filename, struct xml_params *params, str
return ret; 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; int ret;
std::string mem; 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]; char *header[8];
int i, time; int i, time;
timestamp_t date; timestamp_t date;
struct dive *dive;
struct divecomputer *dc; struct divecomputer *dc;
for (i = 0; i < 8; i++) { 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) if (!date)
return 0; return 0;
dive = alloc_dive(); auto dive = std::make_unique<struct dive>();
dive->when = date; dive->when = date;
dive->number = atoi(header[1]); dive->number = atoi(header[1]);
dc = &dive->dc; dc = &dive->dcs[0];
time = 0; time = 0;
for (;;) { 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 = prepare_sample(dc);
sample->time.seconds = time; sample->time.seconds = time;
add_sample_data(sample, type, val); add_sample_data(sample, type, val);
finish_sample(dc);
time++; time++;
dc->duration.seconds = 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; break;
p = end + 1; p = end + 1;
} }
record_dive_to_table(dive, log->dives); log->dives.record_dive(std::move(dive));
return 1; 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; bool has_depth = false, has_setpoint = false, has_ndl = false;
char *lineptr; char *lineptr;
int prev_time = 0; int prev_time = 0;
cylinder_t cyl = empty_cylinder;
struct dive *dive;
struct divecomputer *dc; struct divecomputer *dc;
struct tm cur_tm; 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_min = mm;
cur_tm.tm_sec = ss; cur_tm.tm_sec = ss;
dive = alloc_dive(); auto dive = std::make_unique<struct dive>();
dive->when = utc_mktime(&cur_tm);; 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"); value = parse_mkvi_value(memtxt.data(), "Rig Serial number");
dive->dc.deviceid = atoi(value.c_str()); dive->dcs[0].deviceid = atoi(value.c_str());
dive->dc.divemode = CCR; dive->dcs[0].divemode = CCR;
dive->dc.no_o2sensors = 2; dive->dcs[0].no_o2sensors = 2;
cyl.cylinder_use = OXYGEN; {
cyl.type.size.mliter = 3000; cylinder_t cyl;
cyl.type.workingpressure.mbar = 200000; cyl.cylinder_use = OXYGEN;
cyl.type.description = "3l Mk6"; cyl.type.size.mliter = 3000;
cyl.gasmix.o2.permille = 1000; cyl.type.workingpressure.mbar = 200000;
cyl.manually_added = true; cyl.type.description = "3l Mk6";
cyl.bestmix_o2 = 0; cyl.gasmix.o2.permille = 1000;
cyl.bestmix_he = 0; cyl.manually_added = true;
add_cloned_cylinder(&dive->cylinders, cyl); cyl.bestmix_o2 = 0;
cyl.bestmix_he = 0;
dive->cylinders.push_back(std::move(cyl));
}
cyl.cylinder_use = DILUENT; {
cyl.type.size.mliter = 3000; cylinder_t cyl;
cyl.type.workingpressure.mbar = 200000; cyl.cylinder_use = DILUENT;
cyl.type.description = "3l Mk6"; cyl.type.size.mliter = 3000;
value = parse_mkvi_value(memtxt.data(), "Helium percentage"); cyl.type.workingpressure.mbar = 200000;
he = atoi(value.c_str()); cyl.type.description = "3l Mk6";
value = parse_mkvi_value(memtxt.data(), "Nitrogen percentage"); value = parse_mkvi_value(memtxt.data(), "Helium percentage");
cyl.gasmix.o2.permille = (100 - atoi(value.c_str()) - he) * 10; he = atoi(value.c_str());
cyl.gasmix.he.permille = he * 10; value = parse_mkvi_value(memtxt.data(), "Nitrogen percentage");
add_cloned_cylinder(&dive->cylinders, cyl); 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"); lineptr = strstr(memtxt.data(), "Dive started at");
while (!empty_string(lineptr) && (lineptr = strchr(lineptr, '\n'))) { 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()); std::string value = parse_mkvi_value(lineptr, key.c_str());
if (value.empty()) if (value.empty())
break; 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 * 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); auto [memcsv, err] = readfile(csv);
if (err < 0) { if (err < 0)
free_dive(dive);
return report_error(translate("gettextFromC", "Poseidon import failed: unable to read '%s'"), csv); return report_error(translate("gettextFromC", "Poseidon import failed: unable to read '%s'"), csv);
}
lineptr = memcsv.data(); lineptr = memcsv.data();
for (;;) { for (;;) {
struct sample *sample; 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); add_sample_data(sample, POSEIDON_SETPOINT, prev_setpoint);
if (!has_ndl && prev_ndl >= 0) if (!has_ndl && prev_ndl >= 0)
add_sample_data(sample, POSEIDON_NDL, prev_ndl); add_sample_data(sample, POSEIDON_NDL, prev_ndl);
finish_sample(dc);
if (!lineptr || !*lineptr) if (!lineptr || !*lineptr)
break; break;
} }
record_dive_to_table(dive, log->dives); log->dives.record_dive(std::move(dive));
return 1; return 1;
} else { } else {
return 0; return 0;

View File

@ -21,10 +21,6 @@ enum csv_format {
#define MAXCOLDIGITS 10 #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 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 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); 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_seabear_log(const char *filename, struct divelog *log);
int parse_manual_file(const char *filename, struct xml_params *params, struct divelog *log); int parse_manual_file(const char *filename, struct xml_params *params, struct divelog *log);
#ifdef __cplusplus
}
#endif
#endif // IMPORTCSV_H #endif // IMPORTCSV_H

View File

@ -4,7 +4,6 @@
#pragma clang diagnostic ignored "-Wmissing-field-initializers" #pragma clang diagnostic ignored "-Wmissing-field-initializers"
#endif #endif
#include "ssrf.h"
#include "dive.h" #include "dive.h"
#include "divesite.h" #include "divesite.h"
#include "sample.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 **) static int divinglog_profile(void *param, int, char **data, char **)
{ {
using namespace std::string_literals;
struct parser_state *state = (struct parser_state *)param; struct parser_state *state = (struct parser_state *)param;
int sinterval = 0; 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->temperature.mkelvin = C_to_mkelvin(temp / 10.0f);
state->cur_sample->pressure[0].mbar = pressure * 100; state->cur_sample->pressure[0].mbar = pressure * 100;
state->cur_sample->rbt.seconds = rbt; state->cur_sample->rbt.seconds = rbt;
if (oldcyl != tank && tank >= 0 && tank < state->cur_dive->cylinders.nr) { if (oldcyl != tank && tank >= 0 && static_cast<size_t>(tank) < state->cur_dive->cylinders.size()) {
struct gasmix mix = get_cylinder(state->cur_dive, tank)->gasmix; struct gasmix mix = get_cylinder(state->cur_dive.get(), tank)->gasmix;
int o2 = get_o2(mix); int o2 = get_o2(mix);
int he = get_he(mix); int he = get_he(mix);
event_start(state); event_start(state);
state->cur_event.time.seconds = time; state->cur_event.time.seconds = time;
strcpy(state->cur_event.name, "gaschange"); state->cur_event.name = "gaschange"s;
o2 = (o2 + 5) / 10; o2 = (o2 + 5) / 10;
he = (he + 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 * 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)) { 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->dc.no_o2sensors = state->cur_sample->o2sensor[0].mbar ? 1 : 0 + 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[1].mbar ? 1 : 0 +
state->cur_sample->o2sensor[2].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') { if (ptr1[6] - '0') {
event_start(state); event_start(state);
state->cur_event.time.seconds = time; state->cur_event.time.seconds = time;
strcpy(state->cur_event.name, "rbt"); state->cur_event.name = "rbt"s;
event_end(state); event_end(state);
} }
@ -231,7 +232,7 @@ static int divinglog_profile(void *param, int, char **data, char **)
if (ptr1[7] - '0') { if (ptr1[7] - '0') {
event_start(state); event_start(state);
state->cur_event.time.seconds = time; state->cur_event.time.seconds = time;
strcpy(state->cur_event.name, "ascent"); state->cur_event.name = "ascent"s;
event_end(state); event_end(state);
} }
@ -239,7 +240,7 @@ static int divinglog_profile(void *param, int, char **data, char **)
if (ptr1[8] - '0') { if (ptr1[8] - '0') {
event_start(state); event_start(state);
state->cur_event.time.seconds = time; state->cur_event.time.seconds = time;
strcpy(state->cur_event.name, "violation"); state->cur_event.name = "violation"s;
event_end(state); event_end(state);
} }
@ -247,7 +248,7 @@ static int divinglog_profile(void *param, int, char **data, char **)
if (ptr1[9] - '0') { if (ptr1[9] - '0') {
event_start(state); event_start(state);
state->cur_event.time.seconds = time; state->cur_event.time.seconds = time;
strcpy(state->cur_event.name, "workload"); state->cur_event.name = "workload"s;
event_end(state); 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])); state->cur_dive->when = (time_t)(atol(data[1]));
if (data[2]) 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]) if (data[3])
utf8_string(data[3], &state->cur_dive->buddy); utf8_string_std(data[3], &state->cur_dive->buddy);
if (data[4]) if (data[4])
utf8_string(data[4], &state->cur_dive->notes); utf8_string_std(data[4], &state->cur_dive->notes);
if (data[5]) 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]) 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]) if (data[7])
utf8_string(data[7], &state->cur_dive->diveguide); utf8_string_std(data[7], &state->cur_dive->diveguide);
if (data[8]) if (data[8])
state->cur_dive->airtemp.mkelvin = C_to_mkelvin(atol(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]) { if (data[10]) {
weightsystem_t ws = { { atoi(data[10]) * 1000 }, translate("gettextFromC", "unknown"), false }; 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]) if (data[11])
state->cur_dive->suit = strdup(data[11]); state->cur_dive->suit = data[11];
/* Divinglog has following visibility options: good, medium, bad */ /* Divinglog has following visibility options: good, medium, bad */
if (data[14]) { if (data[14]) {
@ -329,9 +330,9 @@ static int divinglog_dive(void *param, int, char **data, char **)
dc_settings_start(state); dc_settings_start(state);
if (data[12]) { if (data[12]) {
state->cur_dive->dc.model = strdup(data[12]); state->cur_dive->dcs[0].model = data[12];
} else { } 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); 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': case '0':
break; break;
case '1': case '1':
state->cur_dive->dc.divemode = PSCR; state->cur_dive->dcs[0].divemode = PSCR;
break; break;
case '2': case '2':
state->cur_dive->dc.divemode = CCR; state->cur_dive->dcs[0].divemode = CCR;
break; break;
} }
} }
@ -366,9 +367,9 @@ static int divinglog_dive(void *param, int, char **data, char **)
settings_end(state); settings_end(state);
if (data[12]) { if (data[12]) {
state->cur_dive->dc.model = strdup(data[12]); state->cur_dive->dcs[0].model = data[12];
} else { } 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); 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; int retval;
struct parser_state state; struct parser_state state;

View File

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

View File

@ -4,7 +4,6 @@
#pragma clang diagnostic ignored "-Wmissing-field-initializers" #pragma clang diagnostic ignored "-Wmissing-field-initializers"
#endif #endif
#include "ssrf.h"
#include "dive.h" #include "dive.h"
#include "sample.h" #include "sample.h"
#include "subsurface-string.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; struct parser_state *state = (struct parser_state *)param;
cylinder_t *cyl; cylinder_t *cyl;
int o2 = lrint(strtod_flags(data[0], NULL, 0) * 1000); int o2 = lrint(permissive_strtod(data[0], NULL) * 1000);
int he = lrint(strtod_flags(data[1], NULL, 0) * 1000); int he = lrint(permissive_strtod(data[1], NULL) * 1000);
/* Shearwater allows entering only 99%, not 100% /* Shearwater allows entering only 99%, not 100%
* so assume 99% to be pure oxygen */ * 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]) { if (!data[0] || !data[1] || !data[2]) {
return 2; return 2;
} }
int o2 = lrint(strtod_flags(data[1], NULL, 0) * 1000); int o2 = lrint(permissive_strtod(data[1], NULL) * 1000);
int he = lrint(strtod_flags(data[2], NULL, 0) * 1000); int he = lrint(permissive_strtod(data[2], NULL) * 1000);
/* Shearwater allows entering only 99%, not 100% /* Shearwater allows entering only 99%, not 100%
* so assume 99% to be pure oxygen */ * so assume 99% to be pure oxygen */
@ -59,24 +58,20 @@ static int shearwater_changes(void *param, int columns, char **data, char **)
o2 = 1000; o2 = 1000;
// Find the cylinder index // Find the cylinder index
int index; auto it = std::find_if(state->cur_dive->cylinders.begin(), state->cur_dive->cylinders.end(),
bool found = false; [o2, he](auto &cyl)
for (index = 0; index < state->cur_dive->cylinders.nr; ++index) { { return cyl.gasmix.o2.permille == o2 && cyl.gasmix.he.permille == he; });
const cylinder_t *cyl = get_cylinder(state->cur_dive, index); if (it == state->cur_dive->cylinders.end()) {
if (cyl->gasmix.o2.permille == o2 && cyl->gasmix.he.permille == he) {
found = true;
break;
}
}
if (!found) {
// Cylinder not found, creating a new one // Cylinder not found, creating a new one
cyl = cylinder_start(state); cyl = cylinder_start(state);
cyl->gasmix.o2.permille = o2; cyl->gasmix.o2.permille = o2;
cyl->gasmix.he.permille = he; cyl->gasmix.he.permille = he;
cylinder_end(state); 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; return 0;
} }
@ -101,12 +96,11 @@ static int shearwater_profile_sample(void *param, int, char **data, char **)
if (data[1]) 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]) 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]) { 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]) if (data[4])
state->cur_sample->ndl.seconds = atoi(data[4]) * 60; state->cur_sample->ndl.seconds = atoi(data[4]) * 60;
if (data[5]) 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]); state->cur_sample->time.seconds = atoi(data[0]);
if (data[1]) 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]) 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]) { 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]) if (data[4])
state->cur_sample->ndl.seconds = atoi(data[4]) * 60; 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; struct parser_state *state = (struct parser_state *)param;
if (data[0]) 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; return 0;
} }
@ -240,23 +234,23 @@ static int shearwater_dive(void *param, int, char **data, char **)
long int dive_id = atol(data[11]); long int dive_id = atol(data[11]);
if (data[2]) 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]) if (data[3])
utf8_string(data[3], &state->cur_dive->buddy); utf8_string_std(data[3], &state->cur_dive->buddy);
if (data[4]) 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; state->metric = atoi(data[5]) == 1 ? 0 : 1;
/* TODO: verify that metric calculation is correct */ /* TODO: verify that metric calculation is correct */
if (data[6]) 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]) 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]) 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. * 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]) { if (data[10]) {
switch (atoi(data[10])) { switch (atoi(data[10])) {
case 2: case 2:
state->cur_settings.dc.model = strdup("Shearwater Petrel/Perdix"); state->cur_settings.dc.model = "Shearwater Petrel/Perdix";
break; break;
case 4: case 4:
state->cur_settings.dc.model = strdup("Shearwater Predator"); state->cur_settings.dc.model = "Shearwater Predator";
break; break;
default: default:
state->cur_settings.dc.model = strdup("Shearwater import"); state->cur_settings.dc.model = "Shearwater import";
break; break;
} }
} }
@ -286,13 +280,13 @@ static int shearwater_dive(void *param, int, char **data, char **)
if (data[10]) { if (data[10]) {
switch (atoi(data[10])) { switch (atoi(data[10])) {
case 2: case 2:
state->cur_dive->dc.model = strdup("Shearwater Petrel/Perdix"); state->cur_dive->dcs[0].model = "Shearwater Petrel/Perdix";
break; break;
case 4: case 4:
state->cur_dive->dc.model = strdup("Shearwater Predator"); state->cur_dive->dcs[0].model = "Shearwater Predator";
break; break;
default: default:
state->cur_dive->dc.model = strdup("Shearwater import"); state->cur_dive->dcs[0].model = "Shearwater import";
break; break;
} }
} }
@ -370,23 +364,23 @@ static int shearwater_cloud_dive(void *param, int, char **data, char **)
state->sample_rate = 0; state->sample_rate = 0;
if (data[2]) 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]) if (data[3])
utf8_string(data[3], &state->cur_dive->buddy); utf8_string_std(data[3], &state->cur_dive->buddy);
if (data[4]) 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; state->metric = atoi(data[5]) == 1 ? 0 : 1;
/* TODO: verify that metric calculation is correct */ /* TODO: verify that metric calculation is correct */
if (data[6]) 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]) 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]) 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. * 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]) { if (data[10]) {
switch (atoi(data[10])) { switch (atoi(data[10])) {
case 2: case 2:
state->cur_settings.dc.model = strdup("Shearwater Petrel/Perdix"); state->cur_settings.dc.model = "Shearwater Petrel/Perdix";
break; break;
case 4: case 4:
state->cur_settings.dc.model = strdup("Shearwater Predator"); state->cur_settings.dc.model = "Shearwater Predator";
break; break;
default: default:
state->cur_settings.dc.model = strdup("Shearwater import"); state->cur_settings.dc.model = "Shearwater import";
break; break;
} }
} }
@ -416,13 +410,13 @@ static int shearwater_cloud_dive(void *param, int, char **data, char **)
if (data[10]) { if (data[10]) {
switch (atoi(data[10])) { switch (atoi(data[10])) {
case 2: case 2:
state->cur_dive->dc.model = strdup("Shearwater Petrel/Perdix"); state->cur_dive->dcs[0].model = "Shearwater Petrel/Perdix";
break; break;
case 4: case 4:
state->cur_dive->dc.model = strdup("Shearwater Predator"); state->cur_dive->dcs[0].model = "Shearwater Predator";
break; break;
default: default:
state->cur_dive->dc.model = strdup("Shearwater import"); state->cur_dive->dcs[0].model = "Shearwater import";
break; break;
} }
} }
@ -473,7 +467,7 @@ static int shearwater_cloud_dive(void *param, int, char **data, char **)
return SQLITE_OK; 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; int retval;
struct parser_state state; struct parser_state state;
@ -496,7 +490,7 @@ extern "C" int parse_shearwater_buffer(sqlite3 *handle, const char *url, const c
return 0; 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; int retval;
struct parser_state state; struct parser_state state;

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -2,7 +2,6 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include "ssrf.h"
#include "divesite.h" #include "divesite.h"
#include "dive.h" #include "dive.h"
#include "divelog.h" #include "divelog.h"
@ -132,25 +131,23 @@ static int handle_event_ver3(int code, const unsigned char *ps, unsigned int ps_
return skip; 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 int ptr = 0;
unsigned char model; unsigned char model;
struct dive *dive;
struct divecomputer *dc; struct divecomputer *dc;
struct sample *sample; struct sample *sample;
while (ptr < buf_size) { while (ptr < buf_size) {
int i; int i;
dive = alloc_dive(); auto dive = std::make_unique<struct dive>();
memset(&sensor_ids, 0, sizeof(sensor_ids)); 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 */ /* Just the main cylinder until we can handle the buddy cylinder porperly */
for (i = 0; i < 1; i++) { for (i = 0; i < 1; i++) {
cylinder_t cyl = empty_cylinder; cylinder_t cyl = default_cylinder(dive.get());
fill_default_cylinder(dive, &cyl);
add_cylinder(&dive->cylinders, i, cyl); 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); model = *(buf + ptr);
switch (model) { switch (model) {
case 0: case 0:
dc->model = strdup("Xen"); dc->model = "Xen";
break; break;
case 1: case 1:
case 2: case 2:
dc->model = strdup("Xeo"); dc->model = "Xeo";
break; break;
case 4: case 4:
dc->model = strdup("Lynx"); dc->model = "Lynx";
break; break;
default: default:
dc->model = strdup("Liquivision"); dc->model = "Liquivision";
break; break;
} }
ptr++; 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 */ /* Store the location only if we have one */
if (!location.empty()) 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; 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 // Blank notes are better than the default text
std::string notes((char *)buf + ptr, len); std::string notes((char *)buf + ptr, len);
if (!starts_with(notes, "Comment ...")) if (!starts_with(notes, "Comment ..."))
dive->notes = strdup(notes.c_str()); dive->notes = notes;
ptr += len; ptr += len;
dive->id = array_uint32_le(buf + ptr); 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->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 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); add_sample_pressure(sample, event.pressure.sensor, event.pressure.mbar);
finish_sample(dc);
break; break;
} else if (event.time > sample_time) { } 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->time.seconds = sample_time;
sample->depth.mm = depth_mm; sample->depth.mm = depth_mm;
sample->temperature.mkelvin = temp_mk; sample->temperature.mkelvin = temp_mk;
finish_sample(dc);
d++; d++;
continue; continue;
@ -353,7 +348,6 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int
sample->depth.mm = depth_mm; sample->depth.mm = depth_mm;
sample->temperature.mkelvin = temp_mk; sample->temperature.mkelvin = temp_mk;
add_sample_pressure(sample, event.pressure.sensor, event.pressure.mbar); add_sample_pressure(sample, event.pressure.sensor, event.pressure.mbar);
finish_sample(dc);
d++; d++;
break; 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) sample->temperature.mkelvin = last_temp + (temp_mk - last_temp)
* ((int)event.time - (int)last_time) / sample_interval; * ((int)event.time - (int)last_time) / sample_interval;
} }
finish_sample(dc);
break; 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->depth.mm = array_uint16_le(ds + d * 2) * 10; // cm->mm
sample->temperature.mkelvin = sample->temperature.mkelvin =
C_to_mkelvin((float)array_uint16_le(ts + d * 2) / 10); C_to_mkelvin((float)array_uint16_le(ts + d * 2) / 10);
finish_sample(dc);
} }
if (log_version == 3 && model == 4) { 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 // End dive
record_dive_to_table(dive, table); table.record_dive(std::move(dive));
dive = NULL;
// Advance ptr for next dive // Advance ptr for next dive
ptr += ps_ptr + 4; ptr += ps_ptr + 4;
} // while } // 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) 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 // SPDX-License-Identifier: GPL-2.0
#include "ssrf.h"
#include <stdio.h> #include <stdio.h>
#include <ctype.h> #include <ctype.h>
#include <string.h> #include <string.h>
@ -38,8 +37,8 @@ std::string saved_git_id;
struct git_parser_state { struct git_parser_state {
git_repository *repo = nullptr; git_repository *repo = nullptr;
struct divecomputer *active_dc = nullptr; struct divecomputer *active_dc = nullptr;
struct dive *active_dive = nullptr; std::unique_ptr<dive> active_dive;
dive_trip_t *active_trip = nullptr; std::unique_ptr<dive_trip> active_trip;
std::string fulltext_mode; std::string fulltext_mode;
std::string fulltext_query; std::string fulltext_query;
std::string filter_constraint_type; std::string filter_constraint_type;
@ -47,7 +46,7 @@ struct git_parser_state {
std::string filter_constraint_range_mode; std::string filter_constraint_range_mode;
bool filter_constraint_negate = false; bool filter_constraint_negate = false;
std::string filter_constraint_data; std::string filter_constraint_data;
struct picture active_pic = { 0 }; struct picture active_pic;
struct dive_site *active_site = nullptr; struct dive_site *active_site = nullptr;
std::unique_ptr<filter_preset> active_filter; std::unique_ptr<filter_preset> active_filter;
struct divelog *log = nullptr; 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) static void parse_dive_gps(char *line, struct git_parser_state *state)
{ {
location_t location; 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); parse_location(line, &location);
if (!ds) { if (!ds) {
ds = get_dive_site_by_gps(&location, state->log->sites); ds = state->log->sites.get_by_gps(&location);
if (!ds) if (!ds)
ds = create_dive_site_with_gps("", &location, state->log->sites); ds = state->log->sites.create(std::string(), location);
add_dive_to_dive_site(state->active_dive, ds); ds->add_dive(state->active_dive.get());
} else { } 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); std::string coords = printGPSCoordsC(&location);
// we have a dive site that already has GPS coordinates // we have a dive site that already has GPS coordinates
// note 1: there will be much less copying once the core // 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 // note 2: we could include the first newline in the
// translation string, but that would be weird and cause // translation string, but that would be weird and cause
// a new string. // a new string.
std::string new_text = std::string(ds->notes) + '\n' + ds->notes += '\n';
format_string_std(translate("gettextFromC", "multiple GPS locations for this dive site; also %s\n"), coords.c_str()); ds->notes += 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->location = location; 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()); 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) static void parse_dive_location(char *, struct git_parser_state *state)
{ {
std::string name = get_first_converted_string(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) { if (!ds) {
ds = get_dive_site_by_name(name.c_str(), state->log->sites); ds = state->log->sites.get_by_name(name);
if (!ds) if (!ds)
ds = create_dive_site(name.c_str(), state->log->sites); ds = state->log->sites.create(name);
add_dive_to_dive_site(state->active_dive, ds); ds->add_dive(state->active_dive.get());
} else { } else {
// we already had a dive site linked to the dive // we already had a dive site linked to the dive
if (empty_string(ds->name)) { if (ds->name.empty()) {
free(ds->name); // empty_string could mean pointer to a 0-byte! ds->name = name.c_str();
ds->name = strdup(name.c_str());
} else { } else {
// and that dive site had a name. that's weird - if our name is different, add it to the notes // 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())) { if (ds->name == name) {
std::string new_string = std::string(ds->notes) + '\n' + ds->notes += '\n';
format_string_std(translate("gettextFromC", "additional name for site: %s\n"), name.c_str()); ds->notes += format_string_std(translate("gettextFromC", "additional name for site: %s\n"), name.c_str());
ds->notes = strdup(new_string.c_str());
} }
} }
} }
} }
static void parse_dive_diveguide(char *, struct git_parser_state *state) 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) 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) 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) 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) 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. * 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) { for (const std::string &tag: state->converted_strings) {
if (!tag.empty()) 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) 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) 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) 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) 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 origin;
int category; int category;
sscanf(line, "cat %d origin %d \"", &category, &origin); sscanf(line, "cat %d origin %d \"", &category, &origin);
taxonomy_set_category(&state->active_site->taxonomy, (taxonomy_category)category, taxonomy_set_category(state->active_site->taxonomy, (taxonomy_category)category,
get_first_converted_string(state).c_str(), (taxonomy_origin)origin); get_first_converted_string(state), (taxonomy_origin)origin);
} }
static std::string pop_cstring(struct git_parser_state *state, const char *err) 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; return;
} }
if (!strcmp(key, "description")) { if (!strcmp(key, "description")) {
cylinder->type.description = strdup(value.c_str()); cylinder->type.description = value;
return; return;
} }
if (!strcmp(key, "o2")) { 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) static void parse_dive_cylinder(char *line, struct git_parser_state *state)
{ {
cylinder_t cylinder = empty_cylinder; cylinder_t cylinder;
for (;;) { for (;;) {
char c; 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); line = parse_keyvalue_entry(parse_cylinder_keyvalue, &cylinder, line, state);
} }
if (cylinder.cylinder_use == OXYGEN) 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) 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; return;
} }
if (!strcmp(key, "description")) { if (!strcmp(key, "description")) {
ws->description = strdup(value.c_str()); ws->description = value;
return; return;
} }
report_error("Unknown weightsystem key/value pair (%s/%s)", key, value.c_str()); 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) static void parse_dive_weightsystem(char *line, struct git_parser_state *state)
{ {
weightsystem_t ws = empty_weightsystem; weightsystem_t ws;
for (;;) { for (;;) {
char c; 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); 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, 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) 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) static struct sample *new_sample(struct git_parser_state *state)
{ {
struct sample *sample = prepare_sample(state->active_dc); struct sample *sample = prepare_sample(state->active_dc);
if (sample != state->active_dc->sample) { size_t num_samples = state->active_dc->samples.size();
memcpy(sample, sample - 1, sizeof(struct sample)); if (num_samples >= 2) {
*sample = state->active_dc->samples[num_samples - 2];
sample->pressure[0].mbar = 0; sample->pressure[0].mbar = 0;
sample->pressure[1].mbar = 0; sample->pressure[1].mbar = 0;
} else { } else {
sample->sensor[0] = 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, state->o2pressure_sensor); sample->sensor[1] = sanitize_sensor_id(state->active_dive.get(), state->o2pressure_sensor);
} }
return sample; return sample;
} }
@ -722,7 +709,6 @@ static void sample_parser(char *line, struct git_parser_state *state)
line = parse_sample_unit(sample, val, line); line = parse_sample_unit(sample, val, line);
} }
} }
finish_sample(state->active_dc);
} }
static void parse_dc_airtemp(char *line, struct git_parser_state *state) 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); } { state->active_dc->meandepth = get_depth(line); }
static void parse_dc_model(char *, struct git_parser_state *state) 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) static void parse_dc_numberofoxygensensors(char *line, struct git_parser_state *state)
{ state->active_dc->no_o2sensors = get_index(line); } { state->active_dc->no_o2sensors = get_index(line); }
@ -795,7 +781,7 @@ static int get_divemode(const char *divemodestring) {
struct parse_event { struct parse_event {
std::string name; std::string name;
int has_divemode = false; 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) 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) if (state->converted_strings.size() != 2)
return; 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) static void parse_dc_event(char *line, struct git_parser_state *state)
{ {
int m, s = 0; int m, s = 0;
struct parse_event p; struct parse_event p;
struct event *ev;
m = strtol(line, &line, 10); m = strtol(line, &line, 10);
if (*line == ':') 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") if (p.has_divemode && p.name != "modechange")
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. * 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 * Better to mark them being CCR on import so no need for special treatments elsewhere on
* the code. * 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; state->active_dc->divemode = CCR;
if (ev && event_is_gaschange(ev)) { if (ev->is_gaschange()) {
/* /*
* We subtract one here because "0" is "no index", * We subtract one here because "0" is "no index",
* and the parsing will add one for actual cylinder * 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) 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) 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) 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) 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()); 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, create_fingerprint_node_from_hex(fingerprints, fph.model, fph.serial,
fph.hex_data.c_str(), fph.fdeviceid, fph.fdiveid); fph.hex_data, fph.fdeviceid, fph.fdiveid);
} }
static void parse_picture_filename(char *, struct git_parser_state *state) 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) 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); 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->active_filter->add_constraint(state->filter_constraint_type,
state->filter_constraint_string_mode.c_str(), state->filter_constraint_string_mode,
state->filter_constraint_range_mode.c_str(), state->filter_constraint_range_mode,
state->filter_constraint_negate, state->filter_constraint_data.c_str()); state->filter_constraint_negate, state->filter_constraint_data);
state->filter_constraint_type.clear(); state->filter_constraint_type.clear();
state->filter_constraint_string_mode.clear(); state->filter_constraint_string_mode.clear();
state->filter_constraint_range_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); 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_mode.clear();
state->fulltext_query.clear(); state->fulltext_query.clear();
} }
static void parse_filter_preset_name(char *, struct git_parser_state *state) 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! */ /* 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) static void finish_active_trip(struct git_parser_state *state)
{ {
dive_trip_t *trip = state->active_trip; auto &trip = state->active_trip;
if (trip) { if (trip)
state->active_trip = NULL; state->log->trips.put(std::move(trip));
insert_trip(trip, state->log->trips);
}
} }
static void finish_active_dive(struct git_parser_state *state) static void finish_active_dive(struct git_parser_state *state)
{ {
struct dive *dive = state->active_dive; if (state->active_dive)
state->log->dives.record_dive(std::move(state->active_dive));
if (dive) {
state->active_dive = NULL;
record_dive_to_table(dive, state->log->dives);
}
} }
static void create_new_dive(timestamp_t when, struct git_parser_state *state) 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 */ /* We'll fill in more data from the dive file */
state->active_dive->when = when; state->active_dive->when = when;
if (state->active_trip) 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) 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)) if (!validate_date(yyyy, mm, dd))
return GIT_WALK_SKIP; return GIT_WALK_SKIP;
finish_active_trip(state); finish_active_trip(state);
state->active_trip = alloc_trip(); state->active_trip = std::make_unique<dive_trip>();
return GIT_WALK_OK; 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) 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? */ /* Did we already fill that in? */
if (dc->samples || dc->model || dc->when) { if (!dc->samples.empty() || !dc->model.empty() || dc->when) {
struct divecomputer *newdc = (divecomputer *)calloc(1, sizeof(*newdc)); dive->dcs.emplace_back();
if (!newdc) dc = &dive->dcs.back();
return NULL;
dc->next = newdc;
dc = newdc;
} }
dc->when = dive->when; dc->when = dive->when;
dc->duration = dive->duration; dc->duration = dive->duration;
@ -1679,7 +1653,7 @@ static int parse_divecomputer_entry(struct git_parser_state *state, const git_tr
if (!blob) if (!blob)
return report_error("Unable to read divecomputer file"); 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); for_each_line(blob, divecomputer_parser, state);
git_blob_free(blob); git_blob_free(blob);
state->active_dc = NULL; 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) 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); git_blob *blob = git_tree_entry_blob(state->repo, entry);
if (!blob) if (!blob)
return report_error("Unable to read dive file"); return report_error("Unable to read dive file");
if (*suffix) if (*suffix)
dive->number = atoi(suffix + 1); state->active_dive->number = atoi(suffix + 1);
clear_weightsystem_table(&state->active_dive->weightsystems); state->active_dive->weightsystems.clear();
state->o2pressure_sensor = 1; state->o2pressure_sensor = 1;
for_each_line(blob, dive_parser, state); for_each_line(blob, dive_parser, state);
git_blob_free(blob); 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') if (*suffix == '\0')
return report_error("Dive site without uuid"); return report_error("Dive site without uuid");
uint32_t uuid = strtoul(suffix, NULL, 16); 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); git_blob *blob = git_tree_entry_blob(state->repo, entry);
if (!blob) if (!blob)
return report_error("Unable to read dive site file"); 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; state->active_pic.offset.seconds = offset;
for_each_line(blob, picture_parser, state); 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); git_blob_free(blob);
/* add_picture took ownership of the data - /* add_picture took ownership of the data -
* clear out our copy just to be sure. */ * clear out our copy just to be sure. */
state->active_pic = empty_picture; state->active_pic = picture();
return 0; return 0;
} }
@ -1788,7 +1761,7 @@ static int parse_filter_preset(struct git_parser_state *state, const git_tree_en
git_blob_free(blob); 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(); state->active_filter.reset();
return 0; 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) static int walk_tree_file(const char *root, const git_tree_entry *entry, struct git_parser_state *state)
{ {
struct dive *dive = state->active_dive; auto &dive = state->active_dive;
dive_trip_t *trip = state->active_trip; auto &trip = state->active_trip;
const char *name = git_tree_entry_name(entry); const char *name = git_tree_entry_name(entry);
if (verbose > 1) if (verbose > 1)
report_info("git load handling file %s\n", name); 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); return parse_settings_entry(state, entry);
break; 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; return GIT_WALK_SKIP;
} }
@ -1850,12 +1823,12 @@ static int load_dives_from_tree(git_repository *repo, git_tree *tree, struct git
return 0; return 0;
} }
extern "C" void clear_git_id(void) void clear_git_id()
{ {
saved_git_id.clear(); 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]; char git_id_buffer[GIT_OID_HEXSZ + 1];

View File

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

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