diff options
author | Berthold Stoeger <bstoeger@mail.tuwien.ac.at> | 2019-11-13 15:08:40 +0100 |
---|---|---|
committer | bstoeger <32835590+bstoeger@users.noreply.github.com> | 2019-11-14 21:02:07 +0100 |
commit | 5e29245e689fe9401ee1c33ebb07c601ce25e8c6 (patch) | |
tree | a656bcb0e9357b8cab78c5ad196fc6e5a4da4c31 /commands/command_divesite.cpp | |
parent | 685b92b9c2ddd6575ae5df7c95b928e60fcd7005 (diff) | |
download | subsurface-5e29245e689fe9401ee1c33ebb07c601ce25e8c6.tar.gz |
Refactoring: move undo commands to top level
In the future we might want to use undo-commands for mobile as
well (even if not implementing undo).
Therefore, move the undo-command source from desktop-widgets
to their own commands top-level folder.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Diffstat (limited to 'commands/command_divesite.cpp')
-rw-r--r-- | commands/command_divesite.cpp | 395 |
1 files changed, 395 insertions, 0 deletions
diff --git a/commands/command_divesite.cpp b/commands/command_divesite.cpp new file mode 100644 index 000000000..b5fb46f91 --- /dev/null +++ b/commands/command_divesite.cpp @@ -0,0 +1,395 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "command_divesite.h" +#include "command_private.h" +#include "core/divesite.h" +#include "core/subsurface-qt/DiveListNotifier.h" +#include "core/qthelper.h" +#include "core/subsurface-string.h" +#include "qt-models/divelocationmodel.h" +#include "qt-models/filtermodels.h" + +namespace Command { + +// Helper functions to add / remove a set of dive sites + +// Add a set of dive sites to the core. The dives that were associated with +// that dive site will be restored to that dive site. +static std::vector<dive_site *> addDiveSites(std::vector<OwningDiveSitePtr> &sites) +{ + std::vector<dive_site *> res; + QVector<dive *> changedDives; + res.reserve(sites.size()); + + for (OwningDiveSitePtr &ds: sites) { + // Readd the dives that belonged to this site + for (int i = 0; i < ds->dives.nr; ++i) { + // TODO: send dive site changed signal + struct dive *d = ds->dives.dives[i]; + d->dive_site = ds.get(); + changedDives.push_back(d); + } + + // Add dive site to core, but remember a non-owning pointer first. + res.push_back(ds.get()); + int idx = register_dive_site(ds.release()); // Return ownership to backend. + emit diveListNotifier.diveSiteAdded(res.back(), idx); // Inform frontend of new dive site. + } + + emit diveListNotifier.divesChanged(changedDives, DiveField::DIVESITE); + + // Clear vector of unused owning pointers + sites.clear(); + + return res; +} + +// Remove a set of dive sites. Get owning pointers to them. The dives are set to +// being at no dive site, but the dive site will retain a list of dives, so +// that the dives can be readded to the site on undo. +static std::vector<OwningDiveSitePtr> removeDiveSites(std::vector<dive_site *> &sites) +{ + std::vector<OwningDiveSitePtr> res; + QVector<dive *> changedDives; + res.reserve(sites.size()); + + for (dive_site *ds: sites) { + // Reset the dive_site field of the affected dives + for (int i = 0; i < ds->dives.nr; ++i) { + struct dive *d = ds->dives.dives[i]; + d->dive_site = nullptr; + changedDives.push_back(d); + } + + // Remove dive site from core and take ownership. + int idx = unregister_dive_site(ds); + res.emplace_back(ds); + emit diveListNotifier.diveSiteDeleted(ds, idx); // Inform frontend of removed dive site. + } + + emit diveListNotifier.divesChanged(changedDives, DiveField::DIVESITE); + + sites.clear(); + + return res; +} + +AddDiveSite::AddDiveSite(const QString &name) +{ + setText(tr("add dive site")); + sitesToAdd.emplace_back(alloc_dive_site()); + sitesToAdd.back()->name = copy_qstring(name); +} + +bool AddDiveSite::workToBeDone() +{ + return true; +} + +void AddDiveSite::redo() +{ + sitesToRemove = addDiveSites(sitesToAdd); +} + +void AddDiveSite::undo() +{ + sitesToAdd = removeDiveSites(sitesToRemove); +} + +ImportDiveSites::ImportDiveSites(struct dive_site_table *sites, const QString &source) +{ + setText(tr("import dive sites from %1").arg(source)); + + for (int i = 0; i < sites->nr; ++i) { + struct dive_site *new_ds = sites->dive_sites[i]; + + // Don't import dive sites that already exist. Currently we only check for + // the same name. We might want to be smarter here and merge dive site data, etc. + struct dive_site *old_ds = get_same_dive_site(new_ds); + if (old_ds) { + free_dive_site(new_ds); + continue; + } + sitesToAdd.emplace_back(new_ds); + } + + // All site have been consumed + sites->nr = 0; +} + +bool ImportDiveSites::workToBeDone() +{ + return !sitesToAdd.empty(); +} + +void ImportDiveSites::redo() +{ + sitesToRemove = addDiveSites(sitesToAdd); +} + +void ImportDiveSites::undo() +{ + sitesToAdd = removeDiveSites(sitesToRemove); +} + +DeleteDiveSites::DeleteDiveSites(const QVector<dive_site *> &sites) : sitesToRemove(sites.toStdVector()) +{ + setText(tr("delete %n dive site(s)", "", sites.size())); +} + +bool DeleteDiveSites::workToBeDone() +{ + return !sitesToRemove.empty(); +} + +void DeleteDiveSites::redo() +{ + sitesToAdd = removeDiveSites(sitesToRemove); +} + +void DeleteDiveSites::undo() +{ + sitesToRemove = addDiveSites(sitesToAdd); +} + +PurgeUnusedDiveSites::PurgeUnusedDiveSites() +{ + setText(tr("purge unused dive sites")); + for (int i = 0; i < dive_site_table.nr; ++i) { + dive_site *ds = dive_site_table.dive_sites[i]; + if (ds->dives.nr == 0) + sitesToRemove.push_back(ds); + } +} + +bool PurgeUnusedDiveSites::workToBeDone() +{ + return !sitesToRemove.empty(); +} + +void PurgeUnusedDiveSites::redo() +{ + sitesToAdd = removeDiveSites(sitesToRemove); +} + +void PurgeUnusedDiveSites::undo() +{ + sitesToRemove = addDiveSites(sitesToAdd); +} + +// Helper function: swap C and Qt string +static void swap(char *&c, QString &q) +{ + QString s = c; + free(c); + c = copy_qstring(q); + q = s; +} + +EditDiveSiteName::EditDiveSiteName(dive_site *dsIn, const QString &name) : ds(dsIn), + value(name) +{ + setText(tr("Edit dive site name")); +} + +bool EditDiveSiteName::workToBeDone() +{ + return value != QString(ds->name); +} + +void EditDiveSiteName::redo() +{ + swap(ds->name, value); + emit diveListNotifier.diveSiteChanged(ds, LocationInformationModel::NAME); // Inform frontend of changed dive site. +} + +void EditDiveSiteName::undo() +{ + // Undo and redo do the same + redo(); +} + +EditDiveSiteDescription::EditDiveSiteDescription(dive_site *dsIn, const QString &description) : ds(dsIn), + value(description) +{ + setText(tr("Edit dive site description")); +} + +bool EditDiveSiteDescription::workToBeDone() +{ + return value != QString(ds->description); +} + +void EditDiveSiteDescription::redo() +{ + swap(ds->description, value); + emit diveListNotifier.diveSiteChanged(ds, LocationInformationModel::DESCRIPTION); // Inform frontend of changed dive site. +} + +void EditDiveSiteDescription::undo() +{ + // Undo and redo do the same + redo(); +} + +EditDiveSiteNotes::EditDiveSiteNotes(dive_site *dsIn, const QString ¬es) : ds(dsIn), + value(notes) +{ + setText(tr("Edit dive site notes")); +} + +bool EditDiveSiteNotes::workToBeDone() +{ + return value != QString(ds->notes); +} + +void EditDiveSiteNotes::redo() +{ + swap(ds->notes, value); + emit diveListNotifier.diveSiteChanged(ds, LocationInformationModel::NOTES); // Inform frontend of changed dive site. +} + +void EditDiveSiteNotes::undo() +{ + // Undo and redo do the same + redo(); +} + +EditDiveSiteCountry::EditDiveSiteCountry(dive_site *dsIn, const QString &country) : ds(dsIn), + value(country) +{ + setText(tr("Edit dive site country")); +} + +bool EditDiveSiteCountry::workToBeDone() +{ + return !same_string(qPrintable(value), taxonomy_get_country(&ds->taxonomy)); +} + +void EditDiveSiteCountry::redo() +{ + QString old = taxonomy_get_country(&ds->taxonomy); + taxonomy_set_country(&ds->taxonomy, copy_qstring(value), taxonomy_origin::GEOMANUAL); + value = old; + emit diveListNotifier.diveSiteChanged(ds, LocationInformationModel::TAXONOMY); // Inform frontend of changed dive site. +} + +void EditDiveSiteCountry::undo() +{ + // Undo and redo do the same + redo(); +} + +EditDiveSiteLocation::EditDiveSiteLocation(dive_site *dsIn, const location_t location) : ds(dsIn), + value(location) +{ + setText(tr("Edit dive site location")); +} + +bool EditDiveSiteLocation::workToBeDone() +{ + bool ok = has_location(&value); + bool old_ok = has_location(&ds->location); + if (ok != old_ok) + return true; + return ok && !same_location(&value, &ds->location); +} + +void EditDiveSiteLocation::redo() +{ + std::swap(value, ds->location); + emit diveListNotifier.diveSiteChanged(ds, LocationInformationModel::LOCATION); // Inform frontend of changed dive site. +} + +void EditDiveSiteLocation::undo() +{ + // Undo and redo do the same + redo(); +} + +EditDiveSiteTaxonomy::EditDiveSiteTaxonomy(dive_site *dsIn, taxonomy_data &taxonomy) : ds(dsIn), + value(taxonomy) +{ + // We did a dumb copy. Erase the source to remove double references to strings. + memset(&taxonomy, 0, sizeof(taxonomy)); + setText(tr("Edit dive site taxonomy")); +} + +EditDiveSiteTaxonomy::~EditDiveSiteTaxonomy() +{ + free_taxonomy(&value); +} + +bool EditDiveSiteTaxonomy::workToBeDone() +{ + // TODO: Apparently we have no way of comparing taxonomies? + return true; +} + +void EditDiveSiteTaxonomy::redo() +{ + std::swap(value, ds->taxonomy); + emit diveListNotifier.diveSiteChanged(ds, LocationInformationModel::TAXONOMY); // Inform frontend of changed dive site. +} + +void EditDiveSiteTaxonomy::undo() +{ + // Undo and redo do the same + redo(); +} + +MergeDiveSites::MergeDiveSites(dive_site *dsIn, const QVector<dive_site *> &sites) : ds(dsIn) +{ + setText(tr("merge dive sites")); + sitesToRemove.reserve(sites.size()); + for (dive_site *site: sites) { + if (site != ds) + sitesToRemove.push_back(site); + } +} + +bool MergeDiveSites::workToBeDone() +{ + return !sitesToRemove.empty(); +} + +void MergeDiveSites::redo() +{ + // First, remove all dive sites + sitesToAdd = removeDiveSites(sitesToRemove); + + // Remember which dives changed so that we can send a single dives-edited signal + QVector<dive *> divesChanged; + + // The dives of the above dive sites were reset to no dive sites. + // Add them to the merged-into dive site. Thankfully, we remember + // the dives in the sitesToAdd vector. + for (const OwningDiveSitePtr &site: sitesToAdd) { + for (int i = 0; i < site->dives.nr; ++i) { + add_dive_to_dive_site(site->dives.dives[i], ds); + divesChanged.push_back(site->dives.dives[i]); + } + } + emit diveListNotifier.divesChanged(divesChanged, DiveField::DIVESITE); +} + +void MergeDiveSites::undo() +{ + // Remember which dives changed so that we can send a single dives-edited signal + QVector<dive *> divesChanged; + + // Before readding the dive sites, unregister the corresponding dives so that they can be + // readded to their old dive sites. + for (const OwningDiveSitePtr &site: sitesToAdd) { + for (int i = 0; i < site->dives.nr; ++i) { + unregister_dive_from_dive_site(site->dives.dives[i]); + divesChanged.push_back(site->dives.dives[i]); + } + } + + sitesToRemove = addDiveSites(sitesToAdd); + + emit diveListNotifier.divesChanged(divesChanged, DiveField::DIVESITE); +} + +} // namespace Command |