mirror of
https://github.com/subsurface/subsurface.git
synced 2024-11-30 22:20:21 +00:00
Undo: implement rudimentary support for undo of dive-splitting
For this, the core functionality of the split_dive() and split_dive_at_time() functions were split out into new split_dive_dont_insert() and split_dive_at_time_dont_insert(), which do not add the new dives to the log. Thus, the undo-command can take ownership of these dives, without having to remove them first. The split-dive functionality is temporarily made desktop-only until mobile also supports "UndoObjects". Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
parent
12df9faaa2
commit
302f6adb79
6 changed files with 145 additions and 36 deletions
76
core/dive.c
76
core/dive.c
|
@ -3388,7 +3388,7 @@ struct dive *merge_dives(struct dive *a, struct dive *b, int offset, bool prefer
|
|||
}
|
||||
|
||||
// copy_dive(), but retaining the new ID for the copied dive
|
||||
static struct dive *create_new_copy(struct dive *from)
|
||||
static struct dive *create_new_copy(const struct dive *from)
|
||||
{
|
||||
struct dive *to = alloc_dive();
|
||||
int id;
|
||||
|
@ -3450,9 +3450,11 @@ static void force_fixup_dive(struct dive *d)
|
|||
|
||||
/*
|
||||
* Split a dive that has a surface interval from samples 'a' to 'b'
|
||||
* into two dives.
|
||||
* into two dives, but don't add them to the log yet.
|
||||
* Returns the nr of the old dive or <0 on failure.
|
||||
* On success, the newly allocated dives are returned in out1 and out2.
|
||||
*/
|
||||
static int split_dive_at(struct dive *dive, int a, int b)
|
||||
static int split_dive_at(const struct dive *dive, int a, int b, struct dive **out1, struct dive **out2)
|
||||
{
|
||||
int i, nr;
|
||||
uint32_t t;
|
||||
|
@ -3462,15 +3464,16 @@ static int split_dive_at(struct dive *dive, int a, int b)
|
|||
|
||||
/* if we can't find the dive in the dive list, don't bother */
|
||||
if ((nr = get_divenr(dive)) < 0)
|
||||
return 0;
|
||||
return -1;
|
||||
|
||||
/* Splitting should leave at least 3 samples per dive */
|
||||
if (a < 3 || b > dive->dc.samples - 4)
|
||||
return 0;
|
||||
return -1;
|
||||
|
||||
/* We're not trying to be efficient here.. */
|
||||
d1 = create_new_copy(dive);
|
||||
d2 = create_new_copy(dive);
|
||||
d1->divetrip = d2->divetrip = 0;
|
||||
|
||||
/* now unselect the first first segment so we don't keep all
|
||||
* dives selected by mistake. But do keep the second one selected
|
||||
|
@ -3544,15 +3547,6 @@ static int split_dive_at(struct dive *dive, int a, int b)
|
|||
force_fixup_dive(d1);
|
||||
force_fixup_dive(d2);
|
||||
|
||||
if (dive->divetrip) {
|
||||
d1->divetrip = d2->divetrip = 0;
|
||||
add_dive_to_trip(d1, dive->divetrip);
|
||||
add_dive_to_trip(d2, dive->divetrip);
|
||||
}
|
||||
|
||||
delete_single_dive(nr);
|
||||
add_single_dive(nr, d1);
|
||||
|
||||
/*
|
||||
* Was the dive numbered? If it was the last dive, then we'll
|
||||
* increment the dive number for the tail part that we split off.
|
||||
|
@ -3564,16 +3558,28 @@ static int split_dive_at(struct dive *dive, int a, int b)
|
|||
else
|
||||
d2->number = 0;
|
||||
}
|
||||
add_single_dive(nr + 1, d2);
|
||||
|
||||
mark_divelist_changed(true);
|
||||
|
||||
return 1;
|
||||
*out1 = d1;
|
||||
*out2 = d2;
|
||||
return nr;
|
||||
}
|
||||
|
||||
static void finish_split(int nr, struct dive *old, struct dive *d1, struct dive *d2)
|
||||
{
|
||||
if (old->divetrip) {
|
||||
add_dive_to_trip(d1, old->divetrip);
|
||||
add_dive_to_trip(d2, old->divetrip);
|
||||
}
|
||||
delete_single_dive(nr);
|
||||
add_single_dive(nr, d1);
|
||||
add_single_dive(nr + 1, d2);
|
||||
}
|
||||
|
||||
/* in freedive mode we split for as little as 10 seconds on the surface,
|
||||
* otherwise we use a minute */
|
||||
static bool should_split(struct divecomputer *dc, int t1, int t2)
|
||||
static bool should_split(const struct divecomputer *dc, int t1, int t2)
|
||||
{
|
||||
int threshold = dc->divemode == FREEDIVE ? 10 : 60;
|
||||
|
||||
|
@ -3590,14 +3596,14 @@ static bool should_split(struct divecomputer *dc, int t1, int t2)
|
|||
*
|
||||
* In other words, this is a (simplified) reversal of the dive merging.
|
||||
*/
|
||||
int split_dive(struct dive *dive)
|
||||
int split_dive_dont_insert(const struct dive *dive, struct dive **new1, struct dive **new2)
|
||||
{
|
||||
int i;
|
||||
int at_surface, surface_start;
|
||||
struct divecomputer *dc;
|
||||
const struct divecomputer *dc;
|
||||
|
||||
if (!dive)
|
||||
return 0;
|
||||
return -1;
|
||||
|
||||
dc = &dive->dc;
|
||||
surface_start = 0;
|
||||
|
@ -3627,25 +3633,43 @@ int split_dive(struct dive *dive)
|
|||
if (!should_split(dc, dc->sample[surface_start].time.seconds, sample[-1].time.seconds))
|
||||
continue;
|
||||
|
||||
return split_dive_at(dive, surface_start, i-1);
|
||||
return split_dive_at(dive, surface_start, i-1, new1, new2);
|
||||
}
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void split_dive_at_time(struct dive *dive, duration_t time)
|
||||
void split_dive(struct dive *dive)
|
||||
{
|
||||
int nr;
|
||||
struct dive *new1, *new2;
|
||||
|
||||
if ((nr = split_dive_dont_insert(dive, &new1, &new2)) >= 0)
|
||||
finish_split(nr, dive, new1, new2);
|
||||
}
|
||||
|
||||
int split_dive_at_time_dont_insert(const struct dive *dive, duration_t time, struct dive **new1, struct dive **new2)
|
||||
{
|
||||
int i = 0;
|
||||
struct sample *sample = dive->dc.sample;
|
||||
|
||||
if (!dive)
|
||||
return;
|
||||
return -1;
|
||||
while(sample->time.seconds < time.seconds) {
|
||||
++sample;
|
||||
++i;
|
||||
if (dive->dc.samples == i)
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
split_dive_at(dive, i, i - 1);
|
||||
return split_dive_at(dive, i, i - 1, new1, new2);
|
||||
}
|
||||
|
||||
void split_dive_at_time(struct dive *dive, duration_t time)
|
||||
{
|
||||
int nr;
|
||||
struct dive *new1, *new2;
|
||||
|
||||
if ((nr = split_dive_at_time_dont_insert(dive, time, &new1, &new2)) >= 0)
|
||||
finish_split(nr, dive, new1, new2);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -569,7 +569,9 @@ extern void fixup_dc_duration(struct divecomputer *dc);
|
|||
extern int dive_getUniqID();
|
||||
extern unsigned int dc_airtemp(const struct divecomputer *dc);
|
||||
extern unsigned int dc_watertemp(const struct divecomputer *dc);
|
||||
extern int split_dive(struct dive *);
|
||||
extern int split_dive_dont_insert(const struct dive *dive, struct dive **new1, struct dive **new2);
|
||||
extern void split_dive(struct dive *);
|
||||
extern int split_dive_at_time_dont_insert(const struct dive *dive, duration_t time, struct dive **new1, struct dive **new2);
|
||||
extern void split_dive_at_time(struct dive *dive, duration_t time);
|
||||
extern struct dive *merge_dives(struct dive *a, struct dive *b, int offset, bool prefer_downloaded);
|
||||
extern struct dive *try_to_merge(struct dive *a, struct dive *b, bool prefer_downloaded);
|
||||
|
|
|
@ -633,12 +633,16 @@ void DiveListView::splitDives()
|
|||
int i;
|
||||
struct dive *dive;
|
||||
|
||||
// Let's collect the dives to be split first, so that we don't catch newly inserted dives!
|
||||
QVector<struct dive *> dives;
|
||||
for_each_dive (i, dive) {
|
||||
if (dive->selected)
|
||||
split_dive(dive);
|
||||
dives.append(dive);
|
||||
}
|
||||
for (struct dive *d: dives) {
|
||||
UndoSplitDives *undoCommand = new UndoSplitDives(d, duration_t{-1});
|
||||
MainWindow::instance()->undoStack->push(undoCommand);
|
||||
}
|
||||
MainWindow::instance()->refreshProfile();
|
||||
MainWindow::instance()->refreshDisplay();
|
||||
}
|
||||
|
||||
void DiveListView::renumberDives()
|
||||
|
|
|
@ -180,6 +180,7 @@ void UndoRemoveDivesFromTrip::undo()
|
|||
for (auto &pair: divesToAdd)
|
||||
add_dive_to_trip(pair.first, pair.second);
|
||||
divesToAdd.clear();
|
||||
mark_divelist_changed(true);
|
||||
|
||||
// Finally, do the UI stuff:
|
||||
MainWindow::instance()->refreshDisplay();
|
||||
|
@ -204,3 +205,58 @@ void UndoRemoveDivesFromTrip::redo()
|
|||
// Finally, do the UI stuff:
|
||||
MainWindow::instance()->refreshDisplay();
|
||||
}
|
||||
|
||||
UndoSplitDives::UndoSplitDives(dive *d, duration_t time)
|
||||
{
|
||||
setText(gettextFromC::tr("split dive"));
|
||||
|
||||
// Split the dive
|
||||
dive *new1, *new2;
|
||||
int idx = time.seconds < 0 ?
|
||||
split_dive_dont_insert(d, &new1, &new2) :
|
||||
split_dive_at_time_dont_insert(d, time, &new1, &new2);
|
||||
|
||||
// If this didn't work, reset pointers so that redo() and undo() do nothing
|
||||
if (idx < 0) {
|
||||
diveToSplit = nullptr;
|
||||
divesToUnsplit[0] = divesToUnsplit[1];
|
||||
return;
|
||||
}
|
||||
|
||||
diveToSplit = d;
|
||||
splitDives[0].dive.reset(new1);
|
||||
splitDives[0].trip = d->divetrip;
|
||||
splitDives[0].idx = idx;
|
||||
splitDives[1].dive.reset(new2);
|
||||
splitDives[1].trip = d->divetrip;
|
||||
splitDives[1].idx = idx + 1;
|
||||
}
|
||||
|
||||
void UndoSplitDives::redo()
|
||||
{
|
||||
if (!diveToSplit)
|
||||
return;
|
||||
divesToUnsplit[0] = addDive(splitDives[0]);
|
||||
divesToUnsplit[1] = addDive(splitDives[1]);
|
||||
unsplitDive = removeDive(diveToSplit);
|
||||
mark_divelist_changed(true);
|
||||
|
||||
// Finally, do the UI stuff:
|
||||
MainWindow::instance()->refreshDisplay();
|
||||
MainWindow::instance()->refreshProfile();
|
||||
}
|
||||
|
||||
void UndoSplitDives::undo()
|
||||
{
|
||||
if (!unsplitDive.dive)
|
||||
return;
|
||||
// Note: reverse order with respect to redo()
|
||||
diveToSplit = addDive(unsplitDive);
|
||||
splitDives[1] = removeDive(divesToUnsplit[1]);
|
||||
splitDives[0] = removeDive(divesToUnsplit[0]);
|
||||
mark_divelist_changed(true);
|
||||
|
||||
// Finally, do the UI stuff:
|
||||
MainWindow::instance()->refreshDisplay();
|
||||
MainWindow::instance()->refreshProfile();
|
||||
}
|
||||
|
|
|
@ -227,4 +227,23 @@ private:
|
|||
std::vector<OwningTripPtr> tripsToAdd;
|
||||
};
|
||||
|
||||
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];
|
||||
};
|
||||
|
||||
#endif // UNDOCOMMANDS_H
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "desktop-widgets/diveplanner.h"
|
||||
#include "desktop-widgets/simplewidgets.h"
|
||||
#include "desktop-widgets/divepicturewidget.h"
|
||||
#include "desktop-widgets/undocommands.h"
|
||||
#include "desktop-widgets/mainwindow.h"
|
||||
#include "core/qthelper.h"
|
||||
#include "core/gettextfromc.h"
|
||||
|
@ -1675,16 +1676,19 @@ void ProfileWidget2::addSetpointChange()
|
|||
|
||||
void ProfileWidget2::splitDive()
|
||||
{
|
||||
#ifndef SUBSURFACE_MOBILE
|
||||
// Make sure that this is an actual dive and we're not in add mode
|
||||
dive *d = get_dive_by_uniq_id(displayed_dive.id);
|
||||
if (!d)
|
||||
return;
|
||||
QAction *action = qobject_cast<QAction *>(sender());
|
||||
QPointF scenePos = mapToScene(mapFromGlobal(action->data().toPoint()));
|
||||
duration_t time;
|
||||
time.seconds = lrint(timeAxis->valueAt((scenePos)));
|
||||
split_dive_at_time(&displayed_dive, time);
|
||||
time.seconds = lrint(timeAxis->valueAt(scenePos));
|
||||
UndoSplitDives *undoCommand = new UndoSplitDives(d, time);
|
||||
MainWindow::instance()->undoStack->push(undoCommand);
|
||||
emit updateDiveInfo(false);
|
||||
mark_divelist_changed(true);
|
||||
replot();
|
||||
MainWindow::instance()->refreshProfile();
|
||||
MainWindow::instance()->refreshDisplay();
|
||||
#endif
|
||||
}
|
||||
|
||||
void ProfileWidget2::changeGas()
|
||||
|
|
Loading…
Reference in a new issue