Merge pull request #2643 from bstoeger/cylinder4

First steps of cylinder-editing undo
This commit is contained in:
Dirk Hohndel 2020-04-11 11:03:05 -07:00 committed by GitHub
commit 6d187b5f4a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 1335 additions and 795 deletions

View file

@ -14,6 +14,8 @@ set(SUBSURFACE_GENERIC_COMMANDS_SRCS
command_edit.h
command_edit_trip.cpp
command_edit_trip.h
command_event.cpp
command_event.h
)
add_library(subsurface_commands STATIC ${SUBSURFACE_GENERIC_COMMANDS_SRCS})
target_link_libraries(subsurface_commands ${QT_LIBRARIES})

View file

@ -5,6 +5,7 @@
#include "command_divesite.h"
#include "command_edit.h"
#include "command_edit_trip.h"
#include "command_event.h"
namespace Command {
@ -293,6 +294,21 @@ int editWeight(int index, weightsystem_t ws, bool currentDiveOnly)
return execute_edit(new EditWeight(index, ws, currentDiveOnly));
}
int addCylinder(bool currentDiveOnly)
{
return execute_edit(new AddCylinder(currentDiveOnly));
}
int removeCylinder(int index, bool currentDiveOnly)
{
return execute_edit(new RemoveCylinder(index, currentDiveOnly));
}
int editCylinder(int index, cylinder_t cyl, EditCylinderType type, bool currentDiveOnly)
{
return execute_edit(new EditCylinder(index, cyl, type, currentDiveOnly));
}
// Trip editing related commands
void editTripLocation(dive_trip *trip, const QString &s)
{
@ -311,4 +327,36 @@ void editDive(dive *oldDive, dive *newDive, dive_site *createDs, dive_site *chan
}
#endif // SUBSURFACE_MOBILE
// Event commands
void addEventBookmark(struct dive *d, int dcNr, int seconds)
{
execute(new AddEventBookmark(d, dcNr, seconds));
}
void addEventDivemodeSwitch(struct dive *d, int dcNr, int seconds, int divemode)
{
execute(new AddEventDivemodeSwitch(d, dcNr, seconds, divemode));
}
void addEventSetpointChange(struct dive *d, int dcNr, int seconds, pressure_t pO2)
{
execute(new AddEventSetpointChange(d, dcNr, seconds, pO2));
}
void renameEvent(struct dive *d, int dcNr, struct event *ev, const char *name)
{
execute(new RenameEvent(d, dcNr, ev, name));
}
void removeEvent(struct dive *d, int dcNr, struct event *ev)
{
execute(new RemoveEvent(d, dcNr, ev));
}
void addGasSwitch(struct dive *d, int dcNr, int seconds, int tank)
{
execute(new AddGasSwitch(d, dcNr, seconds, tank));
}
} // namespace Command

View file

@ -90,6 +90,14 @@ void editProfile(dive *d); // dive computer(s) and cylinder(s) will be reset!
int addWeight(bool currentDiveOnly);
int removeWeight(int index, bool currentDiveOnly);
int editWeight(int index, weightsystem_t ws, bool currentDiveOnly);
int addCylinder(bool currentDiveOnly);
int removeCylinder(int index, bool currentDiveOnly);
enum class EditCylinderType {
TYPE,
PRESSURE,
GASMIX
};
int editCylinder(int index, cylinder_t cyl, EditCylinderType type, bool currentDiveOnly);
#ifdef SUBSURFACE_MOBILE
// Edits a dive and creates a divesite (if createDs != NULL) or edits a divesite (if changeDs != NULL).
// Takes ownership of newDive and createDs!
@ -101,6 +109,15 @@ void editDive(dive *oldDive, dive *newDive, dive_site *createDs, dive_site *chan
void editTripLocation(dive_trip *trip, const QString &s);
void editTripNotes(dive_trip *trip, const QString &s);
// 6) Event commands
void addEventBookmark(struct dive *d, int dcNr, int seconds);
void addEventDivemodeSwitch(struct dive *d, int dcNr, int seconds, int divemode);
void addEventSetpointChange(struct dive *d, int dcNr, int seconds, pressure_t pO2);
void renameEvent(struct dive *d, int dcNr, struct event *ev, const char *name);
void removeEvent(struct dive *d, int dcNr, struct event *ev);
void addGasSwitch(struct dive *d, int dcNr, int seconds, int tank);
} // namespace Command
#endif // COMMAND_H

View file

@ -141,7 +141,7 @@
// We put everything in a namespace, so that we can shorten names without polluting the global namespace
namespace Command {
// Classes used to automatically call free_dive()/free_trip for owning pointers that go out of scope.
// Classes used to automatically call the appropriate free_*() function for owning pointers that go out of scope.
struct DiveDeleter {
void operator()(dive *d) { free_dive(d); }
};
@ -151,11 +151,15 @@ struct TripDeleter {
struct DiveSiteDeleter {
void operator()(dive_site *ds) { free_dive_site(ds); }
};
struct EventDeleter {
void operator()(event *ev) { free(ev); }
};
// Owning pointers to dive and dive_trip objects.
// Owning pointers to dive, dive_trip, dive_site and event objects.
typedef std::unique_ptr<dive, DiveDeleter> OwningDivePtr;
typedef std::unique_ptr<dive_trip, TripDeleter> OwningTripPtr;
typedef std::unique_ptr<dive_site, DiveSiteDeleter> OwningDiveSitePtr;
typedef std::unique_ptr<event, EventDeleter> OwningEventPtr;
// This is the base class of all commands.
// It defines the Qt-translation functions
@ -182,4 +186,3 @@ QString getListOfDives(QVector<struct dive *> dives);
} // namespace Command
#endif // COMMAND_BASE_H

View file

@ -8,6 +8,7 @@
#include "core/subsurface-string.h"
#include "core/tag.h"
#include "qt-models/weightsysteminfomodel.h"
#include "qt-models/tankinfomodel.h"
#ifdef SUBSURFACE_MOBILE
#include "qt-models/divelocationmodel.h"
#endif
@ -946,6 +947,7 @@ bool EditWeightBase::workToBeDone()
return !dives.empty();
}
// ***** Remove Weight *****
RemoveWeight::RemoveWeight(int index, bool currentDiveOnly) :
EditWeightBase(index, currentDiveOnly)
{
@ -972,6 +974,7 @@ void RemoveWeight::redo()
}
}
// ***** Edit Weight *****
EditWeight::EditWeight(int index, weightsystem_t wsIn, bool currentDiveOnly) :
EditWeightBase(index, currentDiveOnly),
new_ws(empty_weightsystem)
@ -1028,6 +1031,241 @@ void EditWeight::undo()
redo();
}
// ***** Add Cylinder *****
AddCylinder::AddCylinder(bool currentDiveOnly) :
EditDivesBase(currentDiveOnly),
cyl(empty_cylinder)
{
if (dives.empty())
return;
else if (dives.size() == 1)
setText(tr("Add cylinder"));
else
setText(tr("Add cylinder (%n dive(s))", "", dives.size()));
cyl = create_new_cylinder(dives[0]);
}
AddCylinder::~AddCylinder()
{
free_cylinder(cyl);
}
bool AddCylinder::workToBeDone()
{
return true;
}
void AddCylinder::undo()
{
for (dive *d: dives) {
if (d->cylinders.nr <= 0)
continue;
remove_cylinder(d, d->cylinders.nr - 1);
emit diveListNotifier.cylinderRemoved(d, d->cylinders.nr);
invalidate_dive_cache(d); // Ensure that dive is written in git_save()
}
}
void AddCylinder::redo()
{
for (dive *d: dives) {
add_cloned_cylinder(&d->cylinders, cyl);
emit diveListNotifier.cylinderAdded(d, d->cylinders.nr - 1);
invalidate_dive_cache(d); // Ensure that dive is written in git_save()
}
}
static bool same_cylinder_type(const cylinder_t &cyl1, const cylinder_t &cyl2)
{
return same_string(cyl1.type.description, cyl2.type.description) &&
cyl1.cylinder_use == cyl2.cylinder_use;
}
static bool same_cylinder_size(const cylinder_t &cyl1, const cylinder_t &cyl2)
{
return cyl1.type.size.mliter == cyl2.type.size.mliter &&
cyl1.type.workingpressure.mbar == cyl2.type.workingpressure.mbar;
}
static bool same_cylinder_pressure(const cylinder_t &cyl1, const cylinder_t &cyl2)
{
return cyl1.start.mbar == cyl2.start.mbar &&
cyl1.end.mbar == cyl2.end.mbar;
}
// Flags for comparing cylinders
static const constexpr int SAME_TYPE = 1 << 0;
static const constexpr int SAME_SIZE = 1 << 1;
static const constexpr int SAME_PRESS = 1 << 2;
static const constexpr int SAME_GAS = 1 << 3;
static bool same_cylinder_with_flags(const cylinder_t &cyl1, const cylinder_t &cyl2, int sameCylinderFlags)
{
return (((sameCylinderFlags & SAME_TYPE) == 0 || same_cylinder_type(cyl1, cyl2)) &&
((sameCylinderFlags & SAME_SIZE) == 0 || same_cylinder_size(cyl1, cyl2)) &&
((sameCylinderFlags & SAME_PRESS) == 0 || same_cylinder_pressure(cyl1, cyl2)) &&
((sameCylinderFlags & SAME_GAS) == 0 || same_gasmix(cyl1.gasmix, cyl2.gasmix)));
}
static int find_cylinder_index(const struct dive *d, const cylinder_t &cyl, int sameCylinderFlags)
{
for (int idx = 0; idx < d->cylinders.nr; ++idx) {
if (same_cylinder_with_flags(cyl, d->cylinders.cylinders[idx], sameCylinderFlags))
return idx;
}
return -1;
}
EditCylinderBase::EditCylinderBase(int index, bool currentDiveOnly, bool nonProtectedOnly, int sameCylinderFlags) :
EditDivesBase(currentDiveOnly)
{
// Get the old cylinder, bail if index is invalid
if (!current || index < 0 || index >= current->cylinders.nr) {
dives.clear();
return;
}
const cylinder_t &orig = current->cylinders.cylinders[index];
std::vector<dive *> divesNew;
divesNew.reserve(dives.size());
indexes.reserve(dives.size());
cyl.reserve(dives.size());
for (dive *d: dives) {
int idx = d == current ? index : find_cylinder_index(d, orig, sameCylinderFlags);
if (idx < 0 || (nonProtectedOnly && is_cylinder_prot(d, idx)))
continue;
divesNew.push_back(d);
indexes.push_back(idx);
cyl.push_back(clone_cylinder(d->cylinders.cylinders[idx]));
}
dives = std::move(divesNew);
}
EditCylinderBase::~EditCylinderBase()
{
for (cylinder_t c: cyl)
free_cylinder(c);
}
bool EditCylinderBase::workToBeDone()
{
return !dives.empty();
}
// ***** Remove Cylinder *****
RemoveCylinder::RemoveCylinder(int index, bool currentDiveOnly) :
EditCylinderBase(index, currentDiveOnly, true, SAME_TYPE | SAME_PRESS | SAME_GAS)
{
if (dives.size() == 1)
setText(tr("Remove cylinder"));
else
setText(tr("Remove cylinder (%n dive(s))", "", dives.size()));
}
void RemoveCylinder::undo()
{
for (size_t i = 0; i < dives.size(); ++i) {
std::vector<int> mapping = get_cylinder_map_for_add(dives[i]->cylinders.nr, indexes[i]);
add_to_cylinder_table(&dives[i]->cylinders, indexes[i], clone_cylinder(cyl[i]));
emit diveListNotifier.cylinderAdded(dives[i], indexes[i]);
invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save()
}
}
void RemoveCylinder::redo()
{
for (size_t i = 0; i < dives.size(); ++i) {
std::vector<int> mapping = get_cylinder_map_for_remove(dives[i]->cylinders.nr, indexes[i]);
remove_cylinder(dives[i], indexes[i]);
cylinder_renumber(dives[i], &mapping[0]);
emit diveListNotifier.cylinderRemoved(dives[i], indexes[i]);
invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save()
}
}
static int editCylinderTypeToFlags(EditCylinderType type)
{
switch (type) {
default:
case EditCylinderType::TYPE:
return SAME_TYPE | SAME_SIZE;
case EditCylinderType::PRESSURE:
return SAME_PRESS;
case EditCylinderType::GASMIX:
return SAME_GAS;
}
}
// ***** Edit Cylinder *****
EditCylinder::EditCylinder(int index, cylinder_t cylIn, EditCylinderType typeIn, bool currentDiveOnly) :
EditCylinderBase(index, currentDiveOnly, false, editCylinderTypeToFlags(typeIn)),
type(typeIn)
{
if (dives.empty())
return;
if (dives.size() == 1)
setText(tr("Edit cylinder"));
else
setText(tr("Edit cylinder (%n dive(s))", "", dives.size()));
// Try to untranslate the cylinder type
QString description = cylIn.type.description;
for (int i = 0; i < MAX_TANK_INFO && tank_info[i].name; ++i) {
if (gettextFromC::tr(tank_info[i].name) == description) {
description = tank_info[i].name;
break;
}
}
// Update the tank info model
TankInfoModel *tim = TankInfoModel::instance();
QModelIndexList matches = tim->match(tim->index(0, 0), Qt::DisplayRole, gettextFromC::tr(cylIn.type.description));
if (!matches.isEmpty()) {
if (cylIn.type.size.mliter != cyl[0].type.size.mliter)
tim->setData(tim->index(matches.first().row(), TankInfoModel::ML), cylIn.type.size.mliter);
if (cylIn.type.workingpressure.mbar != cyl[0].type.workingpressure.mbar)
tim->setData(tim->index(matches.first().row(), TankInfoModel::BAR), cylIn.type.workingpressure.mbar / 1000.0);
}
// The base class copied the cylinders for us, let's edit them
for (int i = 0; i < (int)indexes.size(); ++i) {
switch (type) {
case EditCylinderType::TYPE:
free((void *)cyl[i].type.description);
cyl[i].type = cylIn.type;
cyl[i].type.description = copy_qstring(description);
cyl[i].cylinder_use = cylIn.cylinder_use;
break;
case EditCylinderType::PRESSURE:
cyl[i].start.mbar = cylIn.start.mbar;
cyl[i].end.mbar = cylIn.end.mbar;
break;
case EditCylinderType::GASMIX:
cyl[i].gasmix = cylIn.gasmix;
cyl[i].bestmix_o2 = cylIn.bestmix_o2;
cyl[i].bestmix_he = cylIn.bestmix_he;
break;
}
}
}
void EditCylinder::redo()
{
for (size_t i = 0; i < dives.size(); ++i) {
std::swap(dives[i]->cylinders.cylinders[indexes[i]], cyl[i]);
emit diveListNotifier.cylinderEdited(dives[i], indexes[i]);
invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save()
}
}
// Undo and redo do the same as just the stored value is exchanged
void EditCylinder::undo()
{
redo();
}
#ifdef SUBSURFACE_MOBILE
EditDive::EditDive(dive *oldDiveIn, dive *newDiveIn, dive_site *createDs, dive_site *editDs, location_t dsLocationIn)

View file

@ -5,6 +5,7 @@
#define COMMAND_EDIT_H
#include "command_base.h"
#include "command.h" // for EditCylinderType
#include "core/subsurface-qt/divelistnotifier.h"
#include <QVector>
@ -377,6 +378,49 @@ private:
void redo() override;
};
class AddCylinder : public EditDivesBase {
public:
AddCylinder(bool currentDiveOnly);
~AddCylinder();
private:
cylinder_t cyl;
void undo() override;
void redo() override;
bool workToBeDone() override;
};
class EditCylinderBase : public EditDivesBase {
protected:
EditCylinderBase(int index, bool currentDiveOnly, bool nonProtectedOnly, int sameCylinderFlags);
~EditCylinderBase();
std::vector<cylinder_t> cyl;
std::vector<int> indexes; // An index for each dive in the dives vector.
bool workToBeDone() override;
};
class RemoveCylinder : public EditCylinderBase {
public:
RemoveCylinder(int index, bool currentDiveOnly);
private:
void undo() override;
void redo() override;
};
// Instead of implementing an undo command for every single field in a cylinder,
// we only have one and pass an edit "type". We either edit the type, pressure
// or gasmix fields. This has mostly historical reasons rooted in the way the
// CylindersModel code works. The model works for undo and also in the planner
// without undo. Having a single undo-command simplifies the code there.
class EditCylinder : public EditCylinderBase {
public:
EditCylinder(int index, cylinder_t cyl, EditCylinderType type, bool currentDiveOnly); // Clones cylinder
private:
EditCylinderType type;
void undo() override;
void redo() override;
};
#ifdef SUBSURFACE_MOBILE
// Edit a full dive. This is used on mobile where we don't have per-field granularity.
// It may add or edit a dive site.
@ -406,5 +450,4 @@ private:
#endif // SUBSURFACE_MOBILE
} // namespace Command
#endif

210
commands/command_event.cpp Normal file
View file

@ -0,0 +1,210 @@
// SPDX-License-Identifier: GPL-2.0
#include "command_event.h"
#include "core/dive.h"
#include "core/selection.h"
#include "core/subsurface-qt/divelistnotifier.h"
#include "core/libdivecomputer.h"
#include "core/gettextfromc.h"
#include <QVector>
namespace Command {
EventBase::EventBase(struct dive *dIn, int dcNrIn) :
d(dIn), dcNr(dcNrIn)
{
}
void EventBase::redo()
{
redoit(); // Call actual function in base class
updateDive();
}
void EventBase::undo()
{
undoit(); // Call actual function in base class
updateDive();
}
void EventBase::updateDive()
{
invalidate_dive_cache(d);
emit diveListNotifier.eventsChanged(d);
dc_number = dcNr;
setSelection({ d }, d);
}
AddEventBase::AddEventBase(struct dive *d, int dcNr, struct event *ev) : EventBase(d, dcNr),
eventToAdd(ev)
{
}
bool AddEventBase::workToBeDone()
{
return true;
}
void AddEventBase::redoit()
{
struct divecomputer *dc = get_dive_dc(d, dcNr);
eventToRemove = eventToAdd.get();
add_event_to_dc(dc, eventToAdd.release()); // return ownership to backend
}
void AddEventBase::undoit()
{
struct divecomputer *dc = get_dive_dc(d, dcNr);
remove_event_from_dc(dc, eventToRemove);
eventToAdd.reset(eventToRemove); // take ownership of event
eventToRemove = nullptr;
}
AddEventBookmark::AddEventBookmark(struct dive *d, int dcNr, int seconds) :
AddEventBase(d, dcNr, create_event(seconds, SAMPLE_EVENT_BOOKMARK, 0, 0, "bookmark"))
{
setText(tr("Add bookmark"));
}
AddEventDivemodeSwitch::AddEventDivemodeSwitch(struct dive *d, int dcNr, int seconds, int divemode) :
AddEventBase(d, dcNr, create_event(seconds, SAMPLE_EVENT_BOOKMARK, 0, divemode, QT_TRANSLATE_NOOP("gettextFromC", "modechange")))
{
setText(tr("Add dive mode switch to %1").arg(gettextFromC::tr(divemode_text_ui[divemode])));
}
AddEventSetpointChange::AddEventSetpointChange(struct dive *d, int dcNr, int seconds, pressure_t pO2) :
AddEventBase(d, dcNr, create_event(seconds, SAMPLE_EVENT_PO2, 0, pO2.mbar, QT_TRANSLATE_NOOP("gettextFromC", "SP change")))
{
setText(tr("Add set point change")); // TODO: format pO2 value in bar or psi.
}
RenameEvent::RenameEvent(struct dive *d, int dcNr, struct event *ev, const char *name) : EventBase(d, dcNr),
eventToAdd(clone_event_rename(ev, name)),
eventToRemove(ev)
{
setText(tr("Rename bookmark to %1").arg(name));
}
bool RenameEvent::workToBeDone()
{
return true;
}
void RenameEvent::redoit()
{
struct divecomputer *dc = get_dive_dc(d, dcNr);
swap_event(dc, eventToRemove, eventToAdd.get());
event *tmp = eventToRemove;
eventToRemove = eventToAdd.release();
eventToAdd.reset(tmp);
}
void RenameEvent::undoit()
{
// Undo and redo do the same thing - they simply swap events
redoit();
}
RemoveEvent::RemoveEvent(struct dive *d, int dcNr, struct event *ev) : EventBase(d, dcNr),
eventToRemove(ev),
cylinder(ev->type == SAMPLE_EVENT_GASCHANGE2 || ev->type == SAMPLE_EVENT_GASCHANGE ?
ev->gas.index : -1)
{
setText(tr("Remove %1 event").arg(ev->name));
}
bool RemoveEvent::workToBeDone()
{
return true;
}
void RemoveEvent::redoit()
{
struct divecomputer *dc = get_dive_dc(d, dcNr);
remove_event_from_dc(dc, eventToRemove);
eventToAdd.reset(eventToRemove); // take ownership of event
eventToRemove = nullptr;
}
void RemoveEvent::undoit()
{
struct divecomputer *dc = get_dive_dc(d, dcNr);
eventToRemove = eventToAdd.get();
add_event_to_dc(dc, eventToAdd.release()); // return ownership to backend
}
void RemoveEvent::post() const
{
if (cylinder < 0)
return;
fixup_dive(d);
emit diveListNotifier.cylinderEdited(d, cylinder);
// TODO: This is silly we send a DURATION change event so that the statistics are recalculated.
// We should instead define a proper DiveField that expresses the change caused by a gas switch.
emit diveListNotifier.divesChanged(QVector<dive *>{ d }, DiveField::DURATION | DiveField::DEPTH);
}
AddGasSwitch::AddGasSwitch(struct dive *d, int dcNr, int seconds, int tank) : EventBase(d, dcNr)
{
// If there is a gas change at this time stamp, remove it before adding the new one.
// There shouldn't be more than one gas change per time stamp. Just in case we'll
// support that anyway.
struct divecomputer *dc = get_dive_dc(d, dcNr);
struct event *gasChangeEvent = dc->events;
while ((gasChangeEvent = get_next_event_mutable(gasChangeEvent, "gaschange")) != NULL) {
if (gasChangeEvent->time.seconds == seconds) {
eventsToRemove.push_back(gasChangeEvent);
int idx = gasChangeEvent->gas.index;
if (std::find(cylinders.begin(), cylinders.end(), idx) == cylinders.end())
cylinders.push_back(idx); // cylinders might have changed their status
}
gasChangeEvent = gasChangeEvent->next;
}
eventsToAdd.emplace_back(create_gas_switch_event(d, dc, seconds, tank));
}
bool AddGasSwitch::workToBeDone()
{
return true;
}
void AddGasSwitch::redoit()
{
std::vector<OwningEventPtr> newEventsToAdd;
std::vector<event *> newEventsToRemove;
newEventsToAdd.reserve(eventsToRemove.size());
newEventsToRemove.reserve(eventsToAdd.size());
struct divecomputer *dc = get_dive_dc(d, dcNr);
for (event *ev: eventsToRemove) {
remove_event_from_dc(dc, ev);
newEventsToAdd.emplace_back(ev); // take ownership of event
}
for (OwningEventPtr &ev: eventsToAdd) {
newEventsToRemove.push_back(ev.get());
add_event_to_dc(dc, ev.release()); // return ownership to backend
}
eventsToAdd = std::move(newEventsToAdd);
eventsToRemove = std::move(newEventsToRemove);
// this means we potentially have a new tank that is being used and needs to be shown
fixup_dive(d);
for (int idx: cylinders)
emit diveListNotifier.cylinderEdited(d, idx);
// TODO: This is silly we send a DURATION change event so that the statistics are recalculated.
// We should instead define a proper DiveField that expresses the change caused by a gas switch.
emit diveListNotifier.divesChanged(QVector<dive *>{ d }, DiveField::DURATION | DiveField::DEPTH);
}
void AddGasSwitch::undoit()
{
// Undo and redo do the same thing, as the dives-to-be-added and dives-to-be-removed are exchanged.
redoit();
}
} // namespace Command

104
commands/command_event.h Normal file
View file

@ -0,0 +1,104 @@
// SPDX-License-Identifier: GPL-2.0
// Note: this header file is used by the undo-machinery and should not be included elsewhere.
#ifndef COMMAND_EVENT_H
#define COMMAND_EVENT_H
#include "command_base.h"
// We put everything in a namespace, so that we can shorten names without polluting the global namespace
namespace Command {
// Events are a strange thing: they contain there own description which means
// that on changing the description a new object must be allocated. Moreover,
// it means that these objects can't be collected in a table.
// Therefore, the undo commands work on events as they do with dives: using
// owning pointers. See comments in command_base.h
class EventBase : public Base {
protected:
EventBase(struct dive *d, int dcNr);
void undo() override;
void redo() override;
virtual void redoit() = 0;
virtual void undoit() = 0;
// Note: we store dive and the divecomputer-number instead of a pointer to the divecomputer.
// Since one divecomputer is integrated into the dive structure, pointers to divecomputers
// are probably not stable.
struct dive *d;
int dcNr;
private:
void updateDive();
};
class AddEventBase : public EventBase {
public:
AddEventBase(struct dive *d, int dcNr, struct event *ev); // Takes ownership of event!
private:
bool workToBeDone() override;
void undoit() override;
void redoit() override;
OwningEventPtr eventToAdd; // for redo
event *eventToRemove; // for undo
};
class AddEventBookmark : public AddEventBase {
public:
AddEventBookmark(struct dive *d, int dcNr, int seconds);
};
class AddEventDivemodeSwitch : public AddEventBase {
public:
AddEventDivemodeSwitch(struct dive *d, int dcNr, int seconds, int divemode);
};
class AddEventSetpointChange : public AddEventBase {
public:
AddEventSetpointChange(struct dive *d, int dcNr, int seconds, pressure_t pO2);
};
class RenameEvent : public EventBase {
public:
RenameEvent(struct dive *d, int dcNr, struct event *ev, const char *name);
private:
bool workToBeDone() override;
void undoit() override;
void redoit() override;
OwningEventPtr eventToAdd; // for undo and redo
event *eventToRemove; // for undo and redo
};
class RemoveEvent : public EventBase {
public:
RemoveEvent(struct dive *d, int dcNr, struct event *ev);
private:
bool workToBeDone() override;
void undoit() override;
void redoit() override;
void post() const; // Called to fix up dives should a gas-change have happened.
OwningEventPtr eventToAdd; // for undo
event *eventToRemove; // for redo
int cylinder; // affected cylinder (if removing gas switch). <0: not a gas switch.
};
class AddGasSwitch : public EventBase {
public:
AddGasSwitch(struct dive *d, int dcNr, int seconds, int tank);
private:
bool workToBeDone() override;
void undoit() override;
void redoit() override;
std::vector<int> cylinders; // cylinders that are modified
std::vector<OwningEventPtr> eventsToAdd;
std::vector<event *> eventsToRemove;
};
} // namespace Command
#endif // COMMAND_EVENT_H