filter: set dive selection at once

For each selected dive that is hidden by the filter,
unselect_dive() was called, which led to a recalculation
of the current dive and divecomputer.

Instead, collect all deselected dives and deselect them
at the end. Thus, these calculations are performed
only once.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
Berthold Stoeger 2022-05-21 13:14:47 +02:00 committed by bstoeger
parent 9f455b1457
commit 6df1c62dfc
4 changed files with 98 additions and 39 deletions

View file

@ -15,7 +15,7 @@
#endif #endif
// Set filter status of dive and return whether it has been changed // 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<dive *> &removeFromSelection) const
{ {
bool old_shown, changed; bool old_shown, changed;
if (!d) if (!d)
@ -23,16 +23,16 @@ bool DiveFilter::setFilterStatus(struct dive *d, bool shown) const
old_shown = !d->hidden_by_filter; old_shown = !d->hidden_by_filter;
d->hidden_by_filter = !shown; d->hidden_by_filter = !shown;
if (!shown && d->selected) if (!shown && d->selected)
deselect_dive(d); removeFromSelection.push_back(d);
changed = old_shown != shown; changed = old_shown != shown;
if (changed) if (changed)
shown_dives += shown - old_shown; shown_dives += shown - old_shown;
return changed; return changed;
} }
void DiveFilter::updateDiveStatus(dive *d, bool newStatus, ShownChange &change) const void DiveFilter::updateDiveStatus(dive *d, bool newStatus, ShownChange &change, std::vector<dive *> &removeFromSelection) const
{ {
if (setFilterStatus(d, newStatus)) { if (setFilterStatus(d, newStatus, removeFromSelection)) {
if (newStatus) if (newStatus)
change.newShown.push_back(d); change.newShown.push_back(d);
else else
@ -54,19 +54,20 @@ bool FilterData::validFilter() const
ShownChange DiveFilter::update(const QVector<dive *> &dives) const ShownChange DiveFilter::update(const QVector<dive *> &dives) const
{ {
dive *old_current = current_dive;
ShownChange res; ShownChange res;
bool doDS = diveSiteMode(); bool doDS = diveSiteMode();
bool doFullText = filterData.fullText.doit(); bool doFullText = filterData.fullText.doit();
std::vector<dive *> selection = getDiveSelection();
std::vector<dive *> removeFromSelection;
for (dive *d: dives) { for (dive *d: dives) {
// There are three modes: divesite, fulltext, normal // There are three modes: divesite, fulltext, normal
bool newStatus = doDS ? dive_sites.contains(d->dive_site) : bool newStatus = doDS ? dive_sites.contains(d->dive_site) :
doFullText ? fulltext_dive_matches(d, filterData.fullText, filterData.fulltextStringMode) && showDive(d) : doFullText ? fulltext_dive_matches(d, filterData.fullText, filterData.fulltextStringMode) && showDive(d) :
showDive(d); showDive(d);
updateDiveStatus(d, newStatus, res); updateDiveStatus(d, newStatus, res, removeFromSelection);
} }
res.currentChanged = old_current != current_dive; updateSelection(selection, std::vector<dive *>(), removeFromSelection);
res.currentChanged = setSelection(selection);
return res; return res;
} }
@ -82,30 +83,31 @@ void DiveFilter::reset()
ShownChange DiveFilter::updateAll() const ShownChange DiveFilter::updateAll() const
{ {
dive *old_current = current_dive;
ShownChange res; ShownChange res;
int i; int i;
dive *d; dive *d;
std::vector<dive *> selection = getDiveSelection();
std::vector<dive *> removeFromSelection;
// There are three modes: divesite, fulltext, normal // There are three modes: divesite, fulltext, normal
if (diveSiteMode()) { if (diveSiteMode()) {
for_each_dive(i, d) { for_each_dive(i, d) {
bool newStatus = dive_sites.contains(d->dive_site); bool newStatus = dive_sites.contains(d->dive_site);
updateDiveStatus(d, newStatus, res); updateDiveStatus(d, newStatus, res, removeFromSelection);
} }
} else if (filterData.fullText.doit()) { } else if (filterData.fullText.doit()) {
FullTextResult ft = fulltext_find_dives(filterData.fullText, filterData.fulltextStringMode); FullTextResult ft = fulltext_find_dives(filterData.fullText, filterData.fulltextStringMode);
for_each_dive(i, d) { for_each_dive(i, d) {
bool newStatus = ft.dive_matches(d) && showDive(d); bool newStatus = ft.dive_matches(d) && showDive(d);
updateDiveStatus(d, newStatus, res); updateDiveStatus(d, newStatus, res, removeFromSelection);
} }
} else { } else {
for_each_dive(i, d) { for_each_dive(i, d) {
bool newStatus = showDive(d); bool newStatus = showDive(d);
updateDiveStatus(d, newStatus, res); updateDiveStatus(d, newStatus, res, removeFromSelection);
} }
} }
res.currentChanged = old_current != current_dive; updateSelection(selection, std::vector<dive *>(), removeFromSelection);
res.currentChanged = setSelection(selection);
return res; return res;
} }

View file

@ -57,8 +57,10 @@ public:
private: private:
DiveFilter(); DiveFilter();
bool showDive(const struct dive *d) const; // Should that dive be shown? bool showDive(const struct dive *d) const; // Should that dive be shown?
bool setFilterStatus(struct dive *d, bool shown) const; bool setFilterStatus(struct dive *d, bool shown,
void updateDiveStatus(dive *d, bool newStatus, ShownChange &change) const; std::vector<dive *> &removeFromSelection) const;
void updateDiveStatus(dive *d, bool newStatus, ShownChange &change,
std::vector<dive *> &removeFromSelection) const;
QVector<dive_site *> dive_sites; QVector<dive_site *> dive_sites;
FilterData filterData; FilterData filterData;

View file

@ -130,51 +130,63 @@ extern "C" void dump_selection(void)
} }
#endif #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<dive *> &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, // Set the current dive either from a list of selected dives,
// or a newly selected dive. In both cases, try to select the // 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. // This mimics the old behavior when the current dive changed.
// If a current dive outside of the selection was set, add // If a current dive outside of the selection was set, add
// it to the list of selected dives, so that we never end up // it to the list of selected dives, so that we never end up
// in a situation where we display a non-selected dive. // in a situation where we display a non-selected dive.
static void setClosestCurrentDive(timestamp_t when, const std::vector<dive *> &selection, QVector<dive *> &divesToSelect) static void setClosestCurrentDive(timestamp_t when, const std::vector<dive *> &selection, QVector<dive *> &divesToSelect)
{ {
// Start from back until we get the first dive that is before if (dive *d = closestInSelection(when, selection)) {
// the supposed-to-be selected dive. (Note: this mimics the current_dive = d;
// old behavior when the current dive changed). return;
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;
}
} }
// No selected dive is visible! Take the closest dive. Note, this might // No selected dive is visible! Take the closest dive. Note, this might
// return null, but that just means unsetting the current dive (as no // return null, but that just means unsetting the current dive (as no
// dive is visible anyway). // dive is visible anyway).
current_dive = find_next_visible_dive(when); current_dive = find_next_visible_dive(when);
if (current_dive) if (current_dive) {
current_dive->selected = true;
amount_selected++;
divesToSelect.push_back(current_dive); divesToSelect.push_back(current_dive);
}
} }
// Reset the selection to the dives of the "selection" vector and send the appropriate signals. // 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 // 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. // null if "selection" is empty). Return true if the current dive changed.
void setSelection(const std::vector<dive *> &selection, dive *currentDive, int currentDc) bool setSelection(const std::vector<dive *> &selection, dive *currentDive, int currentDc)
{ {
// To do so, generate vectors of dives to be selected and deselected. // 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. // We send signals batched by trip, so keep track of trip/dive pairs.
QVector<dive *> divesToSelect; QVector<dive *> divesToSelect;
divesToSelect.reserve(selection.size()); divesToSelect.reserve(selection.size());
const dive *oldCurrent = current_dive;
// Since we select only dives, there are no selected trips! // Since we select only dives, there are no selected trips!
amount_trips_selected = 0; amount_trips_selected = 0;
@ -223,6 +235,15 @@ void setSelection(const std::vector<dive *> &selection, dive *currentDive, int c
// Send the new selection // Send the new selection
emit diveListNotifier.divesSelected(divesToSelect); emit diveListNotifier.divesSelected(divesToSelect);
return current_dive != oldCurrent;
}
bool setSelection(const std::vector<dive *> &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) extern "C" void select_single_dive(dive *d)
@ -249,6 +270,32 @@ std::vector<dive *> getDiveSelection()
return res; return res;
} }
bool diveInSelection(const std::vector<dive *> &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<dive *> &selection, const std::vector<dive *> &add, const std::vector<dive *> &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 // Select the first dive that is visible
extern "C" void select_newest_visible_dive() extern "C" void select_newest_visible_dive()
{ {

View file

@ -45,10 +45,18 @@ extern void dump_selection(void);
// Set the current dive to "currentDive" and the current dive computer to "currentDc". // 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). // "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. // If "currentDc" is negative, an attempt will be made to keep the current computer number.
void setSelection(const std::vector<dive *> &selection, dive *currentDive, int currentDc); // Returns true if the current dive changed.
bool setSelection(const std::vector<dive *> &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<dive *> &selection);
// Get currently selected dives
std::vector<dive *> getDiveSelection(); std::vector<dive *> getDiveSelection();
bool diveInSelection(const std::vector<dive *> &selection, const dive *d);
void updateSelection(std::vector<dive *> &selection, const std::vector<dive *> &add, const std::vector<dive *> &remove);
#endif // __cplusplus #endif // __cplusplus