diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index 0eb4b83dc..7fa4d7905 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -462,7 +462,7 @@ ImportDives::ImportDives(struct divelog *log, int flags, const QString &source) currentDive = nullptr; auto [dives_to_add, dives_to_remove, trips_to_add, sites_to_add, devices_to_add] = - process_imported_dives(*log, flags); + divelog.process_imported_dives(*log, flags); // Add devices to devicesToAddAndRemove structure devicesToAddAndRemove = std::move(devices_to_add); diff --git a/core/divelist.cpp b/core/divelist.cpp index 57e736dd5..8a23c2159 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -14,7 +14,7 @@ #include "fulltext.h" #include "interpolate.h" #include "planner.h" -#include "qthelper.h" +#include "qthelper.h" // for emit_reset_signal() -> should be removed #include "range.h" #include "gettext.h" #include "git-access.h" @@ -661,28 +661,6 @@ int comp_dives_ptr(const struct dive *a, const struct dive *b) return comp_dives(*a, *b); } -/* - * Walk the dives from the oldest dive in the given table, and see if we - * can autogroup them. But only do this when the user selected autogrouping. - */ -static void autogroup_dives(struct dive_table &table, struct trip_table &trip_table) -{ - if (!divelog.autogroup) - return; - - for (auto &entry: get_dives_to_autogroup(table)) { - for (auto it = table.begin() + entry.from; it != table.begin() + entry.to; ++it) - entry.trip->add_dive(it->get()); - /* If this was newly allocated, add trip to list */ - if (entry.created_trip) - trip_table.put(std::move(entry.created_trip)); - } - trip_table.sort(); // Necessary? -#ifdef DEBUG_TRIP - dump_trip_list(); -#endif -} - /* This removes a dive from the global dive table but doesn't free the * resources associated with the dive. The caller must removed the dive * from the trip-list. Returns a pointer to the unregistered dive. @@ -723,424 +701,6 @@ struct dive *dive_table::register_dive(std::unique_ptr d) return res; } -void process_loaded_dives() -{ - divelog.dives.sort(); - divelog.trips.sort(); - - /* Autogroup dives if desired by user. */ - autogroup_dives(divelog.dives, divelog.trips); - - fulltext_populate(); - - /* Inform frontend of reset data. This should reset all the models. */ - emit_reset_signal(); - - /* Now that everything is settled, select the newest dive. */ - select_newest_visible_dive(); -} - -/* - * Merge subsequent dives in a table, if mergeable. This assumes - * that the dives are neither selected, not part of a trip, as - * is the case of freshly imported dives. - */ -static void merge_imported_dives(struct dive_table &table) -{ - for (size_t i = 1; i < table.size(); i++) { - auto &prev = table[i - 1]; - auto &dive = table[i]; - - /* only try to merge overlapping dives - or if one of the dives has - * zero duration (that might be a gps marker from the webservice) */ - if (prev->duration.seconds && dive->duration.seconds && - prev->endtime() < dive->when) - continue; - - auto merged = try_to_merge(*prev, *dive, false); - if (!merged) - continue; - - /* Add dive to dive site; try_to_merge() does not do that! */ - struct dive_site *ds = merged->dive_site; - if (ds) { - merged->dive_site = NULL; - ds->add_dive(merged.get()); - } - unregister_dive_from_dive_site(prev.get()); - unregister_dive_from_dive_site(dive.get()); - unregister_dive_from_trip(prev.get()); - unregister_dive_from_trip(dive.get()); - - /* Overwrite the first of the two dives and remove the second */ - table[i - 1] = std::move(merged); - table.erase(table.begin() + i); - - /* Redo the new 'i'th dive */ - i--; - } -} - -/* - * Try to merge a new dive into the dive at position idx. Return - * true on success. On success, the old dive will be added to the - * dives_to_remove table and the merged dive to the dives_to_add - * table. On failure everything stays unchanged. - * If "prefer_imported" is true, use data of the new dive. - */ -static bool try_to_merge_into(struct dive &dive_to_add, struct dive *old_dive, bool prefer_imported, - /* output parameters: */ - struct dive_table &dives_to_add, struct std::vector &dives_to_remove) -{ - auto merged = try_to_merge(*old_dive, dive_to_add, prefer_imported); - if (!merged) - return false; - - merged->divetrip = old_dive->divetrip; - range_insert_sorted(dives_to_remove, old_dive, comp_dives_ptr); - dives_to_add.put(std::move(merged)); - - return true; -} - -/* Check if a dive is ranked after the last dive of the global dive list */ -static bool dive_is_after_last(const struct dive &d) -{ - if (divelog.dives.empty()) - return true; - return dive_less_than(*divelog.dives.back(), d); -} - -/* Merge dives from "dives_from", owned by "delete" into the owned by "dives_to". - * Overlapping dives will be merged, non-overlapping dives will be moved. The results - * will be added to the "dives_to_add" table. Dives that were merged are added to - * the "dives_to_remove" table. Any newly added (not merged) dive will be assigned - * to the trip of the "trip" paremeter. If "delete_from" is non-null dives will be - * removed from this table. - * This function supposes that all input tables are sorted. - * Returns true if any dive was added (not merged) that is not past the - * last dive of the global dive list (i.e. the sequence will change). - * The integer referenced by "num_merged" will be increased for every - * merged dive that is added to "dives_to_add" */ -static bool merge_dive_tables(const std::vector &dives_from, struct dive_table &delete_from, - const std::vector &dives_to, - bool prefer_imported, struct dive_trip *trip, - /* output parameters: */ - struct dive_table &dives_to_add, struct std::vector &dives_to_remove, - int &num_merged) -{ - bool sequence_changed = false; - - /* Merge newly imported dives into the dive table. - * Since both lists (old and new) are sorted, we can step - * through them concurrently and locate the insertions points. - * Once found, check if the new dive can be merged in the - * previous or next dive. - * Note that this doesn't consider pathological cases such as: - * - New dive "connects" two old dives (turn three into one). - * - New dive can not be merged into adjacent but some further dive. - */ - size_t j = 0; /* Index in dives_to */ - size_t last_merged_into = std::string::npos; - for (dive *add: dives_from) { - /* This gets an owning pointer to the dive to add and removes it from - * the delete_from table. If the dive is not explicitly stored, it will - * be automatically deleting when ending the loop iteration */ - auto [dive_to_add, idx] = delete_from.pull(add); - if (!dive_to_add) { - report_info("merging unknown dives!"); - continue; - } - - /* Find insertion point. */ - while (j < dives_to.size() && dive_less_than(*dives_to[j], *dive_to_add)) - j++; - - /* Try to merge into previous dive. - * We are extra-careful to not merge into the same dive twice, as that - * would put the merged-into dive twice onto the dives-to-delete list. - * In principle that shouldn't happen as all dives that compare equal - * by is_same_dive() were already merged, and is_same_dive() should be - * transitive. But let's just go *completely* sure for the odd corner-case. */ - if (j > 0 && (last_merged_into == std::string::npos || j > last_merged_into + 1) && - dives_to[j - 1]->endtime() > dive_to_add->when) { - if (try_to_merge_into(*dive_to_add, dives_to[j - 1], prefer_imported, - dives_to_add, dives_to_remove)) { - last_merged_into = j - 1; - num_merged++; - continue; - } - } - - /* That didn't merge into the previous dive. - * Try to merge into next dive. */ - if (j < dives_to.size() && (last_merged_into == std::string::npos || j > last_merged_into) && - dive_to_add->endtime() > dives_to[j]->when) { - if (try_to_merge_into(*dive_to_add, dives_to[j], prefer_imported, - dives_to_add, dives_to_remove)) { - last_merged_into = j; - num_merged++; - continue; - } - } - - sequence_changed |= !dive_is_after_last(*dive_to_add); - dives_to_add.put(std::move(dive_to_add)); - } - - 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 */ -void add_imported_dives(struct divelog &import_log, int flags) -{ - /* Process imported dives and generate lists of dives - * to-be-added and to-be-removed */ - auto [dives_to_add, dives_to_remove, trips_to_add, dive_sites_to_add, devices_to_add] = - process_imported_dives(import_log, flags); - - /* Start by deselecting all dives, so that we don't end up with an invalid selection */ - select_single_dive(NULL); - - /* Add new dives to trip and site to get reference count correct. */ - for (auto &d: dives_to_add) { - struct dive_trip *trip = d->divetrip; - struct dive_site *site = d->dive_site; - d->divetrip = NULL; - d->dive_site = NULL; - trip->add_dive(d.get()); - if (site) - site->add_dive(d.get()); - } - - /* Remove old dives */ - divelog.delete_multiple_dives(dives_to_remove); - - /* Add new dives */ - for (auto &d: dives_to_add) - divelog.dives.put(std::move(d)); - dives_to_add.clear(); - - /* Add new trips */ - for (auto &trip: trips_to_add) - divelog.trips.put(std::move(trip)); - trips_to_add.clear(); - - /* Add new dive sites */ - for (auto &ds: dive_sites_to_add) - divelog.sites.register_site(std::move(ds)); - - /* Add new devices */ - for (auto &dev: devices_to_add) - add_to_device_table(divelog.devices, dev); - - /* We might have deleted the old selected dive. - * Choose the newest dive as selected (if any) */ - current_dive = !divelog.dives.empty() ? divelog.dives.back().get() : nullptr; - - /* Inform frontend of reset data. This should reset all the models. */ - emit_reset_signal(); -} - -/* Helper function for process_imported_dives(): - * Try to merge a trip into one of the existing trips. - * The bool pointed to by "sequence_changed" is set to true, if the sequence of - * the existing dives changes. - * The int pointed to by "start_renumbering_at" keeps track of the first dive - * to be renumbered in the dives_to_add table. - * For other parameters see process_imported_dives() - * Returns true if trip was merged. In this case, the trip will be - * freed. - */ -static bool try_to_merge_trip(dive_trip &trip_import, struct dive_table &import_table, bool prefer_imported, - /* output parameters: */ - struct dive_table &dives_to_add, std::vector &dives_to_remove, - bool &sequence_changed, int &start_renumbering_at) -{ - for (auto &trip_old: divelog.trips) { - if (trips_overlap(trip_import, *trip_old)) { - sequence_changed |= merge_dive_tables(trip_import.dives, import_table, trip_old->dives, - prefer_imported, trip_old.get(), - dives_to_add, dives_to_remove, - start_renumbering_at); - /* we took care of all dives of the trip, clean up the table */ - trip_import.dives.clear(); - return true; - } - } - - return false; -} - -// Helper function to convert a table of owned dives into a table of non-owning pointers. -// Used to merge *all* dives of a log into a different table. -static std::vector dive_table_to_non_owning(const dive_table &dives) -{ - std::vector res; - res.reserve(dives.size()); - for (auto &d: dives) - res.push_back(d.get()); - return res; -} - -/* Process imported dives: take a table of dives to be imported and - * generate five lists: - * 1) Dives to be added (newly created, owned) - * 2) Dives to be removed (old, non-owned, references global divelog) - * 3) Trips to be added (newly created, owned) - * 4) Dive sites to be added (newly created, owned) - * 5) Devices to be added (newly created, owned) - * The dives, trips and sites in import_log are consumed. - * On return, the tables have * size 0. - * - * Note: The new dives will have their divetrip- and divesites-fields - * set, but will *not* be part of the trip and site. The caller has to - * add them to the trip and site. - * - * The lists are generated by merging dives if possible. This is - * performed trip-wise. Finer control on merging is provided by - * the "flags" parameter: - * - If IMPORT_PREFER_IMPORTED is set, data of the new dives are - * prioritized on merging. - * - If IMPORT_MERGE_ALL_TRIPS is set, all overlapping trips will - * be merged, not only non-autogenerated trips. - * - If IMPORT_IS_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. - * - If IMPORT_ADD_TO_NEW_TRIP is true, dives that are not assigned - * to a trip will be added to a newly generated trip. - */ -process_imported_dives_result process_imported_dives(struct divelog &import_log, int flags) -{ - int start_renumbering_at = 0; - bool sequence_changed = false; - bool last_old_dive_is_numbered; - - process_imported_dives_result res; - - /* If no dives were imported, don't bother doing anything */ - if (import_log.dives.empty()) - return res; - - /* Check if any of the new dives has a number. This will be - * important later to decide if we want to renumber the added - * dives */ - bool new_dive_has_number = std::any_of(import_log.dives.begin(), import_log.dives.end(), - [](auto &d) { return d->number > 0; }); - - /* Add only the devices that we don't know about yet. */ - for (auto &dev: import_log.devices) { - if (!device_exists(divelog.devices, dev)) - add_to_device_table(res.devices_to_add, dev); - } - - /* Sort the table of dives to be imported and combine mergable dives */ - import_log.dives.sort(); - merge_imported_dives(import_log.dives); - - /* Autogroup tripless dives if desired by user. But don't autogroup - * if tripless dives should be added to a new trip. */ - if (!(flags & IMPORT_ADD_TO_NEW_TRIP)) - autogroup_dives(import_log.dives, import_log.trips); - - /* If dive sites already exist, use the existing versions. */ - for (auto &new_ds: import_log.sites) { - /* Check if it dive site is actually used by new dives. */ - if (std::none_of(import_log.dives.begin(), import_log.dives.end(), [ds=new_ds.get()] - (auto &d) { return d->dive_site == ds; })) - continue; - - struct dive_site *old_ds = divelog.sites.get_same(*new_ds); - if (!old_ds) { - /* Dive site doesn't exist. Add it to list of dive sites to be added. */ - new_ds->dives.clear(); /* Caller is responsible for adding dives to site */ - res.sites_to_add.put(std::move(new_ds)); - } else { - /* Dive site already exists - use the old one. */ - for (auto &d: import_log.dives) { - if (d->dive_site == new_ds.get()) - d->dive_site = old_ds; - } - } - } - import_log.sites.clear(); - - /* 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 (auto &trip_import: import_log.trips) { - if ((flags & IMPORT_MERGE_ALL_TRIPS) || trip_import->autogen) { - if (try_to_merge_trip(*trip_import, import_log.dives, flags & IMPORT_PREFER_IMPORTED, - res.dives_to_add, res.dives_to_remove, - sequence_changed, start_renumbering_at)) - continue; - } - - /* If no trip to merge-into was found, add trip as-is. - * First, add dives to list of dives to add */ - for (struct dive *d: trip_import->dives) { - /* Add dive to list of dives to-be-added. */ - auto [owned, idx] = import_log.dives.pull(d); - if (!owned) - continue; - sequence_changed |= !dive_is_after_last(*owned); - res.dives_to_add.put(std::move(owned)); - } - - trip_import->dives.clear(); /* Caller is responsible for adding dives to trip */ - - /* Finally, add trip to list of trips to add */ - res.trips_to_add.put(std::move(trip_import)); - } - import_log.trips.clear(); /* All trips were consumed */ - - if ((flags & IMPORT_ADD_TO_NEW_TRIP) && !import_log.dives.empty()) { - /* Create a new trip for unassigned dives, if desired. */ - auto [new_trip, idx] = res.trips_to_add.put( - create_trip_from_dive(import_log.dives.front().get()) - ); - - /* Add all remaining dives to this trip */ - for (auto &d: import_log.dives) { - sequence_changed |= !dive_is_after_last(*d); - d->divetrip = new_trip; - res.dives_to_add.put(std::move(d)); - } - - import_log.dives.clear(); /* All dives were consumed */ - } else if (!import_log.dives.empty()) { - /* The remaining dives in import_log.dives are those that don't belong to - * a trip and the caller does not want them to be associated to a - * new trip. Merge them into the global table. */ - sequence_changed |= merge_dive_tables(dive_table_to_non_owning(import_log.dives), - import_log.dives, - dive_table_to_non_owning(divelog.dives), - flags & IMPORT_PREFER_IMPORTED, NULL, - res.dives_to_add, res.dives_to_remove, start_renumbering_at); - } - - /* If new dives were only added at the end, renumber the added dives. - * But only if - * - The last dive in the old dive table had a number itself (if there is a last dive). - * - None of the new dives has a number. - */ - last_old_dive_is_numbered = divelog.dives.empty() || divelog.dives.back()->number > 0; - - /* We counted the number of merged dives that were added to dives_to_add. - * Skip those. Since sequence_changed is false all added dives are *after* - * all merged dives. */ - if (!sequence_changed && last_old_dive_is_numbered && !new_dive_has_number) { - int nr = !divelog.dives.empty() ? divelog.dives.back()->number : 0; - for (auto it = res.dives_to_add.begin() + start_renumbering_at; it < res.dives_to_add.end(); ++it) - (*it)->number = ++nr; - } - - return res; -} - /* return the number a dive gets when inserted at the given index. * this function is supposed to be called *before* a dive was added. * this returns: diff --git a/core/divelist.h b/core/divelist.h index a1b6b6024..5278224de 100644 --- a/core/divelist.h +++ b/core/divelist.h @@ -34,26 +34,6 @@ private: /* this is used for both git and xml format */ #define DATAFORMAT_VERSION 3 - -/* divelist core logic functions */ -extern void process_loaded_dives(); -/* flags for process_imported_dives() */ -#define IMPORT_PREFER_IMPORTED (1 << 0) -#define IMPORT_IS_DOWNLOADED (1 << 1) -#define IMPORT_MERGE_ALL_TRIPS (1 << 2) -#define IMPORT_ADD_TO_NEW_TRIP (1 << 3) -extern void add_imported_dives(struct divelog &log, int flags); - -struct process_imported_dives_result { - dive_table dives_to_add; - std::vector dives_to_remove; - trip_table trips_to_add; - dive_site_table sites_to_add; - std::vector devices_to_add; -}; - -extern process_imported_dives_result process_imported_dives(struct divelog &import_log, int flags); - extern void get_dive_gas(const struct dive *dive, int *o2_p, int *he_p, int *o2low_p); int get_min_datafile_version(); diff --git a/core/divelog.cpp b/core/divelog.cpp index 5f4d73a52..bdbdda4d2 100644 --- a/core/divelog.cpp +++ b/core/divelog.cpp @@ -7,6 +7,9 @@ #include "errorhelper.h" #include "filterpreset.h" #include "filterpresettable.h" +#include "qthelper.h" // for emit_reset_signal() -> should be removed +#include "range.h" +#include "selection.h" // clearly, a layering violation -> should be removed #include "trip.h" struct divelog divelog; @@ -89,3 +92,445 @@ bool divelog::is_trip_before_after(const struct dive *dive, bool before) const return it != dives.end() && (*it)->divetrip != nullptr; } } + +/* + * Merge subsequent dives in a table, if mergeable. This assumes + * that the dives are neither selected, not part of a trip, as + * is the case of freshly imported dives. + */ +static void merge_imported_dives(struct dive_table &table) +{ + for (size_t i = 1; i < table.size(); i++) { + auto &prev = table[i - 1]; + auto &dive = table[i]; + + /* only try to merge overlapping dives - or if one of the dives has + * zero duration (that might be a gps marker from the webservice) */ + if (prev->duration.seconds && dive->duration.seconds && + prev->endtime() < dive->when) + continue; + + auto merged = try_to_merge(*prev, *dive, false); + if (!merged) + continue; + + /* Add dive to dive site; try_to_merge() does not do that! */ + struct dive_site *ds = merged->dive_site; + if (ds) { + merged->dive_site = NULL; + ds->add_dive(merged.get()); + } + unregister_dive_from_dive_site(prev.get()); + unregister_dive_from_dive_site(dive.get()); + unregister_dive_from_trip(prev.get()); + unregister_dive_from_trip(dive.get()); + + /* Overwrite the first of the two dives and remove the second */ + table[i - 1] = std::move(merged); + table.erase(table.begin() + i); + + /* Redo the new 'i'th dive */ + i--; + } +} + +/* + * Walk the dives from the oldest dive in the given table, and see if we + * can autogroup them. But only do this when the user selected autogrouping. + */ +static void autogroup_dives(struct divelog &log) +{ + if (!log.autogroup) + return; + + for (auto &entry: get_dives_to_autogroup(log.dives)) { + for (auto it = log.dives.begin() + entry.from; it != log.dives.begin() + entry.to; ++it) + entry.trip->add_dive(it->get()); + /* If this was newly allocated, add trip to list */ + if (entry.created_trip) + log.trips.put(std::move(entry.created_trip)); + } + log.trips.sort(); // Necessary? +#ifdef DEBUG_TRIP + dump_trip_list(); +#endif +} + +/* Check if a dive is ranked after the last dive of the global dive list */ +static bool dive_is_after_last(const struct dive &d) +{ + if (divelog.dives.empty()) + return true; + return dive_less_than(*divelog.dives.back(), d); +} + +/* + * Try to merge a new dive into the dive at position idx. Return + * true on success. On success, the old dive will be added to the + * dives_to_remove table and the merged dive to the dives_to_add + * table. On failure everything stays unchanged. + * If "prefer_imported" is true, use data of the new dive. + */ +static bool try_to_merge_into(struct dive &dive_to_add, struct dive *old_dive, bool prefer_imported, + /* output parameters: */ + struct dive_table &dives_to_add, struct std::vector &dives_to_remove) +{ + auto merged = try_to_merge(*old_dive, dive_to_add, prefer_imported); + if (!merged) + return false; + + merged->divetrip = old_dive->divetrip; + range_insert_sorted(dives_to_remove, old_dive, comp_dives_ptr); + dives_to_add.put(std::move(merged)); + + return true; +} + +/* Merge dives from "dives_from", owned by "delete" into the owned by "dives_to". + * Overlapping dives will be merged, non-overlapping dives will be moved. The results + * will be added to the "dives_to_add" table. Dives that were merged are added to + * the "dives_to_remove" table. Any newly added (not merged) dive will be assigned + * to the trip of the "trip" paremeter. If "delete_from" is non-null dives will be + * removed from this table. + * This function supposes that all input tables are sorted. + * Returns true if any dive was added (not merged) that is not past the + * last dive of the global dive list (i.e. the sequence will change). + * The integer referenced by "num_merged" will be increased for every + * merged dive that is added to "dives_to_add" */ +static bool merge_dive_tables(const std::vector &dives_from, struct dive_table &delete_from, + const std::vector &dives_to, + bool prefer_imported, struct dive_trip *trip, + /* output parameters: */ + struct dive_table &dives_to_add, struct std::vector &dives_to_remove, + int &num_merged) +{ + bool sequence_changed = false; + + /* Merge newly imported dives into the dive table. + * Since both lists (old and new) are sorted, we can step + * through them concurrently and locate the insertions points. + * Once found, check if the new dive can be merged in the + * previous or next dive. + * Note that this doesn't consider pathological cases such as: + * - New dive "connects" two old dives (turn three into one). + * - New dive can not be merged into adjacent but some further dive. + */ + size_t j = 0; /* Index in dives_to */ + size_t last_merged_into = std::string::npos; + for (dive *add: dives_from) { + /* This gets an owning pointer to the dive to add and removes it from + * the delete_from table. If the dive is not explicitly stored, it will + * be automatically deleting when ending the loop iteration */ + auto [dive_to_add, idx] = delete_from.pull(add); + if (!dive_to_add) { + report_info("merging unknown dives!"); + continue; + } + + /* Find insertion point. */ + while (j < dives_to.size() && dive_less_than(*dives_to[j], *dive_to_add)) + j++; + + /* Try to merge into previous dive. + * We are extra-careful to not merge into the same dive twice, as that + * would put the merged-into dive twice onto the dives-to-delete list. + * In principle that shouldn't happen as all dives that compare equal + * by is_same_dive() were already merged, and is_same_dive() should be + * transitive. But let's just go *completely* sure for the odd corner-case. */ + if (j > 0 && (last_merged_into == std::string::npos || j > last_merged_into + 1) && + dives_to[j - 1]->endtime() > dive_to_add->when) { + if (try_to_merge_into(*dive_to_add, dives_to[j - 1], prefer_imported, + dives_to_add, dives_to_remove)) { + last_merged_into = j - 1; + num_merged++; + continue; + } + } + + /* That didn't merge into the previous dive. + * Try to merge into next dive. */ + if (j < dives_to.size() && (last_merged_into == std::string::npos || j > last_merged_into) && + dive_to_add->endtime() > dives_to[j]->when) { + if (try_to_merge_into(*dive_to_add, dives_to[j], prefer_imported, + dives_to_add, dives_to_remove)) { + last_merged_into = j; + num_merged++; + continue; + } + } + + sequence_changed |= !dive_is_after_last(*dive_to_add); + dives_to_add.put(std::move(dive_to_add)); + } + + return sequence_changed; +} + +/* Helper function for process_imported_dives(): + * Try to merge a trip into one of the existing trips. + * The bool pointed to by "sequence_changed" is set to true, if the sequence of + * the existing dives changes. + * The int pointed to by "start_renumbering_at" keeps track of the first dive + * to be renumbered in the dives_to_add table. + * For other parameters see process_imported_dives() + * Returns true if trip was merged. In this case, the trip will be + * freed. + */ +static bool try_to_merge_trip(dive_trip &trip_import, struct dive_table &import_table, bool prefer_imported, + /* output parameters: */ + struct dive_table &dives_to_add, std::vector &dives_to_remove, + bool &sequence_changed, int &start_renumbering_at) +{ + for (auto &trip_old: divelog.trips) { + if (trips_overlap(trip_import, *trip_old)) { + sequence_changed |= merge_dive_tables(trip_import.dives, import_table, trip_old->dives, + prefer_imported, trip_old.get(), + dives_to_add, dives_to_remove, + start_renumbering_at); + /* we took care of all dives of the trip, clean up the table */ + trip_import.dives.clear(); + return true; + } + } + + return false; +} + +// Helper function to convert a table of owned dives into a table of non-owning pointers. +// Used to merge *all* dives of a log into a different table. +static std::vector dive_table_to_non_owning(const dive_table &dives) +{ + std::vector res; + res.reserve(dives.size()); + for (auto &d: dives) + res.push_back(d.get()); + return res; +} + +/* Process imported dives: take a log.trips of dives to be imported and + * generate five lists: + * 1) Dives to be added (newly created, owned) + * 2) Dives to be removed (old, non-owned, references global divelog) + * 3) Trips to be added (newly created, owned) + * 4) Dive sites to be added (newly created, owned) + * 5) Devices to be added (newly created, owned) + * The dives, trips and sites in import_log are consumed. + * On return, the tables have * size 0. + * + * Note: The new dives will have their divetrip- and divesites-fields + * set, but will *not* be part of the trip and site. The caller has to + * add them to the trip and site. + * + * The lists are generated by merging dives if possible. This is + * performed trip-wise. Finer control on merging is provided by + * the "flags" parameter: + * - If import_flags::prefer_imported is set, data of the new dives are + * prioritized on merging. + * - If import_flags::merge_all_trips is set, all overlapping trips will + * be merged, not only non-autogenerated trips. + * - If import_flags::is_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. + * - If import_flags::add_to_new_trip is true, dives that are not assigned + * to a trip will be added to a newly generated trip. + */ +divelog::process_imported_dives_result divelog::process_imported_dives(struct divelog &import_log, int flags) +{ + int start_renumbering_at = 0; + bool sequence_changed = false; + bool last_old_dive_is_numbered; + + process_imported_dives_result res; + + /* If no dives were imported, don't bother doing anything */ + if (import_log.dives.empty()) + return res; + + /* Check if any of the new dives has a number. This will be + * important later to decide if we want to renumber the added + * dives */ + bool new_dive_has_number = std::any_of(import_log.dives.begin(), import_log.dives.end(), + [](auto &d) { return d->number > 0; }); + + /* Add only the devices that we don't know about yet. */ + for (auto &dev: import_log.devices) { + if (!device_exists(devices, dev)) + add_to_device_table(res.devices_to_add, dev); + } + + /* Sort the table of dives to be imported and combine mergable dives */ + import_log.dives.sort(); + merge_imported_dives(import_log.dives); + + /* Autogroup tripless dives if desired by user. But don't autogroup + * if tripless dives should be added to a new trip. */ + if (!(flags & import_flags::add_to_new_trip)) + autogroup_dives(import_log); + + /* If dive sites already exist, use the existing versions. */ + for (auto &new_ds: import_log.sites) { + /* Check if it dive site is actually used by new dives. */ + if (std::none_of(import_log.dives.begin(), import_log.dives.end(), [ds=new_ds.get()] + (auto &d) { return d->dive_site == ds; })) + continue; + + struct dive_site *old_ds = sites.get_same(*new_ds); + if (!old_ds) { + /* Dive site doesn't exist. Add it to list of dive sites to be added. */ + new_ds->dives.clear(); /* Caller is responsible for adding dives to site */ + res.sites_to_add.put(std::move(new_ds)); + } else { + /* Dive site already exists - use the old one. */ + for (auto &d: import_log.dives) { + if (d->dive_site == new_ds.get()) + d->dive_site = old_ds; + } + } + } + import_log.sites.clear(); + + /* 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 (auto &trip_import: import_log.trips) { + if ((flags & import_flags::merge_all_trips) || trip_import->autogen) { + if (try_to_merge_trip(*trip_import, import_log.dives, flags & import_flags::prefer_imported, + res.dives_to_add, res.dives_to_remove, + sequence_changed, start_renumbering_at)) + continue; + } + + /* If no trip to merge-into was found, add trip as-is. + * First, add dives to list of dives to add */ + for (struct dive *d: trip_import->dives) { + /* Add dive to list of dives to-be-added. */ + auto [owned, idx] = import_log.dives.pull(d); + if (!owned) + continue; + sequence_changed |= !dive_is_after_last(*owned); + res.dives_to_add.put(std::move(owned)); + } + + trip_import->dives.clear(); /* Caller is responsible for adding dives to trip */ + + /* Finally, add trip to list of trips to add */ + res.trips_to_add.put(std::move(trip_import)); + } + import_log.trips.clear(); /* All trips were consumed */ + + if ((flags & import_flags::add_to_new_trip) && !import_log.dives.empty()) { + /* Create a new trip for unassigned dives, if desired. */ + auto [new_trip, idx] = res.trips_to_add.put( + create_trip_from_dive(import_log.dives.front().get()) + ); + + /* Add all remaining dives to this trip */ + for (auto &d: import_log.dives) { + sequence_changed |= !dive_is_after_last(*d); + d->divetrip = new_trip; + res.dives_to_add.put(std::move(d)); + } + + import_log.dives.clear(); /* All dives were consumed */ + } else if (!import_log.dives.empty()) { + /* The remaining dives in import_log.dives are those that don't belong to + * a trip and the caller does not want them to be associated to a + * new trip. Merge them into the global table. */ + sequence_changed |= merge_dive_tables(dive_table_to_non_owning(import_log.dives), + import_log.dives, + dive_table_to_non_owning(dives), + flags & import_flags::prefer_imported, NULL, + res.dives_to_add, res.dives_to_remove, start_renumbering_at); + } + + /* If new dives were only added at the end, renumber the added dives. + * But only if + * - The last dive in the old dive table had a number itself (if there is a last dive). + * - None of the new dives has a number. + */ + last_old_dive_is_numbered = dives.empty() || dives.back()->number > 0; + + /* We counted the number of merged dives that were added to dives_to_add. + * Skip those. Since sequence_changed is false all added dives are *after* + * all merged dives. */ + if (!sequence_changed && last_old_dive_is_numbered && !new_dive_has_number) { + int nr = !dives.empty() ? dives.back()->number : 0; + for (auto it = res.dives_to_add.begin() + start_renumbering_at; it < res.dives_to_add.end(); ++it) + (*it)->number = ++nr; + } + + return res; +} + +// TODO: This accesses global state, namely fulltext and selection. +// TODO: Move up the call chain? +void divelog::process_loaded_dives() +{ + dives.sort(); + trips.sort(); + + /* Autogroup dives if desired by user. */ + autogroup_dives(*this); + + fulltext_populate(); + + /* Inform frontend of reset data. This should reset all the models. */ + emit_reset_signal(); + + /* Now that everything is settled, select the newest dive. */ + select_newest_visible_dive(); +} + +/* 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 */ +void divelog::add_imported_dives(struct divelog &import_log, int flags) +{ + /* Process imported dives and generate lists of dives + * to-be-added and to-be-removed */ + auto [dives_to_add, dives_to_remove, trips_to_add, dive_sites_to_add, devices_to_add] = + process_imported_dives(import_log, flags); + + /* Start by deselecting all dives, so that we don't end up with an invalid selection */ + select_single_dive(NULL); + + /* Add new dives to trip and site to get reference count correct. */ + for (auto &d: dives_to_add) { + struct dive_trip *trip = d->divetrip; + struct dive_site *site = d->dive_site; + d->divetrip = NULL; + d->dive_site = NULL; + trip->add_dive(d.get()); + if (site) + site->add_dive(d.get()); + } + + /* Remove old dives */ + delete_multiple_dives(dives_to_remove); + + /* Add new dives */ + for (auto &d: dives_to_add) + dives.put(std::move(d)); + dives_to_add.clear(); + + /* Add new trips */ + for (auto &trip: trips_to_add) + trips.put(std::move(trip)); + trips_to_add.clear(); + + /* Add new dive sites */ + for (auto &ds: dive_sites_to_add) + sites.register_site(std::move(ds)); + + /* Add new devices */ + for (auto &dev: devices_to_add) + add_to_device_table(devices, dev); + + /* We might have deleted the old selected dive. + * Choose the newest dive as selected (if any) */ + current_dive = !dives.empty() ? dives.back().get() : nullptr; + + /* Inform frontend of reset data. This should reset all the models. */ + emit_reset_signal(); +} diff --git a/core/divelog.h b/core/divelog.h index b77ec1214..545afe672 100644 --- a/core/divelog.h +++ b/core/divelog.h @@ -12,6 +12,14 @@ struct device; +/* flags for process_imported_dives() */ +struct import_flags { + static constexpr int prefer_imported = 1 << 0; + static constexpr int is_downloaded = 1 << 1; + static constexpr int merge_all_trips = 1 << 2; + static constexpr int add_to_new_trip = 1 << 3; +}; + struct divelog { dive_table dives; trip_table trips; @@ -29,6 +37,19 @@ struct divelog { void delete_multiple_dives(const std::vector &dives); void clear(); bool is_trip_before_after(const struct dive *dive, bool before) const; + + struct process_imported_dives_result { + dive_table dives_to_add; + std::vector dives_to_remove; + trip_table trips_to_add; + dive_site_table sites_to_add; + std::vector devices_to_add; + }; + + /* divelist core logic functions */ + process_imported_dives_result process_imported_dives(struct divelog &import_log, int flags); // import_log will be consumed + void process_loaded_dives(); + void add_imported_dives(struct divelog &log, int flags); // log will be consumed }; extern struct divelog divelog; diff --git a/desktop-widgets/divelogimportdialog.cpp b/desktop-widgets/divelogimportdialog.cpp index 23ac210b7..760c65244 100644 --- a/desktop-widgets/divelogimportdialog.cpp +++ b/desktop-widgets/divelogimportdialog.cpp @@ -967,7 +967,7 @@ void DiveLogImportDialog::on_buttonBox_accepted() } QString source = fileNames.size() == 1 ? fileNames[0] : tr("multiple files"); - Command::importDives(&log, IMPORT_MERGE_ALL_TRIPS, source); + Command::importDives(&log, import_flags::merge_all_trips, source); } TagDragDelegate::TagDragDelegate(QObject *parent) : QStyledItemDelegate(parent) diff --git a/desktop-widgets/downloadfromdivecomputer.cpp b/desktop-widgets/downloadfromdivecomputer.cpp index 16827c57b..6ac7d9b9e 100644 --- a/desktop-widgets/downloadfromdivecomputer.cpp +++ b/desktop-widgets/downloadfromdivecomputer.cpp @@ -565,11 +565,11 @@ void DownloadFromDCWidget::on_ok_clicked() if (currentState != DONE && currentState != ERRORED) return; - int flags = IMPORT_IS_DOWNLOADED; + int flags = import_flags::is_downloaded; if (preferDownloaded()) - flags |= IMPORT_PREFER_IMPORTED; + flags |= import_flags::prefer_imported; if (ui.createNewTrip->isChecked()) - flags |= IMPORT_ADD_TO_NEW_TRIP; + flags |= import_flags::add_to_new_trip; diveImportedModel->recordDives(flags); diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index 230ec6cb7..2bf78d2f0 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -419,7 +419,7 @@ void MainWindow::on_actionCloudstorageopen_triggered() std::string encoded = encodeFileName(*filename); if (!parse_file(encoded.c_str(), &divelog)) setCurrentFile(encoded); - process_loaded_dives(); + divelog.process_loaded_dives(); hideProgressBar(); refreshDisplay(); updateAutogroup(); @@ -1313,7 +1313,7 @@ void MainWindow::importFiles(const std::vector &fileNames) parse_file(encoded.c_str(), &log); } QString source = fileNames.size() == 1 ? QString::fromStdString(fileNames[0]) : tr("multiple files"); - Command::importDives(&log, IMPORT_MERGE_ALL_TRIPS, source); + Command::importDives(&log, import_flags::merge_all_trips, source); } void MainWindow::loadFiles(const std::vector &fileNames) @@ -1334,7 +1334,7 @@ void MainWindow::loadFiles(const std::vector &fileNames) } hideProgressBar(); updateRecentFiles(); - process_loaded_dives(); + divelog.process_loaded_dives(); refreshDisplay(); updateAutogroup(); diff --git a/desktop-widgets/subsurfacewebservices.cpp b/desktop-widgets/subsurfacewebservices.cpp index 1f911c2d6..7364dbd59 100644 --- a/desktop-widgets/subsurfacewebservices.cpp +++ b/desktop-widgets/subsurfacewebservices.cpp @@ -7,7 +7,7 @@ #include "desktop-widgets/mainwindow.h" #include "commands/command.h" #include "core/device.h" -#include "core/divelist.h" // For IMPORT_MERGE_ALL_TRIPS +#include "core/divelist.h" #include "core/divelog.h" #include "core/errorhelper.h" #include "core/file.h" @@ -458,7 +458,7 @@ void DivelogsDeWebServices::buttonClicked(QAbstractButton *button) /* parse file and import dives */ struct divelog log; parse_file(QFile::encodeName(zipFile.fileName()), &log); - Command::importDives(&log, IMPORT_MERGE_ALL_TRIPS, QStringLiteral("divelogs.de")); + Command::importDives(&log, import_flags::merge_all_trips, QStringLiteral("divelogs.de")); /* store last entered user/pass in config */ qPrefCloudStorage::set_divelogde_user(ui.userID->text()); diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index c15eb191d..9f5756ea4 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -414,7 +414,7 @@ void QMLManager::openLocalThenRemote(QString url) qPrefPartialPressureGas::set_po2(git_prefs.pp_graphs.po2); // the following steps can take a long time, so provide updates setNotificationText(tr("Processing %1 dives").arg(divelog.dives.size())); - process_loaded_dives(); + divelog.process_loaded_dives(); setNotificationText(tr("%1 dives loaded from local dive data file").arg(divelog.dives.size())); } if (qPrefCloudStorage::cloud_verification_status() == qPrefCloudStorage::CS_NEED_TO_VERIFY) { @@ -478,7 +478,7 @@ void QMLManager::mergeLocalRepo() { struct divelog log; parse_file(qPrintable(nocloud_localstorage()), &log); - add_imported_dives(log, IMPORT_MERGE_ALL_TRIPS); + divelog.add_imported_dives(log, import_flags::merge_all_trips); mark_divelist_changed(true); } @@ -891,7 +891,7 @@ void QMLManager::consumeFinishedLoad() prefs.show_ccr_setpoint = git_prefs.show_ccr_setpoint; prefs.show_ccr_sensors = git_prefs.show_ccr_sensors; prefs.pp_graphs.po2 = git_prefs.pp_graphs.po2; - process_loaded_dives(); + divelog.process_loaded_dives(); appendTextToLog(QStringLiteral("%1 dives loaded").arg(divelog.dives.size())); if (divelog.dives.empty()) setStartPageText(tr("Cloud storage open successfully. No dives in dive list.")); @@ -2356,7 +2356,7 @@ void QMLManager::importCacheRepo(QString repo) QString repoPath = QString::fromStdString(system_default_directory() + "/cloudstorage/") + repo; appendTextToLog(QString("importing %1").arg(repoPath)); parse_file(qPrintable(repoPath), &log); - add_imported_dives(log, IMPORT_MERGE_ALL_TRIPS); + divelog.add_imported_dives(log, import_flags::merge_all_trips); changesNeedSaving(); } diff --git a/qt-models/diveimportedmodel.h b/qt-models/diveimportedmodel.h index 31316185d..a7874910b 100644 --- a/qt-models/diveimportedmodel.h +++ b/qt-models/diveimportedmodel.h @@ -25,7 +25,7 @@ public: struct divelog consumeTables(); // Returns downloaded tables and resets model. int numDives() const; - Q_INVOKABLE void recordDives(int flags = IMPORT_PREFER_IMPORTED | IMPORT_IS_DOWNLOADED); + Q_INVOKABLE void recordDives(int flags = import_flags::prefer_imported | import_flags::is_downloaded); Q_INVOKABLE void startDownload(); Q_INVOKABLE void waitForDownload(); diff --git a/tests/testgitstorage.cpp b/tests/testgitstorage.cpp index ae77b3f60..b84c5a0bb 100644 --- a/tests/testgitstorage.cpp +++ b/tests/testgitstorage.cpp @@ -243,7 +243,7 @@ void TestGitStorage::testGitStorageCloudOfflineSync() QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test10.xml", &divelog), 0); // calling process_loaded_dives() sorts the table, but calling add_imported_dives() // causes it to try to update the window title... let's not do that - process_loaded_dives(); + divelog.process_loaded_dives(); // now save only to the local cache but not to the remote server git_local_only = true; QCOMPARE(save_dives(cloudTestRepo.c_str()), 0); @@ -297,7 +297,7 @@ void TestGitStorage::testGitStorageCloudMerge() git_local_only = false; QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test11.xml", &divelog), 0); - process_loaded_dives(); + divelog.process_loaded_dives(); QCOMPARE(save_dives(cloudTestRepo.c_str()), 0); clear_dive_file_data(); @@ -309,7 +309,7 @@ void TestGitStorage::testGitStorageCloudMerge() git_local_only = true; QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test12.xml", &divelog), 0); - process_loaded_dives(); + divelog.process_loaded_dives(); QCOMPARE(save_dives(cloudTestRepo.c_str()), 0); clear_dive_file_data(); @@ -323,12 +323,12 @@ void TestGitStorage::testGitStorageCloudMerge() QCOMPARE(parse_file("./SampleDivesV3plus10local.ssrf", &divelog), 0); QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test11.xml", &divelog), 0); QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test12.xml", &divelog), 0); - process_loaded_dives(); + divelog.process_loaded_dives(); QCOMPARE(save_dives("./SampleDivesV3plus10-11-12.ssrf"), 0); // then load from the cloud clear_dive_file_data(); QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); - process_loaded_dives(); + divelog.process_loaded_dives(); QCOMPARE(save_dives("./SampleDivesV3plus10-11-12-merged.ssrf"), 0); // finally compare what we have QFile org("./SampleDivesV3plus10-11-12-merged.ssrf"); @@ -345,7 +345,7 @@ void TestGitStorage::testGitStorageCloudMerge() // (6) move ourselves back to the first client and compare data there moveDir(localCacheDir + "client1", localCacheDir); QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); - process_loaded_dives(); + divelog.process_loaded_dives(); QCOMPARE(save_dives("./SampleDivesV3plus10-11-12-merged-client1.ssrf"), 0); QFile client1("./SampleDivesV3plus10-11-12-merged-client1.ssrf"); client1.open(QFile::ReadOnly); @@ -361,7 +361,7 @@ void TestGitStorage::testGitStorageCloudMerge2() // merge // (1) open repo, delete second dive, save offline QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); - process_loaded_dives(); + divelog.process_loaded_dives(); struct dive *dive = get_dive(1); divelog.delete_multiple_dives(std::vector{ dive }); QCOMPARE(save_dives("./SampleDivesMinus1.ssrf"), 0); @@ -375,7 +375,7 @@ void TestGitStorage::testGitStorageCloudMerge2() // (3) now we open the cloud storage repo and modify that second dive QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); - process_loaded_dives(); + divelog.process_loaded_dives(); dive = get_dive(1); QVERIFY(dive != NULL); dive->notes = "These notes have been modified by TestGitStorage"; @@ -409,7 +409,7 @@ void TestGitStorage::testGitStorageCloudMerge3() // (1) open repo, edit notes of first three dives QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); - process_loaded_dives(); + divelog.process_loaded_dives(); struct dive *dive; QVERIFY((dive = get_dive(0)) != 0); dive->notes = "Create multi line dive notes\nLine 2\nLine 3\nLine 4\nLine 5\nThat should be enough"; @@ -422,7 +422,7 @@ void TestGitStorage::testGitStorageCloudMerge3() // (2) make different edits offline QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); - process_loaded_dives(); + divelog.process_loaded_dives(); QVERIFY((dive = get_dive(0)) != 0); dive->notes = "Create multi line dive notes\nDifferent line 2 and removed 3-5\n\nThat should be enough"; QVERIFY((dive = get_dive(1)) != 0); @@ -438,7 +438,7 @@ void TestGitStorage::testGitStorageCloudMerge3() // those first dive notes differently while online moveDir(localCacheDir, localCacheDir + "save"); QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); - process_loaded_dives(); + divelog.process_loaded_dives(); QVERIFY((dive = get_dive(0)) != 0); dive->notes = "Completely different dive notes\nBut also multi line"; QVERIFY((dive = get_dive(1)) != 0); diff --git a/tests/testmerge.cpp b/tests/testmerge.cpp index 96c7fa880..86a90dc5a 100644 --- a/tests/testmerge.cpp +++ b/tests/testmerge.cpp @@ -28,9 +28,9 @@ void TestMerge::testMergeEmpty() */ struct divelog log; QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47.xml", &log), 0); - add_imported_dives(log, IMPORT_MERGE_ALL_TRIPS); + divelog.add_imported_dives(log, import_flags::merge_all_trips); QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test48.xml", &log), 0); - add_imported_dives(log, IMPORT_MERGE_ALL_TRIPS); + divelog.add_imported_dives(log, import_flags::merge_all_trips); QCOMPARE(save_dives("./testmerge47+48.ssrf"), 0); QFile org(SUBSURFACE_TEST_DATA "/dives/test47+48.xml"); org.open(QFile::ReadOnly); @@ -51,9 +51,9 @@ void TestMerge::testMergeBackwards() */ struct divelog log; QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test48.xml", &log), 0); - add_imported_dives(log, IMPORT_MERGE_ALL_TRIPS); + divelog.add_imported_dives(log, import_flags::merge_all_trips); QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47.xml", &log), 0); - add_imported_dives(log, IMPORT_MERGE_ALL_TRIPS); + divelog.add_imported_dives(log, import_flags::merge_all_trips); QCOMPARE(save_dives("./testmerge47+48.ssrf"), 0); QFile org(SUBSURFACE_TEST_DATA "/dives/test48+47.xml"); org.open(QFile::ReadOnly); diff --git a/tests/testrenumber.cpp b/tests/testrenumber.cpp index a78c2c57f..3fe629a8c 100644 --- a/tests/testrenumber.cpp +++ b/tests/testrenumber.cpp @@ -13,14 +13,14 @@ void TestRenumber::setup() { prefs.cloud_base_url = default_prefs.cloud_base_url; QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47.xml", &divelog), 0); - process_loaded_dives(); + divelog.process_loaded_dives(); } void TestRenumber::testMerge() { struct divelog log; QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47b.xml", &log), 0); - add_imported_dives(log, IMPORT_MERGE_ALL_TRIPS); + divelog.add_imported_dives(log, import_flags::merge_all_trips); QCOMPARE(divelog.dives.size(), 1); } @@ -28,7 +28,7 @@ void TestRenumber::testMergeAndAppend() { struct divelog log; QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47c.xml", &log), 0); - add_imported_dives(log, IMPORT_MERGE_ALL_TRIPS); + divelog.add_imported_dives(log, import_flags::merge_all_trips); QCOMPARE(divelog.dives.size(), 2); struct dive *d = get_dive(1); QVERIFY(d != NULL);