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/filtermodels.h"
|
|
|
|
#include "qt-models/models.h"
|
|
|
|
#include "core/display.h"
|
2018-08-28 18:44:11 +00:00
|
|
|
#include "core/qthelper.h"
|
2019-03-04 22:20:29 +00:00
|
|
|
#include "core/divesite.h"
|
2019-05-31 14:09:14 +00:00
|
|
|
#include "core/trip.h"
|
2018-05-11 15:25:41 +00:00
|
|
|
#include "core/subsurface-string.h"
|
2018-09-06 07:52:02 +00:00
|
|
|
#include "core/subsurface-qt/DiveListNotifier.h"
|
2016-04-05 05:02:03 +00:00
|
|
|
#include "qt-models/divetripmodel.h"
|
2016-07-09 19:45:55 +00:00
|
|
|
|
|
|
|
#if !defined(SUBSURFACE_MOBILE)
|
2016-07-07 19:55:17 +00:00
|
|
|
#include "desktop-widgets/divelistview.h"
|
|
|
|
#include "desktop-widgets/mainwindow.h"
|
2019-08-31 06:46:11 +00:00
|
|
|
#include "desktop-widgets/mapwidget.h"
|
2016-07-09 19:45:55 +00:00
|
|
|
#endif
|
2015-09-03 17:20:19 +00:00
|
|
|
|
|
|
|
#include <QDebug>
|
Replace bool * array by std::vector<char> in MultiFilterInterface
This replaces a dynamically allocated array of bool by std::vector<char>.
1) This makes the code shorter and less error prone, because memory
management has not to be done by hand.
2) It fixes a bug in the old code:
memset(checkState, false, list.count()) is wrong, because bool is
not guaranteed to be the same size as char!
Two notes:
1) QMap<>, QVector<>, etc. are used numerous times in the code, so
this doesn't introduce a new C++ concept. Here, the std:: version
is used, because there is no need for reference counting, COW
semantics, etc.
2) std::vector<char> is used instead of std::vector<bool>, because
the latter does a pessimization where a bitfield is used!
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2017-11-25 14:04:38 +00:00
|
|
|
#include <algorithm>
|
2014-11-13 18:31:03 +00:00
|
|
|
|
2018-11-16 12:49:19 +00:00
|
|
|
namespace {
|
2019-01-19 08:59:33 +00:00
|
|
|
// Check if a string-list contains at least one string containing the second argument.
|
|
|
|
// Comparison is non case sensitive and removes white space.
|
|
|
|
bool listContainsSuperstring(const QStringList &list, const QString &s)
|
2018-11-16 12:49:19 +00:00
|
|
|
{
|
2019-01-19 08:59:33 +00:00
|
|
|
return std::any_of(list.begin(), list.end(), [&s](const QString &s2)
|
2019-01-22 08:32:39 +00:00
|
|
|
{ return s2.trimmed().contains(s.trimmed(), Qt::CaseInsensitive); } );
|
2018-11-16 12:49:19 +00:00
|
|
|
}
|
|
|
|
|
2019-02-19 15:31:21 +00:00
|
|
|
// Check whether either all, any or none of the items of the first list is
|
|
|
|
// in the second list as a super string.
|
|
|
|
// The mode is controlled by the second argument
|
|
|
|
bool check(const QStringList &items, const QStringList &list, FilterData::Mode mode)
|
|
|
|
{
|
|
|
|
bool negate = mode == FilterData::Mode::NONE_OF;
|
|
|
|
bool any_of = mode == FilterData::Mode::ANY_OF;
|
|
|
|
auto fun = [&list, negate](const QString &item)
|
|
|
|
{ return listContainsSuperstring(list, item) != negate; };
|
|
|
|
return any_of ? std::any_of(items.begin(), items.end(), fun)
|
|
|
|
: std::all_of(items.begin(), items.end(), fun);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool hasTags(const QStringList &tags, const struct dive *d, FilterData::Mode mode)
|
2018-11-16 12:49:19 +00:00
|
|
|
{
|
2019-01-19 08:59:33 +00:00
|
|
|
if (tags.isEmpty())
|
|
|
|
return true;
|
|
|
|
QStringList dive_tags = get_taglist_string(d->tag_list).split(",");
|
2019-02-23 19:37:45 +00:00
|
|
|
dive_tags.append(gettextFromC::tr(divemode_text_ui[d->dc.divemode]));
|
2019-02-19 15:31:21 +00:00
|
|
|
return check(tags, dive_tags, mode);
|
2018-11-16 12:49:19 +00:00
|
|
|
}
|
|
|
|
|
2019-02-19 15:31:21 +00:00
|
|
|
bool hasPersons(const QStringList &people, const struct dive *d, FilterData::Mode mode)
|
2018-11-16 12:49:19 +00:00
|
|
|
{
|
2019-01-19 08:59:33 +00:00
|
|
|
if (people.isEmpty())
|
|
|
|
return true;
|
|
|
|
QStringList dive_people = QString(d->buddy).split(",", QString::SkipEmptyParts)
|
|
|
|
+ QString(d->divemaster).split(",", QString::SkipEmptyParts);
|
2019-02-19 15:31:21 +00:00
|
|
|
return check(people, dive_people, mode);
|
2019-01-19 08:59:33 +00:00
|
|
|
}
|
|
|
|
|
2019-02-19 15:31:21 +00:00
|
|
|
bool hasLocations(const QStringList &locations, const struct dive *d, FilterData::Mode mode)
|
2019-01-19 08:59:33 +00:00
|
|
|
{
|
|
|
|
if (locations.isEmpty())
|
|
|
|
return true;
|
|
|
|
QStringList diveLocations;
|
|
|
|
if (d->divetrip)
|
|
|
|
diveLocations.push_back(QString(d->divetrip->location));
|
|
|
|
|
|
|
|
if (d->dive_site)
|
|
|
|
diveLocations.push_back(QString(d->dive_site->name));
|
|
|
|
|
2019-02-19 15:31:21 +00:00
|
|
|
return check(locations, diveLocations, mode);
|
2018-11-16 12:49:19 +00:00
|
|
|
}
|
2018-11-16 14:23:39 +00:00
|
|
|
|
2019-02-19 15:31:21 +00:00
|
|
|
// TODO: Finish this implementation.
|
|
|
|
bool hasEquipment(const QStringList &, const struct dive *, FilterData::Mode)
|
2018-11-16 14:23:39 +00:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2019-02-28 23:35:34 +00:00
|
|
|
|
|
|
|
bool hasSuits(const QStringList &suits, const struct dive *d, FilterData::Mode mode)
|
|
|
|
{
|
|
|
|
if (suits.isEmpty())
|
|
|
|
return true;
|
|
|
|
QStringList diveSuits;
|
|
|
|
if (d->suit)
|
|
|
|
diveSuits.push_back(QString(d->suit));
|
|
|
|
return check(suits, diveSuits, mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool hasNotes(const QStringList &dnotes, const struct dive *d, FilterData::Mode mode)
|
|
|
|
{
|
|
|
|
if (dnotes.isEmpty())
|
|
|
|
return true;
|
|
|
|
QStringList diveNotes;
|
|
|
|
if (d->notes)
|
|
|
|
diveNotes.push_back(QString(d->notes));
|
|
|
|
return check(dnotes, diveNotes, mode);
|
|
|
|
}
|
|
|
|
|
2018-11-16 12:49:19 +00:00
|
|
|
}
|
|
|
|
|
2018-10-21 16:00:02 +00:00
|
|
|
MultiFilterSortModel *MultiFilterSortModel::instance()
|
2018-09-06 07:52:02 +00:00
|
|
|
{
|
2018-10-21 16:00:02 +00:00
|
|
|
static MultiFilterSortModel self;
|
|
|
|
return &self;
|
2017-11-26 21:21:58 +00:00
|
|
|
}
|
|
|
|
|
2017-12-24 16:39:21 +00:00
|
|
|
MultiFilterSortModel::MultiFilterSortModel(QObject *parent) : QSortFilterProxyModel(parent),
|
2019-05-05 10:18:04 +00:00
|
|
|
divesDisplayed(0),
|
|
|
|
diveSiteRefCount(0)
|
2014-11-13 18:31:03 +00:00
|
|
|
{
|
2018-10-29 13:56:48 +00:00
|
|
|
setFilterKeyColumn(-1); // filter all columns
|
|
|
|
setFilterCaseSensitivity(Qt::CaseInsensitive);
|
2019-01-25 20:30:43 +00:00
|
|
|
|
|
|
|
connect(&diveListNotifier, &DiveListNotifier::divesAdded, this, &MultiFilterSortModel::divesAdded);
|
|
|
|
connect(&diveListNotifier, &DiveListNotifier::divesDeleted, this, &MultiFilterSortModel::divesDeleted);
|
2018-10-29 13:56:48 +00:00
|
|
|
}
|
|
|
|
|
2018-12-27 09:06:11 +00:00
|
|
|
void MultiFilterSortModel::resetModel(DiveTripModelBase::Layout layout)
|
2018-10-29 13:56:48 +00:00
|
|
|
{
|
2018-12-27 09:06:11 +00:00
|
|
|
DiveTripModelBase::resetModel(layout);
|
|
|
|
// DiveTripModelBase::resetModel() generates a new instance.
|
|
|
|
// Thus, the source model must be reset.
|
|
|
|
setSourceModel(DiveTripModelBase::instance());
|
2014-11-13 18:31:03 +00:00
|
|
|
}
|
|
|
|
|
2018-08-14 18:16:25 +00:00
|
|
|
bool MultiFilterSortModel::showDive(const struct dive *d) const
|
2014-11-13 18:31:03 +00:00
|
|
|
{
|
2019-08-31 07:09:14 +00:00
|
|
|
if (diveSiteMode())
|
2019-04-12 13:47:41 +00:00
|
|
|
return dive_sites.contains(d->dive_site);
|
2019-01-18 18:14:43 +00:00
|
|
|
|
2018-11-16 12:49:19 +00:00
|
|
|
if (!filterData.validFilter)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (d->visibility < filterData.minVisibility || d->visibility > filterData.maxVisibility)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (d->rating < filterData.minRating || d->rating > filterData.maxRating)
|
|
|
|
return false;
|
|
|
|
|
2019-01-20 20:01:00 +00:00
|
|
|
auto temp_comp = prefs.units.temperature == units::CELSIUS ? C_to_mkelvin : F_to_mkelvin;
|
2019-01-18 16:08:48 +00:00
|
|
|
if (d->watertemp.mkelvin &&
|
2019-01-20 20:01:00 +00:00
|
|
|
(d->watertemp.mkelvin < (*temp_comp)(filterData.minWaterTemp) || d->watertemp.mkelvin > (*temp_comp)(filterData.maxWaterTemp)))
|
2018-11-16 12:49:19 +00:00
|
|
|
return false;
|
|
|
|
|
2019-01-18 16:08:48 +00:00
|
|
|
if (d->airtemp.mkelvin &&
|
2019-01-20 20:01:00 +00:00
|
|
|
(d->airtemp.mkelvin < (*temp_comp)(filterData.minAirTemp) || d->airtemp.mkelvin > (*temp_comp)(filterData.maxAirTemp)))
|
2018-11-16 12:49:19 +00:00
|
|
|
return false;
|
|
|
|
|
2019-01-20 16:00:56 +00:00
|
|
|
QDateTime t = filterData.fromDate;
|
|
|
|
t.setTime(filterData.fromTime);
|
|
|
|
if (filterData.fromDate.isValid() && filterData.fromTime.isValid() &&
|
|
|
|
d->when < t.toMSecsSinceEpoch()/1000 + t.offsetFromUtc())
|
2018-11-16 12:49:19 +00:00
|
|
|
return false;
|
|
|
|
|
2019-01-20 16:00:56 +00:00
|
|
|
t = filterData.toDate;
|
|
|
|
t.setTime(filterData.toTime);
|
|
|
|
if (filterData.toDate.isValid() && filterData.toTime.isValid() &&
|
|
|
|
d->when > t.toMSecsSinceEpoch()/1000 + t.offsetFromUtc())
|
2018-11-16 12:49:19 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
// tags.
|
2019-02-19 15:31:21 +00:00
|
|
|
if (!hasTags(filterData.tags, d, filterData.tagsMode))
|
2018-11-16 12:49:19 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
// people
|
2019-02-19 15:31:21 +00:00
|
|
|
if (!hasPersons(filterData.people, d, filterData.peopleMode))
|
2018-11-16 12:49:19 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
// Location
|
2019-02-19 15:31:21 +00:00
|
|
|
if (!hasLocations(filterData.location, d, filterData.locationMode))
|
2018-11-16 12:49:19 +00:00
|
|
|
return false;
|
2015-05-26 20:42:45 +00:00
|
|
|
|
2019-02-28 23:35:34 +00:00
|
|
|
// Suit
|
|
|
|
if (!hasSuits(filterData.suit, d, filterData.suitMode))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Notes
|
|
|
|
if (!hasNotes(filterData.dnotes, d, filterData.dnotesMode))
|
|
|
|
return false;
|
|
|
|
|
2019-02-19 15:31:21 +00:00
|
|
|
if (!hasEquipment(filterData.equipment, d, filterData.equipmentMode))
|
2018-11-16 14:23:39 +00:00
|
|
|
return false;
|
|
|
|
|
2019-01-01 17:49:56 +00:00
|
|
|
// Planned/Logged
|
|
|
|
if (!filterData.logged && !has_planned(d, true))
|
|
|
|
return false;
|
|
|
|
if (!filterData.planned && !has_planned(d, false))
|
|
|
|
return false;
|
|
|
|
|
2018-08-14 18:16:25 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MultiFilterSortModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
|
|
|
|
{
|
Dive list: cache shown flag in model (quick-fix for undo-crash)
We have a very fundamental problem with data-duplication in
core and qt-models. In a particular case, this led to an easily
reproducible crash:
1) An undo command moved the last dive of a trip to another.
2) When an undo-command removed the last dive of
a trip to a different trip, the dive was removed from the
trip in the core. Then, the model was updated.
3) That lead at first to a rearrangement of the trips, because
the trip with the added dive is moved before the trip with
the removed dive.
4) In such a case, the filter-model checks the visibility of
the trip.
5) Since the trip with the removed dive has no dives in the core,
visibility was determined as false.
6) From this point on the mappings of the QSortFilterProxyModel
were messed up. Accesses led to crashes. It is unclear
whether this is a Qt bug or only a QOI issue.
As a quick-fix, cache the visibility flag of trips directly
in the Qt-models. Don't set the visibility directly in the
core, but go via the Qt-models. Thus, a more clear layering
is achieved.
In the long run, we can hopefully get rid of the data-duplication
in the models.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-06-22 18:48:59 +00:00
|
|
|
QAbstractItemModel *m = sourceModel();
|
|
|
|
QModelIndex index0 = m->index(source_row, 0, source_parent);
|
|
|
|
return m->data(index0, DiveTripModelBase::SHOWN_ROLE).value<bool>();
|
2014-11-13 18:31:03 +00:00
|
|
|
}
|
|
|
|
|
2018-09-06 20:03:03 +00:00
|
|
|
void MultiFilterSortModel::filterChanged(const QModelIndex &from, const QModelIndex &to, const QVector<int> &roles)
|
|
|
|
{
|
|
|
|
// Only redo the filter if a checkbox changed. If the count of an entry changed,
|
|
|
|
// we do *not* want to recalculate the filters.
|
|
|
|
if (roles.contains(Qt::CheckStateRole))
|
|
|
|
myInvalidate();
|
|
|
|
}
|
|
|
|
|
2014-11-13 18:31:03 +00:00
|
|
|
void MultiFilterSortModel::myInvalidate()
|
|
|
|
{
|
Dive list: cache shown flag in model (quick-fix for undo-crash)
We have a very fundamental problem with data-duplication in
core and qt-models. In a particular case, this led to an easily
reproducible crash:
1) An undo command moved the last dive of a trip to another.
2) When an undo-command removed the last dive of
a trip to a different trip, the dive was removed from the
trip in the core. Then, the model was updated.
3) That lead at first to a rearrangement of the trips, because
the trip with the added dive is moved before the trip with
the removed dive.
4) In such a case, the filter-model checks the visibility of
the trip.
5) Since the trip with the removed dive has no dives in the core,
visibility was determined as false.
6) From this point on the mappings of the QSortFilterProxyModel
were messed up. Accesses led to crashes. It is unclear
whether this is a Qt bug or only a QOI issue.
As a quick-fix, cache the visibility flag of trips directly
in the Qt-models. Don't set the visibility directly in the
core, but go via the Qt-models. Thus, a more clear layering
is achieved.
In the long run, we can hopefully get rid of the data-duplication
in the models.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-06-22 18:48:59 +00:00
|
|
|
QAbstractItemModel *m = sourceModel();
|
|
|
|
if (!m)
|
|
|
|
return;
|
2014-11-13 18:31:03 +00:00
|
|
|
|
2019-05-06 20:52:52 +00:00
|
|
|
{
|
|
|
|
// This marker prevents the UI from getting notifications on selection changes.
|
|
|
|
// It is active until the end of the scope.
|
|
|
|
// This is actually meant for the undo-commands, so that they can do their work
|
|
|
|
// without having the UI updated.
|
|
|
|
// Here, it is used because invalidating the filter can generate numerous
|
|
|
|
// selection changes, which do full ui reloads. Instead, do that all at once
|
|
|
|
// as a consequence of the filterFinished signal right after the local scope.
|
|
|
|
auto marker = diveListNotifier.enterCommand();
|
|
|
|
|
|
|
|
divesDisplayed = 0;
|
|
|
|
|
Dive list: cache shown flag in model (quick-fix for undo-crash)
We have a very fundamental problem with data-duplication in
core and qt-models. In a particular case, this led to an easily
reproducible crash:
1) An undo command moved the last dive of a trip to another.
2) When an undo-command removed the last dive of
a trip to a different trip, the dive was removed from the
trip in the core. Then, the model was updated.
3) That lead at first to a rearrangement of the trips, because
the trip with the added dive is moved before the trip with
the removed dive.
4) In such a case, the filter-model checks the visibility of
the trip.
5) Since the trip with the removed dive has no dives in the core,
visibility was determined as false.
6) From this point on the mappings of the QSortFilterProxyModel
were messed up. Accesses led to crashes. It is unclear
whether this is a Qt bug or only a QOI issue.
As a quick-fix, cache the visibility flag of trips directly
in the Qt-models. Don't set the visibility directly in the
core, but go via the Qt-models. Thus, a more clear layering
is achieved.
In the long run, we can hopefully get rid of the data-duplication
in the models.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-06-22 18:48:59 +00:00
|
|
|
for (int i = 0; i < m->rowCount(QModelIndex()); ++i) {
|
|
|
|
QModelIndex idx = m->index(i, 0, QModelIndex());
|
|
|
|
|
|
|
|
dive_trip *trip = m->data(idx, DiveTripModelBase::TRIP_ROLE).value<dive_trip *>();
|
|
|
|
if (trip) {
|
|
|
|
// This is a trip -> loop over all dives and see if any is selected
|
|
|
|
|
|
|
|
bool showTrip = false;
|
|
|
|
for (int j = 0; j < m->rowCount(idx); ++j) {
|
|
|
|
QModelIndex idx2 = m->index(j, 0, idx);
|
|
|
|
dive *d = m->data(idx2, DiveTripModelBase::DIVE_ROLE).value<dive *>();
|
|
|
|
if (!d) {
|
|
|
|
qWarning("MultiFilterSortModel::myInvalidate(): subitem not a dive!?");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
bool show = showDive(d);
|
|
|
|
if (show) {
|
|
|
|
divesDisplayed++;
|
|
|
|
showTrip = true;
|
|
|
|
}
|
|
|
|
m->setData(idx2, show, DiveTripModelBase::SHOWN_ROLE);
|
|
|
|
}
|
|
|
|
m->setData(idx, showTrip, DiveTripModelBase::SHOWN_ROLE);
|
|
|
|
} else {
|
|
|
|
dive *d = m->data(idx, DiveTripModelBase::DIVE_ROLE).value<dive *>();
|
|
|
|
bool show = showDive(d);
|
|
|
|
if (show)
|
|
|
|
divesDisplayed++;
|
|
|
|
m->setData(idx, show, DiveTripModelBase::SHOWN_ROLE);
|
|
|
|
}
|
2019-05-06 20:52:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
invalidateFilter();
|
|
|
|
|
|
|
|
// Tell the dive trip model to update the displayed-counts
|
|
|
|
DiveTripModelBase::instance()->filterFinished();
|
|
|
|
countsChanged();
|
2018-08-14 22:30:49 +00:00
|
|
|
}
|
|
|
|
|
2019-08-31 06:46:11 +00:00
|
|
|
#if !defined(SUBSURFACE_MOBILE)
|
|
|
|
// The shown maps may have changed -> reload the map widget.
|
2019-08-31 07:09:14 +00:00
|
|
|
// But don't do this in dive site mode, because then we show all
|
|
|
|
// dive sites and only change the selected flag.
|
|
|
|
if (!diveSiteMode())
|
|
|
|
MapWidget::instance()->reload();
|
2019-08-31 06:46:11 +00:00
|
|
|
#endif
|
|
|
|
|
2014-11-16 16:04:06 +00:00
|
|
|
emit filterFinished();
|
2015-05-26 21:03:37 +00:00
|
|
|
|
2018-10-20 09:56:06 +00:00
|
|
|
#if !defined(SUBSURFACE_MOBILE)
|
2019-08-31 07:09:14 +00:00
|
|
|
if (diveSiteMode())
|
2018-10-20 09:56:06 +00:00
|
|
|
MainWindow::instance()->diveList->expandAll();
|
2016-07-09 19:45:55 +00:00
|
|
|
#endif
|
2014-11-13 18:31:03 +00:00
|
|
|
}
|
|
|
|
|
2019-03-22 19:55:05 +00:00
|
|
|
bool MultiFilterSortModel::updateDive(struct dive *d)
|
|
|
|
{
|
|
|
|
bool oldStatus = !d->hidden_by_filter;
|
|
|
|
bool newStatus = showDive(d);
|
|
|
|
bool changed = oldStatus != newStatus;
|
|
|
|
if (changed) {
|
|
|
|
filter_dive(d, newStatus);
|
|
|
|
divesDisplayed += newStatus - oldStatus;
|
|
|
|
}
|
|
|
|
return changed;
|
|
|
|
}
|
|
|
|
|
2014-11-13 18:31:03 +00:00
|
|
|
void MultiFilterSortModel::clearFilter()
|
|
|
|
{
|
|
|
|
myInvalidate();
|
|
|
|
}
|
2015-05-26 20:36:06 +00:00
|
|
|
|
2019-04-12 13:47:41 +00:00
|
|
|
void MultiFilterSortModel::startFilterDiveSites(QVector<dive_site *> ds)
|
2015-05-26 20:36:06 +00:00
|
|
|
{
|
2019-04-12 13:47:41 +00:00
|
|
|
dive_sites = ds;
|
2019-05-05 10:30:54 +00:00
|
|
|
if (++diveSiteRefCount > 1) {
|
|
|
|
setFilterDiveSite(ds);
|
|
|
|
} else {
|
|
|
|
std::sort(ds.begin(), ds.end());
|
|
|
|
dive_sites = ds;
|
2019-08-31 07:09:14 +00:00
|
|
|
#if !defined(SUBSURFACE_MOBILE)
|
|
|
|
// When switching into dive site mode, reload the dive sites.
|
|
|
|
// We won't do this in myInvalidate() once we are in dive site mode.
|
|
|
|
MapWidget::instance()->reload();
|
|
|
|
#endif
|
2019-05-05 10:30:54 +00:00
|
|
|
myInvalidate();
|
|
|
|
}
|
2015-05-26 20:36:06 +00:00
|
|
|
}
|
|
|
|
|
2019-04-12 13:47:41 +00:00
|
|
|
void MultiFilterSortModel::stopFilterDiveSites()
|
2015-05-26 20:36:06 +00:00
|
|
|
{
|
2019-05-05 10:18:04 +00:00
|
|
|
if (--diveSiteRefCount > 0)
|
|
|
|
return;
|
2019-04-12 13:47:41 +00:00
|
|
|
dive_sites.clear();
|
2015-05-26 20:36:06 +00:00
|
|
|
myInvalidate();
|
|
|
|
}
|
2018-10-29 19:17:53 +00:00
|
|
|
|
2019-05-05 10:18:04 +00:00
|
|
|
void MultiFilterSortModel::setFilterDiveSite(QVector<dive_site *> ds)
|
|
|
|
{
|
2019-05-05 10:30:54 +00:00
|
|
|
// If the filter didn't change, return early to avoid a full
|
|
|
|
// map reload. For a well-defined comparison, sort the vector first.
|
|
|
|
std::sort(ds.begin(), ds.end());
|
|
|
|
if (ds == dive_sites)
|
|
|
|
return;
|
2019-05-05 10:18:04 +00:00
|
|
|
dive_sites = ds;
|
2019-08-31 07:09:14 +00:00
|
|
|
|
|
|
|
#if !defined(SUBSURFACE_MOBILE)
|
|
|
|
MapWidget::instance()->setSelected(dive_sites);
|
|
|
|
#endif
|
2019-05-05 10:18:04 +00:00
|
|
|
myInvalidate();
|
|
|
|
}
|
|
|
|
|
2019-05-03 21:37:42 +00:00
|
|
|
const QVector<dive_site *> &MultiFilterSortModel::filteredDiveSites() const
|
|
|
|
{
|
|
|
|
return dive_sites;
|
|
|
|
}
|
|
|
|
|
2019-04-29 06:17:41 +00:00
|
|
|
bool MultiFilterSortModel::diveSiteMode() const
|
|
|
|
{
|
2019-05-05 10:30:54 +00:00
|
|
|
return diveSiteRefCount > 0;
|
2019-04-29 06:17:41 +00:00
|
|
|
}
|
|
|
|
|
2018-10-29 19:17:53 +00:00
|
|
|
bool MultiFilterSortModel::lessThan(const QModelIndex &i1, const QModelIndex &i2) const
|
|
|
|
{
|
|
|
|
// Hand sorting down to the source model.
|
2018-12-27 09:06:11 +00:00
|
|
|
return DiveTripModelBase::instance()->lessThan(i1, i2);
|
2018-10-29 19:17:53 +00:00
|
|
|
}
|
2018-12-06 19:07:47 +00:00
|
|
|
|
2018-12-16 19:43:01 +00:00
|
|
|
void MultiFilterSortModel::filterDataChanged(const FilterData &data)
|
2018-12-06 19:07:47 +00:00
|
|
|
{
|
|
|
|
filterData = data;
|
|
|
|
myInvalidate();
|
|
|
|
}
|
2019-01-25 20:30:43 +00:00
|
|
|
|
|
|
|
void MultiFilterSortModel::divesAdded(dive_trip *, bool, const QVector<dive *> &dives)
|
|
|
|
{
|
|
|
|
for (dive *d: dives) {
|
|
|
|
if (!d->hidden_by_filter)
|
|
|
|
++divesDisplayed;
|
|
|
|
}
|
2019-04-12 13:31:11 +00:00
|
|
|
countsChanged();
|
2019-01-25 20:30:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MultiFilterSortModel::divesDeleted(dive_trip *, bool, const QVector<dive *> &dives)
|
|
|
|
{
|
|
|
|
for (dive *d: dives) {
|
|
|
|
if (!d->hidden_by_filter)
|
|
|
|
--divesDisplayed;
|
|
|
|
}
|
2019-04-12 13:31:11 +00:00
|
|
|
countsChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MultiFilterSortModel::countsChanged()
|
|
|
|
{
|
|
|
|
updateWindowTitle();
|
2019-01-25 20:30:43 +00:00
|
|
|
}
|