// SPDX-License-Identifier: GPL-2.0 #include "command_edit.h" #include "core/divelist.h" #include "core/qthelper.h" // for copy_qstring namespace Command { template EditBase::EditBase(const QVector &divesIn, T newValue, T oldValue) : value(std::move(newValue)), old(std::move(oldValue)), dives(divesIn.toStdVector()) { // If there is nothing to do, clear the dives vector. // This signals that no action has to be taken. if (old == value) dives.clear(); } // This is quite hackish: we can't use virtual functions in the constructor and // therefore can't initialize the list of dives [the values of the dives are // accessed by virtual functions]. Therefore, we (mis)use the fact that workToBeDone() // is called exactly once before adding the Command to the system and perform this here. // To be more explicit about this, we might think about renaming workToBeDone() to init(). template bool EditBase::workToBeDone() { std::vector divesNew; divesNew.reserve(dives.size()); for (dive *d: dives) { if (data(d) == old) divesNew.push_back(d); } dives = std::move(divesNew); // Create a text for the menu entry. In the case of multiple dives add the number size_t num_dives = dives.size(); if (num_dives > 0) //: remove the part in parantheses for %n = 1 setText(tr("Edit %1 (%n dive(s))", "", num_dives).arg(fieldName())); return num_dives; } template void EditBase::undo() { if (dives.empty()) { qWarning("Edit command called with empty dives list (shouldn't happen)"); return; } for (dive *d: dives) { set(d, value); invalidate_dive_cache(d); // Ensure that dive is written in git_save() } std::swap(old, value); emit diveListNotifier.divesEdited(QVector::fromStdVector(dives), fieldId()); mark_divelist_changed(true); } // We have to manually instantiate the constructors of the EditBase class, // because the base class is never constructed and the derived classes // don't have their own constructor. They simply delegate to the base // class by virtue of a "using" declaration. template EditBase::EditBase(const QVector &dives, QString oldValue, QString newValue); template EditBase::EditBase(const QVector &dives, int oldValue, int newValue); template EditBase::EditBase(const QVector &dives, struct dive_site *oldValue, struct dive_site *newValue); // Undo and redo do the same as just the stored value is exchanged template void EditBase::redo() { // Note: here, we explicitly call the undo function of EditBase and don't do // virtual dispatch. Thus, derived classes can call this redo function without // having their own undo() function called. EditBase::undo(); } // Implementation of virtual functions // ***** Notes ***** void EditNotes::set(struct dive *d, QString s) const { free(d->notes); d->notes = strdup(qPrintable(s)); } QString EditNotes::data(struct dive *d) const { return QString(d->notes); } QString EditNotes::fieldName() const { return tr("notes"); } DiveField EditNotes::fieldId() const { return DiveField::NOTES; } // ***** Suit ***** void EditSuit::set(struct dive *d, QString s) const { free(d->suit); d->suit = strdup(qPrintable(s)); } QString EditSuit::data(struct dive *d) const { return QString(d->suit); } QString EditSuit::fieldName() const { return tr("suit"); } DiveField EditSuit::fieldId() const { return DiveField::SUIT; } // ***** Rating ***** void EditRating::set(struct dive *d, int value) const { d->rating = value; } int EditRating::data(struct dive *d) const { return d->rating; } QString EditRating::fieldName() const { return tr("rating"); } DiveField EditRating::fieldId() const { return DiveField::RATING; } // ***** Visibility ***** void EditVisibility::set(struct dive *d, int value) const { d->visibility = value; } int EditVisibility::data(struct dive *d) const { return d->visibility; } QString EditVisibility::fieldName() const { return tr("visibility"); } DiveField EditVisibility::fieldId() const { return DiveField::VISIBILITY; } // ***** Air Temperature ***** void EditAirTemp::set(struct dive *d, int value) const { d->airtemp.mkelvin = value > 0 ? (uint32_t)value : 0u; } int EditAirTemp::data(struct dive *d) const { return (int)d->airtemp.mkelvin; } QString EditAirTemp::fieldName() const { return tr("air temperature"); } DiveField EditAirTemp::fieldId() const { return DiveField::AIR_TEMP; } // ***** Water Temperature ***** void EditWaterTemp::set(struct dive *d, int value) const { d->watertemp.mkelvin = value > 0 ? (uint32_t)value : 0u; } int EditWaterTemp::data(struct dive *d) const { return (int)d->watertemp.mkelvin; } QString EditWaterTemp::fieldName() const { return tr("water temperature"); } DiveField EditWaterTemp::fieldId() const { return DiveField::WATER_TEMP; } // ***** DiveSite ***** void EditDiveSite::set(struct dive *d, struct dive_site *dive_site) const { unregister_dive_from_dive_site(d); add_dive_to_dive_site(d, dive_site); } struct dive_site *EditDiveSite::data(struct dive *d) const { return d->dive_site; } QString EditDiveSite::fieldName() const { return tr("dive site"); } DiveField EditDiveSite::fieldId() const { return DiveField::DIVESITE; } void EditDiveSite::undo() { // Do the normal undo thing, then send dive site changed signals EditBase::undo(); if (value) emit diveListNotifier.diveSiteDivesChanged(value); if (old) emit diveListNotifier.diveSiteDivesChanged(old); } void EditDiveSite::redo() { EditDiveSite::undo(); // Undo and redo do the same } static struct dive_site *createDiveSite(const QString &name, struct dive_site *old) { struct dive_site *ds = alloc_dive_site(); if (old) { copy_dive_site(old, ds); free(ds->name); // Free name, as we will overwrite it with our own version } ds->name = copy_qstring(name); return ds; } EditDiveSiteNew::EditDiveSiteNew(const QVector &dives, const QString &newName, struct dive_site *oldValue) : EditDiveSite(dives, createDiveSite(newName, oldValue), oldValue), diveSiteToAdd(value), diveSiteToRemove(nullptr) { } void EditDiveSiteNew::undo() { EditDiveSite::undo(); int idx = unregister_dive_site(diveSiteToRemove); diveSiteToAdd.reset(diveSiteToRemove); emit diveListNotifier.diveSiteDeleted(diveSiteToRemove, idx); // Inform frontend of removed dive site. diveSiteToRemove = nullptr; } void EditDiveSiteNew::redo() { diveSiteToRemove = diveSiteToAdd.get(); int idx = register_dive_site(diveSiteToAdd.release()); // Return ownership to backend. emit diveListNotifier.diveSiteAdded(diveSiteToRemove, idx); // Inform frontend of new dive site. EditDiveSite::redo(); } // ***** Mode ***** // Editing the dive mode has very peculiar semantics for historic reasons: // Since the dive-mode depends on the dive computer, the i-th dive computer // of each dive will be edited. If the dive has less than i dive computers, // the default dive computer will be edited. // The index "i" will be stored as an additional payload with the command. // Thus, we need an explicit constructor. Since the actual handling is done // by the base class, which knows nothing about this index, it will not be // sent via signals. Currently this is not needed. Should it turn out to // become necessary, then we might either // - Not derive EditMode from EditBase. // - Change the semantics of the mode-editing. // The future will tell. EditMode::EditMode(const QVector &dives, int indexIn, int newValue, int oldValue) : EditBase(dives, newValue, oldValue), index(indexIn) { } void EditMode::set(struct dive *d, int i) const { get_dive_dc(d, index)->divemode = (enum divemode_t)i; update_setpoint_events(d, get_dive_dc(d, index)); } int EditMode::data(struct dive *d) const { return get_dive_dc(d, index)->divemode; } QString EditMode::fieldName() const { return tr("dive mode"); } DiveField EditMode::fieldId() const { return DiveField::MODE; } } // namespace Command