diff options
-rw-r--r-- | core/dive.h | 8 | ||||
-rw-r--r-- | core/divelist.c | 65 | ||||
-rw-r--r-- | core/divelist.h | 8 | ||||
-rw-r--r-- | core/divesite.c | 67 | ||||
-rw-r--r-- | core/divesite.h | 9 | ||||
-rw-r--r-- | core/downloadfromdcthread.cpp | 5 | ||||
-rw-r--r-- | core/downloadfromdcthread.h | 2 | ||||
-rw-r--r-- | desktop-widgets/command.cpp | 4 | ||||
-rw-r--r-- | desktop-widgets/command.h | 2 | ||||
-rw-r--r-- | desktop-widgets/command_base.h | 4 | ||||
-rw-r--r-- | desktop-widgets/command_divelist.cpp | 102 | ||||
-rw-r--r-- | desktop-widgets/command_divelist.h | 33 | ||||
-rw-r--r-- | desktop-widgets/divelogimportdialog.cpp | 13 | ||||
-rw-r--r-- | desktop-widgets/downloadfromdivecomputer.cpp | 5 | ||||
-rw-r--r-- | desktop-widgets/mainwindow.cpp | 5 | ||||
-rw-r--r-- | desktop-widgets/subsurfacewebservices.cpp | 5 | ||||
-rw-r--r-- | mobile-widgets/qml/DownloadFromDiveComputer.qml | 2 | ||||
-rw-r--r-- | mobile-widgets/qmlmanager.cpp | 5 | ||||
-rw-r--r-- | qt-models/diveimportedmodel.cpp | 8 | ||||
-rw-r--r-- | qt-models/diveimportedmodel.h | 3 | ||||
-rw-r--r-- | tests/testmerge.cpp | 18 | ||||
-rw-r--r-- | tests/testrenumber.cpp | 10 |
22 files changed, 269 insertions, 114 deletions
diff --git a/core/dive.h b/core/dive.h index d40e53043..03d6fc2df 100644 --- a/core/dive.h +++ b/core/dive.h @@ -758,15 +758,17 @@ extern void average_max_depth(struct diveplan *dive, int *avg_depth, int *max_de #ifdef __cplusplus } -/* Make pointers to dive, dive_trip and dive_table "Qt metatypes" so that they can - * be passed through QVariants and through QML. +/* Make pointers to dive, dive_trip, dive_table, trip_table and dive_site_table + * "Qt metatypes" so that they can be passed through QVariants and through QML. * Note: we have to use the typedef "dive_table_t" instead of "struct dive_table", * because MOC removes the "struct", but dive_table is already the name of a global - * variable, leading to compilation errors. Likewise for "struct trip_table". */ + * variable, leading to compilation errors. Likewise for "struct trip_table" and + * "struct dive_site_table". */ Q_DECLARE_METATYPE(struct dive *); Q_DECLARE_METATYPE(struct dive_trip *); Q_DECLARE_METATYPE(dive_table_t *); Q_DECLARE_METATYPE(trip_table_t *); +Q_DECLARE_METATYPE(dive_site_table_t *); #endif diff --git a/core/divelist.c b/core/divelist.c index 8e1ba1628..09f08acd0 100644 --- a/core/divelist.c +++ b/core/divelist.c @@ -1518,17 +1518,18 @@ static bool merge_dive_tables(struct dive_table *dives_from, struct dive_table * /* 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 dive_table *import_table, struct trip_table *import_trip_table, int flags) +void add_imported_dives(struct dive_table *import_table, struct trip_table *import_trip_table, struct dive_site_table *import_sites_table, int flags) { int i, idx; struct dive_table dives_to_add = { 0 }; struct dive_table dives_to_remove = { 0 }; struct trip_table trips_to_add = { 0 }; + struct dive_site_table dive_sites_to_add = { 0 }; /* Process imported dives and generate lists of dives * to-be-added and to-be-removed */ - process_imported_dives(import_table, import_trip_table, flags, - &dives_to_add, &dives_to_remove, &trips_to_add); + process_imported_dives(import_table, import_trip_table, import_sites_table, flags, + &dives_to_add, &dives_to_remove, &trips_to_add, &dive_sites_to_add); /* Add new dives to trip, so that trips don't get deleted * on deletion of old dives */ @@ -1560,6 +1561,11 @@ void add_imported_dives(struct dive_table *import_table, struct trip_table *impo insert_trip(trips_to_add.trips[i], &trip_table); trips_to_add.nr = 0; + /* Add new dive sites */ + for (i = 0; i < dive_sites_to_add.nr; i++) + register_dive_site(dive_sites_to_add.dive_sites[i]); + dive_sites_to_add.nr = 0; + /* We might have deleted the old selected dive. * Choose the newest dive as selected (if any) */ current_dive = dive_table.nr > 0 ? dive_table.dives[dive_table.nr - 1] : NULL; @@ -1600,21 +1606,23 @@ bool try_to_merge_trip(struct dive_trip *trip_import, struct dive_table *import_ } /* Process imported dives: take a table of dives to be imported and - * generate three lists: + * generate four lists: * 1) Dives to be added * 2) Dives to be removed * 3) Trips to be added + * 4) Dive sites to be added * The dives to be added are owning (i.e. the caller is responsible * for freeing them). - * The dives and trips in "import_table" and "import_trip_table" are - * consumed. On return, both tables have size 0. - * "import_trip_table" may be NULL if all dives are not associated + * The dives, trips and sites in "import_table", "import_trip_table" + * and "import_sites_table" are consumed. On return, the tables have + * size 0. "import_trip_table" may be NULL if all dives are not associated * with a trip. - * The output parameters should be empty - if not, their content + * The output tables should be empty - if not, their content * will be cleared! * - * Note: The new dives will have their divetrip-field set, but will - * *not* be part of the trip. The caller has to add them to the trip. + * 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 @@ -1629,10 +1637,11 @@ bool try_to_merge_trip(struct dive_trip *trip_import, struct dive_table *import_ * - If IMPORT_ADD_TO_NEW_TRIP is true, dives that are not assigned * to a trip will be added to a newly generated trip. */ -void process_imported_dives(struct dive_table *import_table, struct trip_table *import_trip_table, int flags, +void process_imported_dives(struct dive_table *import_table, struct trip_table *import_trip_table, + struct dive_site_table *import_sites_table, int flags, /* output parameters: */ struct dive_table *dives_to_add, struct dive_table *dives_to_remove, - struct trip_table *trips_to_add) + struct trip_table *trips_to_add, struct dive_site_table *sites_to_add) { int i, j, nr, start_renumbering_at = 0; struct dive_trip *trip_import, *new_trip; @@ -1651,6 +1660,7 @@ void process_imported_dives(struct dive_table *import_table, struct trip_table * clear_table(dives_to_add); clear_table(dives_to_remove); clear_trip_table(trips_to_add); + clear_dive_site_table(sites_to_add); /* Check if any of the new dives has a number. This will be * important later to decide if we want to renumber the added @@ -1687,6 +1697,37 @@ void process_imported_dives(struct dive_table *import_table, struct trip_table * preexisting = dive_table.nr; /* Remember old size for renumbering */ + /* If dive sites already exist, use the existing versions. */ + for (i = 0; i < import_sites_table->nr; i++) { + struct dive_site *new_ds = import_sites_table->dive_sites[i]; + struct dive_site *old_ds = get_same_dive_site(new_ds); + + /* Check if it dive site is actually used by new dives. */ + for (j = 0; j < import_table->nr; j++) { + if (import_table->dives[j]->dive_site == new_ds) + break; + } + + if (j == import_table->nr) { + /* Dive site not even used - free it and go to next. */ + free_dive_site(new_ds); + continue; + } + + if (!old_ds) { + /* Dive site doesn't exist. Add it to list of dive sites to be added. */ + add_dive_site_to_table(new_ds, sites_to_add); + continue; + } + /* Dive site already exists - use the old and free the new. */ + for (j = 0; j < import_table->nr; j++) { + if (import_table->dives[j]->dive_site == new_ds) + import_table->dives[j]->dive_site = old_ds; + } + free_dive_site(new_ds); + } + import_sites_table->nr = 0; /* All dive sites were consumed */ + /* 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. diff --git a/core/divelist.h b/core/divelist.h index e1ff706f7..1880856df 100644 --- a/core/divelist.h +++ b/core/divelist.h @@ -23,10 +23,12 @@ extern void process_loaded_dives(); #define IMPORT_IS_DOWNLOADED (1 << 1) #define IMPORT_MERGE_ALL_TRIPS (1 << 2) #define IMPORT_ADD_TO_NEW_TRIP (1 << 3) -extern void add_imported_dives(struct dive_table *import_table, struct trip_table *import_trip_table, int flags); -extern void process_imported_dives(struct dive_table *import_table, struct trip_table *import_trip_table, int flags, +extern void add_imported_dives(struct dive_table *import_table, struct trip_table *import_trip_table, struct dive_site_table *import_sites_table, + int flags); +extern void process_imported_dives(struct dive_table *import_table, struct trip_table *import_trip_table, struct dive_site_table *import_sites_table, + int flags, struct dive_table *dives_to_add, struct dive_table *dives_to_remove, - struct trip_table *trips_to_add); + struct trip_table *trips_to_add, struct dive_site_table *sites_to_add); extern char *get_dive_gas_string(const struct dive *dive); extern struct dive **grow_dive_table(struct dive_table *table); diff --git a/core/divesite.c b/core/divesite.c index 9baabd872..bf99ed395 100644 --- a/core/divesite.c +++ b/core/divesite.c @@ -117,14 +117,13 @@ static uint32_t dive_site_getUniqId(struct dive_site_table *ds_table) return id; } -/* we never allow a second dive site with the same uuid */ -struct dive_site *alloc_or_get_dive_site(uint32_t uuid, struct dive_site_table *ds_table) +void register_dive_site(struct dive_site *ds) { - struct dive_site *ds; - - if (uuid && (ds = get_dive_site_by_uuid(uuid, ds_table)) != NULL) - return ds; + add_dive_site_to_table(ds, &dive_site_table); +} +void add_dive_site_to_table(struct dive_site *ds, struct dive_site_table *ds_table) +{ int nr = ds_table->nr; int allocated = ds_table->allocated; struct dive_site **sites = ds_table->dive_sites; @@ -137,11 +136,23 @@ struct dive_site *alloc_or_get_dive_site(uint32_t uuid, struct dive_site_table * ds_table->dive_sites = sites; ds_table->allocated = allocated; } + sites[nr] = ds; + ds_table->nr = nr + 1; +} + +/* we never allow a second dive site with the same 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 = calloc(1, sizeof(*ds)); if (!ds) exit(1); - sites[nr] = ds; - ds_table->nr = nr + 1; + add_dive_site_to_table(ds, ds_table); + // we should always be called with a valid uuid except in the special // case where we want to copy a dive site into the memory we allocated // here - then we need to pass in 0 and create a temporary uuid here @@ -195,12 +206,16 @@ void free_dive_site(struct dive_site *ds) } } -void delete_dive_site(struct dive_site *ds, struct dive_site_table *ds_table) +void unregister_dive_site(struct dive_site *ds) +{ + remove_dive_site_from_table(ds, &dive_site_table); +} + +void remove_dive_site_from_table(struct dive_site *ds, struct dive_site_table *ds_table) { int nr = ds_table->nr; for (int i = 0; i < nr; i++) { if (ds == get_dive_site(i, ds_table)) { - free_dive_site(ds); if (nr - 1 > i) memmove(&ds_table->dive_sites[i], &ds_table->dive_sites[i+1], @@ -211,6 +226,14 @@ void delete_dive_site(struct dive_site *ds, struct dive_site_table *ds_table) } } +void delete_dive_site(struct dive_site *ds, struct dive_site_table *ds_table) +{ + if (!ds) + return; + remove_dive_site_from_table(ds, ds_table); + free_dive_site(ds); +} + static uint32_t create_divesite_uuid(const char *name, timestamp_t divetime) { if (name == NULL) @@ -291,6 +314,30 @@ static void merge_string(char **a, char **b) 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, &dive_site_table) + 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; diff --git a/core/divesite.h b/core/divesite.h index ba4cd32a5..1f841a7d2 100644 --- a/core/divesite.h +++ b/core/divesite.h @@ -24,10 +24,10 @@ struct dive_site struct taxonomy_data taxonomy; }; -struct dive_site_table { +typedef struct dive_site_table { int nr, allocated; struct dive_site **dive_sites; -}; +} dive_site_table_t; extern struct dive_site_table dive_site_table; @@ -45,6 +45,10 @@ static inline struct dive_site *get_dive_site(int nr, struct dive_site_table *ds 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 dive_site_table_sort(struct dive_site_table *ds_table); +void add_dive_site_to_table(struct dive_site *ds, struct dive_site_table *ds_table); +void remove_dive_site_from_table(struct dive_site *ds, struct dive_site_table *ds_table); +void register_dive_site(struct dive_site *ds); +void unregister_dive_site(struct dive_site *ds); struct dive_site *alloc_or_get_dive_site(uint32_t uuid, struct dive_site_table *ds_table); int nr_of_dives_at_dive_site(struct dive_site *ds, bool select_only); bool is_dive_site_used(struct dive_site *ds, bool select_only); @@ -56,6 +60,7 @@ struct dive_site *get_dive_site_by_name(const char *name, struct dive_site_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(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); diff --git a/core/downloadfromdcthread.cpp b/core/downloadfromdcthread.cpp index 5b336dee5..f2a65ac5f 100644 --- a/core/downloadfromdcthread.cpp +++ b/core/downloadfromdcthread.cpp @@ -306,6 +306,11 @@ struct dive_table *DownloadThread::table() return &downloadTable; } +struct dive_site_table *DownloadThread::sites() +{ + return &diveSiteTable; +} + QString DCDeviceData::vendor() const { return data.vendor; diff --git a/core/downloadfromdcthread.h b/core/downloadfromdcthread.h index e4cfb2351..d14a5402b 100644 --- a/core/downloadfromdcthread.h +++ b/core/downloadfromdcthread.h @@ -59,6 +59,7 @@ private: class DownloadThread : public QThread { Q_OBJECT Q_PROPERTY(dive_table_t *table READ table CONSTANT) + Q_PROPERTY(dive_site_table_t *sites READ sites CONSTANT) public: DownloadThread(); @@ -66,6 +67,7 @@ public: DCDeviceData *data(); struct dive_table *table(); + struct dive_site_table *sites(); QString error; private: diff --git a/desktop-widgets/command.cpp b/desktop-widgets/command.cpp index 638be74d1..d8ec0812f 100644 --- a/desktop-widgets/command.cpp +++ b/desktop-widgets/command.cpp @@ -11,9 +11,9 @@ void addDive(dive *d, bool autogroup, bool newNumber) execute(new AddDive(d, autogroup, newNumber)); } -void importDives(struct dive_table *dives, struct trip_table *trips, int flags, const QString &source) +void importDives(struct dive_table *dives, struct trip_table *trips, struct dive_site_table *sites, int flags, const QString &source) { - execute(new ImportDives(dives, trips, flags, source)); + execute(new ImportDives(dives, trips, sites, flags, source)); } void deleteDive(const QVector<struct dive*> &divesToDelete) diff --git a/desktop-widgets/command.h b/desktop-widgets/command.h index 4c6240a93..f2594787d 100644 --- a/desktop-widgets/command.h +++ b/desktop-widgets/command.h @@ -21,7 +21,7 @@ void addDive(dive *d, bool autogroup, bool newNumber); // If d->dive_trip is nul // distance are added to a trip. dive d is consumed (the structure is reset)! // If newNumber is true, the dive is assigned a new number, depending on the // insertion position. -void importDives(struct dive_table *dives, struct trip_table *trips, int flags, const QString &source); +void importDives(struct dive_table *dives, struct trip_table *trips, struct dive_site_table *sites, int flags, const QString &source); void deleteDive(const QVector<struct dive*> &divesToDelete); void shiftTime(const QVector<dive *> &changedDives, int amount); void renumberDives(const QVector<QPair<dive *, int>> &divesToRenumber); diff --git a/desktop-widgets/command_base.h b/desktop-widgets/command_base.h index f965b8689..fbcc61f73 100644 --- a/desktop-widgets/command_base.h +++ b/desktop-widgets/command_base.h @@ -147,10 +147,14 @@ struct DiveDeleter { struct TripDeleter { void operator()(dive_trip *t) { free_trip(t); } }; +struct DiveSiteDeleter { + void operator()(dive_site *ds) { free_dive_site(ds); } +}; // Owning pointers to dive and dive_trip objects. typedef std::unique_ptr<dive, DiveDeleter> OwningDivePtr; typedef std::unique_ptr<dive_trip, TripDeleter> OwningTripPtr; +typedef std::unique_ptr<dive_site, DiveSiteDeleter> OwningDiveSitePtr; // This is the base class of all commands. // It defines the Qt-translation functions diff --git a/desktop-widgets/command_divelist.cpp b/desktop-widgets/command_divelist.cpp index 96c5ead95..9a5a6d398 100644 --- a/desktop-widgets/command_divelist.cpp +++ b/desktop-widgets/command_divelist.cpp @@ -106,15 +106,23 @@ dive *DiveListBase::addDive(DiveToAdd &d) // returns a vector of corresponding DiveToAdd objects, which can later be readded. // Moreover, a vector of deleted trips is returned, if trips became empty. // The passed in vector is cleared. -DivesAndTripsToAdd DiveListBase::removeDives(std::vector<dive *> &divesToDelete) +DivesAndTripsToAdd DiveListBase::removeDives(DivesAndSitesToRemove &divesAndSitesToDelete) { std::vector<DiveToAdd> divesToAdd; std::vector<OwningTripPtr> tripsToAdd; - divesToAdd.reserve(divesToDelete.size()); + std::vector<OwningDiveSitePtr> sitesToAdd; + divesToAdd.reserve(divesAndSitesToDelete.dives.size()); + sitesToAdd.reserve(divesAndSitesToDelete.sites.size()); - for (dive *d: divesToDelete) + for (dive *d: divesAndSitesToDelete.dives) divesToAdd.push_back(removeDive(d, tripsToAdd)); - divesToDelete.clear(); + divesAndSitesToDelete.dives.clear(); + + for (dive_site *ds: divesAndSitesToDelete.sites) { + unregister_dive_site(ds); + sitesToAdd.emplace_back(ds); + } + divesAndSitesToDelete.sites.clear(); // We send one dives-deleted signal per trip (see comments in DiveListNotifier.h). // Therefore, collect all dives in an array and sort by trip. @@ -131,17 +139,19 @@ DivesAndTripsToAdd DiveListBase::removeDives(std::vector<dive *> &divesToDelete) { return ptr.get() == trip; }) != tripsToAdd.end(); emit diveListNotifier.divesDeleted(trip, deleteTrip, divesInTrip); }); - return { std::move(divesToAdd), std::move(tripsToAdd) }; + return { std::move(divesToAdd), std::move(tripsToAdd), std::move(sitesToAdd) }; } // This helper function is the counterpart fo removeDives(): it calls addDive() on a list // of dives to be (re)added and returns a vector of the added dives. It does this in reverse // order, so that trips are created appropriately and indexing is correct. // The passed in vector is cleared. -std::vector<dive *> DiveListBase::addDives(DivesAndTripsToAdd &toAdd) +DivesAndSitesToRemove DiveListBase::addDives(DivesAndTripsToAdd &toAdd) { std::vector<dive *> res; + std::vector<dive_site *> sites; res.resize(toAdd.dives.size()); + sites.reserve(toAdd.sites.size()); // Now, add the dives // Note: the idiomatic STL-way would be std::transform, but let's use a loop since @@ -161,6 +171,13 @@ std::vector<dive *> DiveListBase::addDives(DivesAndTripsToAdd &toAdd) } toAdd.trips.clear(); + // Finally, add any necessary dive sites + for (OwningDiveSitePtr &ds: toAdd.sites) { + sites.push_back(ds.get()); + register_dive_site(ds.release()); // Return ownership to backend + } + toAdd.sites.clear(); + // We send one dives-deleted signal per trip (see comments in DiveListNotifier.h). // Therefore, collect all dives in a array and sort by trip. std::vector<std::pair<dive_trip *, dive *>> dives; @@ -175,7 +192,7 @@ std::vector<dive *> DiveListBase::addDives(DivesAndTripsToAdd &toAdd) // Finally, emit the signal emit diveListNotifier.divesAdded(trip, createTrip, divesInTrip); }); - return res; + return { res, sites }; } // This helper function renumbers dives according to an array of id/number pairs. @@ -520,12 +537,12 @@ void AddDive::redoit() selection = getDiveSelection(); currentDive = current_dive; - divesToRemove = addDives(divesToAdd); + divesAndSitesToRemove = addDives(divesToAdd); sort_trip_table(&trip_table); // Though unlikely, adding a dive may reorder trips mark_divelist_changed(true); // Select the newly added dive - restoreSelection(divesToRemove, divesToRemove[0]); + restoreSelection(divesAndSitesToRemove.dives, divesAndSitesToRemove.dives[0]); // Exit from edit mode, but don't recalculate dive list // TODO: Remove edit mode @@ -535,7 +552,7 @@ void AddDive::redoit() void AddDive::undoit() { // Simply remove the dive that was previously added... - divesToAdd = removeDives(divesToRemove); + divesToAdd = removeDives(divesAndSitesToRemove); sort_trip_table(&trip_table); // Though unlikely, removing a dive may reorder trips // ...and restore the selection @@ -546,20 +563,26 @@ void AddDive::undoit() MainWindow::instance()->refreshDisplay(false); } -ImportDives::ImportDives(struct dive_table *dives, struct trip_table *trips, int flags, const QString &source) +ImportDives::ImportDives(struct dive_table *dives, struct trip_table *trips, struct dive_site_table *sites, int flags, const QString &source) { setText(tr("import %n dive(s) from %1", "", dives->nr).arg(source)); struct dive_table dives_to_add = { 0 }; struct dive_table dives_to_remove = { 0 }; struct trip_table trips_to_add = { 0 }; - process_imported_dives(dives, trips, flags, &dives_to_add, &dives_to_remove, &trips_to_add); + struct dive_site_table sites_to_add = { 0 }; + process_imported_dives(dives, trips, sites, flags, &dives_to_add, &dives_to_remove, &trips_to_add, &sites_to_add); // Add trips to the divesToAdd.trips structure divesToAdd.trips.reserve(trips_to_add.nr); for (int i = 0; i < trips_to_add.nr; ++i) divesToAdd.trips.emplace_back(trips_to_add.trips[i]); + // Add sites to the divesToAdd.sites structure + divesToAdd.sites.reserve(sites_to_add.nr); + for (int i = 0; i < sites_to_add.nr; ++i) + divesToAdd.sites.emplace_back(sites_to_add.dive_sites[i]); + // Add dives to the divesToAdd.dives structure divesToAdd.dives.reserve(dives_to_add.nr); for (int i = 0; i < dives_to_add.nr; ++i) { @@ -577,9 +600,9 @@ ImportDives::ImportDives(struct dive_table *dives, struct trip_table *trips, int } // Add dive to be deleted to the divesToRemove structure - divesToRemove.reserve(dives_to_remove.nr); + divesAndSitesToRemove.dives.reserve(dives_to_remove.nr); for (int i = 0; i < dives_to_remove.nr; ++i) - divesToRemove.push_back(dives_to_remove.dives[i]); + divesAndSitesToRemove.dives.push_back(dives_to_remove.dives[i]); } bool ImportDives::workToBeDone() @@ -592,31 +615,31 @@ void ImportDives::redoit() // Remember selection so that we can undo it currentDive = current_dive; - // Add new dives - std::vector<dive *> divesToRemoveNew = addDives(divesToAdd); + // Add new dives and sites + DivesAndSitesToRemove divesAndSitesToRemoveNew = addDives(divesToAdd); - // Remove old dives - divesToAdd = removeDives(divesToRemove); + // Remove old dives and sites + divesToAdd = removeDives(divesAndSitesToRemove); // Select the newly added dives - restoreSelection(divesToRemoveNew, divesToRemoveNew.back()); + restoreSelection(divesAndSitesToRemoveNew.dives, divesAndSitesToRemoveNew.dives.back()); - // Remember dives to remove - divesToRemove = std::move(divesToRemoveNew); + // Remember dives and sites to remove + divesAndSitesToRemove = std::move(divesAndSitesToRemoveNew); mark_divelist_changed(true); } void ImportDives::undoit() { - // Add new dives - std::vector<dive *> divesToRemoveNew = addDives(divesToAdd); + // Add new dives and sites + DivesAndSitesToRemove divesAndSitesToRemoveNew = addDives(divesToAdd); - // Remove old dives - divesToAdd = removeDives(divesToRemove); + // Remove old dives and sites + divesToAdd = removeDives(divesAndSitesToRemove); - // Remember dives to remove - divesToRemove = std::move(divesToRemoveNew); + // Remember dives and sites to remove + divesAndSitesToRemove = std::move(divesAndSitesToRemoveNew); // ...and restore the selection restoreSelection(selection, currentDive); @@ -624,14 +647,15 @@ void ImportDives::undoit() mark_divelist_changed(true); } -DeleteDive::DeleteDive(const QVector<struct dive*> &divesToDeleteIn) : divesToDelete(divesToDeleteIn.toStdVector()) +DeleteDive::DeleteDive(const QVector<struct dive*> &divesToDeleteIn) { - setText(tr("delete %n dive(s)", "", divesToDelete.size())); + divesToDelete.dives = divesToDeleteIn.toStdVector(); + setText(tr("delete %n dive(s)", "", divesToDelete.dives.size())); } bool DeleteDive::workToBeDone() { - return !divesToDelete.empty(); + return !divesToDelete.dives.empty(); } void DeleteDive::undoit() @@ -641,8 +665,8 @@ void DeleteDive::undoit() mark_divelist_changed(true); // Select all re-added dives and make the first one current - dive *currentDive = !divesToDelete.empty() ? divesToDelete[0] : nullptr; - restoreSelection(divesToDelete, currentDive); + dive *currentDive = !divesToDelete.dives.empty() ? divesToDelete.dives[0] : nullptr; + restoreSelection(divesToDelete.dives, currentDive); } void DeleteDive::redoit() @@ -853,7 +877,7 @@ SplitDivesBase::SplitDivesBase(dive *d, std::array<dive *, 2> newDives) if (idx1 == idx2 && dive_less_than(newDives[0], newDives[1])) ++idx2; - diveToSplit.push_back(d); + diveToSplit.dives.push_back(d); splitDives.dives.resize(2); splitDives.dives[0].dive.reset(newDives[0]); splitDives.dives[0].trip = d->divetrip; @@ -865,7 +889,7 @@ SplitDivesBase::SplitDivesBase(dive *d, std::array<dive *, 2> newDives) bool SplitDivesBase::workToBeDone() { - return !diveToSplit.empty(); + return !diveToSplit.dives.empty(); } void SplitDivesBase::redoit() @@ -875,7 +899,7 @@ void SplitDivesBase::redoit() mark_divelist_changed(true); // Select split dives and make first dive current - restoreSelection(divesToUnsplit, divesToUnsplit[0]); + restoreSelection(divesToUnsplit.dives, divesToUnsplit.dives[0]); } void SplitDivesBase::undoit() @@ -886,7 +910,7 @@ void SplitDivesBase::undoit() mark_divelist_changed(true); // Select unsplit dive and make it current - restoreSelection(diveToSplit, diveToSplit[0] ); + restoreSelection(diveToSplit.dives, diveToSplit.dives[0] ); } static std::array<dive *, 2> doSplitDives(const dive *d, duration_t time) @@ -1003,7 +1027,7 @@ MergeDives::MergeDives(const QVector <dive *> &dives) mergedDive.dives[0].dive = std::move(d); mergedDive.dives[0].idx = get_divenr(dives[0]); mergedDive.dives[0].trip = preferred_trip; - divesToMerge = dives.toStdVector(); + divesToMerge.dives = dives.toStdVector(); } bool MergeDives::workToBeDone() @@ -1018,7 +1042,7 @@ void MergeDives::redoit() unmergedDives = removeDives(divesToMerge); // Select merged dive and make it current - restoreSelection(diveToUnmerge, diveToUnmerge[0]); + restoreSelection(diveToUnmerge.dives, diveToUnmerge.dives[0]); } void MergeDives::undoit() @@ -1028,7 +1052,7 @@ void MergeDives::undoit() renumberDives(divesToRenumber); // Select unmerged dives and make first one current - restoreSelection(divesToMerge, divesToMerge[0]); + restoreSelection(divesToMerge.dives, divesToMerge.dives[0]); } } // namespace Command diff --git a/desktop-widgets/command_divelist.h b/desktop-widgets/command_divelist.h index 7742cf729..8a70c69d3 100644 --- a/desktop-widgets/command_divelist.h +++ b/desktop-widgets/command_divelist.h @@ -19,10 +19,17 @@ struct DiveToAdd { int idx; // Position in divelist }; -// Multiple trips and dives that have to be added for a command +// Multiple trips, dives and dive sites that have to be added for a command struct DivesAndTripsToAdd { std::vector<DiveToAdd> dives; std::vector<OwningTripPtr> trips; + std::vector<OwningDiveSitePtr> sites; +}; + +// Dives and sites that have to be removed for a command +struct DivesAndSitesToRemove { + std::vector<dive *> dives; + std::vector<dive_site *> sites; }; // This helper structure describes a dive that should be moved to / removed from @@ -56,8 +63,8 @@ protected: // which set the selectionChanged flag if the added / removed dive was selected. DiveToAdd removeDive(struct dive *d, std::vector<OwningTripPtr> &tripsToAdd); dive *addDive(DiveToAdd &d); - DivesAndTripsToAdd removeDives(std::vector<dive *> &divesToDelete); - std::vector<dive *> addDives(DivesAndTripsToAdd &toAdd); + DivesAndTripsToAdd removeDives(DivesAndSitesToRemove &divesAndSitesToDelete); + DivesAndSitesToRemove addDives(DivesAndTripsToAdd &toAdd); // Set the selection to a given state. Set the selectionChanged flag if anything changed. void restoreSelection(const std::vector<dive *> &selection, dive *currentDive); @@ -90,7 +97,7 @@ private: DivesAndTripsToAdd divesToAdd; // For undo - std::vector<dive *> divesToRemove; + DivesAndSitesToRemove divesAndSitesToRemove; std::vector<dive *> selection; dive * currentDive; }; @@ -98,7 +105,7 @@ private: class ImportDives : public DiveListBase { public: // Note: dives and trips are consumed - after the call they will be empty. - ImportDives(struct dive_table *dives, struct trip_table *trips, int flags, const QString &source); + ImportDives(struct dive_table *dives, struct trip_table *trips, struct dive_site_table *sites, int flags, const QString &source); private: void undoit() override; void redoit() override; @@ -106,9 +113,13 @@ private: // For redo and undo DivesAndTripsToAdd divesToAdd; - std::vector<dive *> divesToRemove; + DivesAndSitesToRemove divesAndSitesToRemove; + + // For redo + std::vector<OwningDiveSitePtr> sitesToAdd; // For undo + std::vector<dive_site *> sitesToRemove; std::vector<dive *> selection; dive * currentDive; }; @@ -122,7 +133,7 @@ private: bool workToBeDone() override; // For redo - std::vector<struct dive*> divesToDelete; + DivesAndSitesToRemove divesToDelete; std::vector<OwningTripPtr> tripsToAdd; DivesAndTripsToAdd divesToAdd; @@ -197,7 +208,7 @@ private: // For each dive to split, we remove one from and put two dives into the backend // Note: we use a vector even though we split only a single dive, so // that we can reuse the multi-dive functions of the other commands. - std::vector<dive *> diveToSplit; + DivesAndSitesToRemove diveToSplit; DivesAndTripsToAdd splitDives; // For undo @@ -205,7 +216,7 @@ private: // Note: we use a multi-dive structure even though we unsplit only a single dive, so // that we can reuse the multi-dive functions of the other commands. DivesAndTripsToAdd unsplitDive; - std::vector<dive *> divesToUnsplit; + DivesAndSitesToRemove divesToUnsplit; }; class SplitDives : public SplitDivesBase { @@ -233,13 +244,13 @@ private: // Note: we use a multi-dives structure even though we add only a single dive, so // that we can reuse the multi-dive functions of the other commands. DivesAndTripsToAdd mergedDive; - std::vector<dive *> divesToMerge; + DivesAndSitesToRemove divesToMerge; // For undo // Remove one and add a batch of dives // Note: we use a vector even though we remove only a single dive, so // that we can reuse the multi-dive functions of the other commands. - std::vector<dive *> diveToUnmerge; + DivesAndSitesToRemove diveToUnmerge; DivesAndTripsToAdd unmergedDives; // For undo and redo diff --git a/desktop-widgets/divelogimportdialog.cpp b/desktop-widgets/divelogimportdialog.cpp index f1e616514..0bbb0dcb5 100644 --- a/desktop-widgets/divelogimportdialog.cpp +++ b/desktop-widgets/divelogimportdialog.cpp @@ -901,14 +901,15 @@ void DiveLogImportDialog::on_buttonBox_accepted() { struct dive_table table = { 0 }; struct trip_table trips = { 0 }; + struct dive_site_table sites = { 0 }; QStringList r = resultModel->result(); if (ui->knownImports->currentText() != "Manual import") { for (int i = 0; i < fileNames.size(); ++i) { if (ui->knownImports->currentText() == "Seabear CSV") { - parse_seabear_log(qPrintable(fileNames[i]), &table, &trips, &dive_site_table); + parse_seabear_log(qPrintable(fileNames[i]), &table, &trips, &sites); } else if (ui->knownImports->currentText() == "Poseidon MkVI") { QPair<QString, QString> pair = poseidonFileNames(fileNames[i]); - parse_txt_file(qPrintable(pair.second), qPrintable(pair.first), &table, &trips, &dive_site_table); + parse_txt_file(qPrintable(pair.second), qPrintable(pair.first), &table, &trips, &sites); } else { char *params[49]; int pnr = 0; @@ -925,7 +926,7 @@ void DiveLogImportDialog::on_buttonBox_accepted() pnr = setup_csv_params(r, params, pnr); parse_csv_file(qPrintable(fileNames[i]), params, pnr - 1, specialCSV.contains(ui->knownImports->currentIndex()) ? qPrintable(CSVApps[ui->knownImports->currentIndex()].name) : "csv", - &table, &trips, &dive_site_table); + &table, &trips, &sites); } } } else { @@ -989,7 +990,7 @@ void DiveLogImportDialog::on_buttonBox_accepted() params[pnr++] = intdup(r.indexOf(tr("Rating"))); params[pnr++] = NULL; - parse_manual_file(qPrintable(fileNames[i]), params, pnr - 1, &table, &trips, &dive_site_table); + parse_manual_file(qPrintable(fileNames[i]), params, pnr - 1, &table, &trips, &sites); } else { char *params[51]; int pnr = 0; @@ -1006,13 +1007,13 @@ void DiveLogImportDialog::on_buttonBox_accepted() pnr = setup_csv_params(r, params, pnr); parse_csv_file(qPrintable(fileNames[i]), params, pnr - 1, specialCSV.contains(ui->knownImports->currentIndex()) ? qPrintable(CSVApps[ui->knownImports->currentIndex()].name) : "csv", - &table, &trips, &dive_site_table); + &table, &trips, &sites); } } } QString source = fileNames.size() == 1 ? fileNames[0] : tr("multiple files"); - Command::importDives(&table, &trips, IMPORT_MERGE_ALL_TRIPS, source); + Command::importDives(&table, &trips, &sites, IMPORT_MERGE_ALL_TRIPS, source); } TagDragDelegate::TagDragDelegate(QObject *parent) : QStyledItemDelegate(parent) diff --git a/desktop-widgets/downloadfromdivecomputer.cpp b/desktop-widgets/downloadfromdivecomputer.cpp index e28a90e26..6d9ba9288 100644 --- a/desktop-widgets/downloadfromdivecomputer.cpp +++ b/desktop-widgets/downloadfromdivecomputer.cpp @@ -484,7 +484,7 @@ void DownloadFromDCWidget::onDownloadThreadFinished() } ui.downloadCancelRetryButton->setText(tr("Retry download")); ui.downloadCancelRetryButton->setEnabled(true); - diveImportedModel->repopulate(thread.table()); + diveImportedModel->repopulate(thread.table(), thread.sites()); } void DownloadFromDCWidget::on_cancel_clicked() @@ -502,6 +502,7 @@ void DownloadFromDCWidget::on_ok_clicked() if (currentState != DONE && currentState != ERROR) return; struct dive_table *table = thread.table(); + struct dive_site_table *sites = thread.sites(); // delete non-selected dives int total = table->nr; @@ -520,7 +521,7 @@ void DownloadFromDCWidget::on_ok_clicked() flags |= IMPORT_PREFER_IMPORTED; if (ui.createNewTrip->isChecked()) flags |= IMPORT_ADD_TO_NEW_TRIP; - Command::importDives(table, nullptr, flags, data->devName()); + Command::importDives(table, nullptr, sites, flags, data->devName()); } if (ostcFirmwareCheck && currentState == DONE) diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index 8483dc3c4..de8ce9b0a 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -1648,13 +1648,14 @@ void MainWindow::importFiles(const QStringList fileNames) QByteArray fileNamePtr; struct dive_table table = { 0 }; struct trip_table trips = { 0 }; + struct dive_site_table sites = { 0 }; for (int i = 0; i < fileNames.size(); ++i) { fileNamePtr = QFile::encodeName(fileNames.at(i)); - parse_file(fileNamePtr.data(), &table, &trips, &dive_site_table); + parse_file(fileNamePtr.data(), &table, &trips, &sites); } QString source = fileNames.size() == 1 ? fileNames[0] : tr("multiple files"); - Command::importDives(&table, &trips, IMPORT_MERGE_ALL_TRIPS, source); + Command::importDives(&table, &trips, &sites, IMPORT_MERGE_ALL_TRIPS, source); } void MainWindow::loadFiles(const QStringList fileNames) diff --git a/desktop-widgets/subsurfacewebservices.cpp b/desktop-widgets/subsurfacewebservices.cpp index 4d438aa28..87db7947c 100644 --- a/desktop-widgets/subsurfacewebservices.cpp +++ b/desktop-widgets/subsurfacewebservices.cpp @@ -771,8 +771,9 @@ void DivelogsDeWebServices::buttonClicked(QAbstractButton *button) /* parse file and import dives */ struct dive_table table = { 0 }; struct trip_table trips = { 0 }; - parse_file(QFile::encodeName(zipFile.fileName()), &table, &trips, &dive_site_table); - Command::importDives(&table, &trips, IMPORT_MERGE_ALL_TRIPS, QStringLiteral("divelogs.de")); + struct dive_site_table sites = { 0 }; + parse_file(QFile::encodeName(zipFile.fileName()), &table, &trips, &sites); + Command::importDives(&table, &trips, &sites, IMPORT_MERGE_ALL_TRIPS, QStringLiteral("divelogs.de")); /* store last entered user/pass in config */ QSettings s; diff --git a/mobile-widgets/qml/DownloadFromDiveComputer.qml b/mobile-widgets/qml/DownloadFromDiveComputer.qml index a1403b07d..2377db951 100644 --- a/mobile-widgets/qml/DownloadFromDiveComputer.qml +++ b/mobile-widgets/qml/DownloadFromDiveComputer.qml @@ -28,7 +28,7 @@ Kirigami.Page { id: downloadThread onFinished : { - importModel.repopulate(table) + importModel.repopulate(table, sites) progressBar.visible = false if (dcImportModel.rowCount() > 0) { console.log(dcImportModel.rowCount() + " dive downloaded") diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 9603a2ad0..4db9cbb63 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -341,8 +341,9 @@ void QMLManager::mergeLocalRepo() char *filename = NOCLOUD_LOCALSTORAGE; struct dive_table table = { 0 }; struct trip_table trips = { 0 }; - parse_file(filename, &table, &trips, &dive_site_table); - add_imported_dives(&table, &trips, IMPORT_MERGE_ALL_TRIPS); + struct dive_site_table sites = { 0 }; + parse_file(filename, &table, &trips, &sites); + add_imported_dives(&table, &trips, &sites, IMPORT_MERGE_ALL_TRIPS); } void QMLManager::copyAppLogToClipboard() diff --git a/qt-models/diveimportedmodel.cpp b/qt-models/diveimportedmodel.cpp index 2001f031e..5de0f7cb4 100644 --- a/qt-models/diveimportedmodel.cpp +++ b/qt-models/diveimportedmodel.cpp @@ -4,7 +4,8 @@ DiveImportedModel::DiveImportedModel(QObject *o) : QAbstractTableModel(o), firstIndex(0), lastIndex(-1), - diveTable(nullptr) + diveTable(nullptr), + sitesTable(nullptr) { } @@ -127,11 +128,12 @@ void DiveImportedModel::clearTable() endRemoveRows(); } -void DiveImportedModel::repopulate(dive_table_t *table) +void DiveImportedModel::repopulate(dive_table_t *table, struct dive_site_table *sites) { beginResetModel(); diveTable = table; + sitesTable = sites; firstIndex = 0; lastIndex = diveTable->nr - 1; checkStates.resize(diveTable->nr); @@ -158,7 +160,7 @@ void DiveImportedModel::recordDives() } // TODO: Might want to let the user select IMPORT_ADD_TO_NEW_TRIP - add_imported_dives(diveTable, nullptr, IMPORT_PREFER_IMPORTED | IMPORT_IS_DOWNLOADED); + add_imported_dives(diveTable, nullptr, sitesTable, IMPORT_PREFER_IMPORTED | IMPORT_IS_DOWNLOADED); } QHash<int, QByteArray> DiveImportedModel::roleNames() const { diff --git a/qt-models/diveimportedmodel.h b/qt-models/diveimportedmodel.h index 0c5ba34cd..8b0038478 100644 --- a/qt-models/diveimportedmodel.h +++ b/qt-models/diveimportedmodel.h @@ -20,7 +20,7 @@ public: Qt::ItemFlags flags(const QModelIndex &index) const; Q_INVOKABLE void clearTable(); QHash<int, QByteArray> roleNames() const; - Q_INVOKABLE void repopulate(dive_table_t *table); + Q_INVOKABLE void repopulate(dive_table_t *table, dive_site_table_t *sites); Q_INVOKABLE void recordDives(); public slots: @@ -34,6 +34,7 @@ private: int lastIndex; std::vector<char> checkStates; // char instead of bool to avoid silly pessimization of std::vector. struct dive_table *diveTable; + struct dive_site_table *sitesTable; }; #endif diff --git a/tests/testmerge.cpp b/tests/testmerge.cpp index 70f22ee73..c8a103c82 100644 --- a/tests/testmerge.cpp +++ b/tests/testmerge.cpp @@ -23,10 +23,11 @@ void TestMerge::testMergeEmpty() */ struct dive_table table = { 0 }; struct trip_table trips = { 0 }; - QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47.xml", &table, &trips, &dive_site_table), 0); - add_imported_dives(&table, &trips, IMPORT_MERGE_ALL_TRIPS); - QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test48.xml", &table, &trips, &dive_site_table), 0); - add_imported_dives(&table, &trips, IMPORT_MERGE_ALL_TRIPS); + struct dive_site_table sites = { 0 }; + QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47.xml", &table, &trips, &sites), 0); + add_imported_dives(&table, &trips, &sites, IMPORT_MERGE_ALL_TRIPS); + QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test48.xml", &table, &trips, &sites), 0); + add_imported_dives(&table, &trips, &sites, IMPORT_MERGE_ALL_TRIPS); QCOMPARE(save_dives("./testmerge47+48.ssrf"), 0); QFile org(SUBSURFACE_TEST_DATA "/dives/test47+48.xml"); org.open(QFile::ReadOnly); @@ -48,10 +49,11 @@ void TestMerge::testMergeBackwards() */ struct dive_table table = { 0 }; struct trip_table trips = { 0 }; - QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test48.xml", &table, &trips, &dive_site_table), 0); - add_imported_dives(&table, &trips, IMPORT_MERGE_ALL_TRIPS); - QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47.xml", &table, &trips, &dive_site_table), 0); - add_imported_dives(&table, &trips, IMPORT_MERGE_ALL_TRIPS); + struct dive_site_table sites = { 0 }; + QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test48.xml", &table, &trips, &sites), 0); + add_imported_dives(&table, &trips, &sites, IMPORT_MERGE_ALL_TRIPS); + QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47.xml", &table, &trips, &sites), 0); + add_imported_dives(&table, &trips, &sites, IMPORT_MERGE_ALL_TRIPS); QCOMPARE(save_dives("./testmerge47+48.ssrf"), 0); QFile org(SUBSURFACE_TEST_DATA "/dives/test47+48.xml"); org.open(QFile::ReadOnly); diff --git a/tests/testrenumber.cpp b/tests/testrenumber.cpp index d674c4922..71f4e2952 100644 --- a/tests/testrenumber.cpp +++ b/tests/testrenumber.cpp @@ -15,8 +15,9 @@ void TestRenumber::testMerge() { struct dive_table table = { 0 }; struct trip_table trips = { 0 }; - QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47b.xml", &table, &trip_table, &dive_site_table), 0); - add_imported_dives(&table, &trips, IMPORT_MERGE_ALL_TRIPS); + struct dive_site_table sites = { 0 }; + QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47b.xml", &table, &trips, &sites), 0); + add_imported_dives(&table, &trips, &sites, IMPORT_MERGE_ALL_TRIPS); QCOMPARE(dive_table.nr, 1); QCOMPARE(unsaved_changes(), 1); mark_divelist_changed(false); @@ -26,8 +27,9 @@ void TestRenumber::testMergeAndAppend() { struct dive_table table = { 0 }; struct trip_table trips = { 0 }; - QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47c.xml", &table, &trip_table, &dive_site_table), 0); - add_imported_dives(&table, &trips, IMPORT_MERGE_ALL_TRIPS); + struct dive_site_table sites = { 0 }; + QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47c.xml", &table, &trips, &sites), 0); + add_imported_dives(&table, &trips, &sites, IMPORT_MERGE_ALL_TRIPS); QCOMPARE(dive_table.nr, 2); QCOMPARE(unsaved_changes(), 1); struct dive *d = get_dive(1); |