diff options
-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 |