diff options
-rw-r--r-- | core/dive.c | 85 | ||||
-rw-r--r-- | core/dive.h | 1 | ||||
-rw-r--r-- | desktop-widgets/command.cpp | 5 | ||||
-rw-r--r-- | desktop-widgets/command.h | 1 | ||||
-rw-r--r-- | desktop-widgets/command_divelist.cpp | 86 | ||||
-rw-r--r-- | desktop-widgets/command_divelist.h | 19 | ||||
-rw-r--r-- | profile-widget/profilewidget2.cpp | 9 | ||||
-rw-r--r-- | profile-widget/profilewidget2.h | 1 |
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 == ¤t_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 = ¤t_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); |