From 82c47bdd794c05e51c73afda14b8bfc95f6694e1 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 23 Dec 2018 23:45:12 +0100 Subject: [PATCH] Undo: make dive-import undoable On desktop, replace all add_imported_dives() calls by a new undo-command. This was rather straight forward, as all the preparation work was done in previous commits. By using an undo-command, a full UI-reset can be avoided, making the UI react smoother. Signed-off-by: Berthold Stoeger --- desktop-widgets/command.cpp | 7 ++ desktop-widgets/command.h | 3 + desktop-widgets/command_divelist.cpp | 83 +++++++++++++++++++- desktop-widgets/command_divelist.h | 20 +++++ desktop-widgets/divelogimportdialog.cpp | 5 +- desktop-widgets/downloadfromdivecomputer.cpp | 19 +---- desktop-widgets/mainwindow.cpp | 5 +- desktop-widgets/subsurfacewebservices.cpp | 4 +- qt-models/diveimportedmodel.cpp | 1 + 9 files changed, 122 insertions(+), 25 deletions(-) diff --git a/desktop-widgets/command.cpp b/desktop-widgets/command.cpp index 2bff1aad0..1fc6fbe1c 100644 --- a/desktop-widgets/command.cpp +++ b/desktop-widgets/command.cpp @@ -11,6 +11,13 @@ void addDive(dive *d, bool autogroup, bool newNumber) execute(new AddDive(d, autogroup, newNumber)); } +void importDives(struct dive_table *dives, struct trip_table *trips, + bool prefer_imported, bool downloaded, bool merge_all_trips, + const QString &source) +{ + execute(new ImportDives(dives, trips, prefer_imported, downloaded, merge_all_trips, source)); +} + void deleteDive(const QVector &divesToDelete) { execute(new DeleteDive(divesToDelete)); diff --git a/desktop-widgets/command.h b/desktop-widgets/command.h index 71ef2f9da..83bde3b3a 100644 --- a/desktop-widgets/command.h +++ b/desktop-widgets/command.h @@ -21,6 +21,9 @@ 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, + bool prefer_imported, bool downloaded, bool merge_all_trips, + const QString &source); void deleteDive(const QVector &divesToDelete); void shiftTime(const QVector &changedDives, int amount); void renumberDives(const QVector> &divesToRenumber); diff --git a/desktop-widgets/command_divelist.cpp b/desktop-widgets/command_divelist.cpp index c8ec5d082..6d2eafc6d 100644 --- a/desktop-widgets/command_divelist.cpp +++ b/desktop-widgets/command_divelist.cpp @@ -43,7 +43,7 @@ void processByTrip(std::vector> &dives, Function // This helper function removes a dive, takes ownership of the dive and adds it to a DiveToAdd structure. // If the trip the dive belongs to becomes empty, it is removed and added to the tripsToAdd vector. -// It is crucial that dives are added in reverse order of deletion, so the the indices are correctly +// It is crucial that dives are added in reverse order of deletion, so that the indices are correctly // set and that the trips are added before they are used! DiveToAdd DiveListBase::removeDive(struct dive *d, std::vector &tripsToAdd) { @@ -558,6 +558,87 @@ void AddDive::undoit() MainWindow::instance()->refreshDisplay(false); } +ImportDives::ImportDives(struct dive_table *dives, struct trip_table *trips, + bool prefer_imported, bool downloaded, bool merge_all_trips, + 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, prefer_imported, downloaded, merge_all_trips, + &dives_to_add, &dives_to_remove, &trips_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 dives to the divesToAdd.dives structure + divesToAdd.dives.reserve(dives_to_add.nr); + for (int i = 0; i < dives_to_add.nr; ++i) { + OwningDivePtr divePtr(dives_to_add.dives[i]); + divePtr->selected = false; // See above in AddDive::AddDive() + dive_trip *trip = divePtr->divetrip; + divePtr->divetrip = nullptr; // See above in AddDive::AddDive() + int idx = dive_table_get_insertion_index(&dive_table, divePtr.get()); + + // Note: The dives are added in reverse order of the divesToAdd array. + // This, and the fact that we populate the array in chronological order + // means that wo do *not* have to manipulated the indices. + // Yes, that's all horribly subtle. + divesToAdd.dives.push_back({ std::move(divePtr), trip, idx }); + } + + // Add dive to be deleted to the divesToRemove structure + divesToRemove.reserve(dives_to_remove.nr); + for (int i = 0; i < dives_to_remove.nr; ++i) + divesToRemove.push_back(dives_to_remove.dives[i]); +} + +bool ImportDives::workToBeDone() +{ + return !divesToAdd.dives.empty(); +} + +void ImportDives::redoit() +{ + // Remember selection so that we can undo it + currentDive = current_dive; + + // Add new dives + std::vector divesToRemoveNew = addDives(divesToAdd); + + // Remove old dives + divesToAdd = removeDives(divesToRemove); + + // Select the newly added dives + restoreSelection(divesToRemoveNew, divesToRemoveNew.back()); + + // Remember dives to remove + divesToRemove = std::move(divesToRemoveNew); + + mark_divelist_changed(true); +} + +void ImportDives::undoit() +{ + // Add new dives + std::vector divesToRemoveNew = addDives(divesToAdd); + + // Remove old dives + divesToAdd = removeDives(divesToRemove); + + // Remember dives to remove + divesToRemove = std::move(divesToRemoveNew); + + // ...and restore the selection + restoreSelection(selection, currentDive); + + mark_divelist_changed(true); +} + DeleteDive::DeleteDive(const QVector &divesToDeleteIn) : divesToDelete(divesToDeleteIn.toStdVector()) { setText(tr("delete %n dive(s)", "", divesToDelete.size())); diff --git a/desktop-widgets/command_divelist.h b/desktop-widgets/command_divelist.h index 77116c302..8cf8986e3 100644 --- a/desktop-widgets/command_divelist.h +++ b/desktop-widgets/command_divelist.h @@ -104,6 +104,26 @@ private: dive * currentDive; }; +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, + bool prefer_imported, bool downloaded, bool merge_all_trips, + const QString &source); +private: + void undoit() override; + void redoit() override; + bool workToBeDone() override; + + // For redo and undo + DivesAndTripsToAdd divesToAdd; + std::vector divesToRemove; + + // For undo + std::vector selection; + dive * currentDive; +}; + class DeleteDive : public DiveListBase { public: DeleteDive(const QVector &divesToDelete); diff --git a/desktop-widgets/divelogimportdialog.cpp b/desktop-widgets/divelogimportdialog.cpp index f8e44067d..638cf6094 100644 --- a/desktop-widgets/divelogimportdialog.cpp +++ b/desktop-widgets/divelogimportdialog.cpp @@ -1011,9 +1011,8 @@ void DiveLogImportDialog::on_buttonBox_accepted() } } - add_imported_dives(&table, &trips, false, false, true); - Command::clear(); - MainWindow::instance()->refreshDisplay(); + QString source = fileNames.size() == 1 ? fileNames[0] : tr("multiple files"); + Command::importDives(&table, &trips, false, false, true, source); } TagDragDelegate::TagDragDelegate(QObject *parent) : QStyledItemDelegate(parent) diff --git a/desktop-widgets/downloadfromdivecomputer.cpp b/desktop-widgets/downloadfromdivecomputer.cpp index 547c740c8..ff703d98e 100644 --- a/desktop-widgets/downloadfromdivecomputer.cpp +++ b/desktop-widgets/downloadfromdivecomputer.cpp @@ -521,25 +521,12 @@ void DownloadFromDCWidget::on_ok_clicked() } if (table->nr > 0) { - MainWindow::instance()->diveList->unselectDives(); - // remember the last downloaded dive (on most dive computers this will be the chronologically - // first new dive) and select it again after processing all the dives - int uniqId = table->dives[table->nr - 1]->id; - add_imported_dives(table, trips, preferDownloaded(), true, false); - Command::clear(); - // after add_imported_dives does any merging or resorting needed, we need - // to recreate the model for the dive list so we can select the newest dive - MainWindow::instance()->recreateDiveList(); - int idx = get_idx_by_uniq_id(uniqId); - // this shouldn't be necessary - but there are reports that somehow existing dives stay selected - // (but not visible as selected) - MainWindow::instance()->diveList->unselectDives(); - MainWindow::instance()->diveList->selectDive(idx, true); + auto data = thread.data(); + Command::importDives(table, trips, preferDownloaded(), true, false, data->devName()); } - if (ostcFirmwareCheck && currentState == DONE) { + if (ostcFirmwareCheck && currentState == DONE) ostcFirmwareCheck->checkLatest(this, thread.data()->internalData()); - } accept(); } diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index 474ce0f55..2e4342511 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -1714,9 +1714,8 @@ void MainWindow::importFiles(const QStringList fileNames) fileNamePtr = QFile::encodeName(fileNames.at(i)); parse_file(fileNamePtr.data(), &table, &trips); } - add_imported_dives(&table, &trips, false, false, true); - Command::clear(); - refreshDisplay(); + QString source = fileNames.size() == 1 ? fileNames[0] : tr("multiple files"); + Command::importDives(&table, &trips, false, false, true, source); } void MainWindow::loadFiles(const QStringList fileNames) diff --git a/desktop-widgets/subsurfacewebservices.cpp b/desktop-widgets/subsurfacewebservices.cpp index 58806a75b..e6ef6668e 100644 --- a/desktop-widgets/subsurfacewebservices.cpp +++ b/desktop-widgets/subsurfacewebservices.cpp @@ -4,6 +4,7 @@ #include "core/webservice.h" #include "core/settings/qPrefCloudStorage.h" #include "desktop-widgets/mainwindow.h" +#include "desktop-widgets/command.h" #include "desktop-widgets/usersurvey.h" #include "core/divelist.h" #include "desktop-widgets/mapwidget.h" @@ -771,8 +772,7 @@ void DivelogsDeWebServices::buttonClicked(QAbstractButton *button) struct dive_table table = { 0 }; struct trip_table trips = { 0 }; parse_file(QFile::encodeName(zipFile.fileName()), &table, &trips); - add_imported_dives(&table, &trips, false, false, true); - MainWindow::instance()->refreshDisplay(); + Command::importDives(&table, &trips, false, false, true, QStringLiteral("divelogs.de")); /* store last entered user/pass in config */ QSettings s; diff --git a/qt-models/diveimportedmodel.cpp b/qt-models/diveimportedmodel.cpp index ac7e26c26..566bda173 100644 --- a/qt-models/diveimportedmodel.cpp +++ b/qt-models/diveimportedmodel.cpp @@ -141,6 +141,7 @@ void DiveImportedModel::repopulate(dive_table_t *table, trip_table_t *trips) endResetModel(); } +// Note: this function is only used from mobile - perhaps move it there or unify. void DiveImportedModel::recordDives() { if (diveTable->nr == 0)