2017-04-27 18:26:05 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2015-02-11 06:28:43 +00:00
|
|
|
#ifndef UNDOCOMMANDS_H
|
|
|
|
#define UNDOCOMMANDS_H
|
|
|
|
|
Undo: fix multi-level undo of delete-dive and remove-dive-from-trip
The original undo-code was fundamentally broken. Not only did it leak
resources (copied trips were never freed), it also kept references
to trips or dives that could be changed by other commands. Thus,
anything more than a single undo could lead to crashes.
Two ways of fixing this were considered
1) Don't store pointers, but unique dive-ids and trip-ids.
Whereas such unique ids exist for dives, they would have to be
implemented for trips.
2) Don't free objects in the backend.
Instead, take ownership of deleted objects in the undo-object.
Thus, all references in previous undo-objects are guaranteed to
still exist (unless the objects are deleted elsewhere).
After some contemplation, the second method was chosen, because
it is significantly less intrusive. While touching the undo-objects,
clearly separate backend from ui-code, such that they can ultimately
be reused for mobile.
Note that if other parts of the code delete dives, crashes can still
be provoked. Notable examples are split/merge dives. These will have
to be fixed later. Nevertheless, the new code is a significant
improvement over the old state.
While touching the code, implement proper translation string based
on Qt's plural-feature (using %n).
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2018-07-19 12:44:27 +00:00
|
|
|
#include "core/dive.h"
|
|
|
|
|
2015-02-11 06:28:43 +00:00
|
|
|
#include <QUndoCommand>
|
Undo: fix multi-level undo of delete-dive and remove-dive-from-trip
The original undo-code was fundamentally broken. Not only did it leak
resources (copied trips were never freed), it also kept references
to trips or dives that could be changed by other commands. Thus,
anything more than a single undo could lead to crashes.
Two ways of fixing this were considered
1) Don't store pointers, but unique dive-ids and trip-ids.
Whereas such unique ids exist for dives, they would have to be
implemented for trips.
2) Don't free objects in the backend.
Instead, take ownership of deleted objects in the undo-object.
Thus, all references in previous undo-objects are guaranteed to
still exist (unless the objects are deleted elsewhere).
After some contemplation, the second method was chosen, because
it is significantly less intrusive. While touching the undo-objects,
clearly separate backend from ui-code, such that they can ultimately
be reused for mobile.
Note that if other parts of the code delete dives, crashes can still
be provoked. Notable examples are split/merge dives. These will have
to be fixed later. Nevertheless, the new code is a significant
improvement over the old state.
While touching the code, implement proper translation string based
on Qt's plural-feature (using %n).
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2018-07-19 12:44:27 +00:00
|
|
|
#include <QCoreApplication> // For Q_DECLARE_TR_FUNCTIONS
|
|
|
|
#include <QVector>
|
|
|
|
#include <memory>
|
|
|
|
|
|
|
|
// The classes declared in this file represent units-of-work, which can be exectuted / undone
|
|
|
|
// repeatedly. The command objects are collected in a linear list implemented in the QUndoStack class.
|
|
|
|
// They contain the information that is necessary to either perform or undo the unit-of-work.
|
|
|
|
// The usage is:
|
|
|
|
// constructor: generate information that is needed for executing the unit-of-work
|
|
|
|
// redo(): performs the unit-of-work and generates the information that is needed for undo()
|
|
|
|
// undo(): undos the unit-of-work and regenerates the initial information needed in redo()
|
|
|
|
// The needed information is mostly kept in pointers to dives and/or trips, which have to be added
|
|
|
|
// or removed.
|
|
|
|
// For this to work it is crucial that
|
|
|
|
// 1) Pointers to dives and trips remain valid as long as referencing command-objects exist.
|
|
|
|
// 2) The dive-table is not resorted, because dives are inserted at given indices.
|
|
|
|
//
|
|
|
|
// Thus, if a command deletes a dive or a trip, the actual object must not be deleted. Instead,
|
|
|
|
// the command object removes pointers to the dive/trip object from the backend and takes ownership.
|
|
|
|
// To reverse such a deletion, the object is re-injected into the backend and ownership is given up.
|
|
|
|
// Once ownership of a dive is taken, any reference to it was removed from the backend. Thus,
|
|
|
|
// subsequent redo()/undo() actions cannot access this object and integrity of the data is ensured.
|
|
|
|
//
|
|
|
|
// As an example, consider the following course of events: Dive 1 is renumbered and deleted, dive 2
|
|
|
|
// is added and renumbered. The undo list looks like this (---> non-owning, ***> owning pointers,
|
|
|
|
// ===> next item in list)
|
|
|
|
//
|
|
|
|
// Undo-List
|
|
|
|
// +-----------------+ +---------------+ +------------+ +-----------------+
|
|
|
|
// | Renumber dive 1 |====>| Delete dive 1 |====>| Add dive 2 |====>| Renumber dive 2 |
|
|
|
|
// +------------------ +---------------+ +------------+ +-----------------+
|
|
|
|
// | * | |
|
|
|
|
// | +--------+ * | +--------+ |
|
|
|
|
// +----->| Dive 1 |<****** +--->| Dive 2 |<------+
|
|
|
|
// +--------+ +--------+
|
|
|
|
// ^
|
|
|
|
// +---------+ *
|
|
|
|
// | Backend |****************
|
|
|
|
// +---------+
|
|
|
|
// Two points of note:
|
|
|
|
// 1) Every dive is owned by either the backend or exactly one command object.
|
|
|
|
// 2) All references to dive 1 are *before* the owner "delete dive 2", thus the pointer is always valid.
|
|
|
|
// 3) References by the backend are *always* owning.
|
|
|
|
//
|
|
|
|
// The user undos the last two commands. The situation now looks like this:
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// Undo-List Redo-List
|
|
|
|
// +-----------------+ +---------------+ +------------+ +-----------------+
|
|
|
|
// | Renumber dive 1 |====>| Delete dive 1 | | Add dive 2 |<====| Renumber dive 2 |
|
|
|
|
// +------------------ +---------------+ +------------+ +-----------------+
|
|
|
|
// | * * |
|
|
|
|
// | +--------+ * * +--------+ |
|
|
|
|
// +----->| Dive 1 |<****** ****>| Dive 2 |<------+
|
|
|
|
// +--------+ +--------+
|
|
|
|
//
|
|
|
|
// +---------+
|
|
|
|
// | Backend |
|
|
|
|
// +---------+
|
|
|
|
// Again:
|
|
|
|
// 1) Every dive is owned by either the backend (here none) or exactly one command object.
|
|
|
|
// 2) All references to dive 1 are *before* the owner "delete dive 1", thus the pointer is always valid.
|
|
|
|
// 3) All references to dive 2 are *after* the owner "add dive 2", thus the pointer is always valid.
|
|
|
|
//
|
|
|
|
// The user undos one more command:
|
|
|
|
//
|
|
|
|
// Undo-List Redo-List
|
|
|
|
// +-----------------+ +---------------+ +------------+ +-----------------+
|
|
|
|
// | Renumber dive 1 | | Delete dive 1 |<====| Add dive 2 |<====| Renumber dive 2 |
|
|
|
|
// +------------------ +---------------+ +------------+ +-----------------+
|
|
|
|
// | | * |
|
|
|
|
// | +--------+ | * +--------+ |
|
|
|
|
// +----->| Dive 1 |<-----+ ****>| Dive 2 |<------+
|
|
|
|
// +--------+ +--------+
|
|
|
|
// ^
|
|
|
|
// * +---------+
|
|
|
|
// ***************| Backend |
|
|
|
|
// +---------+
|
|
|
|
// Same points as above.
|
|
|
|
// The user now adds a dive 3. The redo list will be deleted:
|
|
|
|
//
|
|
|
|
// Undo-List
|
|
|
|
// +-----------------+ +------------+
|
|
|
|
// | Renumber dive 1 |=============================================>| Add dive 3 |
|
|
|
|
// +------------------ +------------+
|
|
|
|
// | |
|
|
|
|
// | +--------+ +--------+ |
|
|
|
|
// +----->| Dive 1 | | Dive 3 |<---+
|
|
|
|
// +--------+ +--------+
|
|
|
|
// ^ ^
|
|
|
|
// * +---------+ *
|
|
|
|
// ***************| Backend |****************
|
|
|
|
// +---------+
|
|
|
|
// Note:
|
|
|
|
// 1) Dive 2 was deleted with the "add dive 2" command, because that was the owner.
|
|
|
|
// 2) Dive 1 was not deleted, because it is owned by the backend.
|
|
|
|
//
|
|
|
|
// To take ownership of dives/trips, the OnwingDivePtr and OwningTripPtr types are used. These
|
|
|
|
// are simply derived from std::unique_ptr and therefore use well-established semantics.
|
|
|
|
// Expressed in C-terms: std::unique_ptr<T> is exactly the same as T* with the following
|
|
|
|
// twists:
|
|
|
|
// 1) default-initialized to NULL.
|
|
|
|
// 2) if it goes out of scope (local scope or containing object destroyed), it does:
|
|
|
|
// if (ptr) free_function(ptr);
|
|
|
|
// whereby free_function can be configured (defaults to delete ptr).
|
|
|
|
// 3) assignment between two std::unique_ptr<T> compiles only if the source is reset (to NULL).
|
|
|
|
// (hence the name - there's a *unique* owner).
|
|
|
|
// While this sounds trivial, experience shows that this distinctly simplifies memory-management
|
|
|
|
// (it's not necessary to manually delete all vector items in the destructur, etc).
|
|
|
|
// Note that Qt's own implementation (QScoperPointer) is not up to the job, because it doesn't implement
|
|
|
|
// move-semantics and Qt's containers are incompatible, owing to COW semantics.
|
|
|
|
//
|
|
|
|
// Usage:
|
|
|
|
// OwningDivePtr dPtr; // Initialize to null-state: not owning any dive.
|
|
|
|
// OwningDivePtr dPtr(dive); // Take ownership of dive (which is of type struct dive *).
|
|
|
|
// // If dPtr goes out of scope, the dive will be freed with free_dive().
|
|
|
|
// struct dive *d = dPtr.release(); // Give up ownership of dive. dPtr is reset to null.
|
|
|
|
// struct dive *d = d.get(); // Get pointer dive, but don't release ownership.
|
|
|
|
// dPtr.reset(dive2); // Delete currently owned dive with free_dive() and get ownership of dive2.
|
|
|
|
// dPtr.reset(); // Delete currently owned dive and reset to null.
|
|
|
|
// dPtr2 = dPtr1; // Fails to compile.
|
|
|
|
// dPtr2 = std::move(dPtr1); // dPtr2 takes ownership, dPtr1 is reset to null.
|
|
|
|
// OwningDivePtr fun();
|
|
|
|
// dPtr1 = fun(); // Compiles. Simply put: the compiler knows that the result of fun() will
|
|
|
|
// // be trashed and therefore can be moved-from.
|
|
|
|
// std::vector<OwningDivePtr> v: // Define an empty vector of owning pointers.
|
|
|
|
// v.emplace_back(dive); // Take ownership of dive and add at end of vector
|
|
|
|
// // If the vector goes out of scope, all dives will be freed with free_dive().
|
|
|
|
// v.clear(v); // Reset the vector to zero length. If the elements weren't release()d,
|
|
|
|
// // the pointed-to dives are freed with free_dive()
|
|
|
|
|
|
|
|
// Classes used to automatically call free_dive()/free_trip for owning pointers that go out of scope.
|
|
|
|
struct DiveDeleter {
|
|
|
|
void operator()(dive *d) { free_dive(d); }
|
|
|
|
};
|
|
|
|
struct TripDeleter {
|
|
|
|
void operator()(dive_trip *t) { free_trip(t); }
|
|
|
|
};
|
|
|
|
|
|
|
|
// Owning pointers to dive and dive_trip objects.
|
|
|
|
typedef std::unique_ptr<dive, DiveDeleter> OwningDivePtr;
|
|
|
|
typedef std::unique_ptr<dive_trip, TripDeleter> OwningTripPtr;
|
2015-02-11 06:28:43 +00:00
|
|
|
|
2018-07-19 20:35:25 +00:00
|
|
|
// This helper structure describes a dive that we want to add.
|
|
|
|
// Potentially it also adds a trip (if deletion of the dive resulted in deletion of the trip)
|
|
|
|
struct DiveToAdd {
|
|
|
|
OwningDivePtr dive; // Dive to add
|
|
|
|
OwningTripPtr tripToAdd; // Not-null if we also have to add a dive
|
|
|
|
dive_trip *trip; // Trip the dive belongs to, may be null
|
|
|
|
int idx; // Position in divelist
|
|
|
|
};
|
|
|
|
|
|
|
|
class UndoAddDive : public QUndoCommand {
|
|
|
|
public:
|
|
|
|
UndoAddDive(dive *dive); // Warning: old dive will be erased (moved in C++-speak)!
|
|
|
|
private:
|
|
|
|
void undo() override;
|
|
|
|
void redo() override;
|
|
|
|
|
|
|
|
// For redo
|
|
|
|
DiveToAdd diveToAdd;
|
|
|
|
|
|
|
|
// For undo
|
|
|
|
dive *diveToRemove;
|
|
|
|
};
|
|
|
|
|
2015-02-11 06:28:43 +00:00
|
|
|
class UndoDeleteDive : public QUndoCommand {
|
Undo: fix multi-level undo of delete-dive and remove-dive-from-trip
The original undo-code was fundamentally broken. Not only did it leak
resources (copied trips were never freed), it also kept references
to trips or dives that could be changed by other commands. Thus,
anything more than a single undo could lead to crashes.
Two ways of fixing this were considered
1) Don't store pointers, but unique dive-ids and trip-ids.
Whereas such unique ids exist for dives, they would have to be
implemented for trips.
2) Don't free objects in the backend.
Instead, take ownership of deleted objects in the undo-object.
Thus, all references in previous undo-objects are guaranteed to
still exist (unless the objects are deleted elsewhere).
After some contemplation, the second method was chosen, because
it is significantly less intrusive. While touching the undo-objects,
clearly separate backend from ui-code, such that they can ultimately
be reused for mobile.
Note that if other parts of the code delete dives, crashes can still
be provoked. Notable examples are split/merge dives. These will have
to be fixed later. Nevertheless, the new code is a significant
improvement over the old state.
While touching the code, implement proper translation string based
on Qt's plural-feature (using %n).
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2018-07-19 12:44:27 +00:00
|
|
|
Q_DECLARE_TR_FUNCTIONS(Command)
|
2015-02-11 06:28:43 +00:00
|
|
|
public:
|
Undo: fix multi-level undo of delete-dive and remove-dive-from-trip
The original undo-code was fundamentally broken. Not only did it leak
resources (copied trips were never freed), it also kept references
to trips or dives that could be changed by other commands. Thus,
anything more than a single undo could lead to crashes.
Two ways of fixing this were considered
1) Don't store pointers, but unique dive-ids and trip-ids.
Whereas such unique ids exist for dives, they would have to be
implemented for trips.
2) Don't free objects in the backend.
Instead, take ownership of deleted objects in the undo-object.
Thus, all references in previous undo-objects are guaranteed to
still exist (unless the objects are deleted elsewhere).
After some contemplation, the second method was chosen, because
it is significantly less intrusive. While touching the undo-objects,
clearly separate backend from ui-code, such that they can ultimately
be reused for mobile.
Note that if other parts of the code delete dives, crashes can still
be provoked. Notable examples are split/merge dives. These will have
to be fixed later. Nevertheless, the new code is a significant
improvement over the old state.
While touching the code, implement proper translation string based
on Qt's plural-feature (using %n).
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2018-07-19 12:44:27 +00:00
|
|
|
UndoDeleteDive(const QVector<dive *> &divesToDelete);
|
|
|
|
private:
|
2018-09-29 20:13:44 +00:00
|
|
|
void undo() override;
|
|
|
|
void redo() override;
|
2015-02-11 06:28:43 +00:00
|
|
|
|
Undo: fix multi-level undo of delete-dive and remove-dive-from-trip
The original undo-code was fundamentally broken. Not only did it leak
resources (copied trips were never freed), it also kept references
to trips or dives that could be changed by other commands. Thus,
anything more than a single undo could lead to crashes.
Two ways of fixing this were considered
1) Don't store pointers, but unique dive-ids and trip-ids.
Whereas such unique ids exist for dives, they would have to be
implemented for trips.
2) Don't free objects in the backend.
Instead, take ownership of deleted objects in the undo-object.
Thus, all references in previous undo-objects are guaranteed to
still exist (unless the objects are deleted elsewhere).
After some contemplation, the second method was chosen, because
it is significantly less intrusive. While touching the undo-objects,
clearly separate backend from ui-code, such that they can ultimately
be reused for mobile.
Note that if other parts of the code delete dives, crashes can still
be provoked. Notable examples are split/merge dives. These will have
to be fixed later. Nevertheless, the new code is a significant
improvement over the old state.
While touching the code, implement proper translation string based
on Qt's plural-feature (using %n).
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2018-07-19 12:44:27 +00:00
|
|
|
// For redo
|
2018-07-21 16:28:33 +00:00
|
|
|
std::vector<struct dive*> divesToDelete;
|
Undo: fix multi-level undo of delete-dive and remove-dive-from-trip
The original undo-code was fundamentally broken. Not only did it leak
resources (copied trips were never freed), it also kept references
to trips or dives that could be changed by other commands. Thus,
anything more than a single undo could lead to crashes.
Two ways of fixing this were considered
1) Don't store pointers, but unique dive-ids and trip-ids.
Whereas such unique ids exist for dives, they would have to be
implemented for trips.
2) Don't free objects in the backend.
Instead, take ownership of deleted objects in the undo-object.
Thus, all references in previous undo-objects are guaranteed to
still exist (unless the objects are deleted elsewhere).
After some contemplation, the second method was chosen, because
it is significantly less intrusive. While touching the undo-objects,
clearly separate backend from ui-code, such that they can ultimately
be reused for mobile.
Note that if other parts of the code delete dives, crashes can still
be provoked. Notable examples are split/merge dives. These will have
to be fixed later. Nevertheless, the new code is a significant
improvement over the old state.
While touching the code, implement proper translation string based
on Qt's plural-feature (using %n).
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2018-07-19 12:44:27 +00:00
|
|
|
|
|
|
|
std::vector<OwningTripPtr> tripsToAdd;
|
|
|
|
std::vector<DiveToAdd> divesToAdd;
|
2015-02-11 06:28:43 +00:00
|
|
|
};
|
|
|
|
|
2015-02-14 17:12:05 +00:00
|
|
|
class UndoShiftTime : public QUndoCommand {
|
Undo: fix multi-level undo of delete-dive and remove-dive-from-trip
The original undo-code was fundamentally broken. Not only did it leak
resources (copied trips were never freed), it also kept references
to trips or dives that could be changed by other commands. Thus,
anything more than a single undo could lead to crashes.
Two ways of fixing this were considered
1) Don't store pointers, but unique dive-ids and trip-ids.
Whereas such unique ids exist for dives, they would have to be
implemented for trips.
2) Don't free objects in the backend.
Instead, take ownership of deleted objects in the undo-object.
Thus, all references in previous undo-objects are guaranteed to
still exist (unless the objects are deleted elsewhere).
After some contemplation, the second method was chosen, because
it is significantly less intrusive. While touching the undo-objects,
clearly separate backend from ui-code, such that they can ultimately
be reused for mobile.
Note that if other parts of the code delete dives, crashes can still
be provoked. Notable examples are split/merge dives. These will have
to be fixed later. Nevertheless, the new code is a significant
improvement over the old state.
While touching the code, implement proper translation string based
on Qt's plural-feature (using %n).
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2018-07-19 12:44:27 +00:00
|
|
|
Q_DECLARE_TR_FUNCTIONS(Command)
|
2015-02-14 17:12:05 +00:00
|
|
|
public:
|
2018-07-22 07:23:47 +00:00
|
|
|
UndoShiftTime(const QVector<dive *> &changedDives, int amount);
|
Undo: fix multi-level undo of delete-dive and remove-dive-from-trip
The original undo-code was fundamentally broken. Not only did it leak
resources (copied trips were never freed), it also kept references
to trips or dives that could be changed by other commands. Thus,
anything more than a single undo could lead to crashes.
Two ways of fixing this were considered
1) Don't store pointers, but unique dive-ids and trip-ids.
Whereas such unique ids exist for dives, they would have to be
implemented for trips.
2) Don't free objects in the backend.
Instead, take ownership of deleted objects in the undo-object.
Thus, all references in previous undo-objects are guaranteed to
still exist (unless the objects are deleted elsewhere).
After some contemplation, the second method was chosen, because
it is significantly less intrusive. While touching the undo-objects,
clearly separate backend from ui-code, such that they can ultimately
be reused for mobile.
Note that if other parts of the code delete dives, crashes can still
be provoked. Notable examples are split/merge dives. These will have
to be fixed later. Nevertheless, the new code is a significant
improvement over the old state.
While touching the code, implement proper translation string based
on Qt's plural-feature (using %n).
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2018-07-19 12:44:27 +00:00
|
|
|
private:
|
2018-09-29 20:13:44 +00:00
|
|
|
void undo() override;
|
|
|
|
void redo() override;
|
2015-02-14 17:12:05 +00:00
|
|
|
|
Undo: fix multi-level undo of delete-dive and remove-dive-from-trip
The original undo-code was fundamentally broken. Not only did it leak
resources (copied trips were never freed), it also kept references
to trips or dives that could be changed by other commands. Thus,
anything more than a single undo could lead to crashes.
Two ways of fixing this were considered
1) Don't store pointers, but unique dive-ids and trip-ids.
Whereas such unique ids exist for dives, they would have to be
implemented for trips.
2) Don't free objects in the backend.
Instead, take ownership of deleted objects in the undo-object.
Thus, all references in previous undo-objects are guaranteed to
still exist (unless the objects are deleted elsewhere).
After some contemplation, the second method was chosen, because
it is significantly less intrusive. While touching the undo-objects,
clearly separate backend from ui-code, such that they can ultimately
be reused for mobile.
Note that if other parts of the code delete dives, crashes can still
be provoked. Notable examples are split/merge dives. These will have
to be fixed later. Nevertheless, the new code is a significant
improvement over the old state.
While touching the code, implement proper translation string based
on Qt's plural-feature (using %n).
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2018-07-19 12:44:27 +00:00
|
|
|
// For redo and undo
|
2018-07-22 07:23:47 +00:00
|
|
|
QVector<dive *> diveList;
|
2015-02-14 17:12:05 +00:00
|
|
|
int timeChanged;
|
|
|
|
};
|
|
|
|
|
2015-02-28 04:42:37 +00:00
|
|
|
class UndoRenumberDives : public QUndoCommand {
|
Undo: fix multi-level undo of delete-dive and remove-dive-from-trip
The original undo-code was fundamentally broken. Not only did it leak
resources (copied trips were never freed), it also kept references
to trips or dives that could be changed by other commands. Thus,
anything more than a single undo could lead to crashes.
Two ways of fixing this were considered
1) Don't store pointers, but unique dive-ids and trip-ids.
Whereas such unique ids exist for dives, they would have to be
implemented for trips.
2) Don't free objects in the backend.
Instead, take ownership of deleted objects in the undo-object.
Thus, all references in previous undo-objects are guaranteed to
still exist (unless the objects are deleted elsewhere).
After some contemplation, the second method was chosen, because
it is significantly less intrusive. While touching the undo-objects,
clearly separate backend from ui-code, such that they can ultimately
be reused for mobile.
Note that if other parts of the code delete dives, crashes can still
be provoked. Notable examples are split/merge dives. These will have
to be fixed later. Nevertheless, the new code is a significant
improvement over the old state.
While touching the code, implement proper translation string based
on Qt's plural-feature (using %n).
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2018-07-19 12:44:27 +00:00
|
|
|
Q_DECLARE_TR_FUNCTIONS(Command)
|
2015-02-28 04:42:37 +00:00
|
|
|
public:
|
Undo: fix multi-level undo of delete-dive and remove-dive-from-trip
The original undo-code was fundamentally broken. Not only did it leak
resources (copied trips were never freed), it also kept references
to trips or dives that could be changed by other commands. Thus,
anything more than a single undo could lead to crashes.
Two ways of fixing this were considered
1) Don't store pointers, but unique dive-ids and trip-ids.
Whereas such unique ids exist for dives, they would have to be
implemented for trips.
2) Don't free objects in the backend.
Instead, take ownership of deleted objects in the undo-object.
Thus, all references in previous undo-objects are guaranteed to
still exist (unless the objects are deleted elsewhere).
After some contemplation, the second method was chosen, because
it is significantly less intrusive. While touching the undo-objects,
clearly separate backend from ui-code, such that they can ultimately
be reused for mobile.
Note that if other parts of the code delete dives, crashes can still
be provoked. Notable examples are split/merge dives. These will have
to be fixed later. Nevertheless, the new code is a significant
improvement over the old state.
While touching the code, implement proper translation string based
on Qt's plural-feature (using %n).
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2018-07-19 12:44:27 +00:00
|
|
|
UndoRenumberDives(const QVector<QPair<int, int>> &divesToRenumber);
|
|
|
|
private:
|
2018-09-29 20:13:44 +00:00
|
|
|
void undo() override;
|
|
|
|
void redo() override;
|
2015-02-28 04:42:37 +00:00
|
|
|
|
Undo: fix multi-level undo of delete-dive and remove-dive-from-trip
The original undo-code was fundamentally broken. Not only did it leak
resources (copied trips were never freed), it also kept references
to trips or dives that could be changed by other commands. Thus,
anything more than a single undo could lead to crashes.
Two ways of fixing this were considered
1) Don't store pointers, but unique dive-ids and trip-ids.
Whereas such unique ids exist for dives, they would have to be
implemented for trips.
2) Don't free objects in the backend.
Instead, take ownership of deleted objects in the undo-object.
Thus, all references in previous undo-objects are guaranteed to
still exist (unless the objects are deleted elsewhere).
After some contemplation, the second method was chosen, because
it is significantly less intrusive. While touching the undo-objects,
clearly separate backend from ui-code, such that they can ultimately
be reused for mobile.
Note that if other parts of the code delete dives, crashes can still
be provoked. Notable examples are split/merge dives. These will have
to be fixed later. Nevertheless, the new code is a significant
improvement over the old state.
While touching the code, implement proper translation string based
on Qt's plural-feature (using %n).
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2018-07-19 12:44:27 +00:00
|
|
|
// For redo and undo: pairs of dive-id / new number
|
|
|
|
QVector<QPair<int, int>> divesToRenumber;
|
2015-02-28 04:42:37 +00:00
|
|
|
};
|
|
|
|
|
2015-04-10 06:39:51 +00:00
|
|
|
class UndoRemoveDivesFromTrip : public QUndoCommand {
|
Undo: fix multi-level undo of delete-dive and remove-dive-from-trip
The original undo-code was fundamentally broken. Not only did it leak
resources (copied trips were never freed), it also kept references
to trips or dives that could be changed by other commands. Thus,
anything more than a single undo could lead to crashes.
Two ways of fixing this were considered
1) Don't store pointers, but unique dive-ids and trip-ids.
Whereas such unique ids exist for dives, they would have to be
implemented for trips.
2) Don't free objects in the backend.
Instead, take ownership of deleted objects in the undo-object.
Thus, all references in previous undo-objects are guaranteed to
still exist (unless the objects are deleted elsewhere).
After some contemplation, the second method was chosen, because
it is significantly less intrusive. While touching the undo-objects,
clearly separate backend from ui-code, such that they can ultimately
be reused for mobile.
Note that if other parts of the code delete dives, crashes can still
be provoked. Notable examples are split/merge dives. These will have
to be fixed later. Nevertheless, the new code is a significant
improvement over the old state.
While touching the code, implement proper translation string based
on Qt's plural-feature (using %n).
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2018-07-19 12:44:27 +00:00
|
|
|
Q_DECLARE_TR_FUNCTIONS(Command)
|
2015-04-10 06:39:51 +00:00
|
|
|
public:
|
Undo: fix multi-level undo of delete-dive and remove-dive-from-trip
The original undo-code was fundamentally broken. Not only did it leak
resources (copied trips were never freed), it also kept references
to trips or dives that could be changed by other commands. Thus,
anything more than a single undo could lead to crashes.
Two ways of fixing this were considered
1) Don't store pointers, but unique dive-ids and trip-ids.
Whereas such unique ids exist for dives, they would have to be
implemented for trips.
2) Don't free objects in the backend.
Instead, take ownership of deleted objects in the undo-object.
Thus, all references in previous undo-objects are guaranteed to
still exist (unless the objects are deleted elsewhere).
After some contemplation, the second method was chosen, because
it is significantly less intrusive. While touching the undo-objects,
clearly separate backend from ui-code, such that they can ultimately
be reused for mobile.
Note that if other parts of the code delete dives, crashes can still
be provoked. Notable examples are split/merge dives. These will have
to be fixed later. Nevertheless, the new code is a significant
improvement over the old state.
While touching the code, implement proper translation string based
on Qt's plural-feature (using %n).
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2018-07-19 12:44:27 +00:00
|
|
|
UndoRemoveDivesFromTrip(const QVector<dive *> &divesToRemove);
|
|
|
|
private:
|
2018-09-29 20:13:44 +00:00
|
|
|
void undo() override;
|
|
|
|
void redo() override;
|
2015-04-10 06:39:51 +00:00
|
|
|
|
Undo: fix multi-level undo of delete-dive and remove-dive-from-trip
The original undo-code was fundamentally broken. Not only did it leak
resources (copied trips were never freed), it also kept references
to trips or dives that could be changed by other commands. Thus,
anything more than a single undo could lead to crashes.
Two ways of fixing this were considered
1) Don't store pointers, but unique dive-ids and trip-ids.
Whereas such unique ids exist for dives, they would have to be
implemented for trips.
2) Don't free objects in the backend.
Instead, take ownership of deleted objects in the undo-object.
Thus, all references in previous undo-objects are guaranteed to
still exist (unless the objects are deleted elsewhere).
After some contemplation, the second method was chosen, because
it is significantly less intrusive. While touching the undo-objects,
clearly separate backend from ui-code, such that they can ultimately
be reused for mobile.
Note that if other parts of the code delete dives, crashes can still
be provoked. Notable examples are split/merge dives. These will have
to be fixed later. Nevertheless, the new code is a significant
improvement over the old state.
While touching the code, implement proper translation string based
on Qt's plural-feature (using %n).
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2018-07-19 12:44:27 +00:00
|
|
|
// For redo
|
|
|
|
QVector<dive *> divesToRemove;
|
|
|
|
|
|
|
|
// For undo
|
|
|
|
std::vector<std::pair<dive *, dive_trip *>> divesToAdd;
|
|
|
|
std::vector<OwningTripPtr> tripsToAdd;
|
2015-04-10 06:39:51 +00:00
|
|
|
};
|
|
|
|
|
2018-07-20 18:26:06 +00:00
|
|
|
class UndoSplitDives : public QUndoCommand {
|
|
|
|
public:
|
|
|
|
// If time is < 0, split at first surface interval
|
|
|
|
UndoSplitDives(dive *d, duration_t time);
|
|
|
|
private:
|
|
|
|
void undo() override;
|
|
|
|
void redo() override;
|
|
|
|
|
|
|
|
// For redo
|
|
|
|
// For each dive to split, we remove one from and put two dives into the backend
|
|
|
|
dive *diveToSplit;
|
|
|
|
DiveToAdd splitDives[2];
|
|
|
|
|
|
|
|
// For undo
|
|
|
|
// For each dive to unsplit, we remove two dives from and add one into the backend
|
|
|
|
DiveToAdd unsplitDive;
|
|
|
|
dive *divesToUnsplit[2];
|
|
|
|
};
|
|
|
|
|
2018-07-21 16:28:33 +00:00
|
|
|
class UndoMergeDives : public QUndoCommand {
|
|
|
|
public:
|
|
|
|
UndoMergeDives(const QVector<dive *> &dives);
|
|
|
|
private:
|
|
|
|
void undo() override;
|
|
|
|
void redo() override;
|
|
|
|
|
|
|
|
// For redo
|
|
|
|
// Add one and remove a batch of dives
|
|
|
|
DiveToAdd mergedDive;
|
|
|
|
std::vector<dive *> divesToMerge;
|
|
|
|
|
|
|
|
// For undo
|
|
|
|
// Remove one and add a batch of dives
|
|
|
|
dive *diveToUnmerge;
|
|
|
|
std::vector<DiveToAdd> unmergedDives;
|
|
|
|
|
|
|
|
// For undo and redo
|
|
|
|
QVector<QPair<int, int>> divesToRenumber;
|
|
|
|
};
|
|
|
|
|
2015-02-11 06:28:43 +00:00
|
|
|
#endif // UNDOCOMMANDS_H
|