core: turn event-list of divecomputer into std::vector<>

This is a rather long commit, because it refactors lots of the event
code from pointer to value semantics: pointers to entries in an
std::vector<> are not stable, so better use indexes.

To step through the event-list at diven time stamps, add *_loop classes,
which encapsulate state that had to be manually handled before by
the caller. I'm not happy about the interface, but it tries to
mirror the one we had before.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
Berthold Stoeger 2024-05-25 08:16:57 +02:00 committed by bstoeger
parent 8ddc960fa0
commit 27dbdd35c6
36 changed files with 644 additions and 821 deletions

View file

@ -2,7 +2,6 @@
#include "command_event.h"
#include "core/dive.h"
#include "core/event.h"
#include "core/selection.h"
#include "core/subsurface-qt/divelistnotifier.h"
#include "core/libdivecomputer.h"
@ -35,8 +34,8 @@ void EventBase::updateDive()
setSelection({ d }, d, dcNr);
}
AddEventBase::AddEventBase(struct dive *d, int dcNr, struct event *ev) : EventBase(d, dcNr),
eventToAdd(ev)
AddEventBase::AddEventBase(struct dive *d, int dcNr, struct event ev) : EventBase(d, dcNr),
ev(std::move(ev))
{
}
@ -48,32 +47,29 @@ bool AddEventBase::workToBeDone()
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
idx = add_event_to_dc(dc, ev); // 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;
ev = remove_event_from_dc(dc, idx);
}
AddEventBookmark::AddEventBookmark(struct dive *d, int dcNr, int seconds) :
AddEventBase(d, dcNr, create_event(seconds, SAMPLE_EVENT_BOOKMARK, 0, 0, "bookmark"))
AddEventBase(d, dcNr, event(seconds, SAMPLE_EVENT_BOOKMARK, 0, 0, "bookmark"))
{
setText(Command::Base::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")))
AddEventBase(d, dcNr, event(seconds, SAMPLE_EVENT_BOOKMARK, 0, divemode, QT_TRANSLATE_NOOP("gettextFromC", "modechange")))
{
setText(Command::Base::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"))),
AddEventBase(d, dcNr, event(seconds, SAMPLE_EVENT_PO2, 0, pO2.mbar, QT_TRANSLATE_NOOP("gettextFromC", "SP change"))),
divemode(CCR)
{
setText(Command::Base::tr("Add set point change")); // TODO: format pO2 value in bar or psi.
@ -91,11 +87,11 @@ void AddEventSetpointChange::redoit()
std::swap(get_dive_dc(d, dcNr)->divemode, divemode);
}
RenameEvent::RenameEvent(struct dive *d, int dcNr, struct event *ev, const char *name) : EventBase(d, dcNr),
eventToAdd(clone_event_rename(ev, name)),
eventToRemove(ev)
RenameEvent::RenameEvent(struct dive *d, int dcNr, int idx, const std::string name) : EventBase(d, dcNr),
idx(idx),
name(std::move(name))
{
setText(Command::Base::tr("Rename bookmark to %1").arg(name));
setText(Command::Base::tr("Rename bookmark to %1").arg(name.c_str()));
}
bool RenameEvent::workToBeDone()
@ -106,23 +102,24 @@ bool RenameEvent::workToBeDone()
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);
event *ev = get_event(dc, idx);
if (ev)
std::swap(ev->name, name);
}
void RenameEvent::undoit()
{
// Undo and redo do the same thing - they simply swap events
// Undo and redo do the same thing - they simply swap names
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)
RemoveEvent::RemoveEvent(struct dive *d, int dcNr, int idx) : EventBase(d, dcNr),
idx(idx), cylinder(-1)
{
struct divecomputer *dc = get_dive_dc(d, dcNr);
event *ev = get_event(dc, idx);
if (ev && (ev->type == SAMPLE_EVENT_GASCHANGE2 || ev->type == SAMPLE_EVENT_GASCHANGE))
cylinder = ev->gas.index;
setText(Command::Base::tr("Remove %1 event").arg(ev->name.c_str()));
}
@ -134,16 +131,13 @@ bool RemoveEvent::workToBeDone()
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;
ev = remove_event_from_dc(dc, idx);
}
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
idx = add_event_to_dc(dc, std::move(ev));
}
void RemoveEvent::post() const
@ -165,18 +159,18 @@ AddGasSwitch::AddGasSwitch(struct dive *d, int dcNr, int seconds, int tank) : Ev
// 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(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;
// Note that we remove events in reverse order so that the indexes don't change
// meaning while removing. This should be an extremely rare case anyway.
for (int idx = static_cast<int>(dc->events.size()) - 1; idx > 0; --idx) {
const event &ev = dc->events[idx];
if (ev.time.seconds == seconds && ev.name == "gaschange")
eventsToRemove.push_back(idx);
if (std::find(cylinders.begin(), cylinders.end(), ev.gas.index) == cylinders.end())
cylinders.push_back(ev.gas.index); // cylinders might have changed their status
}
eventsToAdd.emplace_back(create_gas_switch_event(d, dc, seconds, tank));
eventsToAdd.push_back(create_gas_switch_event(d, dc, seconds, tank));
}
bool AddGasSwitch::workToBeDone()
@ -186,20 +180,21 @@ bool AddGasSwitch::workToBeDone()
void AddGasSwitch::redoit()
{
std::vector<std::unique_ptr<event>> newEventsToAdd;
std::vector<event *> newEventsToRemove;
std::vector<event> newEventsToAdd;
std::vector<int> 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 (auto &ev: eventsToAdd) {
newEventsToRemove.push_back(ev.get());
add_event_to_dc(dc, ev.release()); // return ownership to backend
}
for (int idx: eventsToRemove)
newEventsToAdd.push_back(remove_event_from_dc(dc, idx));
for (auto &ev: eventsToAdd)
newEventsToRemove.push_back(add_event_to_dc(dc, std::move(ev)));
// Make sure that events are removed in reverse order
std::sort(newEventsToRemove.begin(), newEventsToRemove.end(), std::greater<int>());
eventsToAdd = std::move(newEventsToAdd);
eventsToRemove = std::move(newEventsToRemove);