aboutsummaryrefslogtreecommitdiffstats
path: root/core/divelist.c
diff options
context:
space:
mode:
Diffstat (limited to 'core/divelist.c')
-rw-r--r--core/divelist.c255
1 files changed, 195 insertions, 60 deletions
diff --git a/core/divelist.c b/core/divelist.c
index f293509a9..84694da60 100644
--- a/core/divelist.c
+++ b/core/divelist.c
@@ -783,6 +783,13 @@ timestamp_t trip_date(const struct dive_trip *trip)
return trip->dives.dives[0]->when;
}
+static 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)
{
@@ -968,6 +975,13 @@ static MAKE_GET_IDX(trip_table, struct dive_trip *, trips)
MAKE_SORT(dive_table, struct dive *, dives, comp_dives)
MAKE_SORT(trip_table, struct dive_trip *, trips, comp_trips)
+static void remove_dive(struct dive_table *table, const struct dive *dive)
+{
+ int idx = get_idx_in_dive_table(table, dive);
+ if (idx >= 0)
+ remove_from_dive_table(table, idx);
+}
+
/* 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.
@@ -975,14 +989,11 @@ MAKE_SORT(trip_table, struct dive_trip *, trips, comp_trips)
struct dive_trip *unregister_dive_from_trip(struct dive *dive)
{
dive_trip_t *trip = dive->divetrip;
- int idx;
if (!trip)
return NULL;
- idx = get_idx_in_dive_table(&trip->dives, dive);
- if (idx >= 0)
- remove_from_dive_table(&trip->dives, idx);
+ remove_dive(&trip->dives, dive);
dive->divetrip = NULL;
return trip;
}
@@ -1468,62 +1479,74 @@ static void merge_imported_dives(struct dive_table *table)
/*
* Try to merge a new dive into the dive at position idx. Return
- * true on success. On success, the dive to add and the old dive
- * will be deleted. On failure, they are untouched.
+ * true on success. On success, the old dive will be deleted. On failure,
+ * it is unchanged.
+ * If replace_in is not NULL, the original dive will also be replaced
+ * by the merged dive in this dive table. If it is NULL it will be
+ * replaced in the dive table of its trip. This is used for merging dive
+ * in the trip table *and* the dive table.
* If "prefer_imported" is true, use data of the new dive.
*/
-static bool try_to_merge_into(struct dive *dive_to_add, struct trip_table *dive_to_add_trip_table,
- int idx, bool prefer_imported)
+static bool try_to_merge_into(struct dive *dive_to_add, int idx, struct dive_table *table,
+ struct dive_table *replace_in, bool prefer_imported)
{
- struct dive *old_dive = dive_table.dives[idx];
+ struct dive *old_dive = table->dives[idx];
struct dive *merged = try_to_merge(old_dive, dive_to_add, prefer_imported);
if (!merged)
return false;
+ /* Hack alert! If no replace_in table was passed, we are merging
+ * a non-trip dive into a potentially in-trip dive. In this case
+ * we also have to replace the merged dive for the old dive in the
+ * trip list. This will be removed in a subsequent commit, when
+ * the merging is done outside of processing */
+ if (!replace_in && old_dive->divetrip)
+ replace_in = &old_dive->divetrip->dives;
+
merged->id = old_dive->id;
merged->selected = old_dive->selected;
- dive_table.dives[idx] = merged;
- remove_dive_from_trip(old_dive, &trip_table);
+ merged->divetrip = old_dive->divetrip;
+ old_dive->divetrip = NULL;
+ table->dives[idx] = merged;
+ if (replace_in) {
+ int idx2 = get_idx_in_dive_table(replace_in, old_dive);
+ if (idx2 >= 0)
+ replace_in->dives[idx2] = merged;
+ }
free_dive(old_dive);
- remove_dive_from_trip(dive_to_add, dive_to_add_trip_table);
- free_dive(dive_to_add);
return true;
}
-/*
- * Add imported dive to global dive table. Overlapping dives will
- * be merged if possible. If prefer_imported is true, data of the
- * new dives are prioritized in such a case.
- * If downloaded is true, only the divecomputer of the first dive
- * will be considered, as it is assumed that all dives come from
- * the same computer.
- * Note: the dives in import_table are consumed! On return import_table
- * has size 0.
- */
-void process_imported_dives(struct dive_table *import_table, bool prefer_imported, bool downloaded)
+static bool trips_overlap(const struct dive_trip *t1, const struct dive_trip *t2)
{
- int i, j;
- int preexisting;
- bool sequence_changed = false;
-
- /* If no dives were imported, don't bother doing anything */
- if (!import_table->nr)
- return;
+ /* First, handle the empty-trip cases. */
+ if (t1->dives.nr == 0 || t2->dives.nr == 0)
+ return 0;
- /* check if we need a nickname for the divecomputer for newly downloaded dives;
- * since we know they all came from the same divecomputer we just check for the
- * first one */
- if (downloaded)
- set_dc_nickname(import_table->dives[0]);
+ if (trip_date(t1) < trip_date(t2))
+ return trip_enddate(t1) >= trip_date(t2);
else
- /* they aren't downloaded, so record / check all new ones */
- for (i = 0; i < import_table->nr; i++)
- set_dc_nickname(import_table->dives[i]);
+ return trip_enddate(t2) >= trip_date(t1);
+}
- /* Sort the table of dives to be imported and combine mergable dives */
- sort_dive_table(import_table);
- merge_imported_dives(import_table);
+static bool insert_dive(struct dive_table *table, struct dive *d)
+{
+ int idx = dive_table_get_insertion_index(table, d);
+ add_to_dive_table(table, idx, d);
+ return idx == table->nr - 1;
+}
+
+/* Merge dives from dives_from into dives_to. Overlapping dives will be merged,
+ * non-overlapping dives will be moved. Optionally, if delete_from and add_to
+ * are non-null, dives will be removed / added to these tables. This supposes that
+ * all tables are sorted. */
+static bool merge_dive_tables(struct dive_table *dives_from, struct dive_table *delete_from,
+ struct dive_table *dives_to, struct dive_table *add_to,
+ bool prefer_imported, struct dive_trip *trip)
+{
+ int i, j;
+ bool sequence_changed = false;
/* Merge newly imported dives into the dive table.
* Since both lists (old and new) are sorted, we can step
@@ -1534,54 +1557,166 @@ void process_imported_dives(struct dive_table *import_table, bool prefer_importe
* - New dive "connects" two old dives (turn three into one).
* - New dive can not be merged into adjacent but some further dive.
*/
- j = 0; /* Index in old dives */
- preexisting = dive_table.nr; /* Remember old size for renumbering */
- for (i = 0; i < import_table->nr; i++) {
- struct dive *dive_to_add = import_table->dives[i];
+ j = 0; /* Index in dives_to */
+ for (i = 0; i < dives_from->nr; i++) {
+ struct dive *dive_to_add = dives_from->dives[i];
+
+ if (delete_from)
+ remove_dive(delete_from, dive_to_add);
/* Find insertion point. */
- while (j < dive_table.nr && dive_less_than(dive_table.dives[j], dive_to_add))
+ while (j < dives_to->nr && dive_less_than(dives_to->dives[j], dive_to_add))
j++;
/* Try to merge into previous dive. */
- if (j > 0 && dive_endtime(dive_table.dives[j - 1]) > dive_to_add->when) {
- if (try_to_merge_into(dive_to_add, &trip_table, j - 1, prefer_imported))
+ if (j > 0 && dive_endtime(dives_to->dives[j - 1]) > dive_to_add->when) {
+ if (try_to_merge_into(dive_to_add, j - 1, dives_to, add_to, prefer_imported)) {
+ free_dive(dive_to_add);
continue;
+ }
}
/* That didn't merge into the previous dive. If we're
* at the end of the dive table, quit the loop and add
* all new dives at the end. */
- if (j >= dive_table.nr)
+ if (j >= dives_to->nr)
break;
/* Try to merge into next dive. */
- if (dive_endtime(dive_to_add) > dive_table.dives[j]->when) {
- if (try_to_merge_into(dive_to_add, &trip_table, j, prefer_imported))
+ if (dive_endtime(dive_to_add) > dives_to->dives[j]->when) {
+ if (try_to_merge_into(dive_to_add, j, dives_to, add_to, prefer_imported)) {
+ free_dive(dive_to_add);
continue;
+ }
}
/* We couldnt merge dives, add at the given position. */
- add_single_dive(j, dive_to_add);
+ add_to_dive_table(dives_to, j, dive_to_add);
+ dive_to_add->divetrip = trip;
+ if (add_to)
+ insert_dive(add_to, dive_to_add);
j++;
sequence_changed = true;
}
/* If there are still dives to add, add them at the end of the dive table. */
- for ( ; i < import_table->nr; i++)
- add_single_dive(dive_table.nr, import_table->dives[i]);
+ for ( ; i < dives_from->nr; i++) {
+ struct dive *dive_to_add = dives_from->dives[i];
+ if (delete_from)
+ remove_dive(delete_from, dive_to_add);
+
+ dive_to_add->divetrip = trip;
+ add_to_dive_table(dives_to, dives_to->nr, dive_to_add);
+ if (add_to)
+ sequence_changed |= !insert_dive(add_to, dive_to_add);
+ }
/* we took care of all dives, clean up the import table */
- import_table->nr = 0;
+ dives_from->nr = 0;
+
+ return sequence_changed;
+}
+
+/* Merge the dives of the trip "from" and the dive_table "dives_from" into the trip "to"
+ * and dive_table "dives_to". If "prefer_imported" is true, dive data of "from" takes
+ * precedence */
+static bool merge_trips(struct dive_trip *from, struct dive_table *dives_from,
+ struct dive_trip *to, struct dive_table *dives_to, bool prefer_imported)
+{
+ return merge_dive_tables(&from->dives, dives_from, &to->dives, dives_to, prefer_imported, to);
+}
+
+static bool add_trip_to_table(struct dive_trip *trip, struct dive_table *dives_from,
+ struct trip_table *table, struct dive_table *dives_to)
+{
+ int i;
+ struct dive *d;
+ bool sequence_changed = false;
+ for (i = 0; i < trip->dives.nr; i++) {
+ d = trip->dives.dives[i];
+
+ /* Add dive to copy-to table */
+ sequence_changed |= !insert_dive(dives_to, d);
+
+ /* Remove dive from copy-from table */
+ remove_dive(dives_from, d);
+ }
+
+ /* Add trip to list */
+ insert_trip(trip, table);
+
+ return sequence_changed;
+}
+
+/*
+ * Add imported dive to global dive table. Overlapping dives will
+ * be merged if possible. If prefer_imported is true, data of the
+ * new dives are prioritized in such a case.
+ * If downloaded is true, only the divecomputer of the first dive
+ * will be considered, as it is assumed that all dives come from
+ * the same computer.
+ * Note: the dives in import_table and the trips in import_trip_table
+ * are consumed. On return both tables have size 0.
+ */
+void process_imported_dives(struct dive_table *import_table, struct trip_table *import_trip_table,
+ bool prefer_imported, bool downloaded)
+{
+ int i, j;
+ struct dive_trip *trip_import, *trip_old;
+ int preexisting;
+ bool sequence_changed = false;
+
+ /* If no dives were imported, don't bother doing anything */
+ if (!import_table->nr)
+ return;
+
+ /* check if we need a nickname for the divecomputer for newly downloaded dives;
+ * since we know they all came from the same divecomputer we just check for the
+ * first one */
+ if (downloaded)
+ set_dc_nickname(import_table->dives[0]);
+ else
+ /* they aren't downloaded, so record / check all new ones */
+ for (i = 0; i < import_table->nr; i++)
+ set_dc_nickname(import_table->dives[i]);
+
+ /* Sort the table of dives to be imported and combine mergable dives */
+ sort_dive_table(import_table);
+ merge_imported_dives(import_table);
+
+ /* Autogroup dives if desired by user. */
+ autogroup_dives(import_table, import_trip_table);
+
+ preexisting = dive_table.nr; /* Remember old size for renumbering */
+
+ /* Merge overlapping trips. Since both trip tables are sorted, we
+ * could be smarter here, but realistically not a whole lot of trips
+ * will be imported so do a simple n*m loop until someone complains.
+ */
+ for (i = 0; i < import_trip_table->nr; i++) {
+ trip_import = import_trip_table->trips[i];
+ for (j = 0; j < trip_table.nr; j++) {
+ trip_old = trip_table.trips[j];
+ if (trips_overlap(trip_import, trip_old)) {
+ sequence_changed |= merge_trips(trip_import, import_table, trip_old, &dive_table, prefer_imported);
+ free_trip(trip_import); /* All dives in trip have been consumed -> free */
+ break;
+ }
+ }
+ /* If no trip to merge-into was found, add trip as-is. */
+ if (j == trip_table.nr)
+ sequence_changed |= add_trip_to_table(trip_import, import_table, &trip_table, &dive_table);
+ }
+ import_trip_table->nr = 0; /* All trips were consumed */
+
+ sequence_changed |= merge_dive_tables(import_table, NULL, &dive_table, NULL, prefer_imported, NULL);
/* If the sequence wasn't changed, renumber */
if (!sequence_changed)
try_to_renumber(preexisting);
- /* Autogroup dives if desired by user. */
- autogroup_dives(&dive_table, &trip_table);
-
- /* Trips may have changed - make sure that they are still ordered */
+ /* Unlikely, but trip order may have changed owing to merging dives -
+ * make sure that they are still ordered */
sort_trip_table(&trip_table);
/* We might have deleted the old selected dive.