diff --git a/core/divefilter.cpp b/core/divefilter.cpp index 48e16b48b..15e880613 100644 --- a/core/divefilter.cpp +++ b/core/divefilter.cpp @@ -15,7 +15,7 @@ #endif // Set filter status of dive and return whether it has been changed -bool DiveFilter::setFilterStatus(struct dive *d, bool shown) const +bool DiveFilter::setFilterStatus(struct dive *d, bool shown, std::vector &removeFromSelection) const { bool old_shown, changed; if (!d) @@ -23,16 +23,16 @@ bool DiveFilter::setFilterStatus(struct dive *d, bool shown) const old_shown = !d->hidden_by_filter; d->hidden_by_filter = !shown; if (!shown && d->selected) - deselect_dive(d); + removeFromSelection.push_back(d); changed = old_shown != shown; if (changed) shown_dives += shown - old_shown; return changed; } -void DiveFilter::updateDiveStatus(dive *d, bool newStatus, ShownChange &change) const +void DiveFilter::updateDiveStatus(dive *d, bool newStatus, ShownChange &change, std::vector &removeFromSelection) const { - if (setFilterStatus(d, newStatus)) { + if (setFilterStatus(d, newStatus, removeFromSelection)) { if (newStatus) change.newShown.push_back(d); else @@ -54,19 +54,20 @@ bool FilterData::validFilter() const ShownChange DiveFilter::update(const QVector &dives) const { - dive *old_current = current_dive; - ShownChange res; bool doDS = diveSiteMode(); bool doFullText = filterData.fullText.doit(); + std::vector selection = getDiveSelection(); + std::vector removeFromSelection; for (dive *d: dives) { // There are three modes: divesite, fulltext, normal bool newStatus = doDS ? dive_sites.contains(d->dive_site) : doFullText ? fulltext_dive_matches(d, filterData.fullText, filterData.fulltextStringMode) && showDive(d) : showDive(d); - updateDiveStatus(d, newStatus, res); + updateDiveStatus(d, newStatus, res, removeFromSelection); } - res.currentChanged = old_current != current_dive; + updateSelection(selection, std::vector(), removeFromSelection); + res.currentChanged = setSelection(selection); return res; } @@ -82,30 +83,31 @@ void DiveFilter::reset() ShownChange DiveFilter::updateAll() const { - dive *old_current = current_dive; - ShownChange res; int i; dive *d; + std::vector selection = getDiveSelection(); + std::vector removeFromSelection; // There are three modes: divesite, fulltext, normal if (diveSiteMode()) { for_each_dive(i, d) { bool newStatus = dive_sites.contains(d->dive_site); - updateDiveStatus(d, newStatus, res); + updateDiveStatus(d, newStatus, res, removeFromSelection); } } else if (filterData.fullText.doit()) { FullTextResult ft = fulltext_find_dives(filterData.fullText, filterData.fulltextStringMode); for_each_dive(i, d) { bool newStatus = ft.dive_matches(d) && showDive(d); - updateDiveStatus(d, newStatus, res); + updateDiveStatus(d, newStatus, res, removeFromSelection); } } else { for_each_dive(i, d) { bool newStatus = showDive(d); - updateDiveStatus(d, newStatus, res); + updateDiveStatus(d, newStatus, res, removeFromSelection); } } - res.currentChanged = old_current != current_dive; + updateSelection(selection, std::vector(), removeFromSelection); + res.currentChanged = setSelection(selection); return res; } diff --git a/core/divefilter.h b/core/divefilter.h index ef0ec4408..403e2871a 100644 --- a/core/divefilter.h +++ b/core/divefilter.h @@ -57,8 +57,10 @@ public: private: DiveFilter(); bool showDive(const struct dive *d) const; // Should that dive be shown? - bool setFilterStatus(struct dive *d, bool shown) const; - void updateDiveStatus(dive *d, bool newStatus, ShownChange &change) const; + bool setFilterStatus(struct dive *d, bool shown, + std::vector &removeFromSelection) const; + void updateDiveStatus(dive *d, bool newStatus, ShownChange &change, + std::vector &removeFromSelection) const; QVector dive_sites; FilterData filterData; diff --git a/core/selection.cpp b/core/selection.cpp index 883628b1c..9baa79c53 100644 --- a/core/selection.cpp +++ b/core/selection.cpp @@ -130,51 +130,63 @@ extern "C" void dump_selection(void) } #endif +// Get closest dive in selection, if possible a newer dive. +// Supposes that selection is sorted +static dive *closestInSelection(timestamp_t when, const std::vector &selection) +{ + // Start from back until we get the first dive that is before + // the supposed-to-be selected dive. (Note: this mimics the + // old behavior when the current dive changed). + for (auto it = selection.rbegin(); it < selection.rend(); ++it) { + if ((*it)->when > when && !(*it)->hidden_by_filter) + return *it; + } + + // We didn't find a more recent selected dive -> try to + // find *any* visible selected dive. + for (dive *d: selection) { + if (!d->hidden_by_filter) + return d; + } + + return nullptr; +} + // Set the current dive either from a list of selected dives, // or a newly selected dive. In both cases, try to select the -// dive that is newer that is newer than the given date. +// dive that is newer than the given date. // This mimics the old behavior when the current dive changed. // If a current dive outside of the selection was set, add // it to the list of selected dives, so that we never end up // in a situation where we display a non-selected dive. static void setClosestCurrentDive(timestamp_t when, const std::vector &selection, QVector &divesToSelect) { - // Start from back until we get the first dive that is before - // the supposed-to-be selected dive. (Note: this mimics the - // old behavior when the current dive changed). - for (auto it = selection.rbegin(); it < selection.rend(); ++it) { - if ((*it)->when > when && !(*it)->hidden_by_filter) { - current_dive = *it; - return; - } - } - - // We didn't find a more recent selected dive -> try to - // find *any* visible selected dive. - for (dive *d: selection) { - if (!d->hidden_by_filter) { - current_dive = d; - return; - } + if (dive *d = closestInSelection(when, selection)) { + current_dive = d; + return; } // No selected dive is visible! Take the closest dive. Note, this might // return null, but that just means unsetting the current dive (as no // dive is visible anyway). current_dive = find_next_visible_dive(when); - if (current_dive) + if (current_dive) { + current_dive->selected = true; + amount_selected++; divesToSelect.push_back(current_dive); + } } // Reset the selection to the dives of the "selection" vector and send the appropriate signals. // Set the current dive to "currentDive". "currentDive" must be an element of "selection" (or -// null if "seletion" is empty). Return true if the selection or current dive changed. -void setSelection(const std::vector &selection, dive *currentDive, int currentDc) +// null if "selection" is empty). Return true if the current dive changed. +bool setSelection(const std::vector &selection, dive *currentDive, int currentDc) { // To do so, generate vectors of dives to be selected and deselected. // We send signals batched by trip, so keep track of trip/dive pairs. QVector divesToSelect; divesToSelect.reserve(selection.size()); + const dive *oldCurrent = current_dive; // Since we select only dives, there are no selected trips! amount_trips_selected = 0; @@ -223,6 +235,15 @@ void setSelection(const std::vector &selection, dive *currentDive, int c // Send the new selection emit diveListNotifier.divesSelected(divesToSelect); + return current_dive != oldCurrent; +} + +bool setSelection(const std::vector &selection) +{ + dive *newCurrent = current_dive; + if (current_dive && std::find(selection.begin(), selection.end(), current_dive) == selection.end()) + newCurrent = closestInSelection(current_dive->when, selection); + return setSelection(selection, newCurrent, -1); } extern "C" void select_single_dive(dive *d) @@ -249,6 +270,32 @@ std::vector getDiveSelection() return res; } +bool diveInSelection(const std::vector &selection, const dive *d) +{ + // Do a binary search using the ordering of the dive list. + auto it = std::lower_bound(selection.begin(), selection.end(), d, dive_less_than); + return it != selection.end() && *it == d; +} + +void updateSelection(std::vector &selection, const std::vector &add, const std::vector &remove) +{ + // We could sort the array and merge the vectors as we do in the undo code. But is it necessary? + for (dive *d: add) { + auto it = std::lower_bound(selection.begin(), selection.end(), d, dive_less_than); + if (it != selection.end() && *it == d) + continue; // Ooops. Already there? + selection.insert(it, d); + } + + // Likewise, we could sort the array and be smarter here. Again, is it necessary? + for (dive *d: remove) { + auto it = std::lower_bound(selection.begin(), selection.end(), d, dive_less_than); + if (it == selection.end() || *it != d) + continue; // Ooops. Not there? + selection.erase(it); + } +} + // Select the first dive that is visible extern "C" void select_newest_visible_dive() { diff --git a/core/selection.h b/core/selection.h index 5d72af131..81c647f27 100644 --- a/core/selection.h +++ b/core/selection.h @@ -45,10 +45,18 @@ extern void dump_selection(void); // Set the current dive to "currentDive" and the current dive computer to "currentDc". // "currentDive" must be an element of "selection" (or null if "seletion" is empty). // If "currentDc" is negative, an attempt will be made to keep the current computer number. -void setSelection(const std::vector &selection, dive *currentDive, int currentDc); +// Returns true if the current dive changed. +bool setSelection(const std::vector &selection, dive *currentDive, int currentDc); -// Get currently selectd dives +// Set selection, but try to keep the current dive. If current dive is not in selection, +// find the nearest current dive in the selection +// Returns true if the current dive changed. +bool setSelection(const std::vector &selection); + +// Get currently selected dives std::vector getDiveSelection(); +bool diveInSelection(const std::vector &selection, const dive *d); +void updateSelection(std::vector &selection, const std::vector &add, const std::vector &remove); #endif // __cplusplus