From 30c7499a3c656784cc45825b4b1b2fca61081308 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 3 Mar 2020 22:34:50 +0100 Subject: undo: implement addBookmark undo command Create a new translation unit for event-related undo commands. Create a base class of commands that add events and one subclass that adds a bookmark event. Use this command in the profile-widget. Signed-off-by: Berthold Stoeger --- commands/command_event.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 commands/command_event.cpp (limited to 'commands/command_event.cpp') diff --git a/commands/command_event.cpp b/commands/command_event.cpp new file mode 100644 index 000000000..ea35402e4 --- /dev/null +++ b/commands/command_event.cpp @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "command_event.h" +#include "core/dive.h" +#include "core/subsurface-qt/divelistnotifier.h" +#include "core/libdivecomputer.h" + +namespace Command { + +AddEventBase::AddEventBase(struct dive *dIn, int dcNrIn, struct event *ev) : + d(dIn), dcNr(dcNrIn), eventToAdd(ev) +{ +} + +bool AddEventBase::workToBeDone() +{ + return true; +} + +void AddEventBase::redo() +{ + struct divecomputer *dc = get_dive_dc(d, dcNr); + eventToRemove = eventToAdd.get(); + add_event_to_dc(dc, eventToAdd.release()); // return ownership to backend + invalidate_dive_cache(d); +} + +void AddEventBase::undo() +{ + struct divecomputer *dc = get_dive_dc(d, dcNr); + remove_event_from_dc(dc, eventToRemove); + eventToAdd.reset(eventToRemove); // take ownership of event + eventToRemove = nullptr; + invalidate_dive_cache(d); +} + +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")); +} + +} // namespace Command -- cgit v1.2.3-70-g09d2 From 7018783f64f664af97032da4ba4d357c05b52aaf Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 3 Mar 2020 22:42:51 +0100 Subject: undo: replot profile if event changed Add a DiveListNotifer::eventsChanged signal, which is emitted when the events changed. This is very coarse, at it doesn't differentiate between signal addition / editing / deletion. We might want to be finer in the future. Catch the signal in the profile-widget to replot the dive if this is the currently displayed dive. Reuse the cylindersChanged() slot, but rename it to the now more appropriate profileChanged(). Signed-off-by: Berthold Stoeger --- commands/command_event.cpp | 2 ++ core/subsurface-qt/divelistnotifier.h | 3 +++ profile-widget/profilewidget2.cpp | 5 +++-- profile-widget/profilewidget2.h | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) (limited to 'commands/command_event.cpp') diff --git a/commands/command_event.cpp b/commands/command_event.cpp index ea35402e4..d945fbb02 100644 --- a/commands/command_event.cpp +++ b/commands/command_event.cpp @@ -23,6 +23,7 @@ void AddEventBase::redo() eventToRemove = eventToAdd.get(); add_event_to_dc(dc, eventToAdd.release()); // return ownership to backend invalidate_dive_cache(d); + emit diveListNotifier.eventsChanged(d); } void AddEventBase::undo() @@ -32,6 +33,7 @@ void AddEventBase::undo() eventToAdd.reset(eventToRemove); // take ownership of event eventToRemove = nullptr; invalidate_dive_cache(d); + emit diveListNotifier.eventsChanged(d); } AddEventBookmark::AddEventBookmark(struct dive *d, int dcNr, int seconds) : diff --git a/core/subsurface-qt/divelistnotifier.h b/core/subsurface-qt/divelistnotifier.h index 2a72cdf49..63355fbcc 100644 --- a/core/subsurface-qt/divelistnotifier.h +++ b/core/subsurface-qt/divelistnotifier.h @@ -113,6 +113,9 @@ signals: void numShownChanged(); void filterReset(); + // Event-related signals. Currently, we're very blunt: only one signal for any changes to the events. + void eventsChanged(dive *d); + // This signal is emited every time a command is executed. // This is used to hide an old multi-dives-edited warning message. // This is necessary, so that the user can't click on the "undo" button and undo diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp index 67962b3bb..54881f71a 100644 --- a/profile-widget/profilewidget2.cpp +++ b/profile-widget/profilewidget2.cpp @@ -170,7 +170,8 @@ ProfileWidget2::ProfileWidget2(QWidget *parent) : QGraphicsView(parent), connect(DivePictureModel::instance(), &DivePictureModel::rowsInserted, this, &ProfileWidget2::plotPictures); connect(DivePictureModel::instance(), &DivePictureModel::picturesRemoved, this, &ProfileWidget2::removePictures); connect(DivePictureModel::instance(), &DivePictureModel::modelReset, this, &ProfileWidget2::plotPictures); - connect(&diveListNotifier, &DiveListNotifier::cylinderEdited, this, &ProfileWidget2::cylinderChanged); + connect(&diveListNotifier, &DiveListNotifier::cylinderEdited, this, &ProfileWidget2::profileChanged); + connect(&diveListNotifier, &DiveListNotifier::eventsChanged, this, &ProfileWidget2::profileChanged); #endif // SUBSURFACE_MOBILE #if !defined(QT_NO_DEBUG) && defined(SHOW_PLOT_INFO_TABLE) @@ -2189,7 +2190,7 @@ void ProfileWidget2::removePictures(const QVector &fileUrls) calculatePictureYPositions(); } -void ProfileWidget2::cylinderChanged(dive *d) +void ProfileWidget2::profileChanged(dive *d) { if (!d || d->id != displayed_dive.id) return; // Cylinders of a differnt dive than the shown one changed. diff --git a/profile-widget/profilewidget2.h b/profile-widget/profilewidget2.h index 0c1db3f62..4f60bf885 100644 --- a/profile-widget/profilewidget2.h +++ b/profile-widget/profilewidget2.h @@ -118,7 +118,7 @@ slots: // Necessary to call from QAction's signals. void pointInserted(const QModelIndex &parent, int start, int end); void pointsRemoved(const QModelIndex &, int start, int end); void updateThumbnail(QString filename, QImage thumbnail, duration_t duration); - void cylinderChanged(dive *d); + void profileChanged(dive *d); /* this is called for every move on the handlers. maybe we can speed up this a bit? */ void recreatePlannedDive(); -- cgit v1.2.3-70-g09d2 From 33fb6461fbe7e389081e77853e63638a1a343623 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 3 Mar 2020 22:54:54 +0100 Subject: undo: add undo command for dive-mode switch This basically copies the bookmark code, with the addition that the dive mode is recorded in the text of the undo command. Signed-off-by: Berthold Stoeger --- commands/command.cpp | 5 +++++ commands/command.h | 1 + commands/command_event.cpp | 7 +++++++ commands/command_event.h | 5 +++++ profile-widget/profilewidget2.cpp | 5 +---- 5 files changed, 19 insertions(+), 4 deletions(-) (limited to 'commands/command_event.cpp') diff --git a/commands/command.cpp b/commands/command.cpp index e9b4e0738..4f92038b9 100644 --- a/commands/command.cpp +++ b/commands/command.cpp @@ -334,4 +334,9 @@ 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)); +} + } // namespace Command diff --git a/commands/command.h b/commands/command.h index 88614b1e9..1644063a7 100644 --- a/commands/command.h +++ b/commands/command.h @@ -107,6 +107,7 @@ 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); } // namespace Command diff --git a/commands/command_event.cpp b/commands/command_event.cpp index d945fbb02..ddef502bc 100644 --- a/commands/command_event.cpp +++ b/commands/command_event.cpp @@ -4,6 +4,7 @@ #include "core/dive.h" #include "core/subsurface-qt/divelistnotifier.h" #include "core/libdivecomputer.h" +#include "core/gettextfromc.h" namespace Command { @@ -42,4 +43,10 @@ AddEventBookmark::AddEventBookmark(struct dive *d, int dcNr, int seconds) : 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]))); +} + } // namespace Command diff --git a/commands/command_event.h b/commands/command_event.h index 6e387d543..3130a128f 100644 --- a/commands/command_event.h +++ b/commands/command_event.h @@ -39,6 +39,11 @@ public: AddEventBookmark(struct dive *d, int dcNr, int seconds); }; +class AddEventDivemodeSwitch : public AddEventBase { +public: + AddEventDivemodeSwitch(struct dive *d, int dcNr, int seconds, int divemode); +}; + } // namespace Command #endif // COMMAND_EVENT_H diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp index 54881f71a..76822c196 100644 --- a/profile-widget/profilewidget2.cpp +++ b/profile-widget/profilewidget2.cpp @@ -1606,10 +1606,7 @@ void ProfileWidget2::addBookmark(int seconds) void ProfileWidget2::addDivemodeSwitch(int seconds, int divemode) { - add_event(current_dc, seconds, SAMPLE_EVENT_BOOKMARK, 0, divemode, QT_TRANSLATE_NOOP("gettextFromC", "modechange")); - invalidate_dive_cache(current_dive); - mark_divelist_changed(true); - replot(); + Command::addEventDivemodeSwitch(current_dive, dc_number, seconds, divemode); } void ProfileWidget2::addSetpointChange(int seconds) -- cgit v1.2.3-70-g09d2 From 1971cfad547792ba961f804605ccb12780e17de0 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 3 Mar 2020 23:31:46 +0100 Subject: undo: implement set point change undo command This is a simple copy of the other add-event commands. It could be made more friendly by stating the pO2 value in the text. Signed-off-by: Berthold Stoeger --- commands/command.cpp | 5 +++++ commands/command.h | 1 + commands/command_event.cpp | 6 ++++++ commands/command_event.h | 5 +++++ desktop-widgets/simplewidgets.cpp | 10 ++-------- 5 files changed, 19 insertions(+), 8 deletions(-) (limited to 'commands/command_event.cpp') diff --git a/commands/command.cpp b/commands/command.cpp index 4f92038b9..44eb4b8f0 100644 --- a/commands/command.cpp +++ b/commands/command.cpp @@ -339,4 +339,9 @@ 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)); +} + } // namespace Command diff --git a/commands/command.h b/commands/command.h index 1644063a7..aa7c75ab3 100644 --- a/commands/command.h +++ b/commands/command.h @@ -108,6 +108,7 @@ void editTripNotes(dive_trip *trip, const QString &s); 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); } // namespace Command diff --git a/commands/command_event.cpp b/commands/command_event.cpp index ddef502bc..a1e13f252 100644 --- a/commands/command_event.cpp +++ b/commands/command_event.cpp @@ -49,4 +49,10 @@ AddEventDivemodeSwitch::AddEventDivemodeSwitch(struct dive *d, int dcNr, int sec 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. +} + } // namespace Command diff --git a/commands/command_event.h b/commands/command_event.h index 3130a128f..8cceaeb25 100644 --- a/commands/command_event.h +++ b/commands/command_event.h @@ -44,6 +44,11 @@ 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); +}; + } // namespace Command #endif // COMMAND_EVENT_H diff --git a/desktop-widgets/simplewidgets.cpp b/desktop-widgets/simplewidgets.cpp index c31c93676..423f4b68f 100644 --- a/desktop-widgets/simplewidgets.cpp +++ b/desktop-widgets/simplewidgets.cpp @@ -23,7 +23,6 @@ #include "commands/command.h" #include "core/metadata.h" #include "core/tag.h" -#include "core/divelist.h" // for mark_divelist_changed double MinMaxAvgWidget::average() const { @@ -176,13 +175,8 @@ RenumberDialog::RenumberDialog(QWidget *parent) : QDialog(parent), selectedOnly( void SetpointDialog::buttonClicked(QAbstractButton *button) { - if (ui.buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) { - add_event(get_dive_dc(d, dcNr), time, SAMPLE_EVENT_PO2, 0, (int)(1000.0 * ui.spinbox->value()), - QT_TRANSLATE_NOOP("gettextFromC", "SP change")); - invalidate_dive_cache(current_dive); - } - mark_divelist_changed(true); - MainWindow::instance()->graphics->replot(); + if (ui.buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) + Command::addEventSetpointChange(d, dcNr, time, pressure_t { (int)(1000.0 * ui.spinbox->value()) }); } SetpointDialog::SetpointDialog(struct dive *dIn, int dcNrIn, int seconds) : QDialog(MainWindow::instance()), -- cgit v1.2.3-70-g09d2 From f9fe6d759f33c36f1c0c0d20a591f6517fc9071f Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 4 Mar 2020 17:35:02 +0100 Subject: undo: split out EventBase class All event-based commands will work on a dive computer and need to replot the profile, etc. Therefore, in analogy to the dive-list commands create a base class with two virtual functions undoit() and redoit() that must be defined in the derived classes that do the actual work. Signed-off-by: Berthold Stoeger --- commands/command_event.cpp | 31 +++++++++++++++++++++++-------- commands/command_event.h | 19 ++++++++++++++----- 2 files changed, 37 insertions(+), 13 deletions(-) (limited to 'commands/command_event.cpp') diff --git a/commands/command_event.cpp b/commands/command_event.cpp index a1e13f252..7ecd4f070 100644 --- a/commands/command_event.cpp +++ b/commands/command_event.cpp @@ -8,8 +8,27 @@ namespace Command { -AddEventBase::AddEventBase(struct dive *dIn, int dcNrIn, struct event *ev) : - d(dIn), dcNr(dcNrIn), eventToAdd(ev) +EventBase::EventBase(struct dive *dIn, int dcNrIn) : + d(dIn), dcNr(dcNrIn) +{ +} + +void EventBase::redo() +{ + redoit(); // Call actual function in base class + invalidate_dive_cache(d); + emit diveListNotifier.eventsChanged(d); +} + +void EventBase::undo() +{ + undoit(); // Call actual function in base class + invalidate_dive_cache(d); + emit diveListNotifier.eventsChanged(d); +} + +AddEventBase::AddEventBase(struct dive *d, int dcNr, struct event *ev) : EventBase(d, dcNr), + eventToAdd(ev) { } @@ -18,23 +37,19 @@ bool AddEventBase::workToBeDone() return true; } -void AddEventBase::redo() +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 - invalidate_dive_cache(d); - emit diveListNotifier.eventsChanged(d); } -void AddEventBase::undo() +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; - invalidate_dive_cache(d); - emit diveListNotifier.eventsChanged(d); } AddEventBookmark::AddEventBookmark(struct dive *d, int dcNr, int seconds) : diff --git a/commands/command_event.h b/commands/command_event.h index 8cceaeb25..a674e258b 100644 --- a/commands/command_event.h +++ b/commands/command_event.h @@ -16,19 +16,28 @@ namespace Command { // Therefore, the undo commands work on events as they do with dives: using // owning pointers. See comments in command_base.h -class AddEventBase : public Base { -public: - AddEventBase(struct dive *d, int dcNr, struct event *ev); // Takes ownership of event! -private: - bool workToBeDone() override; +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; +}; + +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 -- cgit v1.2.3-70-g09d2 From ab8e317b28672cc19fd04e994b9adf9b63f0c603 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 4 Mar 2020 18:13:02 +0100 Subject: undo: implement renaming of events There is a slight complexity here owing to the fact that the profile works on a copy of the current dive: We get a copy of the event and have to search for the original event in the current dive. This could be done in the undo command. Nevertheless, here we do it in the profile so that when in the future the profile can work on a non-copied dive we can simply remove this function. Signed-off-by: Berthold Stoeger --- commands/command.cpp | 5 +++++ commands/command.h | 1 + commands/command_event.cpp | 27 +++++++++++++++++++++++++++ commands/command_event.h | 12 ++++++++++++ core/dive.c | 20 +++++++++++++++++++- core/dive.h | 3 +++ profile-widget/profilewidget2.cpp | 29 ++++++++++++++++++++--------- 7 files changed, 87 insertions(+), 10 deletions(-) (limited to 'commands/command_event.cpp') diff --git a/commands/command.cpp b/commands/command.cpp index 44eb4b8f0..09e85f3b2 100644 --- a/commands/command.cpp +++ b/commands/command.cpp @@ -344,4 +344,9 @@ void addEventSetpointChange(struct dive *d, int dcNr, int seconds, pressure_t pO 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)); +} + } // namespace Command diff --git a/commands/command.h b/commands/command.h index aa7c75ab3..eeb400bc4 100644 --- a/commands/command.h +++ b/commands/command.h @@ -109,6 +109,7 @@ void editTripNotes(dive_trip *trip, const QString &s); 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); } // namespace Command diff --git a/commands/command_event.cpp b/commands/command_event.cpp index 7ecd4f070..1114182aa 100644 --- a/commands/command_event.cpp +++ b/commands/command_event.cpp @@ -70,4 +70,31 @@ AddEventSetpointChange::AddEventSetpointChange(struct dive *d, int dcNr, int sec 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(); +} + } // namespace Command diff --git a/commands/command_event.h b/commands/command_event.h index a674e258b..ddfe6f7d4 100644 --- a/commands/command_event.h +++ b/commands/command_event.h @@ -58,6 +58,18 @@ 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 +}; + } // namespace Command #endif // COMMAND_EVENT_H diff --git a/core/dive.c b/core/dive.c index 3ec9b1a82..6d2645cef 100644 --- a/core/dive.c +++ b/core/dive.c @@ -166,6 +166,11 @@ struct event *create_event(unsigned int time, int type, int flags, int value, co return ev; } +struct event *clone_event_rename(const struct event *ev, const char *name) +{ + return create_event(ev->time.seconds, ev->type, ev->flags, ev->value, name); +} + void add_event_to_dc(struct divecomputer *dc, struct event *ev) { struct event **p; @@ -192,7 +197,20 @@ struct event *add_event(struct divecomputer *dc, unsigned int time, int type, in return ev; } -static int same_event(const struct event *a, const struct event *b) +/* Substitutes an event in a divecomputer for another. No reordering is performed! */ +void swap_event(struct divecomputer *dc, struct event *from, struct event *to) +{ + for (struct event **ep = &dc->events; *ep; ep = &(*ep)->next) { + if (*ep == from) { + to->next = from->next; + *ep = to; + from->next = NULL; // For good measure. + break; + } + } +} + +bool same_event(const struct event *a, const struct event *b) { if (a->time.seconds != b->time.seconds) return 0; diff --git a/core/dive.h b/core/dive.h index 1de68f4ff..727d4bbff 100644 --- a/core/dive.h +++ b/core/dive.h @@ -378,7 +378,10 @@ extern bool is_cylinder_used(const struct dive *dive, int idx); extern bool is_cylinder_prot(const struct dive *dive, int idx); extern void add_gas_switch_event(struct dive *dive, struct divecomputer *dc, int time, int idx); extern struct event *create_event(unsigned int time, int type, int flags, int value, const char *name); +extern struct event *clone_event_rename(const struct event *ev, const char *name); extern void add_event_to_dc(struct divecomputer *dc, struct event *ev); +extern void swap_event(struct divecomputer *dc, struct event *from, struct event *to); +extern bool same_event(const struct event *a, const struct event *b); extern struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const char *name); extern void remove_event_from_dc(struct divecomputer *dc, struct event *event); extern void remove_event(const struct event *event); diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp index 76b3c9afb..824c4ccb6 100644 --- a/profile-widget/profilewidget2.cpp +++ b/profile-widget/profilewidget2.cpp @@ -1584,6 +1584,22 @@ void ProfileWidget2::unhideEvents() item->show(); } +// The profile displays a copy of the current_dive, namely displayed_dive. +// Therefore, the events we get are likewise copies. This function finds +// the original event. TODO: Remove function once the profile can display +// arbitrary dives. +static event *find_event(const struct event *ev) +{ + struct divecomputer *dc = current_dc; + if (!dc) + return nullptr; + for (struct event *act = current_dc->events; act; act = act->next) { + if (same_event(act, ev)) + return act; + } + return nullptr; +} + void ProfileWidget2::removeEvent(DiveEventItem *item) { struct event *event = item->getEvent(); @@ -1698,7 +1714,9 @@ double ProfileWidget2::getFontPrintScale() #ifndef SUBSURFACE_MOBILE void ProfileWidget2::editName(DiveEventItem *item) { - struct event *event = item->getEvent(); + struct event *event = find_event(item->getEvent()); + if (!event) + return; bool ok; QString newName = QInputDialog::getText(this, tr("Edit name of bookmark"), tr("Custom name:"), QLineEdit::Normal, @@ -1710,14 +1728,7 @@ void ProfileWidget2::editName(DiveEventItem *item) lengthWarning.exec(); return; } - // order is important! first update the current dive (by matching the unchanged event), - // then update the displayed dive (as event is part of the events on displayed dive - // and will be freed as part of changing the name! - update_event_name(current_dive, event, qPrintable(newName)); - update_event_name(&displayed_dive, event, qPrintable(newName)); - invalidate_dive_cache(current_dive); - mark_divelist_changed(true); - replot(); + Command::renameEvent(current_dive, dc_number, event, qPrintable(newName)); } } #endif -- cgit v1.2.3-70-g09d2 From 3d511b069f5e2c659ed5af039485b1c72c348b8a Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 4 Mar 2020 18:25:47 +0100 Subject: undo: add event removal undo command This was a trivial copy & past of the event-adding undo command with a switch of the undo() and redo() actions. Signed-off-by: Berthold Stoeger --- commands/command.cpp | 5 +++++ commands/command.h | 1 + commands/command_event.cpp | 26 ++++++++++++++++++++++++++ commands/command_event.h | 12 ++++++++++++ profile-widget/profilewidget2.cpp | 12 +++++------- 5 files changed, 49 insertions(+), 7 deletions(-) (limited to 'commands/command_event.cpp') diff --git a/commands/command.cpp b/commands/command.cpp index 09e85f3b2..54f9b22f9 100644 --- a/commands/command.cpp +++ b/commands/command.cpp @@ -349,4 +349,9 @@ 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)); +} + } // namespace Command diff --git a/commands/command.h b/commands/command.h index eeb400bc4..82931747c 100644 --- a/commands/command.h +++ b/commands/command.h @@ -110,6 +110,7 @@ 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); } // namespace Command diff --git a/commands/command_event.cpp b/commands/command_event.cpp index 1114182aa..e32edfc41 100644 --- a/commands/command_event.cpp +++ b/commands/command_event.cpp @@ -97,4 +97,30 @@ void RenameEvent::undoit() redoit(); } +RemoveEvent::RemoveEvent(struct dive *d, int dcNr, struct event *ev) : EventBase(d, dcNr), + eventToRemove(ev) +{ + 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 +} + } // namespace Command diff --git a/commands/command_event.h b/commands/command_event.h index ddfe6f7d4..eec5cb262 100644 --- a/commands/command_event.h +++ b/commands/command_event.h @@ -70,6 +70,18 @@ private: 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; + + OwningEventPtr eventToAdd; // for undo + event *eventToRemove; // for redo +}; + } // namespace Command #endif // COMMAND_EVENT_H diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp index 824c4ccb6..f236ed550 100644 --- a/profile-widget/profilewidget2.cpp +++ b/profile-widget/profilewidget2.cpp @@ -1602,17 +1602,15 @@ static event *find_event(const struct event *ev) void ProfileWidget2::removeEvent(DiveEventItem *item) { - struct event *event = item->getEvent(); + struct event *event = find_event(item->getEvent()); + if (!event) + return; if (QMessageBox::question(this, TITLE_OR_TEXT( tr("Remove the selected event?"), tr("%1 @ %2:%3").arg(event->name).arg(event->time.seconds / 60).arg(event->time.seconds % 60, 2, 10, QChar('0'))), - QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) { - remove_event(event); - invalidate_dive_cache(current_dive); - mark_divelist_changed(true); - replot(); - } + QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) + Command::removeEvent(current_dive, dc_number, event); } void ProfileWidget2::addBookmark(int seconds) -- cgit v1.2.3-70-g09d2 From 0bd821183d715f709d378e8ac1a75aa0c3d0f2cc Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 4 Mar 2020 21:10:05 +0100 Subject: undo: implement gas switch This is a bit hairy as - in theory - one gas switch can remove other gas switch(es) at the same timestamp. However, I did not find a way to test it. Moreover, it is not clear whether the dive-tabs are properly updated on undo/redo. Signed-off-by: Berthold Stoeger --- CHANGELOG.md | 1 + commands/command.cpp | 5 ++++ commands/command.h | 1 + commands/command_event.cpp | 62 +++++++++++++++++++++++++++++++++++++++ commands/command_event.h | 13 ++++++++ profile-widget/profilewidget2.cpp | 21 +------------ 6 files changed, 83 insertions(+), 20 deletions(-) (limited to 'commands/command_event.cpp') diff --git a/CHANGELOG.md b/CHANGELOG.md index 8888b1ab1..d3fefd712 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Mobile: make sure filter header and virtual keyboard are shown when tapping on f Mobile: fix issue where in some circumstances changes weren't written to storage Mobile: fix issues with filtering while editing dives Desktop: implement dive invalidation +Undo: implement undo of event handling (viz. bookmarks, setpoints, gas switches) Desktop: fix tab-order in filter widget Desktop: implement fulltext search Desktop: add starts-with and exact filter modes for textual search diff --git a/commands/command.cpp b/commands/command.cpp index 54f9b22f9..c173494c0 100644 --- a/commands/command.cpp +++ b/commands/command.cpp @@ -354,4 +354,9 @@ 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 diff --git a/commands/command.h b/commands/command.h index 82931747c..66122ac99 100644 --- a/commands/command.h +++ b/commands/command.h @@ -111,6 +111,7 @@ 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 diff --git a/commands/command_event.cpp b/commands/command_event.cpp index e32edfc41..6d217da65 100644 --- a/commands/command_event.cpp +++ b/commands/command_event.cpp @@ -5,6 +5,7 @@ #include "core/subsurface-qt/divelistnotifier.h" #include "core/libdivecomputer.h" #include "core/gettextfromc.h" +#include namespace Command { @@ -123,4 +124,65 @@ void RemoveEvent::undoit() add_event_to_dc(dc, eventToAdd.release()); // return ownership to backend } +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 newEventsToAdd; + std::vector 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{ 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 diff --git a/commands/command_event.h b/commands/command_event.h index eec5cb262..8e75dee6c 100644 --- a/commands/command_event.h +++ b/commands/command_event.h @@ -82,6 +82,19 @@ private: event *eventToRemove; // for redo }; +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 cylinders; // cylinders that are modified + std::vector eventsToAdd; + std::vector eventsToRemove; +}; + } // namespace Command #endif // COMMAND_EVENT_H diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp index f236ed550..6072d4c0d 100644 --- a/profile-widget/profilewidget2.cpp +++ b/profile-widget/profilewidget2.cpp @@ -1645,26 +1645,7 @@ void ProfileWidget2::changeGas(int tank, int seconds) if (!current_dive || tank < 0 || tank >= current_dive->cylinders.nr) return; - // if there is a gas change at this time stamp, remove it before adding the new one - struct event *gasChangeEvent = current_dc->events; - while ((gasChangeEvent = get_next_event_mutable(gasChangeEvent, "gaschange")) != NULL) { - if (gasChangeEvent->time.seconds == seconds) { - remove_event(gasChangeEvent); - gasChangeEvent = current_dc->events; - } else { - gasChangeEvent = gasChangeEvent->next; - } - } - add_gas_switch_event(current_dive, current_dc, seconds, tank); - // this means we potentially have a new tank that is being used and needs to be shown - fixup_dive(current_dive); - invalidate_dive_cache(current_dive); - - // FIXME - this no longer gets written to the dive list - so we need to enableEdition() here - - emit updateDiveInfo(); - mark_divelist_changed(true); - replot(); + Command::addGasSwitch(current_dive, dc_number, seconds, tank); } #endif -- cgit v1.2.3-70-g09d2 From e39063f6df269facaad1430229d8b330385f68ff Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Thu, 5 Mar 2020 23:10:44 +0100 Subject: undo: switch to affected dive on undo/redo of event-changes Select and make current the affected dive. And also switch to the divecomputer that was affected. Signed-off-by: Berthold Stoeger --- commands/command_event.cpp | 11 +++++++++-- commands/command_event.h | 2 ++ 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'commands/command_event.cpp') diff --git a/commands/command_event.cpp b/commands/command_event.cpp index 6d217da65..2b9176af5 100644 --- a/commands/command_event.cpp +++ b/commands/command_event.cpp @@ -2,6 +2,7 @@ #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" @@ -17,15 +18,21 @@ EventBase::EventBase(struct dive *dIn, int dcNrIn) : void EventBase::redo() { redoit(); // Call actual function in base class - invalidate_dive_cache(d); - emit diveListNotifier.eventsChanged(d); + 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), diff --git a/commands/command_event.h b/commands/command_event.h index 8e75dee6c..a363540d5 100644 --- a/commands/command_event.h +++ b/commands/command_event.h @@ -29,6 +29,8 @@ protected: // are probably not stable. struct dive *d; int dcNr; +private: + void updateDive(); }; class AddEventBase : public EventBase { -- cgit v1.2.3-70-g09d2 From 4ae87da58cda895f3573704966ef2e0dd8161864 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Fri, 6 Mar 2020 23:20:58 +0100 Subject: undo: reload dive on removal of gas-switch If a gas-switch is removed we have to perform the same action as if a gas-switch is added: fixup the dive and signal the changed cylinder and stats. Adapt the RemoveEvent command accordingly. Copy the code of the AddGasSwitch command and simplify for the fact that only ony cylinder can be affected. Signed-off-by: Berthold Stoeger --- commands/command_event.cpp | 17 ++++++++++++++++- commands/command_event.h | 2 ++ 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'commands/command_event.cpp') diff --git a/commands/command_event.cpp b/commands/command_event.cpp index 2b9176af5..391dfe0a9 100644 --- a/commands/command_event.cpp +++ b/commands/command_event.cpp @@ -106,7 +106,9 @@ void RenameEvent::undoit() } RemoveEvent::RemoveEvent(struct dive *d, int dcNr, struct event *ev) : EventBase(d, dcNr), - eventToRemove(ev) + 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)); } @@ -131,6 +133,19 @@ void RemoveEvent::undoit() 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{ 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. diff --git a/commands/command_event.h b/commands/command_event.h index a363540d5..b7e67f218 100644 --- a/commands/command_event.h +++ b/commands/command_event.h @@ -79,9 +79,11 @@ 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 { -- cgit v1.2.3-70-g09d2