2017-04-27 18:25:32 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2016-04-05 05:02:03 +00:00
|
|
|
#include "qt-models/divelistmodel.h"
|
2018-06-03 20:15:19 +00:00
|
|
|
#include "core/qthelper.h"
|
2019-05-31 14:09:14 +00:00
|
|
|
#include "core/trip.h"
|
2018-10-20 01:17:39 +00:00
|
|
|
#include "core/settings/qPrefGeneral.h"
|
2018-10-22 13:00:53 +00:00
|
|
|
#include <QDateTime>
|
2015-06-09 19:20:44 +00:00
|
|
|
|
2016-01-29 02:23:14 +00:00
|
|
|
DiveListSortModel::DiveListSortModel(QObject *parent) : QSortFilterProxyModel(parent)
|
|
|
|
{
|
2018-10-22 13:00:53 +00:00
|
|
|
updateFilterState();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DiveListSortModel::updateFilterState()
|
|
|
|
{
|
|
|
|
if (filterString.isEmpty()) {
|
2018-11-22 22:28:06 +00:00
|
|
|
resetFilter();
|
2018-10-22 13:00:53 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
// store this in local variables to avoid having to call these methods over and over
|
|
|
|
bool includeNotes = qPrefGeneral::filterFullTextNotes();
|
|
|
|
Qt::CaseSensitivity cs = qPrefGeneral::filterCaseSensitive() ? Qt::CaseSensitive : Qt::CaseInsensitive;
|
2016-01-29 02:23:14 +00:00
|
|
|
|
2019-08-13 18:23:54 +00:00
|
|
|
int i;
|
|
|
|
struct dive *d;
|
|
|
|
for_each_dive(i, d)
|
|
|
|
d->hidden_by_filter = !diveContainsText(d, filterString, cs, includeNotes);
|
2016-01-29 02:23:14 +00:00
|
|
|
}
|
|
|
|
|
2018-10-22 13:00:53 +00:00
|
|
|
void DiveListSortModel::setSourceModel(QAbstractItemModel *sourceModel)
|
|
|
|
{
|
|
|
|
QSortFilterProxyModel::setSourceModel(sourceModel);
|
|
|
|
}
|
2019-08-13 18:23:54 +00:00
|
|
|
|
2018-02-04 15:46:03 +00:00
|
|
|
void DiveListSortModel::setFilter(QString f)
|
|
|
|
{
|
2018-10-22 13:00:53 +00:00
|
|
|
filterString = f;
|
|
|
|
updateFilterState();
|
|
|
|
invalidateFilter();
|
2018-02-04 15:46:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DiveListSortModel::resetFilter()
|
|
|
|
{
|
2018-11-22 22:28:06 +00:00
|
|
|
int i;
|
|
|
|
struct dive *d;
|
|
|
|
for_each_dive(i, d)
|
|
|
|
d->hidden_by_filter = false;
|
2018-10-22 13:00:53 +00:00
|
|
|
invalidateFilter();
|
|
|
|
}
|
|
|
|
|
|
|
|
// filtering is way too slow on mobile. Maybe we should roll our own?
|
2018-11-22 22:28:06 +00:00
|
|
|
bool DiveListSortModel::filterAcceptsRow(int source_row, const QModelIndex &) const
|
2018-10-22 13:00:53 +00:00
|
|
|
{
|
2018-11-22 22:28:06 +00:00
|
|
|
DiveListModel *mySourceModel = qobject_cast<DiveListModel *>(sourceModel());
|
2019-08-13 19:11:08 +00:00
|
|
|
const dive *d = mySourceModel->getDive(source_row);
|
|
|
|
return d && !d->hidden_by_filter;
|
2018-02-04 15:46:03 +00:00
|
|
|
}
|
|
|
|
|
2018-10-17 10:28:53 +00:00
|
|
|
int DiveListSortModel::shown()
|
|
|
|
{
|
|
|
|
return rowCount();
|
|
|
|
}
|
|
|
|
|
2016-01-29 02:23:14 +00:00
|
|
|
int DiveListSortModel::getIdxForId(int id)
|
|
|
|
{
|
2019-08-13 19:31:53 +00:00
|
|
|
DiveListModel *mySourceModel = qobject_cast<DiveListModel *>(sourceModel());
|
|
|
|
QModelIndex sourceIdx = mySourceModel->getDiveQIdx(id);
|
|
|
|
if (!sourceIdx.isValid())
|
|
|
|
return -1;
|
|
|
|
QModelIndex localIdx = mapFromSource(sourceIdx);
|
|
|
|
return localIdx.row();
|
2016-01-29 02:23:14 +00:00
|
|
|
}
|
|
|
|
|
2019-08-14 16:09:17 +00:00
|
|
|
void DiveListSortModel::reload()
|
2017-05-28 18:48:30 +00:00
|
|
|
{
|
|
|
|
DiveListModel *mySourceModel = qobject_cast<DiveListModel *>(sourceModel());
|
2019-08-14 16:09:17 +00:00
|
|
|
mySourceModel->reload();
|
2017-05-28 18:48:30 +00:00
|
|
|
}
|
|
|
|
|
2019-09-14 17:43:40 +00:00
|
|
|
// 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.
|
2019-09-14 17:58:30 +00:00
|
|
|
static dive_trip *tripIdToObject(const QString &s)
|
2018-11-22 21:10:38 +00:00
|
|
|
{
|
|
|
|
if (s.isEmpty())
|
2019-09-14 17:58:30 +00:00
|
|
|
return nullptr;
|
2019-09-14 17:43:40 +00:00
|
|
|
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);
|
2019-09-14 17:58:30 +00:00
|
|
|
return nullptr;
|
2019-09-14 17:43:40 +00:00
|
|
|
}
|
2019-09-14 17:58:30 +00:00
|
|
|
return *trip;
|
2018-11-22 21:10:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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"
|
2019-09-14 17:58:30 +00:00
|
|
|
QString DiveListSortModel::tripTitle(const QString §ion)
|
2018-11-22 21:10:38 +00:00
|
|
|
{
|
2019-09-14 17:58:30 +00:00
|
|
|
const dive_trip *dt = tripIdToObject(section);
|
2018-11-22 21:10:38 +00:00
|
|
|
if (!dt)
|
|
|
|
return QString();
|
2018-11-22 22:28:06 +00:00
|
|
|
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();
|
2018-11-22 21:10:38 +00:00
|
|
|
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;
|
|
|
|
}
|
2018-11-22 22:28:06 +00:00
|
|
|
return QStringLiteral("%1 %2%3").arg(title, numDives, shownDives);
|
2018-11-22 21:10:38 +00:00
|
|
|
}
|
|
|
|
|
2019-09-14 17:58:30 +00:00
|
|
|
QString DiveListSortModel::tripShortDate(const QString §ion)
|
2018-11-22 21:10:38 +00:00
|
|
|
{
|
2019-09-14 17:58:30 +00:00
|
|
|
const dive_trip *dt = tripIdToObject(section);
|
2018-11-22 21:10:38 +00:00
|
|
|
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"));
|
|
|
|
}
|
|
|
|
|
2019-09-27 23:26:54 +00:00
|
|
|
DiveListModel *DiveListModel::m_instance = NULL;
|
|
|
|
|
2015-06-09 19:20:44 +00:00
|
|
|
DiveListModel::DiveListModel(QObject *parent) : QAbstractListModel(parent)
|
|
|
|
{
|
2019-09-27 23:26:54 +00:00
|
|
|
m_instance = this;
|
2015-06-09 19:20:44 +00:00
|
|
|
}
|
|
|
|
|
2019-08-13 15:22:15 +00:00
|
|
|
void DiveListModel::insertDive(int i)
|
2016-01-27 19:27:41 +00:00
|
|
|
{
|
|
|
|
beginInsertRows(QModelIndex(), i, i);
|
|
|
|
endInsertRows();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DiveListModel::removeDive(int i)
|
|
|
|
{
|
|
|
|
beginRemoveRows(QModelIndex(), i, i);
|
|
|
|
endRemoveRows();
|
|
|
|
}
|
|
|
|
|
2016-01-29 14:25:13 +00:00
|
|
|
void DiveListModel::removeDiveById(int id)
|
|
|
|
{
|
2019-08-13 06:19:04 +00:00
|
|
|
for (int i = 0; i < dive_table.nr; i++) {
|
|
|
|
if (dive_table.dives[i]->id == id) {
|
2016-01-29 14:25:13 +00:00
|
|
|
removeDive(i);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-27 19:50:04 +00:00
|
|
|
void DiveListModel::updateDive(int i, dive *d)
|
|
|
|
{
|
2018-06-25 08:39:56 +00:00
|
|
|
// we need to make sure that QML knows that this dive has changed -
|
|
|
|
// the only reliable way I've found is to remove and re-insert it
|
|
|
|
removeDive(i);
|
2019-08-13 15:22:15 +00:00
|
|
|
insertDive(i);
|
2015-12-08 06:23:09 +00:00
|
|
|
}
|
|
|
|
|
2019-08-14 16:09:17 +00:00
|
|
|
void DiveListModel::reload()
|
2015-11-30 18:09:46 +00:00
|
|
|
{
|
2019-08-13 06:19:04 +00:00
|
|
|
beginResetModel();
|
|
|
|
endResetModel();
|
2015-11-30 18:09:46 +00:00
|
|
|
}
|
|
|
|
|
2018-01-07 15:08:25 +00:00
|
|
|
void DiveListModel::resetInternalData()
|
|
|
|
{
|
|
|
|
// this is a hack. There is a long standing issue, that seems related to a
|
|
|
|
// sync problem between QML engine and underlying model data. It causes delete
|
|
|
|
// from divelist (on mobile) to crash. But not always. This function is part of
|
|
|
|
// an attempt to fix this. See commit.
|
|
|
|
beginResetModel();
|
|
|
|
endResetModel();
|
|
|
|
}
|
|
|
|
|
2015-06-09 19:20:44 +00:00
|
|
|
int DiveListModel::rowCount(const QModelIndex &) const
|
|
|
|
{
|
2019-08-13 06:19:04 +00:00
|
|
|
return dive_table.nr;
|
2015-06-09 19:20:44 +00:00
|
|
|
}
|
|
|
|
|
2019-08-13 19:31:53 +00:00
|
|
|
// Get the index of a dive in the global dive list by the dive's unique id. Returns an integer [0..nrdives).
|
2016-03-02 12:41:36 +00:00
|
|
|
int DiveListModel::getDiveIdx(int id) const
|
|
|
|
{
|
2019-08-13 06:19:04 +00:00
|
|
|
return get_idx_by_uniq_id(id);
|
2016-03-02 12:41:36 +00:00
|
|
|
}
|
|
|
|
|
2019-08-13 19:31:53 +00:00
|
|
|
// Get an index of a dive. In contrast to getDiveIdx, this returns a Qt model-index,
|
|
|
|
// which can be used to access data of a Qt model.
|
|
|
|
QModelIndex DiveListModel::getDiveQIdx(int id)
|
|
|
|
{
|
|
|
|
int idx = getDiveIdx(id);
|
|
|
|
return idx >= 0 ? createIndex(idx, 0) : QModelIndex();
|
|
|
|
}
|
|
|
|
|
2015-06-09 19:20:44 +00:00
|
|
|
QVariant DiveListModel::data(const QModelIndex &index, int role) const
|
|
|
|
{
|
2019-08-13 06:19:04 +00:00
|
|
|
if(index.row() < 0 || index.row() >= dive_table.nr)
|
2015-06-09 19:20:44 +00:00
|
|
|
return QVariant();
|
|
|
|
|
2019-08-13 19:05:55 +00:00
|
|
|
dive *d = dive_table.dives[index.row()];
|
2019-08-14 22:11:39 +00:00
|
|
|
if (!d)
|
|
|
|
return QVariant();
|
2016-01-07 18:01:24 +00:00
|
|
|
switch(role) {
|
2019-08-13 19:05:55 +00:00
|
|
|
case DiveRole: return QVariant::fromValue(DiveObjectHelper(d));
|
2019-08-13 05:28:24 +00:00
|
|
|
case DiveDateRole: return (qlonglong)d->when;
|
2019-09-14 17:43:40 +00:00
|
|
|
// We have to return a QString as trip-id, because that will be used as section
|
|
|
|
// variable in the QtQuick list view. That has to be a string because it will try
|
|
|
|
// to do locale-aware sorting. And amazingly this can't be changed.
|
|
|
|
case TripIdRole: return d->divetrip ? QString::number(d->divetrip->id) : QString();
|
2019-08-14 22:03:15 +00:00
|
|
|
case TripNrDivesRole: return d->divetrip ? d->divetrip->dives.nr : 0;
|
2019-08-14 22:11:39 +00:00
|
|
|
case DateTimeRole: {
|
|
|
|
QDateTime localTime = QDateTime::fromMSecsSinceEpoch(1000 * d->when, Qt::UTC);
|
|
|
|
localTime.setTimeSpec(Qt::UTC);
|
|
|
|
return QStringLiteral("%1 %2").arg(localTime.date().toString(prefs.date_format_short),
|
|
|
|
localTime.time().toString(prefs.time_format));
|
|
|
|
}
|
2019-08-14 22:15:30 +00:00
|
|
|
case IdRole: return d->id;
|
2019-08-14 22:18:25 +00:00
|
|
|
case NumberRole: return d->number;
|
2019-08-14 22:23:25 +00:00
|
|
|
case LocationRole: return get_dive_location(d);
|
2019-08-14 22:30:56 +00:00
|
|
|
case DepthDurationRole: return QStringLiteral("%1 / %2").arg(get_depth_string(d->dc.maxdepth.mm, true, true),
|
|
|
|
get_dive_duration_string(d->duration.seconds, gettextFromC::tr("h"), gettextFromC::tr("min")));
|
2016-01-07 18:01:24 +00:00
|
|
|
}
|
2015-06-09 19:20:44 +00:00
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
|
|
|
|
QHash<int, QByteArray> DiveListModel::roleNames() const
|
|
|
|
{
|
|
|
|
QHash<int, QByteArray> roles;
|
2016-01-07 18:01:24 +00:00
|
|
|
roles[DiveRole] = "dive";
|
|
|
|
roles[DiveDateRole] = "date";
|
2019-08-14 21:53:28 +00:00
|
|
|
roles[TripIdRole] = "tripId";
|
2019-08-14 22:03:15 +00:00
|
|
|
roles[TripNrDivesRole] = "tripNrDives";
|
2019-08-14 22:11:39 +00:00
|
|
|
roles[DateTimeRole] = "dateTime";
|
2019-08-14 22:15:30 +00:00
|
|
|
roles[IdRole] = "id";
|
2019-08-14 22:18:25 +00:00
|
|
|
roles[NumberRole] = "number";
|
2019-08-14 22:23:25 +00:00
|
|
|
roles[LocationRole] = "location";
|
2019-08-14 22:30:56 +00:00
|
|
|
roles[DepthDurationRole] = "depthDuration";
|
2015-06-09 19:20:44 +00:00
|
|
|
return roles;
|
|
|
|
}
|
|
|
|
|
2015-12-27 05:34:45 +00:00
|
|
|
// create a new dive. set the current time and add it to the end of the dive list
|
2015-12-27 05:37:18 +00:00
|
|
|
QString DiveListModel::startAddDive()
|
2015-08-12 12:01:39 +00:00
|
|
|
{
|
2015-08-16 08:57:18 +00:00
|
|
|
struct dive *d;
|
|
|
|
d = alloc_dive();
|
2015-12-27 05:34:45 +00:00
|
|
|
d->when = QDateTime::currentMSecsSinceEpoch() / 1000L + gettimezoneoffset();
|
2016-01-29 19:53:22 +00:00
|
|
|
|
|
|
|
// find the highest dive nr we have and pick the next one
|
|
|
|
struct dive *pd;
|
|
|
|
int i, nr = 0;
|
|
|
|
for_each_dive(i, pd) {
|
|
|
|
if (pd->number > nr)
|
|
|
|
nr = pd->number;
|
|
|
|
}
|
|
|
|
nr++;
|
2015-12-27 05:34:45 +00:00
|
|
|
d->number = nr;
|
2016-01-01 08:32:30 +00:00
|
|
|
d->dc.model = strdup("manually added dive");
|
2019-04-14 13:37:19 +00:00
|
|
|
append_dive(d);
|
2019-08-13 15:22:15 +00:00
|
|
|
insertDive(get_idx_by_uniq_id(d->id));
|
2015-12-27 05:37:18 +00:00
|
|
|
return QString::number(d->id);
|
2015-08-12 12:01:39 +00:00
|
|
|
}
|
|
|
|
|
2019-09-27 23:26:54 +00:00
|
|
|
DiveListModel *DiveListModel::instance()
|
|
|
|
{
|
|
|
|
return m_instance;
|
|
|
|
}
|
|
|
|
|
2019-08-13 19:11:08 +00:00
|
|
|
struct dive *DiveListModel::getDive(int i)
|
2016-01-29 14:25:13 +00:00
|
|
|
{
|
2019-08-13 06:19:04 +00:00
|
|
|
if (i < 0 || i >= dive_table.nr) {
|
2019-08-13 19:11:08 +00:00
|
|
|
qWarning("DiveListModel::getDive(): accessing invalid dive with id %d", i);
|
|
|
|
return nullptr;
|
2019-08-13 06:19:04 +00:00
|
|
|
}
|
2019-08-13 19:11:08 +00:00
|
|
|
return dive_table.dives[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
DiveObjectHelper DiveListModel::at(int i)
|
|
|
|
{
|
|
|
|
// For an invalid id, returns an invalid DiveObjectHelper that will crash on access.
|
|
|
|
dive *d = getDive(i);
|
|
|
|
return d ? DiveObjectHelper(d) : DiveObjectHelper();
|
2016-01-27 19:27:41 +00:00
|
|
|
}
|