diff options
author | Berthold Stoeger <bstoeger@mail.tuwien.ac.at> | 2019-01-25 18:27:31 +0100 |
---|---|---|
committer | Dirk Hohndel <dirk@hohndel.org> | 2019-04-12 18:19:07 +0300 |
commit | 9e603cbe2bbcae1822859b284c39dd804bf321fb (patch) | |
tree | 9b4091c07aeea8b4abe65593ab07abc2c7326a52 /desktop-widgets | |
parent | 8858bfa1f8cf30cc4eb070fb6e709f7051e61241 (diff) | |
download | subsurface-9e603cbe2bbcae1822859b284c39dd804bf321fb.tar.gz |
Undo: implement rudimentary undo of dive-notes editing
Implement a first rudimentary dive-editing command. The main code
resides in a base class Command::Edit, which calls virtual functions
to read / set the fields and extract the field name.
Implement an example: editing of dive notes.
This dose not yet update the UI on undo / redo.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Diffstat (limited to 'desktop-widgets')
-rw-r--r-- | desktop-widgets/CMakeLists.txt | 2 | ||||
-rw-r--r-- | desktop-widgets/command.cpp | 7 | ||||
-rw-r--r-- | desktop-widgets/command.h | 5 | ||||
-rw-r--r-- | desktop-widgets/command_edit.cpp | 93 | ||||
-rw-r--r-- | desktop-widgets/command_edit.h | 58 | ||||
-rw-r--r-- | desktop-widgets/tab-widgets/maintab.cpp | 23 | ||||
-rw-r--r-- | desktop-widgets/tab-widgets/maintab.h | 1 |
7 files changed, 177 insertions, 12 deletions
diff --git a/desktop-widgets/CMakeLists.txt b/desktop-widgets/CMakeLists.txt index c451c46ef..2b1f07d1b 100644 --- a/desktop-widgets/CMakeLists.txt +++ b/desktop-widgets/CMakeLists.txt @@ -64,6 +64,8 @@ set(SUBSURFACE_INTERFACE command_divelist.h command_divesite.cpp command_divesite.h + command_edit.cpp + command_edit.h configuredivecomputerdialog.cpp configuredivecomputerdialog.h divecomputermanagementdialog.cpp diff --git a/desktop-widgets/command.cpp b/desktop-widgets/command.cpp index fc7478f94..7336a5b62 100644 --- a/desktop-widgets/command.cpp +++ b/desktop-widgets/command.cpp @@ -3,6 +3,7 @@ #include "command.h" #include "command_divelist.h" #include "command_divesite.h" +#include "command_edit.h" namespace Command { @@ -128,4 +129,10 @@ void purgeUnusedDiveSites() execute(new PurgeUnusedDiveSites); } +// Dive editing related commands +void editNotes(const QVector<dive *> dives, const QString &newValue, const QString &oldValue) +{ + execute(new EditNotes(dives, newValue, oldValue)); +} + } // namespace Command diff --git a/desktop-widgets/command.h b/desktop-widgets/command.h index 134bb0147..4c31baa6e 100644 --- a/desktop-widgets/command.h +++ b/desktop-widgets/command.h @@ -10,7 +10,6 @@ namespace Command { // 1) General commands - void clear(); // Reset the undo stack. Delete all commands. QAction *undoAction(QObject *parent); // Create an undo action. QAction *redoAction(QObject *parent); // Create an redo action. @@ -51,6 +50,10 @@ void addDiveSite(const QString &name); void mergeDiveSites(dive_site *ds, const QVector<dive_site *> &sites); void purgeUnusedDiveSites(); +// 4) Dive editing related commands + +void editNotes(const QVector<dive *> dives, const QString &newValue, const QString &oldValue); + } // namespace Command #endif // COMMAND_H diff --git a/desktop-widgets/command_edit.cpp b/desktop-widgets/command_edit.cpp new file mode 100644 index 000000000..f39f9339d --- /dev/null +++ b/desktop-widgets/command_edit.cpp @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "command_edit.h" +#include "core/divelist.h" + +namespace Command { + +template<typename T> +EditBase<T>::EditBase(const QVector<dive *> &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<typename T> +bool EditBase<T>::workToBeDone() +{ + std::vector<dive *> 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<typename T> +void EditBase<T>::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); + + 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<QString>::EditBase(const QVector<dive *> &dives, QString oldValue, QString newValue); + +// Undo and redo do the same as just the stored value is exchanged +template<typename T> +void EditBase<T>::redo() +{ + undo(); +} + +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"); +} + +} // namespace Command diff --git a/desktop-widgets/command_edit.h b/desktop-widgets/command_edit.h new file mode 100644 index 000000000..0bcb8114c --- /dev/null +++ b/desktop-widgets/command_edit.h @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0 +// Note: this header file is used by the undo-machinery and should not be included elsewhere. + +#ifndef COMMAND_EDIT_H +#define COMMAND_EDIT_H + +#include "command_base.h" + +#include <QVector> + +// These are commands that edit individual fields of a set of dives. +// The implementation is very OO-style. Out-of-fashion and certainly +// not elegant, but in line with Qt's OO-based design. +// The actual code is in a common base class "Command::EditBase". To +// read and set the fields, the base class calls virtual functions of +// the derived classes. +// +// To deal with different data types, the base class is implemented +// as a template. The template parameter is the type to be read or +// set. Thus, switch-cascades and union trickery can be avoided. + +// We put everything in a namespace, so that we can shorten names without polluting the global namespace +namespace Command { + +template <typename T> +class EditBase : public Base { + T value; // Value to be set + T old; // Previous value + + void undo() override; + void redo() override; + bool workToBeDone() override; + + // Dives to be edited. For historical reasons, the *last* entry was + // the active dive when the user initialized the action. This dive + // will be made the current dive on redo / undo. + std::vector<dive *> dives; +public: + EditBase(const QVector<dive *> &dives, T newValue, T oldValue); + +protected: + // Get and set functions to be overriden by sub-classes. + virtual void set(struct dive *d, T) const = 0; + virtual T data(struct dive *d) const = 0; + virtual QString fieldName() const = 0; // Name of the field, used to create the undo menu-entry +}; + +class EditNotes : public EditBase<QString> { +public: + using EditBase<QString>::EditBase; // Use constructor of base class. + void set(struct dive *d, QString s) const override; + QString data(struct dive *d) const override; + QString fieldName() const override; +}; + +} // namespace Command + +#endif diff --git a/desktop-widgets/tab-widgets/maintab.cpp b/desktop-widgets/tab-widgets/maintab.cpp index a077ad2a2..ab2eabd4c 100644 --- a/desktop-widgets/tab-widgets/maintab.cpp +++ b/desktop-widgets/tab-widgets/maintab.cpp @@ -767,8 +767,6 @@ void MainTab::acceptChanges() // were identical with the master dive shown (and mark the divelist as changed) if (!same_string(displayed_dive.suit, cd->suit)) MODIFY_DIVES(selectedDives, EDIT_TEXT(suit)); - if (!same_string(displayed_dive.notes, cd->notes)) - MODIFY_DIVES(selectedDives, EDIT_TEXT(notes)); if (displayed_dive.rating != cd->rating) MODIFY_DIVES(selectedDives, EDIT_VALUE(rating)); if (displayed_dive.visibility != cd->visibility) @@ -1343,16 +1341,19 @@ void MainTab::on_notes_textChanged() return; free(displayedTrip.notes); displayedTrip.notes = copy_qstring(ui.notes->toPlainText()); - } else { - if (same_string(displayed_dive.notes, qPrintable(ui.notes->toPlainText()))) - return; - free(displayed_dive.notes); - if (ui.notes->toHtml().indexOf("<div") != -1) - displayed_dive.notes = copy_qstring(ui.notes->toHtml()); - else - displayed_dive.notes = copy_qstring(ui.notes->toPlainText()); + markChangedWidget(ui.notes); } - markChangedWidget(ui.notes); +} + +void MainTab::on_notes_editingFinished() +{ + if (currentTrip || !current_dive) + return; // Trip-note editing is done via on_notes_textChanged() + + QString notes = ui.notes->toHtml().indexOf("<div") != -1 ? + ui.notes->toHtml() : ui.notes->toPlainText(); + + Command::editNotes(getSelectedDivesCurrentLast(), notes, QString(current_dive->notes)); } void MainTab::on_rating_valueChanged(int value) diff --git a/desktop-widgets/tab-widgets/maintab.h b/desktop-widgets/tab-widgets/maintab.h index 094f3d053..00b94d1a0 100644 --- a/desktop-widgets/tab-widgets/maintab.h +++ b/desktop-widgets/tab-widgets/maintab.h @@ -75,6 +75,7 @@ slots: void on_suit_textChanged(const QString &text); void on_diveTripLocation_textEdited(const QString& text); void on_notes_textChanged(); + void on_notes_editingFinished(); void on_airtemp_textChanged(const QString &text); void on_duration_textChanged(const QString &text); void on_depth_textChanged(const QString &text); |