summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Berthold Stoeger <bstoeger@mail.tuwien.ac.at>2019-03-31 10:20:13 +0200
committerGravatar Dirk Hohndel <dirk@hohndel.org>2019-04-04 09:38:12 -0700
commit145f70aab59a5ebd1b4ca82432caf38b4821b055 (patch)
tree2527f595c552559ffe74e6cce39f3c52283762d9
parent8c9e5becb20d2d8c8aa1f7108775cc520b08fae1 (diff)
downloadsubsurface-145f70aab59a5ebd1b4ca82432caf38b4821b055.tar.gz
Undo: implement split-out of dive computer
Allow splitting out a dive computer into a distinct dive. This is realized by generating a base class from SplitDive. This turned out to be more cumbersome than expected: we don't know a-priori which of the split dives will come first. Since the undo-command saves the indices where the dives will be insert, these have to be calculated. This is an premature optimization, which makes more pain than necessary. Let's remove it and simply determine the insertion index when executing the command. Original code by Linus Torvalds <torvalds@linux-foundation.org>. Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at> Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
-rw-r--r--core/dive.c85
-rw-r--r--core/dive.h1
-rw-r--r--desktop-widgets/command.cpp5
-rw-r--r--desktop-widgets/command.h1
-rw-r--r--desktop-widgets/command_divelist.cpp86
-rw-r--r--desktop-widgets/command_divelist.h19
-rw-r--r--profile-widget/profilewidget2.cpp9
-rw-r--r--profile-widget/profilewidget2.h1
8 files changed, 169 insertions, 38 deletions
diff --git a/core/dive.c b/core/dive.c
index c73453a04..3ee8e5e2c 100644
--- a/core/dive.c
+++ b/core/dive.c
@@ -596,7 +596,7 @@ void clear_dive(struct dive *d)
/* make a true copy that is independent of the source dive;
* all data structures are duplicated, so the copy can be modified without
* any impact on the source */
-void copy_dive(const struct dive *s, struct dive *d)
+static void copy_dive_nodc(const struct dive *s, struct dive *d)
{
clear_dive(d);
/* simply copy things over, but then make actual copies of the
@@ -614,12 +614,24 @@ void copy_dive(const struct dive *s, struct dive *d)
d->weightsystem[i].description = copy_string(s->weightsystem[i].description);
STRUCTURED_LIST_COPY(struct picture, s->picture_list, d->picture_list, copy_pl);
STRUCTURED_LIST_COPY(struct tag_entry, s->tag_list, d->tag_list, copy_tl);
+}
+
+void copy_dive(const struct dive *s, struct dive *d)
+{
+ copy_dive_nodc(s, d);
// Copy the first dc explicitly, then the list of subsequent dc's
copy_dc(&s->dc, &d->dc);
STRUCTURED_LIST_COPY(struct divecomputer, s->dc.next, d->dc.next, copy_dc);
}
+static void copy_dive_onedc(const struct dive *s, const struct divecomputer *sdc, struct dive *d)
+{
+ copy_dive_nodc(s, d);
+ copy_dc(sdc, &d->dc);
+ d->dc.next = NULL;
+}
+
/* make a clone of the source dive and clean out the source dive;
* this is specifically so we can create a dive in the displayed_dive and then
* add it to the divelist.
@@ -3497,6 +3509,7 @@ static void force_fixup_dive(struct dive *d)
* Split a dive that has a surface interval from samples 'a' to 'b'
* into two dives, but don't add them to the log yet.
* Returns the nr of the old dive or <0 on failure.
+ * Moreover, on failure both output dives are set to NULL.
* On success, the newly allocated dives are returned in out1 and out2.
*/
static int split_dive_at(const struct dive *dive, int a, int b, struct dive **out1, struct dive **out2)
@@ -3507,6 +3520,8 @@ static int split_dive_at(const struct dive *dive, int a, int b, struct dive **ou
struct divecomputer *dc1, *dc2;
struct event *event, **evp;
+ *out1 = *out2 = NULL;
+
/* if we can't find the dive in the dive list, don't bother */
if ((nr = get_divenr(dive)) < 0)
return -1;
@@ -4076,30 +4091,74 @@ unsigned int count_divecomputers(void)
return ret;
}
-/* always acts on the current dive */
-void delete_current_divecomputer(void)
+static void delete_divecomputer(struct dive *d, int num)
{
- struct divecomputer *dc = current_dc;
+ int i;
+
+ /* Refuse to delete the last dive computer */
+ if (!d->dc.next)
+ return;
- if (dc == &current_dive->dc) {
+ if (num == 0) {
/* remove the first one, so copy the second one in place of the first and free the second one
* be careful about freeing the no longer needed structures - since we copy things around we can't use free_dc()*/
- struct divecomputer *fdc = dc->next;
- free_dc_contents(dc);
- memcpy(dc, fdc, sizeof(struct divecomputer));
+ struct divecomputer *fdc = d->dc.next;
+ free_dc_contents(&d->dc);
+ memcpy(&d->dc, fdc, sizeof(struct divecomputer));
free(fdc);
} else {
- struct divecomputer *pdc = &current_dive->dc;
- while (pdc->next != dc && pdc->next)
+ struct divecomputer *pdc = &d->dc;
+ for (i = 0; i < num - 1 && pdc; i++)
pdc = pdc->next;
- if (pdc->next == dc) {
+ if (pdc->next) {
+ struct divecomputer *dc = pdc->next;
pdc->next = dc->next;
free_dc(dc);
}
}
- if (dc_number == count_divecomputers())
+
+ /* If this is the currently displayed dive, we might have to adjust
+ * the currently displayed dive computer. */
+ if (d == current_dive && dc_number >= count_divecomputers())
dc_number--;
- invalidate_dive_cache(current_dive);
+ invalidate_dive_cache(d);
+}
+
+/* always acts on the current dive */
+void delete_current_divecomputer(void)
+{
+ delete_divecomputer(current_dive, dc_number);
+}
+
+/*
+ * This splits the dive src by dive computer. The first output dive has all
+ * dive computers except num, the second only dive computer num.
+ * The dives will not be associated with a trip.
+ * On error, both output parameters are set to NULL.
+ */
+void split_divecomputer(const struct dive *src, int num, struct dive **out1, struct dive **out2)
+{
+ struct divecomputer *srcdc = get_dive_dc(current_dive, dc_number);
+
+ if (src && srcdc) {
+ // Copy the dive, but only using the selected dive computer
+ *out2 = alloc_dive();
+ copy_dive_onedc(src, srcdc, *out2);
+
+ // This will also make fixup_dive() to allocate a new dive id...
+ (*out2)->id = 0;
+ fixup_dive(*out2);
+
+ // Copy the dive with all dive computers
+ *out1 = create_new_copy(src);
+
+ // .. and then delete the split-out dive computer
+ delete_divecomputer(*out1, num);
+
+ (*out1)->divetrip = (*out2)->divetrip = NULL;
+ } else {
+ *out1 = *out2 = NULL;
+ }
}
/* helper function to make it easier to work with our structures
diff --git a/core/dive.h b/core/dive.h
index 4773d3157..874f724c7 100644
--- a/core/dive.h
+++ b/core/dive.h
@@ -440,6 +440,7 @@ extern timestamp_t dive_endtime(const struct dive *dive);
extern void make_first_dc(void);
extern unsigned int count_divecomputers(void);
extern void delete_current_divecomputer(void);
+void split_divecomputer(const struct dive *src, int num, struct dive **out1, struct dive **out2);
/*
* Iterate over each dive, with the first parameter being the index
diff --git a/desktop-widgets/command.cpp b/desktop-widgets/command.cpp
index 84f21265d..638be74d1 100644
--- a/desktop-widgets/command.cpp
+++ b/desktop-widgets/command.cpp
@@ -66,6 +66,11 @@ void splitDives(dive *d, duration_t time)
execute(new SplitDives(d, time));
}
+void splitDiveComputer(dive *d, int dc_num)
+{
+ execute(new SplitDiveComputer(d, dc_num));
+}
+
void mergeDives(const QVector <dive *> &dives)
{
execute(new MergeDives(dives));
diff --git a/desktop-widgets/command.h b/desktop-widgets/command.h
index d527fff3b..4c6240a93 100644
--- a/desktop-widgets/command.h
+++ b/desktop-widgets/command.h
@@ -32,6 +32,7 @@ void createTrip(const QVector<dive *> &divesToAddIn);
void autogroupDives();
void mergeTrips(dive_trip *trip1, dive_trip *trip2);
void splitDives(dive *d, duration_t time);
+void splitDiveComputer(dive *d, int dc_num);
void mergeDives(const QVector <dive *> &dives);
} // namespace Command
diff --git a/desktop-widgets/command_divelist.cpp b/desktop-widgets/command_divelist.cpp
index 845afac75..96c5ead95 100644
--- a/desktop-widgets/command_divelist.cpp
+++ b/desktop-widgets/command_divelist.cpp
@@ -8,6 +8,8 @@
#include "core/subsurface-qt/DiveListNotifier.h"
#include "qt-models/filtermodels.h"
+#include <array>
+
namespace Command {
// Generally, signals are sent in batches per trip. To avoid writing the same loop
@@ -822,42 +824,51 @@ MergeTrips::MergeTrips(dive_trip *trip1, dive_trip *trip2)
divesToMove.divesToMove.push_back( { trip2->dives.dives[i], newTrip } );
}
-SplitDives::SplitDives(dive *d, duration_t time)
+// std::array<dive *, 2> is the same as struct *dive[2], with the fundamental
+// difference that it can be returned from functions. Thus, this constructor
+// can be chained with the result of a function.
+SplitDivesBase::SplitDivesBase(dive *d, std::array<dive *, 2> newDives)
{
- setText(tr("split dive"));
-
- // Split the dive
- dive *new1, *new2;
- int idx = time.seconds < 0 ?
- split_dive(d, &new1, &new2) :
- split_dive_at_time(d, time, &new1, &new2);
-
- // If this didn't work, simply return. Empty arrays indicate that nothing is to be done.
- if (idx < 0)
+ // If either of the new dives is null, simply return. Empty arrays indicate that nothing is to be done.
+ if (!newDives[0] || !newDives[1])
return;
// Currently, the core code selects the dive -> this is not what we want, as
// we manually manage the selection post-command.
// TODO: Reset selection in core.
- new1->selected = false;
- new2->selected = false;
+ newDives[0]->selected = false;
+ newDives[1]->selected = false;
+
+ // Getting the insertion indexes correct is actually not easy, as we don't know
+ // which of the dives will land first when splitting out dive computers!
+ // TODO: We really should think about not storing the insertion index in the undo
+ // command, but calculating it on the fly on execution.
+ int idx_old = get_divenr(d);
+ int idx1 = dive_table_get_insertion_index(&dive_table, newDives[0]);
+ int idx2 = dive_table_get_insertion_index(&dive_table, newDives[1]);
+ if (idx1 > idx_old)
+ --idx1;
+ if (idx2 > idx_old)
+ --idx2;
+ if (idx1 == idx2 && dive_less_than(newDives[0], newDives[1]))
+ ++idx2;
diveToSplit.push_back(d);
splitDives.dives.resize(2);
- splitDives.dives[0].dive.reset(new1);
+ splitDives.dives[0].dive.reset(newDives[0]);
splitDives.dives[0].trip = d->divetrip;
- splitDives.dives[0].idx = idx;
- splitDives.dives[1].dive.reset(new2);
+ splitDives.dives[0].idx = idx1;
+ splitDives.dives[1].dive.reset(newDives[1]);
splitDives.dives[1].trip = d->divetrip;
- splitDives.dives[1].idx = idx + 1;
+ splitDives.dives[1].idx = idx2;
}
-bool SplitDives::workToBeDone()
+bool SplitDivesBase::workToBeDone()
{
return !diveToSplit.empty();
}
-void SplitDives::redoit()
+void SplitDivesBase::redoit()
{
divesToUnsplit = addDives(splitDives);
unsplitDive = removeDives(diveToSplit);
@@ -867,7 +878,7 @@ void SplitDives::redoit()
restoreSelection(divesToUnsplit, divesToUnsplit[0]);
}
-void SplitDives::undoit()
+void SplitDivesBase::undoit()
{
// Note: reverse order with respect to redoit()
diveToSplit = addDives(unsplitDive);
@@ -878,6 +889,41 @@ void SplitDives::undoit()
restoreSelection(diveToSplit, diveToSplit[0] );
}
+static std::array<dive *, 2> doSplitDives(const dive *d, duration_t time)
+{
+ // Split the dive
+ dive *new1, *new2;
+ if (time.seconds < 0)
+ split_dive(d, &new1, &new2);
+ else
+ split_dive_at_time(d, time, &new1, &new2);
+
+ return { new1, new2 };
+}
+
+SplitDives::SplitDives(dive *d, duration_t time) : SplitDivesBase(d, doSplitDives(d, time))
+{
+ setText(tr("split dive"));
+}
+
+static std::array<dive *, 2> splitDiveComputer(const dive *d, int dc_num)
+{
+ // Refuse to do anything if the dive has only one dive computer.
+ // Yes, this should have been checked by the UI, but let's just make sure.
+ if (!d->dc.next)
+ return { nullptr, nullptr};
+
+ dive *new1, *new2;
+ split_divecomputer(d, dc_num, &new1, &new2);
+
+ return { new1, new2 };
+}
+
+SplitDiveComputer::SplitDiveComputer(dive *d, int dc_num) : SplitDivesBase(d, splitDiveComputer(d, dc_num))
+{
+ setText(tr("split dive computer"));
+}
+
MergeDives::MergeDives(const QVector <dive *> &dives)
{
setText(tr("merge dive"));
diff --git a/desktop-widgets/command_divelist.h b/desktop-widgets/command_divelist.h
index 6cf7f48ab..7742cf729 100644
--- a/desktop-widgets/command_divelist.h
+++ b/desktop-widgets/command_divelist.h
@@ -185,10 +185,9 @@ struct MergeTrips : public TripBase {
MergeTrips(dive_trip *trip1, dive_trip *trip2);
};
-class SplitDives : public DiveListBase {
-public:
- // If time is < 0, split at first surface interval
- SplitDives(dive *d, duration_t time);
+class SplitDivesBase : public DiveListBase {
+protected:
+ SplitDivesBase(dive *old, std::array<dive *, 2> newDives);
private:
void undoit() override;
void redoit() override;
@@ -209,6 +208,18 @@ private:
std::vector<dive *> divesToUnsplit;
};
+class SplitDives : public SplitDivesBase {
+public:
+ // If time is < 0, split at first surface interval
+ SplitDives(dive *d, duration_t time);
+};
+
+class SplitDiveComputer : public SplitDivesBase {
+public:
+ // If time is < 0, split at first surface interval
+ SplitDiveComputer(dive *d, int dc_num);
+};
+
class MergeDives : public DiveListBase {
public:
MergeDives(const QVector<dive *> &dives);
diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp
index 5d282fd95..157ab9fcd 100644
--- a/profile-widget/profilewidget2.cpp
+++ b/profile-widget/profilewidget2.cpp
@@ -1436,8 +1436,10 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event)
// create menu to show when right clicking on dive computer name
if (dc_number > 0)
m.addAction(tr("Make first dive computer"), this, SLOT(makeFirstDC()));
- if (count_divecomputers() > 1)
+ if (count_divecomputers() > 1) {
m.addAction(tr("Delete this dive computer"), this, SLOT(deleteCurrentDC()));
+ m.addAction(tr("Split this dive computer into own dive"), this, SLOT(splitCurrentDC()));
+ }
m.exec(event->globalPos());
// don't show the regular profile context menu
return;
@@ -1581,6 +1583,11 @@ void ProfileWidget2::deleteCurrentDC()
emit refreshDisplay(true);
}
+void ProfileWidget2::splitCurrentDC()
+{
+ Command::splitDiveComputer(current_dive, dc_number);
+}
+
void ProfileWidget2::makeFirstDC()
{
make_first_dc();
diff --git a/profile-widget/profilewidget2.h b/profile-widget/profilewidget2.h
index 443b83c64..4cf69efa6 100644
--- a/profile-widget/profilewidget2.h
+++ b/profile-widget/profilewidget2.h
@@ -126,6 +126,7 @@ slots: // Necessary to call from QAction's signals.
void editName();
void makeFirstDC();
void deleteCurrentDC();
+ void splitCurrentDC();
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);