summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--commands/command_divelist.cpp3
-rw-r--r--core/CMakeLists.txt2
-rw-r--r--core/divefilter.cpp238
-rw-r--r--core/divefilter.h97
-rw-r--r--desktop-widgets/divelistview.cpp5
-rw-r--r--desktop-widgets/divelogexportdialog.cpp5
-rw-r--r--desktop-widgets/filterwidget2.cpp2
-rw-r--r--desktop-widgets/filterwidget2.h2
-rw-r--r--desktop-widgets/locationinformation.cpp6
-rw-r--r--desktop-widgets/mainwindow.h1
-rw-r--r--desktop-widgets/tab-widgets/TabDiveSite.cpp8
-rw-r--r--map-widget/qmlmapwidgethelper.cpp4
-rw-r--r--qt-models/divetripmodel.cpp1
-rw-r--r--qt-models/filtermodels.cpp233
-rw-r--r--qt-models/filtermodels.h65
-rw-r--r--qt-models/maplocationmodel.cpp5
16 files changed, 369 insertions, 308 deletions
diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp
index 9943651eb..4fabac9fc 100644
--- a/commands/command_divelist.cpp
+++ b/commands/command_divelist.cpp
@@ -8,6 +8,7 @@
#include "core/subsurface-qt/DiveListNotifier.h"
#include "qt-models/filtermodels.h"
#include "../profile-widget/profilewidget2.h"
+#include "core/divefilter.h"
#include <array>
@@ -68,7 +69,7 @@ dive *DiveListBase::addDive(DiveToAdd &d)
dive *res = d.dive.release(); // Give up ownership of dive
// Set the filter flag according to current filter settings
- bool show = MultiFilterSortModel::instance()->showDive(res);
+ bool show = DiveFilter::instance()->showDive(res);
res->hidden_by_filter = !show;
if (show)
++shown_dives;
diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt
index 3a0806454..3d58dded7 100644
--- a/core/CMakeLists.txt
+++ b/core/CMakeLists.txt
@@ -66,6 +66,8 @@ set(SUBSURFACE_CORE_LIB_SRCS
dive.h
divecomputer.cpp
divecomputer.h
+ divefilter.cpp
+ divefilter.h
divelist.c
divelist.h
divelogexportlogic.cpp
diff --git a/core/divefilter.cpp b/core/divefilter.cpp
new file mode 100644
index 000000000..0b0e446d0
--- /dev/null
+++ b/core/divefilter.cpp
@@ -0,0 +1,238 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "divefilter.h"
+
+#ifdef SUBSURFACE_MOBILE
+
+DiveFilter::DiveFilter()
+{
+}
+
+bool DiveFilter::showDive(const struct dive *d) const
+{
+ // TODO: Do something useful
+ return true;
+}
+
+#else // SUBSURFACE_MOBILE
+
+#include "desktop-widgets/mapwidget.h"
+#include "desktop-widgets/mainwindow.h"
+#include "desktop-widgets/divelistview.h"
+#include "core/qthelper.h"
+#include "core/trip.h"
+#include "core/divesite.h"
+#include "qt-models/filtermodels.h"
+
+namespace {
+ // 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)
+ {
+ return std::any_of(list.begin(), list.end(), [&s](const QString &s2)
+ { return s2.trimmed().contains(s.trimmed(), Qt::CaseInsensitive); } );
+ }
+
+ // 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)
+ {
+ if (tags.isEmpty())
+ return true;
+ QStringList dive_tags = get_taglist_string(d->tag_list).split(",");
+ dive_tags.append(gettextFromC::tr(divemode_text_ui[d->dc.divemode]));
+ return check(tags, dive_tags, mode);
+ }
+
+ bool hasPersons(const QStringList &people, const struct dive *d, FilterData::Mode mode)
+ {
+ if (people.isEmpty())
+ return true;
+ QStringList dive_people = QString(d->buddy).split(",", QString::SkipEmptyParts)
+ + QString(d->divemaster).split(",", QString::SkipEmptyParts);
+ return check(people, dive_people, mode);
+ }
+
+ bool hasLocations(const QStringList &locations, const struct dive *d, FilterData::Mode mode)
+ {
+ 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));
+
+ return check(locations, diveLocations, mode);
+ }
+
+ // TODO: Finish this implementation.
+ bool hasEquipment(const QStringList &, const struct dive *, FilterData::Mode)
+ {
+ return true;
+ }
+
+ 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);
+ }
+
+}
+
+DiveFilter *DiveFilter::instance()
+{
+ static DiveFilter self;
+ return &self;
+}
+
+DiveFilter::DiveFilter() : diveSiteRefCount(0)
+{
+}
+
+bool DiveFilter::showDive(const struct dive *d) const
+{
+ if (diveSiteMode())
+ return dive_sites.contains(d->dive_site);
+
+ 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;
+
+ auto temp_comp = prefs.units.temperature == units::CELSIUS ? C_to_mkelvin : F_to_mkelvin;
+ if (d->watertemp.mkelvin &&
+ (d->watertemp.mkelvin < (*temp_comp)(filterData.minWaterTemp) || d->watertemp.mkelvin > (*temp_comp)(filterData.maxWaterTemp)))
+ return false;
+
+ if (d->airtemp.mkelvin &&
+ (d->airtemp.mkelvin < (*temp_comp)(filterData.minAirTemp) || d->airtemp.mkelvin > (*temp_comp)(filterData.maxAirTemp)))
+ return false;
+
+ QDateTime t = filterData.fromDate;
+ t.setTime(filterData.fromTime);
+ if (filterData.fromDate.isValid() && filterData.fromTime.isValid() &&
+ d->when < t.toMSecsSinceEpoch()/1000 + t.offsetFromUtc())
+ return false;
+
+ t = filterData.toDate;
+ t.setTime(filterData.toTime);
+ if (filterData.toDate.isValid() && filterData.toTime.isValid() &&
+ d->when > t.toMSecsSinceEpoch()/1000 + t.offsetFromUtc())
+ return false;
+
+ // tags.
+ if (!hasTags(filterData.tags, d, filterData.tagsMode))
+ return false;
+
+ // people
+ if (!hasPersons(filterData.people, d, filterData.peopleMode))
+ return false;
+
+ // Location
+ if (!hasLocations(filterData.location, d, filterData.locationMode))
+ return false;
+
+ // Suit
+ if (!hasSuits(filterData.suit, d, filterData.suitMode))
+ return false;
+
+ // Notes
+ if (!hasNotes(filterData.dnotes, d, filterData.dnotesMode))
+ return false;
+
+ if (!hasEquipment(filterData.equipment, d, filterData.equipmentMode))
+ return false;
+
+ // Planned/Logged
+ if (!filterData.logged && !has_planned(d, true))
+ return false;
+ if (!filterData.planned && !has_planned(d, false))
+ return false;
+
+ return true;
+}
+
+void DiveFilter::startFilterDiveSites(QVector<dive_site *> ds)
+{
+ if (++diveSiteRefCount > 1) {
+ setFilterDiveSite(ds);
+ } else {
+ std::sort(ds.begin(), ds.end());
+ dive_sites = ds;
+ // 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();
+ MultiFilterSortModel::instance()->myInvalidate();
+ }
+}
+
+void DiveFilter::stopFilterDiveSites()
+{
+ if (--diveSiteRefCount > 0)
+ return;
+ dive_sites.clear();
+ MultiFilterSortModel::instance()->myInvalidate();
+ MapWidget::instance()->reload();
+}
+
+void DiveFilter::setFilterDiveSite(QVector<dive_site *> ds)
+{
+ // 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;
+ dive_sites = ds;
+
+ MultiFilterSortModel::instance()->myInvalidate();
+ MapWidget::instance()->setSelected(dive_sites);
+ MainWindow::instance()->diveList->expandAll();
+}
+
+const QVector<dive_site *> &DiveFilter::filteredDiveSites() const
+{
+ return dive_sites;
+}
+
+bool DiveFilter::diveSiteMode() const
+{
+ return diveSiteRefCount > 0;
+}
+
+void DiveFilter::setFilter(const FilterData &data)
+{
+ filterData = data;
+ MultiFilterSortModel::instance()->myInvalidate();
+}
+#endif // SUBSURFACE_MOBILE
diff --git a/core/divefilter.h b/core/divefilter.h
new file mode 100644
index 000000000..d225d3c81
--- /dev/null
+++ b/core/divefilter.h
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0
+// A class that filters dives.
+#ifndef DIVE_FILTER_H
+#define DIVE_FILTER_H
+
+// The dive filter for mobile is currently much simpler than for desktop.
+// Therefore, for now we have two completely separate implementations.
+// This should be unified in the future.
+#ifdef SUBSURFACE_MOBILE
+
+class DiveFilter {
+public:
+ static DiveFilter *instance();
+
+ bool showDive(const struct dive *d) const;
+private:
+ DiveFilter();
+};
+
+#else
+
+#include <QDateTime>
+#include <QStringList>
+#include <QVector>
+
+struct dive;
+struct dive_trip;
+struct dive_site;
+
+struct FilterData {
+ // The mode ids are chosen such that they can be directly converted from / to combobox indices.
+ enum class Mode {
+ ALL_OF = 0,
+ ANY_OF = 1,
+ NONE_OF = 2
+ };
+
+ bool validFilter = false;
+ int minVisibility = 0;
+ int maxVisibility = 5;
+ int minRating = 0;
+ int maxRating = 5;
+ // The default minimum and maximum temperatures are set such that all
+ // physically reasonable dives are shown. Note that these values should
+ // work for both Celcius and Fahrenheit scales.
+ double minWaterTemp = -10;
+ double maxWaterTemp = 200;
+ double minAirTemp = -50;
+ double maxAirTemp = 200;
+ QDateTime fromDate = QDateTime(QDate(1980,1,1));
+ QTime fromTime = QTime(0,0);
+ QDateTime toDate = QDateTime::currentDateTime();
+ QTime toTime = QTime::currentTime();
+ QStringList tags;
+ QStringList people;
+ QStringList location;
+ QStringList suit;
+ QStringList dnotes;
+ QStringList equipment;
+ Mode tagsMode = Mode::ALL_OF;
+ Mode peopleMode = Mode::ALL_OF;
+ Mode locationMode = Mode::ANY_OF;
+ Mode dnotesMode = Mode::ALL_OF;
+ Mode suitMode = Mode::ANY_OF;
+ Mode equipmentMode = Mode::ALL_OF;
+ bool logged = true;
+ bool planned = true;
+};
+
+class DiveFilter {
+public:
+ static DiveFilter *instance();
+
+ bool showDive(const struct dive *d) const;
+ bool diveSiteMode() const; // returns true if we're filtering on dive site
+ const QVector<dive_site *> &filteredDiveSites() const;
+ void startFilterDiveSites(QVector<dive_site *> ds);
+ void setFilterDiveSite(QVector<dive_site *> ds);
+ void stopFilterDiveSites();
+ void setFilter(const FilterData &data);
+private:
+ DiveFilter();
+
+ QVector<dive_site *> dive_sites;
+ FilterData filterData;
+
+ // We use ref-counting for the dive site mode. The reason is that when switching
+ // between two tabs that both need dive site mode, the following course of
+ // events may happen:
+ // 1) The new tab appears -> enter dive site mode.
+ // 2) The old tab gets its hide() signal -> exit dive site mode.
+ // The filter is now not in dive site mode, even if it should
+ int diveSiteRefCount;
+};
+#endif // SUBSURFACE_MOBILE
+
+#endif
diff --git a/desktop-widgets/divelistview.cpp b/desktop-widgets/divelistview.cpp
index aa2195434..d6a4d507e 100644
--- a/desktop-widgets/divelistview.cpp
+++ b/desktop-widgets/divelistview.cpp
@@ -10,6 +10,7 @@
#include "desktop-widgets/mainwindow.h"
#include "desktop-widgets/divepicturewidget.h"
#include "core/display.h"
+#include "core/divefilter.h"
#include <unistd.h>
#include <QSettings>
#include <QKeyEvent>
@@ -463,7 +464,7 @@ void DiveListView::selectDives(const QList<int> &newDiveSelection)
// But don't do this if we are in divesite mode, because then
// the dive-site selection is controlled by the filter not
// by the selected dives.
- if (!MultiFilterSortModel::instance()->diveSiteMode()) {
+ if (!DiveFilter::instance()->diveSiteMode()) {
QVector<dive_site *> selectedSites;
for (int idx: newDiveSelection) {
dive *d = get_dive(idx);
@@ -698,7 +699,7 @@ void DiveListView::selectionChanged(const QItemSelection &selected, const QItemS
// But don't do this if we are in divesite mode, because then
// the dive-site selection is controlled by the filter not
// by the selected dives.
- if (!MultiFilterSortModel::instance()->diveSiteMode()) {
+ if (!DiveFilter::instance()->diveSiteMode()) {
QVector<dive_site *> selectedSites;
for (QModelIndex index: selectionModel()->selection().indexes()) {
const QAbstractItemModel *model = index.model();
diff --git a/desktop-widgets/divelogexportdialog.cpp b/desktop-widgets/divelogexportdialog.cpp
index cf859e2b9..99f6eb8a8 100644
--- a/desktop-widgets/divelogexportdialog.cpp
+++ b/desktop-widgets/divelogexportdialog.cpp
@@ -11,6 +11,7 @@
#include "core/save-html.h"
#include "core/settings/qPrefDisplay.h"
#include "core/save-profiledata.h"
+#include "core/divefilter.h"
#include "core/divesite.h"
#include "core/errorhelper.h"
#include "core/file.h"
@@ -134,10 +135,10 @@ static std::vector<const dive_site *> getDiveSitesToExport(bool selectedOnly)
{
std::vector<const dive_site *> res;
- if (selectedOnly && MultiFilterSortModel::instance()->diveSiteMode()) {
+ if (selectedOnly && DiveFilter::instance()->diveSiteMode()) {
// Special case in dive site mode: export all selected dive sites,
// not the dive sites of selected dives.
- QVector<dive_site *> sites = MultiFilterSortModel::instance()->filteredDiveSites();
+ QVector<dive_site *> sites = DiveFilter::instance()->filteredDiveSites();
res.reserve(sites.size());
for (const dive_site *ds: sites)
res.push_back(ds);
diff --git a/desktop-widgets/filterwidget2.cpp b/desktop-widgets/filterwidget2.cpp
index 0ab6bb022..140016a03 100644
--- a/desktop-widgets/filterwidget2.cpp
+++ b/desktop-widgets/filterwidget2.cpp
@@ -237,7 +237,7 @@ void FilterWidget2::hideEvent(QHideEvent *event)
void FilterWidget2::filterDataChanged(const FilterData &data)
{
- MultiFilterSortModel::instance()->filterDataChanged(data);
+ DiveFilter::instance()->setFilter(data);
}
QString FilterWidget2::shownText()
diff --git a/desktop-widgets/filterwidget2.h b/desktop-widgets/filterwidget2.h
index 212f6b5ae..b52386fe7 100644
--- a/desktop-widgets/filterwidget2.h
+++ b/desktop-widgets/filterwidget2.h
@@ -8,7 +8,7 @@
#include <memory>
#include "ui_filterwidget2.h"
-#include "qt-models/filtermodels.h"
+#include "core/divefilter.h"
namespace Ui {
class FilterWidget2;
diff --git a/desktop-widgets/locationinformation.cpp b/desktop-widgets/locationinformation.cpp
index bbf43ec5a..331a8555e 100644
--- a/desktop-widgets/locationinformation.cpp
+++ b/desktop-widgets/locationinformation.cpp
@@ -5,7 +5,7 @@
#include "desktop-widgets/divelistview.h"
#include "core/qthelper.h"
#include "desktop-widgets/mapwidget.h"
-#include "qt-models/filtermodels.h"
+#include "core/divefilter.h"
#include "core/divesitehelpers.h"
#include "desktop-widgets/modeldelegates.h"
#include "core/subsurface-qt/DiveListNotifier.h"
@@ -194,7 +194,7 @@ void LocationInformationWidget::acceptChanges()
MainWindow::instance()->diveList->setEnabled(true);
MainWindow::instance()->setEnabledToolbar(true);
MainWindow::instance()->setApplicationState(ApplicationState::Default);
- MultiFilterSortModel::instance()->stopFilterDiveSites();
+ DiveFilter::instance()->stopFilterDiveSites();
// Subtlety alert: diveSite must be cleared *after* exiting the dive-site mode.
// Exiting dive-site mode removes the focus from the active widget and
@@ -211,7 +211,7 @@ void LocationInformationWidget::initFields(dive_site *ds)
filter_model.set(ds, ds->location);
updateLabels();
enableLocationButtons(dive_site_has_gps_location(ds));
- MultiFilterSortModel::instance()->startFilterDiveSites(QVector<dive_site *>{ ds });
+ DiveFilter::instance()->startFilterDiveSites(QVector<dive_site *>{ ds });
filter_model.invalidate();
} else {
filter_model.set(0, location_t { degrees_t{ 0 }, degrees_t{ 0 } });
diff --git a/desktop-widgets/mainwindow.h b/desktop-widgets/mainwindow.h
index 0eaa5e1fa..075999707 100644
--- a/desktop-widgets/mainwindow.h
+++ b/desktop-widgets/mainwindow.h
@@ -21,6 +21,7 @@
#include "desktop-widgets/filterwidget2.h"
#include "core/applicationstate.h"
#include "core/gpslocation.h"
+#include "core/dive.h"
#define NUM_RECENT_FILES 4
diff --git a/desktop-widgets/tab-widgets/TabDiveSite.cpp b/desktop-widgets/tab-widgets/TabDiveSite.cpp
index d5591c152..3ff693e59 100644
--- a/desktop-widgets/tab-widgets/TabDiveSite.cpp
+++ b/desktop-widgets/tab-widgets/TabDiveSite.cpp
@@ -2,8 +2,8 @@
#include "TabDiveSite.h"
#include "core/subsurface-qt/DiveListNotifier.h"
#include "core/divesite.h"
+#include "core/divefilter.h"
#include "qt-models/divelocationmodel.h"
-#include "qt-models/filtermodels.h"
#include "commands/command.h"
#include <qt-models/divecomputerextradatamodel.h>
@@ -97,18 +97,18 @@ QVector<dive_site *> TabDiveSite::selectedDiveSites()
void TabDiveSite::selectionChanged(const QItemSelection &, const QItemSelection &)
{
- MultiFilterSortModel::instance()->setFilterDiveSite(selectedDiveSites());
+ DiveFilter::instance()->setFilterDiveSite(selectedDiveSites());
}
void TabDiveSite::showEvent(QShowEvent *)
{
// If the user switches to the dive site tab and there was already a selection,
// filter on that selection.
- MultiFilterSortModel::instance()->startFilterDiveSites(selectedDiveSites());
+ DiveFilter::instance()->startFilterDiveSites(selectedDiveSites());
}
void TabDiveSite::hideEvent(QHideEvent *)
{
// If the user switches to a different tab, stop the dive site filtering
- MultiFilterSortModel::instance()->stopFilterDiveSites();
+ DiveFilter::instance()->stopFilterDiveSites();
}
diff --git a/map-widget/qmlmapwidgethelper.cpp b/map-widget/qmlmapwidgethelper.cpp
index 9dba8bfd3..ff5f47f35 100644
--- a/map-widget/qmlmapwidgethelper.cpp
+++ b/map-widget/qmlmapwidgethelper.cpp
@@ -7,10 +7,10 @@
#include "qmlmapwidgethelper.h"
#include "core/divesite.h"
#include "core/qthelper.h"
+#include "core/divefilter.h"
#include "qt-models/maplocationmodel.h"
#include "qt-models/divelocationmodel.h"
#ifndef SUBSURFACE_MOBILE
-#include "qt-models/filtermodels.h"
#include "desktop-widgets/mapwidget.h"
#endif
@@ -106,7 +106,7 @@ void MapWidgetHelper::updateEditMode()
// The filter being set to dive site is the signal that we are in dive site edit mode.
// This is the case when either the dive site edit tab or the dive site list tab are active.
bool old = m_editMode;
- m_editMode = MultiFilterSortModel::instance()->diveSiteMode();
+ m_editMode = DiveFilter::instance()->diveSiteMode();
if (old != m_editMode)
emit editModeChanged();
#endif
diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp
index 5fedd432f..7f9ca9e32 100644
--- a/qt-models/divetripmodel.cpp
+++ b/qt-models/divetripmodel.cpp
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include "qt-models/divetripmodel.h"
#include "qt-models/filtermodels.h"
+#include "core/divefilter.h"
#include "core/gettextfromc.h"
#include "core/metrics.h"
#include "core/trip.h"
diff --git a/qt-models/filtermodels.cpp b/qt-models/filtermodels.cpp
index 9736d3335..b04fce127 100644
--- a/qt-models/filtermodels.cpp
+++ b/qt-models/filtermodels.cpp
@@ -1,113 +1,26 @@
// SPDX-License-Identifier: GPL-2.0
#include "qt-models/filtermodels.h"
-#include "qt-models/models.h"
#include "core/display.h"
#include "core/qthelper.h"
-#include "core/divesite.h"
#include "core/trip.h"
#include "core/subsurface-string.h"
#include "core/subsurface-qt/DiveListNotifier.h"
#include "qt-models/divetripmodel.h"
#if !defined(SUBSURFACE_MOBILE)
-#include "desktop-widgets/divelistview.h"
-#include "desktop-widgets/mainwindow.h"
-#include "desktop-widgets/mapwidget.h"
+#include "core/divefilter.h"
#endif
#include <QDebug>
#include <algorithm>
-namespace {
- // 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)
- {
- return std::any_of(list.begin(), list.end(), [&s](const QString &s2)
- { return s2.trimmed().contains(s.trimmed(), Qt::CaseInsensitive); } );
- }
-
- // 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)
- {
- if (tags.isEmpty())
- return true;
- QStringList dive_tags = get_taglist_string(d->tag_list).split(",");
- dive_tags.append(gettextFromC::tr(divemode_text_ui[d->dc.divemode]));
- return check(tags, dive_tags, mode);
- }
-
- bool hasPersons(const QStringList &people, const struct dive *d, FilterData::Mode mode)
- {
- if (people.isEmpty())
- return true;
- QStringList dive_people = QString(d->buddy).split(",", QString::SkipEmptyParts)
- + QString(d->divemaster).split(",", QString::SkipEmptyParts);
- return check(people, dive_people, mode);
- }
-
- bool hasLocations(const QStringList &locations, const struct dive *d, FilterData::Mode mode)
- {
- 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));
-
- return check(locations, diveLocations, mode);
- }
-
- // TODO: Finish this implementation.
- bool hasEquipment(const QStringList &, const struct dive *, FilterData::Mode)
- {
- return true;
- }
-
- 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);
- }
-
-}
-
MultiFilterSortModel *MultiFilterSortModel::instance()
{
static MultiFilterSortModel self;
return &self;
}
-MultiFilterSortModel::MultiFilterSortModel(QObject *parent) : QSortFilterProxyModel(parent),
- diveSiteRefCount(0)
+MultiFilterSortModel::MultiFilterSortModel(QObject *parent) : QSortFilterProxyModel(parent)
{
setFilterKeyColumn(-1); // filter all columns
setFilterCaseSensitivity(Qt::CaseInsensitive);
@@ -121,73 +34,6 @@ void MultiFilterSortModel::resetModel(DiveTripModelBase::Layout layout)
setSourceModel(DiveTripModelBase::instance());
}
-bool MultiFilterSortModel::showDive(const struct dive *d) const
-{
- if (diveSiteMode())
- return dive_sites.contains(d->dive_site);
-
- 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;
-
- auto temp_comp = prefs.units.temperature == units::CELSIUS ? C_to_mkelvin : F_to_mkelvin;
- if (d->watertemp.mkelvin &&
- (d->watertemp.mkelvin < (*temp_comp)(filterData.minWaterTemp) || d->watertemp.mkelvin > (*temp_comp)(filterData.maxWaterTemp)))
- return false;
-
- if (d->airtemp.mkelvin &&
- (d->airtemp.mkelvin < (*temp_comp)(filterData.minAirTemp) || d->airtemp.mkelvin > (*temp_comp)(filterData.maxAirTemp)))
- return false;
-
- QDateTime t = filterData.fromDate;
- t.setTime(filterData.fromTime);
- if (filterData.fromDate.isValid() && filterData.fromTime.isValid() &&
- d->when < t.toMSecsSinceEpoch()/1000 + t.offsetFromUtc())
- return false;
-
- t = filterData.toDate;
- t.setTime(filterData.toTime);
- if (filterData.toDate.isValid() && filterData.toTime.isValid() &&
- d->when > t.toMSecsSinceEpoch()/1000 + t.offsetFromUtc())
- return false;
-
- // tags.
- if (!hasTags(filterData.tags, d, filterData.tagsMode))
- return false;
-
- // people
- if (!hasPersons(filterData.people, d, filterData.peopleMode))
- return false;
-
- // Location
- if (!hasLocations(filterData.location, d, filterData.locationMode))
- return false;
-
- // Suit
- if (!hasSuits(filterData.suit, d, filterData.suitMode))
- return false;
-
- // Notes
- if (!hasNotes(filterData.dnotes, d, filterData.dnotesMode))
- return false;
-
- if (!hasEquipment(filterData.equipment, d, filterData.equipmentMode))
- return false;
-
- // Planned/Logged
- if (!filterData.logged && !has_planned(d, true))
- return false;
- if (!filterData.planned && !has_planned(d, false))
- return false;
-
- return true;
-}
-
bool MultiFilterSortModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
QAbstractItemModel *m = sourceModel();
@@ -213,6 +59,7 @@ void MultiFilterSortModel::myInvalidate()
shown_dives = 0;
+ DiveFilter *filter = DiveFilter::instance();
for (int i = 0; i < m->rowCount(QModelIndex()); ++i) {
QModelIndex idx = m->index(i, 0, QModelIndex());
@@ -227,7 +74,7 @@ void MultiFilterSortModel::myInvalidate()
qWarning("MultiFilterSortModel::myInvalidate(): subitem not a dive!?");
continue;
}
- bool show = showDive(d);
+ bool show = filter->showDive(d);
if (show) {
shown_dives++;
showTrip = true;
@@ -237,7 +84,7 @@ void MultiFilterSortModel::myInvalidate()
m->setData(idx, showTrip, DiveTripModelBase::SHOWN_ROLE);
} else {
dive *d = m->data(idx, DiveTripModelBase::DIVE_ROLE).value<dive *>();
- bool show = (d != NULL) && showDive(d);
+ bool show = (d != NULL) && filter->showDive(d);
if (show)
shown_dives++;
m->setData(idx, show, DiveTripModelBase::SHOWN_ROLE);
@@ -251,26 +98,13 @@ void MultiFilterSortModel::myInvalidate()
countsChanged();
}
-#if !defined(SUBSURFACE_MOBILE)
- // The shown maps may have changed -> reload the map widget.
- // 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();
-#endif
-
emit filterFinished();
-
-#if !defined(SUBSURFACE_MOBILE)
- if (diveSiteMode())
- MainWindow::instance()->diveList->expandAll();
-#endif
}
bool MultiFilterSortModel::updateDive(struct dive *d)
{
bool oldStatus = !d->hidden_by_filter;
- bool newStatus = showDive(d);
+ bool newStatus = DiveFilter::instance()->showDive(d);
bool changed = oldStatus != newStatus;
if (changed) {
filter_dive(d, newStatus);
@@ -279,67 +113,12 @@ bool MultiFilterSortModel::updateDive(struct dive *d)
return changed;
}
-void MultiFilterSortModel::startFilterDiveSites(QVector<dive_site *> ds)
-{
- if (++diveSiteRefCount > 1) {
- setFilterDiveSite(ds);
- } else {
- std::sort(ds.begin(), ds.end());
- dive_sites = ds;
-#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
- myInvalidate();
- }
-}
-
-void MultiFilterSortModel::stopFilterDiveSites()
-{
- if (--diveSiteRefCount > 0)
- return;
- dive_sites.clear();
- myInvalidate();
-}
-
-void MultiFilterSortModel::setFilterDiveSite(QVector<dive_site *> ds)
-{
- // 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;
- dive_sites = ds;
-
-#if !defined(SUBSURFACE_MOBILE)
- MapWidget::instance()->setSelected(dive_sites);
-#endif
- myInvalidate();
-}
-
-const QVector<dive_site *> &MultiFilterSortModel::filteredDiveSites() const
-{
- return dive_sites;
-}
-
-bool MultiFilterSortModel::diveSiteMode() const
-{
- return diveSiteRefCount > 0;
-}
-
bool MultiFilterSortModel::lessThan(const QModelIndex &i1, const QModelIndex &i2) const
{
// Hand sorting down to the source model.
return DiveTripModelBase::instance()->lessThan(i1, i2);
}
-void MultiFilterSortModel::filterDataChanged(const FilterData &data)
-{
- filterData = data;
- myInvalidate();
-}
-
void MultiFilterSortModel::countsChanged()
{
updateWindowTitle();
diff --git a/qt-models/filtermodels.h b/qt-models/filtermodels.h
index d4a03d134..d279470b1 100644
--- a/qt-models/filtermodels.h
+++ b/qt-models/filtermodels.h
@@ -11,67 +11,16 @@
#include <stdint.h>
#include <vector>
-struct dive;
-struct dive_trip;
-
-struct FilterData {
- // The mode ids are chosen such that they can be directly converted from / to combobox indices.
- enum class Mode {
- ALL_OF = 0,
- ANY_OF = 1,
- NONE_OF = 2
- };
-
- bool validFilter = false;
- int minVisibility = 0;
- int maxVisibility = 5;
- int minRating = 0;
- int maxRating = 5;
- // The default minimum and maximum temperatures are set such that all
- // physically reasonable dives are shown. Note that these values should
- // work for both Celcius and Fahrenheit scales.
- double minWaterTemp = -10;
- double maxWaterTemp = 200;
- double minAirTemp = -50;
- double maxAirTemp = 200;
- QDateTime fromDate = QDateTime(QDate(1980,1,1));
- QTime fromTime = QTime(0,0);
- QDateTime toDate = QDateTime::currentDateTime();
- QTime toTime = QTime::currentTime();
- QStringList tags;
- QStringList people;
- QStringList location;
- QStringList suit;
- QStringList dnotes;
- QStringList equipment;
- Mode tagsMode = Mode::ALL_OF;
- Mode peopleMode = Mode::ALL_OF;
- Mode locationMode = Mode::ANY_OF;
- Mode dnotesMode = Mode::ALL_OF;
- Mode suitMode = Mode::ANY_OF;
- Mode equipmentMode = Mode::ALL_OF;
- bool logged = true;
- bool planned = true;
-};
-
class MultiFilterSortModel : public QSortFilterProxyModel {
Q_OBJECT
public:
static MultiFilterSortModel *instance();
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
- bool showDive(const struct dive *d) const;
bool updateDive(struct dive *d); // returns true if visibility status changed
bool lessThan(const QModelIndex &, const QModelIndex &) const override;
- bool diveSiteMode() const; // returns true if we're filtering on dive site
- const QVector<dive_site *> &filteredDiveSites() const;
-public
-slots:
- void myInvalidate();
- void startFilterDiveSites(QVector<dive_site *> ds);
- void setFilterDiveSite(QVector<dive_site *> ds);
- void stopFilterDiveSites();
+
void resetModel(DiveTripModelBase::Layout layout);
- void filterDataChanged(const FilterData &data);
+ void myInvalidate();
signals:
void filterFinished();
@@ -79,17 +28,7 @@ signals:
private:
MultiFilterSortModel(QObject *parent = 0);
// Dive site filtering has priority over other filters
- QVector<dive_site *> dive_sites;
void countsChanged();
- FilterData filterData;
-
- // We use ref-counting for the dive site mode. The reason is that when switching
- // between two tabs that both need dive site mode, the following course of
- // events may happen:
- // 1) The new tab appears -> enter dive site mode.
- // 2) The old tab gets its hide() signal -> exit dive site mode.
- // The filter is now not in dive site mode, even if it should
- int diveSiteRefCount;
};
#endif
diff --git a/qt-models/maplocationmodel.cpp b/qt-models/maplocationmodel.cpp
index 9b21f7142..c123b19c5 100644
--- a/qt-models/maplocationmodel.cpp
+++ b/qt-models/maplocationmodel.cpp
@@ -2,6 +2,7 @@
#include "maplocationmodel.h"
#include "divelocationmodel.h"
#include "core/divesite.h"
+#include "core/divefilter.h"
#ifndef SUBSURFACE_MOBILE
#include "qt-models/filtermodels.h"
#include "desktop-widgets/mapwidget.h"
@@ -133,9 +134,9 @@ void MapLocationModel::reload(QObject *map)
// the dive site tab), we want to show all dive sites, not only those
// of the non-hidden dives. Moreover, the selected dive sites are those
// that we filter for.
- bool diveSiteMode = MultiFilterSortModel::instance()->diveSiteMode();
+ bool diveSiteMode = DiveFilter::instance()->diveSiteMode();
if (diveSiteMode)
- m_selectedDs = MultiFilterSortModel::instance()->filteredDiveSites();
+ m_selectedDs = DiveFilter::instance()->filteredDiveSites();
#endif
for (int i = 0; i < dive_site_table.nr; ++i) {
struct dive_site *ds = dive_site_table.dive_sites[i];