diff options
author | Berthold Stoeger <bstoeger@mail.tuwien.ac.at> | 2019-05-31 16:09:14 +0200 |
---|---|---|
committer | Dirk Hohndel <dirk@hohndel.org> | 2019-06-19 13:11:10 -0700 |
commit | 7f4d9db962e73aa5d5089c43c99b78b3690ffb87 (patch) | |
tree | 87e143b80ed394fc321765885a75cac229bf9f18 /core/trip.c | |
parent | f1c2cd375e295730d92e23093e44777baf838f1d (diff) | |
download | subsurface-7f4d9db962e73aa5d5089c43c99b78b3690ffb87.tar.gz |
Cleanup: move trip-related functions into own translation unit
These functions were spread out over dive.c and divelist.c.
Move them into their own file to make all this a bit less monolithic.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Diffstat (limited to 'core/trip.c')
-rw-r--r-- | core/trip.c | 353 |
1 files changed, 353 insertions, 0 deletions
diff --git a/core/trip.c b/core/trip.c new file mode 100644 index 000000000..a864e8dfb --- /dev/null +++ b/core/trip.c @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "trip.h" +#include "subsurface-string.h" +#include "table.h" + +struct trip_table trip_table; + +#ifdef DEBUG_TRIP +void dump_trip_list(void) +{ + dive_trip_t *trip; + int i = 0; + timestamp_t last_time = 0; + + for (i = 0; i < trip_table.nr; ++i) { + struct tm tm; + trip = trip_table.trips[i]; + utc_mkdate(trip_date(trip), &tm); + if (trip_date(trip) < last_time) + printf("\n\ntrip_table OUT OF ORDER!!!\n\n\n"); + printf("%s trip %d to \"%s\" on %04u-%02u-%02u %02u:%02u:%02u (%d dives - %p)\n", + trip->autogen ? "autogen " : "", + i + 1, trip->location, + tm.tm_year, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, + trip->dives.nr, trip); + last_time = trip_date(trip); + } + printf("-----\n"); +} +#endif + +/* free resources associated with a trip structure */ +void free_trip(dive_trip_t *trip) +{ + if (trip) { + free(trip->location); + free(trip->notes); + free(trip->dives.dives); + free(trip); + } +} + +/* Trip table functions */ +static MAKE_GET_IDX(trip_table, struct dive_trip *, trips) +static MAKE_GROW_TABLE(trip_table, struct dive_trip *, trips) +static MAKE_GET_INSERTION_INDEX(trip_table, struct dive_trip *, trips, trip_less_than) +static MAKE_ADD_TO(trip_table, struct dive_trip *, trips) +static MAKE_REMOVE_FROM(trip_table, trips) +MAKE_SORT(trip_table, struct dive_trip *, trips, comp_trips) +MAKE_REMOVE(trip_table, struct dive_trip *, trip) + +timestamp_t trip_date(const struct dive_trip *trip) +{ + if (!trip || trip->dives.nr == 0) + return 0; + return trip->dives.dives[0]->when; +} + +timestamp_t trip_enddate(const struct dive_trip *trip) +{ + if (!trip || trip->dives.nr == 0) + return 0; + return dive_endtime(trip->dives.dives[trip->dives.nr - 1]); +} + +/* check if we have a trip right before / after this dive */ +bool is_trip_before_after(const struct dive *dive, bool before) +{ + int idx = get_idx_by_uniq_id(dive->id); + if (before) { + if (idx > 0 && get_dive(idx - 1)->divetrip) + return true; + } else { + if (idx < dive_table.nr - 1 && get_dive(idx + 1)->divetrip) + return true; + } + return false; +} + +/* Add dive to a trip. Caller is responsible for removing dive + * from trip beforehand. */ +void add_dive_to_trip(struct dive *dive, dive_trip_t *trip) +{ + if (dive->divetrip == trip) + return; + if (dive->divetrip) + fprintf(stderr, "Warning: adding dive to trip that has trip set\n"); + insert_dive(&trip->dives, dive); + dive->divetrip = trip; +} + +/* remove a dive from the trip it's associated to, but don't delete the + * trip if this was the last dive in the trip. the caller is responsible + * for removing the trip, if the trip->dives.nr went to 0. + */ +struct dive_trip *unregister_dive_from_trip(struct dive *dive) +{ + dive_trip_t *trip = dive->divetrip; + + if (!trip) + return NULL; + + remove_dive(dive, &trip->dives); + dive->divetrip = NULL; + return trip; +} + +static void delete_trip(dive_trip_t *trip, struct trip_table *trip_table_arg) +{ + remove_trip(trip, trip_table_arg); + free_trip(trip); +} + +void remove_dive_from_trip(struct dive *dive, struct trip_table *trip_table_arg) +{ + struct dive_trip *trip = unregister_dive_from_trip(dive); + if (trip && trip->dives.nr == 0) + delete_trip(trip, trip_table_arg); +} + +dive_trip_t *alloc_trip(void) +{ + return calloc(1, sizeof(dive_trip_t)); +} + +/* insert the trip into the trip table */ +void insert_trip(dive_trip_t *dive_trip, struct trip_table *trip_table_arg) +{ + int idx = trip_table_get_insertion_index(trip_table_arg, dive_trip); + add_to_trip_table(trip_table_arg, idx, dive_trip); +#ifdef DEBUG_TRIP + dump_trip_list(); +#endif +} + +dive_trip_t *create_trip_from_dive(struct dive *dive) +{ + dive_trip_t *trip; + + trip = alloc_trip(); + trip->location = copy_string(get_dive_location(dive)); + + return trip; +} + +dive_trip_t *create_and_hookup_trip_from_dive(struct dive *dive, struct trip_table *trip_table_arg) +{ + dive_trip_t *dive_trip = alloc_trip(); + + dive_trip = create_trip_from_dive(dive); + + add_dive_to_trip(dive, dive_trip); + insert_trip(dive_trip, trip_table_arg); + return dive_trip; +} + +/* random threshold: three days without diving -> new trip + * this works very well for people who usually dive as part of a trip and don't + * regularly dive at a local facility; this is why trips are an optional feature */ +#define TRIP_THRESHOLD 3600 * 24 * 3 + +/* + * Find a trip a new dive should be autogrouped with. If no such trips + * exist, allocate a new trip. The bool "*allocated" is set to true + * if a new trip was allocated. + */ +dive_trip_t *get_trip_for_new_dive(struct dive *new_dive, bool *allocated) +{ + struct dive *d; + dive_trip_t *trip; + int i; + + /* Find dive that is within TRIP_THRESHOLD of current dive */ + for_each_dive(i, d) { + /* Check if we're past the range of possible dives */ + if (d->when >= new_dive->when + TRIP_THRESHOLD) + break; + + if (d->when + TRIP_THRESHOLD >= new_dive->when && d->divetrip) { + /* Found a dive with trip in the range */ + *allocated = false; + return d->divetrip; + } + } + + /* Didn't find a trip -> allocate a new one */ + trip = create_trip_from_dive(new_dive); + trip->autogen = true; + *allocated = true; + return trip; +} + +/* + * Collect dives for auto-grouping. Pass in first dive which should be checked. + * Returns range of dives that should be autogrouped and trip it should be + * associated to. If the returned trip was newly allocated, the last bool + * is set to true. Caller still has to register it in the system. Note + * whereas this looks complicated - it is needed by the undo-system, which + * manually injects the new trips. If there are no dives to be autogrouped, + * return NULL. + */ +dive_trip_t *get_dives_to_autogroup(struct dive_table *table, int start, int *from, int *to, bool *allocated) +{ + int i; + struct dive *lastdive = NULL; + + /* Find first dive that should be merged and remember any previous + * dive that could be merged into. + */ + for (i = start; i < table->nr; i++) { + struct dive *dive = table->dives[i]; + dive_trip_t *trip; + + if (dive->divetrip) { + lastdive = dive; + continue; + } + + /* Only consider dives that have not been explicitly removed from + * a dive trip by the user. */ + if (dive->notrip) { + lastdive = NULL; + continue; + } + + /* We found a dive, let's see if we have to allocate a new trip */ + if (!lastdive || dive->when >= lastdive->when + TRIP_THRESHOLD) { + /* allocate new trip */ + trip = create_trip_from_dive(dive); + trip->autogen = true; + *allocated = true; + } else { + /* use trip of previous dive */ + trip = lastdive->divetrip; + *allocated = false; + } + + // Now, find all dives that will be added to this trip + lastdive = dive; + *from = i; + for (*to = *from + 1; *to < table->nr; (*to)++) { + dive = table->dives[*to]; + if (dive->divetrip || dive->notrip || + dive->when >= lastdive->when + TRIP_THRESHOLD) + break; + if (get_dive_location(dive) && !trip->location) + trip->location = copy_string(get_dive_location(dive)); + lastdive = dive; + } + return trip; + } + + /* Did not find anyhting - mark as end */ + return NULL; +} + +void deselect_dives_in_trip(struct dive_trip *trip) +{ + if (!trip) + return; + for (int i = 0; i < trip->dives.nr; ++i) + deselect_dive(trip->dives.dives[i]); +} + +void select_dives_in_trip(struct dive_trip *trip) +{ + struct dive *dive; + if (!trip) + return; + for (int i = 0; i < trip->dives.nr; ++i) { + dive = trip->dives.dives[i]; + if (!dive->hidden_by_filter) + select_dive(dive); + } +} + +/* Out of two strings, copy the string that is not empty (if any). */ +static char *copy_non_empty_string(const char *a, const char *b) +{ + return copy_string(empty_string(b) ? a : b); +} + +/* This combines the information of two trips, generating a + * new trip. To support undo, we have to preserve the old trips. */ +dive_trip_t *combine_trips(struct dive_trip *trip_a, struct dive_trip *trip_b) +{ + dive_trip_t *trip; + + trip = alloc_trip(); + trip->location = copy_non_empty_string(trip_a->location, trip_b->location); + trip->notes = copy_non_empty_string(trip_a->notes, trip_b->notes); + + return trip; +} + +void clear_trip_table(struct trip_table *table) +{ + for (int i = 0; i < table->nr; i++) + free_trip(table->trips[i]); + table->nr = 0; +} + +/* Trips are compared according to the first dive in the trip. */ +int comp_trips(const struct dive_trip *a, const struct dive_trip *b) +{ + /* This should never happen, nevertheless don't crash on trips + * with no (or worse a negative number of) dives. */ + if (a->dives.nr <= 0) + return b->dives.nr <= 0 ? 0 : -1; + if (b->dives.nr <= 0) + return 1; + return comp_dives(a->dives.dives[0], b->dives.dives[0]); +} + +bool trip_less_than(const struct dive_trip *a, const struct dive_trip *b) +{ + return comp_trips(a, b) < 0; +} + +static bool is_same_day(timestamp_t trip_when, timestamp_t dive_when) +{ + static timestamp_t twhen = (timestamp_t) 0; + static struct tm tmt; + struct tm tmd; + + utc_mkdate(dive_when, &tmd); + + if (twhen != trip_when) { + twhen = trip_when; + utc_mkdate(twhen, &tmt); + } + + return (tmd.tm_mday == tmt.tm_mday) && (tmd.tm_mon == tmt.tm_mon) && (tmd.tm_year == tmt.tm_year); +} + +bool trip_is_single_day(const struct dive_trip *trip) +{ + if (trip->dives.nr <= 1) + return true; + return is_same_day(trip->dives.dives[0]->when, + trip->dives.dives[trip->dives.nr - 1]->when); +} + +int trip_shown_dives(const struct dive_trip *trip) +{ + int res = 0; + for (int i = 0; i < trip->dives.nr; ++i) { + if (!trip->dives.dives[i]->hidden_by_filter) + res++; + } + return res; +} |