diff --git a/commands/command.cpp b/commands/command.cpp index d70ca12fe..6ce7f18bd 100644 --- a/commands/command.cpp +++ b/commands/command.cpp @@ -18,9 +18,10 @@ void addDive(dive *d, bool autogroup, bool newNumber) } void importDives(struct dive_table *dives, struct trip_table *trips, struct dive_site_table *sites, - struct filter_preset_table *presets, int flags, const QString &source) + struct device_table *devices, struct filter_preset_table *presets, + int flags, const QString &source) { - execute(new ImportDives(dives, trips, sites, presets, flags, source)); + execute(new ImportDives(dives, trips, sites, devices, presets, flags, source)); } void deleteDive(const QVector &divesToDelete) diff --git a/commands/command.h b/commands/command.h index b2c6826a3..26340357e 100644 --- a/commands/command.h +++ b/commands/command.h @@ -3,7 +3,6 @@ #define COMMAND_H #include "core/dive.h" -#include "core/filterpreset.h" #include "core/pictureobj.h" #include #include @@ -11,6 +10,8 @@ struct DiveAndLocation; struct FilterData; +struct filter_preset_table; +struct device_table; // We put everything in a namespace, so that we can shorten names without polluting the global namespace namespace Command { @@ -33,7 +34,8 @@ QString changesMade(); // return a string with the texts from all commands on // insertion position. void addDive(dive *d, bool autogroup, bool newNumber); void importDives(struct dive_table *dives, struct trip_table *trips, - struct dive_site_table *sites, struct filter_preset_table *filter_presets, + struct dive_site_table *sites, struct device_table *devices, + struct filter_preset_table *filter_presets, int flags, const QString &source); // The tables are consumed! void deleteDive(const QVector &divesToDelete); void shiftTime(const std::vector &changedDives, int amount); diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index 54dc0983c..c5322f709 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -470,7 +470,8 @@ void AddDive::undoit() } ImportDives::ImportDives(struct dive_table *dives, struct trip_table *trips, struct dive_site_table *sites, - struct filter_preset_table *filter_presets, int flags, const QString &source) + struct device_table *devices, struct filter_preset_table *filter_presets, int flags, + const QString &source) { setText(Command::Base::tr("import %n dive(s) from %1", "", dives->nr).arg(source)); @@ -481,7 +482,9 @@ ImportDives::ImportDives(struct dive_table *dives, struct trip_table *trips, str struct dive_table dives_to_remove = empty_dive_table; struct trip_table trips_to_add = empty_trip_table; struct dive_site_table sites_to_add = empty_dive_site_table; - process_imported_dives(dives, trips, sites, flags, &dives_to_add, &dives_to_remove, &trips_to_add, &sites_to_add); + process_imported_dives(dives, trips, sites, devices, flags, + &dives_to_add, &dives_to_remove, &trips_to_add, + &sites_to_add, &devicesToAddAndRemove); // Add trips to the divesToAdd.trips structure divesToAdd.trips.reserve(trips_to_add.nr); @@ -550,6 +553,12 @@ void ImportDives::redoit() // Remember dives and sites to remove divesAndSitesToRemove = std::move(divesAndSitesToRemoveNew); + // Add devices + for (const device &dev: devicesToAddAndRemove.devices) { + int idx = add_to_device_table(&device_table, &dev); + emit diveListNotifier.deviceAdded(idx); + } + // Add new filter presets for (auto &it: filterPresetsToAdd) { filterPresetsToRemove.push_back(filter_preset_add(it.first, it.second)); @@ -572,6 +581,13 @@ void ImportDives::undoit() // ...and restore the selection setSelection(selection, currentDive); + // Remove devices + for (const device &dev: devicesToAddAndRemove.devices) { + int idx = remove_device(&device_table, &dev); + if (idx >= 0) + emit diveListNotifier.deviceDeleted(idx); + } + // Remove filter presets. Do this in reverse order. for (auto it = filterPresetsToRemove.rbegin(); it != filterPresetsToRemove.rend(); ++it) { int index = *it; diff --git a/commands/command_divelist.h b/commands/command_divelist.h index cf204bedc..893badb39 100644 --- a/commands/command_divelist.h +++ b/commands/command_divelist.h @@ -6,6 +6,7 @@ #include "command_base.h" #include "core/filterpreset.h" +#include "core/device.h" #include @@ -99,7 +100,8 @@ 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, struct dive_site_table *sites, - struct filter_preset_table *filter_presets, int flags, const QString &source); + struct device_table *devices, struct filter_preset_table *filter_presets, int flags, + const QString &source); private: void undoit() override; void redoit() override; @@ -108,6 +110,7 @@ private: // For redo and undo DivesAndTripsToAdd divesToAdd; DivesAndSitesToRemove divesAndSitesToRemove; + struct device_table devicesToAddAndRemove; // For redo std::vector sitesToAdd; diff --git a/core/device.cpp b/core/device.cpp index 764531414..3d3a476d9 100644 --- a/core/device.cpp +++ b/core/device.cpp @@ -416,3 +416,13 @@ extern "C" const char *device_get_nickname(const struct device *dev) { return dev ? dev->nickName.c_str() : NULL; } + +extern "C" struct device_table *alloc_device_table() +{ + return new struct device_table; +} + +extern "C" void free_device_table(struct device_table *devices) +{ + delete devices; +} diff --git a/core/device.h b/core/device.h index 590609c45..b5467ea09 100644 --- a/core/device.h +++ b/core/device.h @@ -39,6 +39,10 @@ const char *device_get_serial(const struct device *dev); const char *device_get_firmware(const struct device *dev); const char *device_get_nickname(const struct device *dev); +// for C code that needs to alloc/free a device table. (Let's try to get rid of those) +extern struct device_table *alloc_device_table(); +extern void free_device_table(struct device_table *devices); + #ifdef __cplusplus } #endif diff --git a/core/divelist.c b/core/divelist.c index 5a47e3c44..3bc4ff229 100644 --- a/core/divelist.c +++ b/core/divelist.c @@ -999,18 +999,22 @@ 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, struct dive_site_table *import_sites_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, struct device_table *import_device_table, + int flags) { int i, idx; struct dive_table dives_to_add = empty_dive_table; struct dive_table dives_to_remove = empty_dive_table; struct trip_table trips_to_add = empty_trip_table; struct dive_site_table dive_sites_to_add = empty_dive_site_table; + struct device_table *devices_to_add = alloc_device_table(); /* Process imported dives and generate lists of dives * to-be-added and to-be-removed */ - 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); + process_imported_dives(import_table, import_trip_table, import_sites_table, import_device_table, + flags, &dives_to_add, &dives_to_remove, &trips_to_add, + &dive_sites_to_add, devices_to_add); /* Add new dives to trip and site to get reference count correct. */ for (i = 0; i < dives_to_add.nr; i++) { @@ -1045,11 +1049,19 @@ void add_imported_dives(struct dive_table *import_table, struct trip_table *impo register_dive_site(dive_sites_to_add.dive_sites[i]); dive_sites_to_add.nr = 0; + /* Add new devices */ + for (i = 0; i < nr_devices(devices_to_add); i++) { + const struct device *dev = get_device(devices_to_add, i); + add_to_device_table(&device_table, dev); + } + /* 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; mark_divelist_changed(true); + free_device_table(devices_to_add); + /* Inform frontend of reset data. This should reset all the models. */ emit_reset_signal(); } @@ -1088,11 +1100,12 @@ 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 four lists: + * generate five lists: * 1) Dives to be added * 2) Dives to be removed * 3) Trips to be added * 4) Dive sites to be added + * 5) Devices to be added * The dives to be added are owning (i.e. the caller is responsible * for freeing them). * The dives, trips and sites in "import_table", "import_trip_table" @@ -1120,10 +1133,12 @@ bool try_to_merge_trip(struct dive_trip *trip_import, struct dive_table *import_ * 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, - struct dive_site_table *import_sites_table, int flags, + struct dive_site_table *import_sites_table, struct device_table *import_device_table, + int flags, /* output parameters: */ struct dive_table *dives_to_add, struct dive_table *dives_to_remove, - struct trip_table *trips_to_add, struct dive_site_table *sites_to_add) + struct trip_table *trips_to_add, struct dive_site_table *sites_to_add, + struct device_table *devices_to_add) { int i, j, nr, start_renumbering_at = 0; struct dive_trip *trip_import, *new_trip; @@ -1143,6 +1158,7 @@ void process_imported_dives(struct dive_table *import_table, struct trip_table * clear_dive_table(dives_to_remove); clear_trip_table(trips_to_add); clear_dive_site_table(sites_to_add); + clear_device_table(devices_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 @@ -1158,15 +1174,22 @@ void process_imported_dives(struct dive_table *import_table, struct trip_table * if (!import_table->nr) return; + /* Add only the devices that we don't know about yet. */ + for (i = 0; i < nr_devices(import_device_table); i++) { + const struct device *dev = get_device(import_device_table, i); + if (!device_exists(&device_table, dev)) + add_to_device_table(devices_to_add, dev); + } + /* check if we need a nickname for the divecomputer for newly downloaded dives; * since we know they all came from the same divecomputer we just check for the * first one */ if (flags & IMPORT_IS_DOWNLOADED) { - set_dc_nickname(import_table->dives[0], &device_table); + set_dc_nickname(import_table->dives[0], devices_to_add); } else { /* they aren't downloaded, so record / check all new ones */ for (i = 0; i < import_table->nr; i++) - set_dc_nickname(import_table->dives[i], &device_table); + set_dc_nickname(import_table->dives[i], devices_to_add); } /* Sort the table of dives to be imported and combine mergable dives */ diff --git a/core/divelist.h b/core/divelist.h index cee52f9c4..1042c88cc 100644 --- a/core/divelist.h +++ b/core/divelist.h @@ -11,6 +11,7 @@ extern "C" { struct dive; struct trip_table; struct dive_site_table; +struct device_table; struct deco_state; extern int shown_dives; @@ -36,12 +37,15 @@ 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, struct dive_site_table *import_sites_table, +extern void add_imported_dives(struct dive_table *import_table, struct trip_table *import_trip_table, + struct dive_site_table *import_sites_table, struct device_table *devices_to_add, 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, +extern void process_imported_dives(struct dive_table *import_table, struct trip_table *import_trip_table, + struct dive_site_table *import_sites_table, struct device_table *import_devices_table, int flags, struct dive_table *dives_to_add, struct dive_table *dives_to_remove, - struct trip_table *trips_to_add, struct dive_site_table *sites_to_add); + struct trip_table *trips_to_add, struct dive_site_table *sites_to_add, + struct device_table *devices_to_add); extern char *get_dive_gas_string(const struct dive *dive); extern int dive_table_get_insertion_index(struct dive_table *table, struct dive *dive); diff --git a/core/subsurface-qt/divelistnotifier.h b/core/subsurface-qt/divelistnotifier.h index eb44d6cde..7f7162dac 100644 --- a/core/subsurface-qt/divelistnotifier.h +++ b/core/subsurface-qt/divelistnotifier.h @@ -10,6 +10,8 @@ #include +struct device; + // Dive and trip fields that can be edited. Use bit fields so that we can pass multiple fields at once. // Provides an inlined flag-based constructur because sadly C-style designated initializers are only supported since C++20. struct DiveField { @@ -125,6 +127,10 @@ signals: void picturesRemoved(dive *d, QVector filenames); void picturesAdded(dive *d, QVector pics); + // Devices related signals + void deviceAdded(int index); + void deviceDeleted(int index); + // Filter related signals void filterPresetAdded(int index); void filterPresetRemoved(int index); diff --git a/desktop-widgets/divelogimportdialog.cpp b/desktop-widgets/divelogimportdialog.cpp index e6243e98d..fc7e7d81e 100644 --- a/desktop-widgets/divelogimportdialog.cpp +++ b/desktop-widgets/divelogimportdialog.cpp @@ -960,7 +960,7 @@ void DiveLogImportDialog::on_buttonBox_accepted() } QString source = fileNames.size() == 1 ? fileNames[0] : tr("multiple files"); - Command::importDives(&table, &trips, &sites, nullptr, IMPORT_MERGE_ALL_TRIPS, source); + Command::importDives(&table, &trips, &sites, &devices, nullptr, IMPORT_MERGE_ALL_TRIPS, source); } TagDragDelegate::TagDragDelegate(QObject *parent) : QStyledItemDelegate(parent) diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index 327eb0578..b1307593a 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -1557,7 +1557,7 @@ void MainWindow::importFiles(const QStringList fileNames) parse_file(fileNamePtr.data(), &table, &trips, &sites, &devices, &filter_presets); } QString source = fileNames.size() == 1 ? fileNames[0] : tr("multiple files"); - Command::importDives(&table, &trips, &sites, &filter_presets, IMPORT_MERGE_ALL_TRIPS, source); + Command::importDives(&table, &trips, &sites, &devices, &filter_presets, IMPORT_MERGE_ALL_TRIPS, source); } void MainWindow::loadFiles(const QStringList fileNames) diff --git a/desktop-widgets/subsurfacewebservices.cpp b/desktop-widgets/subsurfacewebservices.cpp index c5a5cad48..0ca2974b6 100644 --- a/desktop-widgets/subsurfacewebservices.cpp +++ b/desktop-widgets/subsurfacewebservices.cpp @@ -460,7 +460,7 @@ void DivelogsDeWebServices::buttonClicked(QAbstractButton *button) struct device_table devices; struct filter_preset_table filter_presets; parse_file(QFile::encodeName(zipFile.fileName()), &table, &trips, &sites, &devices, &filter_presets); - Command::importDives(&table, &trips, &sites, nullptr, IMPORT_MERGE_ALL_TRIPS, QStringLiteral("divelogs.de")); + Command::importDives(&table, &trips, &sites, &devices, nullptr, IMPORT_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 dc22dbd6e..5d1d02b48 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -454,7 +454,7 @@ void QMLManager::mergeLocalRepo() struct device_table devices; struct filter_preset_table filter_presets; parse_file(qPrintable(nocloud_localstorage()), &table, &trips, &sites, &devices, &filter_presets); - add_imported_dives(&table, &trips, &sites, IMPORT_MERGE_ALL_TRIPS); + add_imported_dives(&table, &trips, &sites, &devices, IMPORT_MERGE_ALL_TRIPS); } void QMLManager::copyAppLogToClipboard() @@ -2242,7 +2242,7 @@ void QMLManager::importCacheRepo(QString repo) QString repoPath = QString("%1/cloudstorage/%2").arg(system_default_directory()).arg(repo); appendTextToLog(QString("importing %1").arg(repoPath)); parse_file(qPrintable(repoPath), &table, &trips, &sites, &devices, &filter_presets); - add_imported_dives(&table, &trips, &sites, IMPORT_MERGE_ALL_TRIPS); + add_imported_dives(&table, &trips, &sites, &devices, IMPORT_MERGE_ALL_TRIPS); changesNeedSaving(); } diff --git a/qt-models/diveimportedmodel.cpp b/qt-models/diveimportedmodel.cpp index 3413e03a5..0541d32d3 100644 --- a/qt-models/diveimportedmodel.cpp +++ b/qt-models/diveimportedmodel.cpp @@ -143,22 +143,24 @@ void DiveImportedModel::startDownload() thread.start(); } -std::pair DiveImportedModel::consumeTables() +std::tuple DiveImportedModel::consumeTables() { beginResetModel(); // Move tables to result struct dive_table dives = empty_dive_table; struct dive_site_table sites = empty_dive_site_table; + struct device_table devices; move_dive_table(&diveTable, &dives); move_dive_site_table(&sitesTable, &sites); + devices = std::move(deviceTable); // Reset indices checkStates.clear(); endResetModel(); - return std::make_pair(dives, sites); + return std::make_tuple(dives, sites, devices); } int DiveImportedModel::numDives() const @@ -191,17 +193,18 @@ void DiveImportedModel::recordDives(int flags) deleteDeselected(); // TODO: use structured bindings once we go C++17 - std::pair tables = consumeTables(); - if (tables.first.nr > 0) { + std::tuple tables = consumeTables(); + if (std::get<0>(tables).nr > 0) { auto data = thread.data(); - Command::importDives(&tables.first, nullptr, &tables.second, nullptr, flags, data->devName()); + Command::importDives(&std::get<0>(tables), nullptr, &std::get<1>(tables), + &std::get<2>(tables), nullptr, flags, data->devName()); } else { - clear_dive_site_table(&tables.second); + clear_dive_site_table(&std::get<1>(tables)); } // The dives and dive sites have been consumed, but the arrays of the tables // still exist. Free them. - free(tables.first.dives); - free(tables.second.dive_sites); + free(std::get<0>(tables).dives); + free(std::get<1>(tables).dive_sites); } QHash DiveImportedModel::roleNames() const { diff --git a/qt-models/diveimportedmodel.h b/qt-models/diveimportedmodel.h index 920bd57c6..f43057d26 100644 --- a/qt-models/diveimportedmodel.h +++ b/qt-models/diveimportedmodel.h @@ -1,11 +1,11 @@ #ifndef DIVEIMPORTEDMODEL_H #define DIVEIMPORTEDMODEL_H -#include -#include +#include "core/device.h" #include "core/divesite.h" #include "core/divelist.h" #include "core/downloadfromdcthread.h" +#include class DiveImportedModel : public QAbstractTableModel { @@ -23,7 +23,7 @@ public: Q_INVOKABLE void clearTable(); QHash roleNames() const; void deleteDeselected(); - std::pair consumeTables(); // Returns dives and sites and resets model. + std::tuple consumeTables(); // Returns downloaded tables and resets model. int numDives() const; Q_INVOKABLE void recordDives(int flags = IMPORT_PREFER_IMPORTED | IMPORT_IS_DOWNLOADED); @@ -48,6 +48,7 @@ private: std::vector checkStates; // char instead of bool to avoid silly pessimization of std::vector. struct dive_table diveTable; struct dive_site_table sitesTable; + struct device_table deviceTable; }; #endif diff --git a/tests/testmerge.cpp b/tests/testmerge.cpp index b71bb0955..e710de66e 100644 --- a/tests/testmerge.cpp +++ b/tests/testmerge.cpp @@ -29,9 +29,9 @@ void TestMerge::testMergeEmpty() struct device_table devices; struct filter_preset_table filter_presets; QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47.xml", &table, &trips, &sites, &devices, &filter_presets), 0); - add_imported_dives(&table, &trips, &sites, IMPORT_MERGE_ALL_TRIPS); + add_imported_dives(&table, &trips, &sites, &devices, IMPORT_MERGE_ALL_TRIPS); QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test48.xml", &table, &trips, &sites, &devices, &filter_presets), 0); - add_imported_dives(&table, &trips, &sites, IMPORT_MERGE_ALL_TRIPS); + add_imported_dives(&table, &trips, &sites, &devices, IMPORT_MERGE_ALL_TRIPS); QCOMPARE(save_dives("./testmerge47+48.ssrf"), 0); QFile org(SUBSURFACE_TEST_DATA "/dives/test47+48.xml"); org.open(QFile::ReadOnly); @@ -57,9 +57,9 @@ void TestMerge::testMergeBackwards() struct device_table devices; struct filter_preset_table filter_presets; QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test48.xml", &table, &trips, &sites, &devices, &filter_presets), 0); - add_imported_dives(&table, &trips, &sites, IMPORT_MERGE_ALL_TRIPS); + add_imported_dives(&table, &trips, &sites, &devices, IMPORT_MERGE_ALL_TRIPS); QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47.xml", &table, &trips, &sites, &devices, &filter_presets), 0); - add_imported_dives(&table, &trips, &sites, IMPORT_MERGE_ALL_TRIPS); + add_imported_dives(&table, &trips, &sites, &devices, IMPORT_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 af5bd0203..69ab15b22 100644 --- a/tests/testrenumber.cpp +++ b/tests/testrenumber.cpp @@ -21,7 +21,7 @@ void TestRenumber::testMerge() struct device_table devices; struct filter_preset_table filter_presets; QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47b.xml", &table, &trips, &sites, &devices, &filter_presets), 0); - add_imported_dives(&table, &trips, &sites, IMPORT_MERGE_ALL_TRIPS); + add_imported_dives(&table, &trips, &sites, &devices, IMPORT_MERGE_ALL_TRIPS); QCOMPARE(dive_table.nr, 1); QCOMPARE(unsaved_changes(), 1); mark_divelist_changed(false); @@ -35,7 +35,7 @@ void TestRenumber::testMergeAndAppend() struct device_table devices; struct filter_preset_table filter_presets; QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47c.xml", &table, &trips, &sites, &devices, &filter_presets), 0); - add_imported_dives(&table, &trips, &sites, IMPORT_MERGE_ALL_TRIPS); + add_imported_dives(&table, &trips, &sites, &devices, IMPORT_MERGE_ALL_TRIPS); QCOMPARE(dive_table.nr, 2); QCOMPARE(unsaved_changes(), 1); struct dive *d = get_dive(1);