mirror of
https://github.com/subsurface/subsurface.git
synced 2024-11-28 05:00:20 +00:00
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:
parent
9f455b1457
commit
6df1c62dfc
4 changed files with 98 additions and 39 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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()
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue