summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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);