mirror of
https://github.com/subsurface/subsurface.git
synced 2025-01-31 20:33:24 +00:00
filter: unify desktop and mobile filters
Switch the mobile code to use the constraint-based filter. The one thing that is still commented out is dive-site mode, since mobile doesn't (yet) have a dive-site edit feature. And even if it had, the dive list probably wouldn't be shown at the same time. Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
parent
6c443ba841
commit
634e26cbce
3 changed files with 37 additions and 175 deletions
|
@ -4,6 +4,12 @@
|
|||
#include "divelist.h" // for filter_dive
|
||||
#include "qthelper.h"
|
||||
#include "subsurface-qt/divelistnotifier.h"
|
||||
#ifndef SUBSURFACE_MOBILE
|
||||
#include "desktop-widgets/mapwidget.h"
|
||||
#include "desktop-widgets/mainwindow.h"
|
||||
#include "desktop-widgets/divelistview.h"
|
||||
#include "qt-models/filtermodels.h"
|
||||
#endif
|
||||
|
||||
static void updateDiveStatus(dive *d, bool newStatus, ShownChange &change)
|
||||
{
|
||||
|
@ -15,132 +21,6 @@ static void updateDiveStatus(dive *d, bool newStatus, ShownChange &change)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef SUBSURFACE_MOBILE
|
||||
|
||||
#include "tag.h"
|
||||
static QStringList getTagList(const dive *d)
|
||||
{
|
||||
QStringList res;
|
||||
for (const tag_entry *tag = d->tag_list; tag; tag = tag->next)
|
||||
res.push_back(QString(tag->tag->name).trimmed());
|
||||
res.append(gettextFromC::tr(divemode_text_ui[d->dc.divemode]));
|
||||
return res;
|
||||
}
|
||||
|
||||
// Check if a string-list contains at least one string that starts with the second argument.
|
||||
// Comparison is non case sensitive and removes white space.
|
||||
static bool listContainsSuperstring(const QStringList &list, const QString &s)
|
||||
{
|
||||
return std::any_of(list.begin(), list.end(), [&s](const QString &s2)
|
||||
{ return s2.startsWith(s, Qt::CaseInsensitive); } );
|
||||
}
|
||||
|
||||
static bool check(const QStringList &items, const QStringList &list)
|
||||
{
|
||||
return std::all_of(items.begin(), items.end(), [&list](const QString &item)
|
||||
{ return listContainsSuperstring(list, item); });
|
||||
}
|
||||
|
||||
static bool hasTags(const QStringList &tags, const struct dive *d)
|
||||
{
|
||||
if (tags.isEmpty())
|
||||
return true;
|
||||
return check(tags, getTagList(d));
|
||||
}
|
||||
|
||||
static bool hasPersons(const QStringList &people, const struct dive *d)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
DiveFilter *DiveFilter::instance()
|
||||
{
|
||||
static DiveFilter self;
|
||||
return &self;
|
||||
}
|
||||
|
||||
DiveFilter::DiveFilter()
|
||||
{
|
||||
}
|
||||
|
||||
ShownChange DiveFilter::update(const QVector<dive *> &dives) const
|
||||
{
|
||||
dive *old_current = current_dive;
|
||||
|
||||
ShownChange res;
|
||||
switch (filterData.mode) {
|
||||
default:
|
||||
case FilterData::Mode::NONE:
|
||||
for (dive *d: dives)
|
||||
updateDiveStatus(d, true, res);
|
||||
break;
|
||||
case FilterData::Mode::FULLTEXT:
|
||||
for (dive *d: dives)
|
||||
updateDiveStatus(d, fulltext_dive_matches(d, filterData.fullText, StringFilterMode::STARTSWITH), res);
|
||||
break;
|
||||
case FilterData::Mode::PEOPLE:
|
||||
for (dive *d: dives)
|
||||
updateDiveStatus(d, hasPersons(filterData.tags, d), res);
|
||||
break;
|
||||
case FilterData::Mode::TAGS:
|
||||
for (dive *d: dives)
|
||||
updateDiveStatus(d, hasTags(filterData.tags, d), res);
|
||||
break;
|
||||
}
|
||||
|
||||
res.currentChanged = old_current != current_dive;
|
||||
return res;
|
||||
}
|
||||
|
||||
ShownChange DiveFilter::updateAll() const
|
||||
{
|
||||
dive *old_current = current_dive;
|
||||
|
||||
ShownChange res;
|
||||
int i;
|
||||
dive *d;
|
||||
switch (filterData.mode) {
|
||||
default:
|
||||
case FilterData::Mode::NONE:
|
||||
for_each_dive(i, d)
|
||||
updateDiveStatus(d, true, res);
|
||||
break;
|
||||
case FilterData::Mode::FULLTEXT: {
|
||||
FullTextResult ft = fulltext_find_dives(filterData.fullText, StringFilterMode::STARTSWITH);
|
||||
for_each_dive(i, d)
|
||||
updateDiveStatus(d, ft.dive_matches(d), res);
|
||||
break;
|
||||
}
|
||||
case FilterData::Mode::PEOPLE:
|
||||
for_each_dive(i, d)
|
||||
updateDiveStatus(d, hasPersons(filterData.tags, d), res);
|
||||
break;
|
||||
case FilterData::Mode::TAGS:
|
||||
for_each_dive(i, d)
|
||||
updateDiveStatus(d, hasTags(filterData.tags, d), res);
|
||||
break;
|
||||
}
|
||||
|
||||
res.currentChanged = old_current != current_dive;
|
||||
return res;
|
||||
}
|
||||
|
||||
void DiveFilter::setFilter(const FilterData &data)
|
||||
{
|
||||
filterData = data;
|
||||
emit diveListNotifier.filterReset();
|
||||
}
|
||||
|
||||
#else // SUBSURFACE_MOBILE
|
||||
|
||||
#include "desktop-widgets/mapwidget.h"
|
||||
#include "desktop-widgets/mainwindow.h"
|
||||
#include "desktop-widgets/divelistview.h"
|
||||
#include "qt-models/filtermodels.h"
|
||||
|
||||
bool FilterData::validFilter() const
|
||||
{
|
||||
|
@ -216,6 +96,7 @@ bool DiveFilter::showDive(const struct dive *d) const
|
|||
[d] (const filter_constraint &c) { return filter_constraint_match_dive(c, d); });
|
||||
}
|
||||
|
||||
#ifndef SUBSURFACE_MOBILE
|
||||
void DiveFilter::startFilterDiveSites(QVector<dive_site *> ds)
|
||||
{
|
||||
if (++diveSiteRefCount > 1) {
|
||||
|
@ -224,7 +105,7 @@ void DiveFilter::startFilterDiveSites(QVector<dive_site *> ds)
|
|||
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.
|
||||
// TODO: why here? why not catch the filterReset signal in the map widget
|
||||
MapWidget::instance()->reload();
|
||||
emit diveListNotifier.filterReset();
|
||||
}
|
||||
|
@ -263,10 +144,15 @@ bool DiveFilter::diveSiteMode() const
|
|||
{
|
||||
return diveSiteRefCount > 0;
|
||||
}
|
||||
#else
|
||||
bool DiveFilter::diveSiteMode() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
void DiveFilter::setFilter(const FilterData &data)
|
||||
{
|
||||
filterData = data;
|
||||
emit diveListNotifier.filterReset();
|
||||
}
|
||||
#endif // SUBSURFACE_MOBILE
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include <QStringList>
|
||||
|
||||
struct dive;
|
||||
struct dive_trip;
|
||||
struct dive_site;
|
||||
|
||||
// Structure describing changes of shown status upon applying the filter
|
||||
struct ShownChange {
|
||||
|
@ -18,43 +20,6 @@ struct ShownChange {
|
|||
bool currentChanged;
|
||||
};
|
||||
|
||||
// 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
|
||||
|
||||
struct FilterData {
|
||||
// On mobile, we support searching fulltext (all fields), people (buddies and divemasters) and tags
|
||||
enum class Mode {
|
||||
NONE = 0,
|
||||
FULLTEXT = 1,
|
||||
PEOPLE = 2,
|
||||
TAGS = 3
|
||||
};
|
||||
|
||||
Mode mode = Mode::NONE;
|
||||
FullTextQuery fullText; // For fulltext
|
||||
QStringList tags; // For people and tags
|
||||
};
|
||||
|
||||
class DiveFilter {
|
||||
public:
|
||||
static DiveFilter *instance();
|
||||
|
||||
ShownChange update(const QVector<dive *> &dives) const; // Update filter status of given dives and return dives whose status changed
|
||||
ShownChange updateAll() const; // Update filter status of all dives and return dives whose status changed
|
||||
void setFilter(const FilterData &data);
|
||||
private:
|
||||
DiveFilter();
|
||||
|
||||
FilterData filterData;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
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 {
|
||||
|
@ -73,11 +38,13 @@ class DiveFilter {
|
|||
public:
|
||||
static DiveFilter *instance();
|
||||
|
||||
bool diveSiteMode() const; // returns true if we're filtering on dive site
|
||||
bool diveSiteMode() const; // returns true if we're filtering on dive site (on mobile always returns false)
|
||||
#ifndef SUBSURFACE_MOBILE
|
||||
const QVector<dive_site *> &filteredDiveSites() const;
|
||||
void startFilterDiveSites(QVector<dive_site *> ds);
|
||||
void setFilterDiveSite(QVector<dive_site *> ds);
|
||||
void stopFilterDiveSites();
|
||||
#endif
|
||||
void setFilter(const FilterData &data);
|
||||
ShownChange update(const QVector<dive *> &dives) const; // Update filter status of given dives and return dives whose status changed
|
||||
ShownChange updateAll() const; // Update filter status of all dives and return dives whose status changed
|
||||
|
@ -96,6 +63,5 @@ private:
|
|||
// The filter is now not in dive site mode, even if it should
|
||||
int diveSiteRefCount;
|
||||
};
|
||||
#endif // SUBSURFACE_MOBILE
|
||||
|
||||
#endif
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "core/errorhelper.h"
|
||||
#include "core/file.h"
|
||||
#include "core/divefilter.h"
|
||||
#include "core/filterconstraint.h"
|
||||
#include "core/qthelper.h"
|
||||
#include "core/qt-gui.h"
|
||||
#include "core/git-access.h"
|
||||
|
@ -2084,6 +2085,13 @@ void QMLManager::restartDownload(QAndroidJniObject usbDevice)
|
|||
|
||||
#endif
|
||||
|
||||
static filter_constraint make_filter_constraint(filter_constraint_type type, const QString &s)
|
||||
{
|
||||
filter_constraint res(type);
|
||||
filter_constraint_set_stringlist(res, s);
|
||||
return res;
|
||||
}
|
||||
|
||||
void QMLManager::setFilter(const QString filterText, int index)
|
||||
{
|
||||
QString f = filterText.trimmed();
|
||||
|
@ -2091,15 +2099,17 @@ void QMLManager::setFilter(const QString filterText, int index)
|
|||
if (!f.isEmpty()) {
|
||||
// This is ugly - the indices of the mode are hardcoded!
|
||||
switch(index) {
|
||||
default:
|
||||
case 0: data.mode = FilterData::Mode::FULLTEXT; break;
|
||||
case 1: data.mode = FilterData::Mode::PEOPLE; break;
|
||||
case 2: data.mode = FilterData::Mode::TAGS; break;
|
||||
}
|
||||
if (data.mode == FilterData::Mode::FULLTEXT)
|
||||
default:
|
||||
case 0:
|
||||
data.fullText = f;
|
||||
else
|
||||
data.tags = f.split(",", QString::SkipEmptyParts);
|
||||
break;
|
||||
case 1:
|
||||
data.constraints.push_back(make_filter_constraint(FILTER_CONSTRAINT_PEOPLE, f));
|
||||
break;
|
||||
case 2:
|
||||
data.constraints.push_back(make_filter_constraint(FILTER_CONSTRAINT_TAGS, f));
|
||||
break;
|
||||
}
|
||||
}
|
||||
DiveFilter::instance()->setFilter(data);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue