diff options
author | Berthold Stoeger <bstoeger@mail.tuwien.ac.at> | 2018-09-06 09:52:02 +0200 |
---|---|---|
committer | Dirk Hohndel <dirk@hohndel.org> | 2018-10-11 16:22:27 -0700 |
commit | 7150d1c6f61631079e7620901fd1939bbc54e621 (patch) | |
tree | f87e4bebdad98cffb7e1db7e675a1fc278e77b02 | |
parent | 11e0b7ac0a6db1a6f2d96ca4cb1f035f42181634 (diff) | |
download | subsurface-7150d1c6f61631079e7620901fd1939bbc54e621.tar.gz |
Filter: Make filters aware of added / removed dives
Instead of reloading all the filter, only increment / decrement the
count of the entries of added / removed dives.
Originally, this was planned to be done via the signals from the
divelist, but it turned out that this was suboptimal, because
if the filter decides that the new item is selected, this has to
be done *before* adding the dive. Otherwise, it wouldn't be shown.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
-rw-r--r-- | desktop-widgets/command_divelist.cpp | 14 | ||||
-rw-r--r-- | qt-models/filtermodels.cpp | 159 | ||||
-rw-r--r-- | qt-models/filtermodels.h | 15 |
3 files changed, 183 insertions, 5 deletions
diff --git a/desktop-widgets/command_divelist.cpp b/desktop-widgets/command_divelist.cpp index 057e852c0..1bc225086 100644 --- a/desktop-widgets/command_divelist.cpp +++ b/desktop-widgets/command_divelist.cpp @@ -109,6 +109,11 @@ std::vector<DiveToAdd> DiveListBase::removeDives(std::vector<dive *> &divesToDel std::vector<DiveToAdd> res; res.reserve(divesToDelete.size()); + // First, tell the filters that dives are removed. This could + // be done later using the emitted signals, but we do this here + // for symmetry with addDives() + MultiFilterSortModel::instance()->divesDeleted(QVector<dive *>::fromStdVector(divesToDelete)); + for (dive *d: divesToDelete) res.push_back(removeDive(d)); divesToDelete.clear(); @@ -141,6 +146,15 @@ std::vector<dive *> DiveListBase::addDives(std::vector<DiveToAdd> &divesToAdd) std::vector<dive *> res; res.resize(divesToAdd.size()); + // First, tell the filters that new dives are added. We do this here + // instead of later by signals, so that the filter can set the + // checkboxes of the new rows to its liking. The added dives will + // then appear in the correct shown/hidden state. + QVector<dive *> divesForFilter; + for (const DiveToAdd &entry: divesToAdd) + divesForFilter.push_back(entry.dive.get()); + MultiFilterSortModel::instance()->divesAdded(divesForFilter); + // At the end of the function, to send the proper dives-added signals, // we the the list of added trips. Create this list now. std::vector<dive_trip *> addedTrips; diff --git a/qt-models/filtermodels.cpp b/qt-models/filtermodels.cpp index f6ff5ea69..1491dbacf 100644 --- a/qt-models/filtermodels.cpp +++ b/qt-models/filtermodels.cpp @@ -4,6 +4,7 @@ #include "core/display.h" #include "core/qthelper.h" #include "core/subsurface-string.h" +#include "core/subsurface-qt/DiveListNotifier.h" #include "qt-models/divetripmodel.h" #if !defined(SUBSURFACE_MOBILE) @@ -159,6 +160,58 @@ void FilterModelBase::updateList(QStringList &newList) endResetModel(); } +// Decrease count of entry with given name. Remove if count reaches zero. +// Exception: Don't remove the "Show Empty Tags" entry. +void FilterModelBase::decreaseCount(const QString &name) +{ + if (name.isEmpty()) { + // Decrease the "Show Empty Tags" entry. Keep it even if count reaches 0. + if (items.empty() || items.back().count <= 0) + return; // Shouldn't happen! + --items.back().count; + int idx = items.size() - 1; + dataChanged(createIndex(idx, 0), createIndex(idx, 0)); + return; + } + + int idx = indexOf(name); + if (idx < 0 || items[idx].count <= 0) + return; // Shouldn't happen + + if(--items[idx].count == 0) { + beginRemoveRows(QModelIndex(), idx, idx); + items.erase(items.begin() + idx); + endRemoveRows(); + } else { + dataChanged(createIndex(idx, 0), createIndex(idx, 0)); + } +} + +// Increase count of entry with given name. If entry doesn't yet exist, add it. +void FilterModelBase::increaseCount(const QString &name) +{ + if (name.isEmpty()) { + // Increase the "Show Empty Tags" entry. Keep it even if count reaches 0. + if (items.empty()) + return; // Shouldn't happen! + ++items.back().count; + int idx = items.size() - 1; + dataChanged(createIndex(idx, 0), createIndex(idx, 0)); + return; + } + + int idx = indexOf(name); + if (idx < 0) { + idx = findInsertionIndex(name); + beginInsertRows(QModelIndex(), idx, idx); + items.insert(items.begin() + idx, { name, anyChecked, 1 }); + endInsertRows(); + } else { + ++items[idx].count; + dataChanged(createIndex(idx, 0), createIndex(idx, 0)); + } +} + Qt::ItemFlags FilterModelBase::flags(const QModelIndex &index) const { return QAbstractListModel::flags(index) | Qt::ItemIsUserCheckable; @@ -258,6 +311,16 @@ bool SuitsFilterModel::doFilter(const dive *d) const return negate; } +void SuitsFilterModel::diveAdded(const dive *d) +{ + increaseCount(QString(d->suit)); +} + +void SuitsFilterModel::diveDeleted(const dive *d) +{ + decreaseCount(QString(d->suit)); +} + void SuitsFilterModel::repopulate() { QStringList list; @@ -322,6 +385,33 @@ bool TagFilterModel::doFilter(const dive *d) const return negate; } +void TagFilterModel::diveAdded(const dive *d) +{ + struct tag_entry *head = d->tag_list; + if (!head) { + increaseCount(QString()); + return; + } + while (head) { + increaseCount(QString()); + increaseCount(QString(head->tag->name)); + head = head->next; + } +} + +void TagFilterModel::diveDeleted(const dive *d) +{ + struct tag_entry *head = d->tag_list; + if (!head) { + decreaseCount(QString()); + return; + } + while (head) { + decreaseCount(QString(head->tag->name)); + head = head->next; + } +} + BuddyFilterModel::BuddyFilterModel(QObject *parent) : FilterModelBase(parent) { } @@ -331,6 +421,15 @@ int BuddyFilterModel::countDives(const char *s) const return count_dives_with_person(s); } +static QStringList getDiveBuddies(const dive *d) +{ + QString persons = QString(d->buddy) + "," + QString(d->divemaster); + QStringList personsList = persons.split(',', QString::SkipEmptyParts); + for (QString &s: personsList) + s = s.trimmed(); + return personsList; +} + bool BuddyFilterModel::doFilter(const dive *d) const { // If there's nothing checked, this should show everything @@ -339,11 +438,7 @@ bool BuddyFilterModel::doFilter(const dive *d) const if (!anyChecked || rowCount() == 0) return true; - // Checked means 'Show', Unchecked means 'Hide'. - QString persons = QString(d->buddy) + "," + QString(d->divemaster); - QStringList personsList = persons.split(',', QString::SkipEmptyParts); - for (QString &s: personsList) - s = s.trimmed(); + QStringList personsList = getDiveBuddies(d); // only show empty buddie dives if the user checked that. if (personsList.isEmpty()) return items[rowCount() - 1].checked != negate; @@ -357,6 +452,28 @@ bool BuddyFilterModel::doFilter(const dive *d) const return negate; } +void BuddyFilterModel::diveAdded(const dive *d) +{ + QStringList buddies = getDiveBuddies(d); + if (buddies.empty()) { + increaseCount(QString()); + return; + } + for(const QString &buddy: buddies) + increaseCount(buddy); +} + +void BuddyFilterModel::diveDeleted(const dive *d) +{ + QStringList buddies = getDiveBuddies(d); + if (buddies.empty()) { + decreaseCount(QString()); + return; + } + for(const QString &buddy: buddies) + decreaseCount(buddy); +} + void BuddyFilterModel::repopulate() { QStringList list; @@ -406,6 +523,16 @@ bool LocationFilterModel::doFilter(const dive *d) const return negate; } +void LocationFilterModel::diveAdded(const dive *d) +{ + increaseCount(get_dive_location(d)); +} + +void LocationFilterModel::diveDeleted(const dive *d) +{ + decreaseCount(get_dive_location(d)); +} + void LocationFilterModel::repopulate() { QStringList list; @@ -437,6 +564,28 @@ MultiFilterSortModel::MultiFilterSortModel(QObject *parent) : QSortFilterProxyMo { } +void MultiFilterSortModel::divesAdded(const QVector<dive *> &dives) +{ + // TODO: We call diveAdded for every dive and model. + // If multiple dives are added (e.g. import dive) this will lead to a large + // number of model changes and might be a pessimization compared to a full + // model reload. Instead, the models should take the vector, calculate the + // new fields and add them at once. + for (FilterModelBase *model: models) { + for (const dive *d: dives) + model->diveAdded(d); + } +} + +void MultiFilterSortModel::divesDeleted(const QVector<dive *> &dives) +{ + // TODO: See comment for divesDeleted + for (FilterModelBase *model: models) { + for (const dive *d: dives) + model->diveDeleted(d); + } +} + bool MultiFilterSortModel::showDive(const struct dive *d) const { if (curr_dive_site) { diff --git a/qt-models/filtermodels.h b/qt-models/filtermodels.h index 08e99fd93..3f5ca5f01 100644 --- a/qt-models/filtermodels.h +++ b/qt-models/filtermodels.h @@ -8,6 +8,7 @@ #include <vector> struct dive; +struct dive_trip; class FilterModelBase : public QAbstractListModel { Q_OBJECT @@ -23,8 +24,12 @@ protected: int indexOf(const QString &name) const; void addItem(const QString &name, bool checked, int count); int rowCount(const QModelIndex &parent = QModelIndex()) const override; + void decreaseCount(const QString &d); + void increaseCount(const QString &d); public: virtual bool doFilter(const dive *d) const = 0; + virtual void diveAdded(const dive *d) = 0; + virtual void diveDeleted(const dive *d) = 0; void clearFilter(); void selectAll(); void invertSelection(); @@ -56,6 +61,8 @@ slots: private: explicit TagFilterModel(QObject *parent = 0); int countDives(const char *) const; + void diveAdded(const dive *d); + void diveDeleted(const dive *d); }; class BuddyFilterModel : public FilterModelBase { @@ -70,6 +77,8 @@ slots: private: explicit BuddyFilterModel(QObject *parent = 0); int countDives(const char *) const; + void diveAdded(const dive *d); + void diveDeleted(const dive *d); }; class LocationFilterModel : public FilterModelBase { @@ -85,6 +94,8 @@ slots: private: explicit LocationFilterModel(QObject *parent = 0); int countDives(const char *) const; + void diveAdded(const dive *d); + void diveDeleted(const dive *d); }; class SuitsFilterModel : public FilterModelBase { @@ -99,6 +110,8 @@ slots: private: explicit SuitsFilterModel(QObject *parent = 0); int countDives(const char *) const; + void diveAdded(const dive *d); + void diveDeleted(const dive *d); }; class MultiFilterSortModel : public QSortFilterProxyModel { @@ -108,6 +121,8 @@ public: bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; void addFilterModel(FilterModelBase *model); void removeFilterModel(FilterModelBase *model); + void divesAdded(const QVector<dive *> &dives); + void divesDeleted(const QVector<dive *> &dives); bool showDive(const struct dive *d) const; int divesDisplayed; public |