summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--desktop-widgets/CMakeLists.txt2
-rw-r--r--desktop-widgets/command.cpp7
-rw-r--r--desktop-widgets/command.h5
-rw-r--r--desktop-widgets/command_edit.cpp93
-rw-r--r--desktop-widgets/command_edit.h58
-rw-r--r--desktop-widgets/tab-widgets/maintab.cpp23
-rw-r--r--desktop-widgets/tab-widgets/maintab.h1
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);