diff options
Diffstat (limited to 'qt-models')
-rw-r--r-- | qt-models/divelistmodel.cpp | 232 | ||||
-rw-r--r-- | qt-models/divelistmodel.h | 39 |
2 files changed, 209 insertions, 62 deletions
diff --git a/qt-models/divelistmodel.cpp b/qt-models/divelistmodel.cpp index 99d8bc588..c9bcaa2e1 100644 --- a/qt-models/divelistmodel.cpp +++ b/qt-models/divelistmodel.cpp @@ -5,7 +5,172 @@ #include "core/trip.h" #include "core/settings/qPrefGeneral.h" #include "core/ssrf.h" // for LOG_STP +#include "core/errorhelper.h" // for verbose #include <QDateTime> +#include <QDebug> + +// the DiveListSortModel creates the sorted, filtered list of dives that the user +// can flip through horizontally +// the CollapsedDiveListSortModel creates the vertical dive list which is a second +// filter on top of the one applied to the DiveListSortModel + +CollapsedDiveListSortModel::CollapsedDiveListSortModel() +{ + setSourceModel(DiveListSortModel::instance()); + setDynamicSortFilter(true); + updateFilterState(); + // make sure that we after changes to the underlying model (and therefore the dive list + // we update the filter state + connect(DiveListModel::instance(), &DiveListModel::rowsInserted, this, &CollapsedDiveListSortModel::updateFilterState); + connect(DiveListModel::instance(), &DiveListModel::rowsMoved, this, &CollapsedDiveListSortModel::updateFilterState); + connect(DiveListModel::instance(), &DiveListModel::rowsRemoved, this, &CollapsedDiveListSortModel::updateFilterState); +} + +CollapsedDiveListSortModel *CollapsedDiveListSortModel::instance() +{ + static CollapsedDiveListSortModel self; + return &self; +} + +void CollapsedDiveListSortModel::setSourceModel(QAbstractItemModel *sourceModel) +{ + QSortFilterProxyModel::setSourceModel(sourceModel); + updateFilterState(); +} + +// In QtQuick ListView, section headings can only be strings. To identify dives +// that belong to the same trip, a string containing the trip-id is passed in. +// To format the trip heading, the string is then converted back with this function. +static dive_trip *tripIdToObject(const QString &s) +{ + if (s.isEmpty()) + return nullptr; + int id = s.toInt(); + dive_trip **trip = std::find_if(&trip_table.trips[0], &trip_table.trips[trip_table.nr], + [id] (const dive_trip *t) { return t->id == id; }); + if (trip == &trip_table.trips[trip_table.nr]) { + fprintf(stderr, "Warning: unknown trip id passed through QML: %d\n", id); + return nullptr; + } + return *trip; +} + +// the trip title is designed to be location (# dives) +// or, if there is no location name date range (# dives) +// where the date range is given as "month year" or "month-month year" or "month year - month year" +QString CollapsedDiveListSortModel::tripTitle(const QString §ion) +{ + const dive_trip *dt = tripIdToObject(section); + if (!dt) + return QString(); + QString numDives = tr("(%n dive(s))", "", dt->dives.nr); + int shown = trip_shown_dives(dt); + QString shownDives = shown != dt->dives.nr ? QStringLiteral(" ") + tr("(%L1 shown)").arg(shown) : QString(); + QString title(dt->location); + + if (title.isEmpty()) { + // so use the date range + QDateTime firstTime = QDateTime::fromMSecsSinceEpoch(1000*trip_date(dt), Qt::UTC); + QString firstMonth = firstTime.toString("MMM"); + QString firstYear = firstTime.toString("yyyy"); + QDateTime lastTime = QDateTime::fromMSecsSinceEpoch(1000*dt->dives.dives[0]->when, Qt::UTC); + QString lastMonth = lastTime.toString("MMM"); + QString lastYear = lastTime.toString("yyyy"); + if (lastMonth == firstMonth && lastYear == firstYear) + title = firstMonth + " " + firstYear; + else if (lastMonth != firstMonth && lastYear == firstYear) + title = firstMonth + "-" + lastMonth + " " + firstYear; + else + title = firstMonth + " " + firstYear + " - " + lastMonth + " " + lastYear; + } + return QStringLiteral("%1 %2%3").arg(title, numDives, shownDives); +} + +QString CollapsedDiveListSortModel::tripShortDate(const QString §ion) +{ + const dive_trip *dt = tripIdToObject(section); + if (!dt) + return QString(); + QDateTime firstTime = QDateTime::fromMSecsSinceEpoch(1000*trip_date(dt), Qt::UTC); + QString firstMonth = firstTime.toString("MMM"); + return QStringLiteral("%1\n'%2").arg(firstMonth,firstTime.toString("yy")); +} + +void CollapsedDiveListSortModel::setActiveTrip(const QString &trip) +{ + m_activeTrip = trip; + updateFilterState(); + invalidateFilter(); +} + +QString CollapsedDiveListSortModel::activeTrip() const +{ + return m_activeTrip; +} + +// tell us if this dive is the first dive in a trip that has at least one +// dive that isn't hidden (even if this dive is hidden) +static bool isFirstInNotCompletelyHiddenTrip(struct dive *d) +{ + struct dive_trip *dt = d->divetrip; + if (dt->dives.nr > 0 && dt->dives.dives[0] == d) { + // ok, this is the first dive in its trip + int i = -1; + while (++i < dt->dives.nr) + if (!dt->dives.dives[i]->hidden_by_filter) + return true; + } + return false; +} + +// the mobile app allows only one selected dive +// that means there are either zero or exactly one expanded trip - +bool CollapsedDiveListSortModel::isExpanded(struct dive_trip *dt) const +{ + return !m_activeTrip.isEmpty() && dt == tripIdToObject(m_activeTrip); +} + +void CollapsedDiveListSortModel::updateFilterState() +{ + // now do something clever to show the right dives + // first make sure that the underlying filtering is taken care of + DiveListSortModel::instance()->updateFilterState(); + int i; + struct dive *d; + for_each_dive(i, d) { + CollapsedState state = DontShow; + struct dive_trip *dt = d->divetrip; + + // we show the dives that are outside of a trip or inside of the one expanded trip + if (!d->hidden_by_filter && (dt == nullptr || isExpanded(dt))) + state = ShowDive; + // we mark the first dive of a trip that contains any unfiltered dives as ShowTrip or ShowDiveAndTrip (if this is the one expanded trip) + if (dt != nullptr && isFirstInNotCompletelyHiddenTrip(d)) + state = (state == ShowDive) ? ShowDiveAndTrip : ShowTrip; + d->collapsed = state; + } + // everything up to here can be done even if we don't have a source model + if (sourceModel() != nullptr) { + QVector<int> changedRoles = { DiveListModel::CollapsedRole }; + dataChanged(index(0,0), index(rowCount() - 1, 0), changedRoles); + } +} + +void CollapsedDiveListSortModel::updateSelectionState() +{ + QVector<int> changedRoles = { DiveListModel::SelectedRole }; + dataChanged(index(0,0), index(rowCount() - 1, 0), changedRoles); +} + +bool CollapsedDiveListSortModel::filterAcceptsRow(int source_row, const QModelIndex &) const +{ + // get the corresponding dive from the DiveListModel and check if we should show it + const dive *d = DiveListModel::instance()->getDive(source_row); + if (verbose > 1) + qDebug() << "FAR source row" << source_row << "dive" << (d ? QString::number(d->number) : "NULL") << "is" << (d != nullptr && d->collapsed != DontShow) << + (d != nullptr ? QString::number(d->collapsed) : ""); + return d != nullptr && d->collapsed != DontShow; +} DiveListSortModel::DiveListSortModel() { @@ -23,6 +188,11 @@ DiveListSortModel *DiveListSortModel::instance() return &self; } +QString DiveListSortModel::getFilterString() const +{ + return filterString; +} + void DiveListSortModel::updateFilterState() { if (filterString.isEmpty()) { @@ -89,64 +259,6 @@ void DiveListSortModel::reload() mySourceModel->resetInternalData(); } -// In QtQuick ListView, section headings can only be strings. To identify dives -// that belong to the same trip, a string containing the trip-id is passed in. -// To format the trip heading, the string is then converted back with this function. -static dive_trip *tripIdToObject(const QString &s) -{ - if (s.isEmpty()) - return nullptr; - int id = s.toInt(); - dive_trip **trip = std::find_if(&trip_table.trips[0], &trip_table.trips[trip_table.nr], - [id] (const dive_trip *t) { return t->id == id; }); - if (trip == &trip_table.trips[trip_table.nr]) { - fprintf(stderr, "Warning: unknown trip id passed through QML: %d\n", id); - return nullptr; - } - return *trip; -} - -// the trip title is designed to be location (# dives) -// or, if there is no location name date range (# dives) -// where the date range is given as "month year" or "month-month year" or "month year - month year" -QString DiveListSortModel::tripTitle(const QString §ion) -{ - const dive_trip *dt = tripIdToObject(section); - if (!dt) - return QString(); - QString numDives = tr("(%n dive(s))", "", dt->dives.nr); - int shown = trip_shown_dives(dt); - QString shownDives = shown != dt->dives.nr ? QStringLiteral(" ") + tr("(%L1 shown)").arg(shown) : QString(); - QString title(dt->location); - - if (title.isEmpty()) { - // so use the date range - QDateTime firstTime = QDateTime::fromMSecsSinceEpoch(1000*trip_date(dt), Qt::UTC); - QString firstMonth = firstTime.toString("MMM"); - QString firstYear = firstTime.toString("yyyy"); - QDateTime lastTime = QDateTime::fromMSecsSinceEpoch(1000*dt->dives.dives[0]->when, Qt::UTC); - QString lastMonth = lastTime.toString("MMM"); - QString lastYear = lastTime.toString("yyyy"); - if (lastMonth == firstMonth && lastYear == firstYear) - title = firstMonth + " " + firstYear; - else if (lastMonth != firstMonth && lastYear == firstYear) - title = firstMonth + "-" + lastMonth + " " + firstYear; - else - title = firstMonth + " " + firstYear + " - " + lastMonth + " " + lastYear; - } - return QStringLiteral("%1 %2%3").arg(title, numDives, shownDives); -} - -QString DiveListSortModel::tripShortDate(const QString §ion) -{ - const dive_trip *dt = tripIdToObject(section); - if (!dt) - return QString(); - QDateTime firstTime = QDateTime::fromMSecsSinceEpoch(1000*trip_date(dt), Qt::UTC); - QString firstMonth = firstTime.toString("MMM"); - return QStringLiteral("%1\n'%2").arg(firstMonth,firstTime.toString("yy")); -} - DiveListModel::DiveListModel() { LOG_STP("run_ui diveListModel started"); @@ -281,6 +393,8 @@ QVariant DiveListModel::data(const QModelIndex &index, int role) const case StartPressureRole: return getStartPressure(d); case EndPressureRole: return getEndPressure(d); case FirstGasRole: return getFirstGas(d); + case CollapsedRole: return d->collapsed; + case SelectedRole: return d->selected; } return QVariant(); } @@ -319,6 +433,8 @@ QHash<int, QByteArray> DiveListModel::roleNames() const roles[StartPressureRole] = "startPressure"; roles[EndPressureRole] = "endPressure"; roles[FirstGasRole] = "firstGas"; + roles[CollapsedRole] = "collapsed"; + roles[SelectedRole] = "selected"; return roles; } diff --git a/qt-models/divelistmodel.h b/qt-models/divelistmodel.h index d9e0928dc..92e7a9291 100644 --- a/qt-models/divelistmodel.h +++ b/qt-models/divelistmodel.h @@ -7,15 +7,46 @@ #include "core/subsurface-qt/DiveObjectHelper.h" +class CollapsedDiveListSortModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + static CollapsedDiveListSortModel *instance(); + void setSourceModel(QAbstractItemModel *sourceModel); + Q_INVOKABLE QString tripTitle(const QString &trip); + Q_INVOKABLE QString tripShortDate(const QString &trip); + Q_INVOKABLE void setActiveTrip(const QString &trip); + Q_INVOKABLE QString activeTrip() const; + // super subtle optimization alert - in order to reduce the number of model accesses from QML, + // the two states where we show the dive in question have odd numbers + enum CollapsedState { + DontShow = 0, + ShowDive = 1, + ShowTrip = 2, + ShowDiveAndTrip = 3 + }; + void updateFilterState(); + void updateSelectionState(); + +protected: + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const; + +private: + CollapsedDiveListSortModel(); + bool isExpanded(struct dive_trip *dt) const; + QString m_activeTrip; +}; + class DiveListSortModel : public QSortFilterProxyModel { Q_OBJECT public: static DiveListSortModel *instance(); void setSourceModel(QAbstractItemModel *sourceModel); + QString getFilterString() const; Q_INVOKABLE void reload(); - Q_INVOKABLE QString tripTitle(const QString &trip); - Q_INVOKABLE QString tripShortDate(const QString &trip); + QString filterString; + void updateFilterState(); public slots: int getIdxForId(int id); void setFilter(QString f); @@ -25,8 +56,6 @@ protected: bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const; private: DiveListSortModel(); - QString filterString; - void updateFilterState(); }; QString formatSac(const dive *d); @@ -74,6 +103,8 @@ public: StartPressureRole, EndPressureRole, FirstGasRole, + CollapsedRole, + SelectedRole, }; static DiveListModel *instance(); |