Introduce negate-toggle buttons to filter lists

Introduce toggle buttons which mean "filter all dives except
those fulfilling the selected criteria".

The old code used to check for rowCount() == 0. This should never happen,
because there is always a row "empty field". This check was moved into
the preamble of the functions to seperate it from the actual logic.

Fixes #435

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
Berthold Stoeger 2017-12-24 14:35:59 +01:00 committed by Dirk Hohndel
parent b86c70ab2c
commit b6bf57a13b
4 changed files with 55 additions and 41 deletions

View file

@ -51,6 +51,16 @@
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="notButton">
<property name="text">
<string>¬</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>

View file

@ -516,6 +516,7 @@ FilterBase::FilterBase(FilterModelBase *model_, QWidget *parent) : QWidget(paren
filter->setSourceModel(model);
filter->setFilterCaseSensitivity(Qt::CaseInsensitive);
connect(ui.filterInternalList, SIGNAL(textChanged(QString)), filter, SLOT(setFilterFixedString(QString)));
connect(ui.notButton, &QToolButton::toggled, model, &FilterModelBase::setNegate);
ui.filterList->setModel(filter);
addContextMenuEntry(tr("Select All"), &FilterModelBase::selectAll);

View file

@ -26,7 +26,8 @@ CREATE_INSTANCE_METHOD(SuitsFilterModel)
CREATE_INSTANCE_METHOD(MultiFilterSortModel)
FilterModelBase::FilterModelBase(QObject *parent) : QStringListModel(parent),
anyChecked(false)
anyChecked(false),
negate(false)
{
}
@ -115,6 +116,12 @@ void FilterModelBase::invertSelection()
emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0));
}
void FilterModelBase::setNegate(bool negateParam)
{
negate = negateParam;
emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0));
}
SuitsFilterModel::SuitsFilterModel(QObject *parent) : FilterModelBase(parent)
{
}
@ -129,28 +136,25 @@ bool SuitsFilterModel::doFilter(dive *d, QModelIndex &index0, QAbstractItemModel
Q_UNUSED(index0);
Q_UNUSED(sourceModel);
if (!anyChecked) {
// rowCount() == 0 should never happen, because we have the "no suits" row
// let's handle it gracefully anyway.
if (!anyChecked || rowCount() == 0)
return true;
}
// Checked means 'Show', Unchecked means 'Hide'.
QString suit(d->suit);
// only show empty suit dives if the user checked that.
if (suit.isEmpty()) {
if (rowCount() > 0)
return checkState[rowCount() - 1];
else
return true;
}
if (suit.isEmpty())
return checkState[rowCount() - 1] != negate;
// there is a suit selected
QStringList suitList = stringList();
// Ignore last item, since this is the "Show Empty Tags" entry
for (int i = 0; i < rowCount() - 1; i++) {
if (checkState[i] && suit == suitList[i])
return true;
return !negate;
}
return false;
return negate;
}
void SuitsFilterModel::repopulate()
@ -200,18 +204,16 @@ bool TagFilterModel::doFilter(dive *d, QModelIndex &index0, QAbstractItemModel *
Q_UNUSED(sourceModel);
// If there's nothing checked, this should show everything
if (!anyChecked) {
// rowCount() == 0 should never happen, because we have the "no tags" row
// let's handle it gracefully anyway.
if (!anyChecked || rowCount() == 0)
return true;
}
// Checked means 'Show', Unchecked means 'Hide'.
struct tag_entry *head = d->tag_list;
if (!head) { // last tag means "Show empty tags";
if (rowCount() > 0)
return checkState[rowCount() - 1];
else
return true;
}
if (!head) // last tag means "Show empty tags";
return checkState[rowCount() - 1] != negate;
// have at least one tag.
QStringList tagList = stringList();
@ -221,11 +223,11 @@ bool TagFilterModel::doFilter(dive *d, QModelIndex &index0, QAbstractItemModel *
QString tagName(head->tag->name);
int index = tagList.indexOf(tagName);
if (checkState[index])
return true;
return !negate;
head = head->next;
}
}
return false;
return negate;
}
BuddyFilterModel::BuddyFilterModel(QObject *parent) : FilterModelBase(parent)
@ -243,30 +245,28 @@ bool BuddyFilterModel::doFilter(dive *d, QModelIndex &index0, QAbstractItemModel
Q_UNUSED(sourceModel);
// If there's nothing checked, this should show everything
if (!anyChecked) {
// rowCount() == 0 should never happen, because we have the "no tags" row
// let's handle it gracefully anyway.
if (!anyChecked || rowCount() == 0)
return true;
}
// Checked means 'Show', Unchecked means 'Hide'.
QString persons = QString(d->buddy) + "," + QString(d->divemaster);
QStringList personsList = persons.split(',', QString::SkipEmptyParts);
for (QString &s : personsList)
s = s.trimmed();
// only show empty buddie dives if the user checked that.
if (personsList.isEmpty()) {
if (rowCount() > 0)
return checkState[rowCount() - 1];
else
return true;
}
if (personsList.isEmpty())
return checkState[rowCount() - 1] != negate;
// have at least one buddy
QStringList buddyList = stringList();
// Ignore last item, since this is the "Show Empty Tags" entry
for (int i = 0; i < rowCount() - 1; i++) {
if (checkState[i] && personsList.contains(buddyList[i], Qt::CaseInsensitive))
return true;
return !negate;
}
return false;
return negate;
}
void BuddyFilterModel::repopulate()
@ -302,27 +302,25 @@ bool LocationFilterModel::doFilter(struct dive *d, QModelIndex &index0, QAbstrac
Q_UNUSED(index0);
Q_UNUSED(sourceModel);
if (!anyChecked) {
// rowCount() == 0 should never happen, because we have the "no location" row
// let's handle it gracefully anyway.
if (!anyChecked || rowCount() == 0)
return true;
}
// Checked means 'Show', Unchecked means 'Hide'.
QString location(get_dive_location(d));
// only show empty location dives if the user checked that.
if (location.isEmpty()) {
if (rowCount() > 0)
return checkState[rowCount() - 1];
else
return true;
}
if (location.isEmpty())
return checkState[rowCount() - 1] != negate;
// There is a location selected
QStringList locationList = stringList();
// Ignore last item, since this is the "Show Empty Tags" entry
for (int i = 0; i < rowCount() - 1; i++) {
if (checkState[i] && location == locationList[i])
return true;
return !negate;
}
return false;
return negate;
}
void LocationFilterModel::repopulate()

View file

@ -8,6 +8,7 @@
#include <vector>
class FilterModelBase : public QStringListModel {
Q_OBJECT
public:
virtual bool doFilter(struct dive *d, QModelIndex &index0, QAbstractItemModel *sourceModel) const = 0;
void clearFilter();
@ -15,6 +16,10 @@ public:
void invertSelection();
std::vector<char> checkState;
bool anyChecked;
bool negate;
public
slots:
void setNegate(bool negate);
protected:
explicit FilterModelBase(QObject *parent = 0);
void updateList(const QStringList &new_list);