mirror of
https://github.com/subsurface/subsurface.git
synced 2025-02-19 22:16:15 +00:00
Filter: replace checked-state by struct
In the future, we might be smarter about the dive-counts and calculate them only once and incrementally (if e.g. new dives are added). Prepare for more complex caching by turning the checked boolean into a struct, which can then be extended by a count and other things (e.g. the name). Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
parent
80fe8fb331
commit
ec586d0e0a
2 changed files with 51 additions and 34 deletions
|
@ -32,30 +32,41 @@ FilterModelBase::FilterModelBase(QObject *parent) : QStringListModel(parent),
|
|||
{
|
||||
}
|
||||
|
||||
// Update the stringList and the checkState array.
|
||||
// Update the stringList and the items array.
|
||||
// The last item is supposed to be the "Show Empty Tags" entry.
|
||||
void FilterModelBase::updateList(const QStringList &newList)
|
||||
{
|
||||
// Keep copy of old checkState array to reimport them later.
|
||||
std::vector<char> oldCheckState = checkState;
|
||||
checkState.resize(newList.count());
|
||||
std::fill(checkState.begin(), checkState.end(), false);
|
||||
anyChecked = false;
|
||||
// Keep copy of the old items array to reimport the checked state later.
|
||||
// Note that by using std::move(), this is an essentially free operation:
|
||||
// The data is moved from the old array to the new one and the old array
|
||||
// is reset to zero size.
|
||||
std::vector<Item> oldItems = std::move(items);
|
||||
|
||||
// Ignore last item, since this is the "Show Empty Tags" entry.
|
||||
for (int i = 0; i < rowCount() - 1; i++) {
|
||||
if (oldCheckState[i]) {
|
||||
// Resize the cleared array to the new size. This leaves the checked
|
||||
// flag in an undefined state (since we didn't define a default constructor).
|
||||
items.resize(newList.count());
|
||||
|
||||
// First, reset all checked states to false
|
||||
anyChecked = false;
|
||||
for (Item &item: items)
|
||||
item.checked = false;
|
||||
|
||||
// Then, restore the checked state. Ignore the last item, since
|
||||
// this is the "Show Empty Tags" entry.
|
||||
for (int i = 0; i < (int)oldItems.size() - 1; i++) {
|
||||
if (oldItems[i].checked) {
|
||||
int ind = newList.indexOf(stringList()[i]);
|
||||
if (ind >= 0 && ind < newList.count() - 1) {
|
||||
checkState[ind] = true;
|
||||
items[ind].checked = true;
|
||||
anyChecked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// On program startup, the old list is empty.
|
||||
if (rowCount() > 0 && oldCheckState.back()) {
|
||||
checkState.back() = true;
|
||||
// Reset the state of the "Show Empty Tags" entry. But be careful:
|
||||
// on program startup, the old list is empty.
|
||||
if (!oldItems.empty() && !items.empty() && oldItems.back().checked) {
|
||||
items.back().checked = true;
|
||||
anyChecked = true;
|
||||
}
|
||||
setStringList(newList);
|
||||
|
@ -69,10 +80,10 @@ Qt::ItemFlags FilterModelBase::flags(const QModelIndex &index) const
|
|||
bool FilterModelBase::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
if (role == Qt::CheckStateRole) {
|
||||
checkState[index.row()] = value.toBool();
|
||||
items[index.row()].checked = value.toBool();
|
||||
anyChecked = false;
|
||||
for (int i = 0; i < rowCount(); i++) {
|
||||
if (checkState[i] == true) {
|
||||
for (const Item &item: items) {
|
||||
if (item.checked) {
|
||||
anyChecked = true;
|
||||
break;
|
||||
}
|
||||
|
@ -86,7 +97,7 @@ bool FilterModelBase::setData(const QModelIndex &index, const QVariant &value, i
|
|||
QVariant FilterModelBase::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (role == Qt::CheckStateRole) {
|
||||
return checkState[index.row()] ? Qt::Checked : Qt::Unchecked;
|
||||
return items[index.row()].checked ? Qt::Checked : Qt::Unchecked;
|
||||
} else if (role == Qt::DisplayRole) {
|
||||
QString value = stringList()[index.row()];
|
||||
int count = countDives((index.row() == rowCount() - 1) ? "" : qPrintable(value));
|
||||
|
@ -97,23 +108,25 @@ QVariant FilterModelBase::data(const QModelIndex &index, int role) const
|
|||
|
||||
void FilterModelBase::clearFilter()
|
||||
{
|
||||
std::fill(checkState.begin(), checkState.end(), false);
|
||||
for (Item &item: items)
|
||||
item.checked = false;
|
||||
anyChecked = false;
|
||||
emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0));
|
||||
}
|
||||
|
||||
void FilterModelBase::selectAll()
|
||||
{
|
||||
std::fill(checkState.begin(), checkState.end(), true);
|
||||
for (Item &item: items)
|
||||
item.checked = true;
|
||||
anyChecked = true;
|
||||
emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0));
|
||||
}
|
||||
|
||||
void FilterModelBase::invertSelection()
|
||||
{
|
||||
for (char &b : checkState)
|
||||
b = !b;
|
||||
anyChecked = std::any_of(checkState.begin(), checkState.end(), [](char b) { return !!b; });
|
||||
for (Item &item : items)
|
||||
item.checked = !item.checked;
|
||||
anyChecked = std::any_of(items.begin(), items.end(), [](Item &item) { return !!item.checked; });
|
||||
emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0));
|
||||
}
|
||||
|
||||
|
@ -143,13 +156,13 @@ bool SuitsFilterModel::doFilter(const dive *d) const
|
|||
QString suit(d->suit);
|
||||
// only show empty suit dives if the user checked that.
|
||||
if (suit.isEmpty())
|
||||
return checkState[rowCount() - 1] != negate;
|
||||
return items[rowCount() - 1].checked != 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])
|
||||
if (items[i].checked && suit == suitList[i])
|
||||
return !negate;
|
||||
}
|
||||
return negate;
|
||||
|
@ -208,7 +221,7 @@ bool TagFilterModel::doFilter(const dive *d) const
|
|||
struct tag_entry *head = d->tag_list;
|
||||
|
||||
if (!head) // last tag means "Show empty tags";
|
||||
return checkState[rowCount() - 1] != negate;
|
||||
return items[rowCount() - 1].checked != negate;
|
||||
|
||||
// have at least one tag.
|
||||
QStringList tagList = stringList();
|
||||
|
@ -217,7 +230,7 @@ bool TagFilterModel::doFilter(const dive *d) const
|
|||
while (head) {
|
||||
QString tagName(head->tag->name);
|
||||
int index = tagList.indexOf(tagName);
|
||||
if (index >= 0 && checkState[index])
|
||||
if (index >= 0 && items[index].checked)
|
||||
return !negate;
|
||||
head = head->next;
|
||||
}
|
||||
|
@ -249,13 +262,13 @@ bool BuddyFilterModel::doFilter(const dive *d) const
|
|||
s = s.trimmed();
|
||||
// only show empty buddie dives if the user checked that.
|
||||
if (personsList.isEmpty())
|
||||
return checkState[rowCount() - 1] != negate;
|
||||
return items[rowCount() - 1].checked != 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))
|
||||
if (items[i].checked && personsList.contains(buddyList[i], Qt::CaseInsensitive))
|
||||
return !negate;
|
||||
}
|
||||
return negate;
|
||||
|
@ -300,13 +313,13 @@ bool LocationFilterModel::doFilter(const dive *d) const
|
|||
QString location(get_dive_location(d));
|
||||
// only show empty location dives if the user checked that.
|
||||
if (location.isEmpty())
|
||||
return checkState[rowCount() - 1] != negate;
|
||||
return items[rowCount() - 1].checked != 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])
|
||||
if (items[i].checked && location == locationList[i])
|
||||
return !negate;
|
||||
}
|
||||
return negate;
|
||||
|
@ -341,8 +354,8 @@ void LocationFilterModel::changeName(const QString &oldName, const QString &newN
|
|||
|
||||
// If there was already an entry with the new name, we are merging entries.
|
||||
// Thus, if the old entry was selected, also select the new entry.
|
||||
if (newIndex >= 0 && checkState[oldIndex])
|
||||
checkState[newIndex] = true;
|
||||
if (newIndex >= 0 && items[oldIndex].checked)
|
||||
items[newIndex].checked = true;
|
||||
setStringList(list);
|
||||
}
|
||||
|
||||
|
@ -356,7 +369,7 @@ void LocationFilterModel::addName(const QString &newName)
|
|||
if (!anyChecked || newName.isEmpty() || list.indexOf(newName) >= 0)
|
||||
return;
|
||||
list.prepend(newName);
|
||||
checkState.insert(checkState.begin(), true);
|
||||
items.insert(items.begin(), { true });
|
||||
setStringList(list);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,12 +11,16 @@ struct dive;
|
|||
|
||||
class FilterModelBase : public QStringListModel {
|
||||
Q_OBJECT
|
||||
protected:
|
||||
struct Item {
|
||||
bool checked;
|
||||
};
|
||||
std::vector<Item> items;
|
||||
public:
|
||||
virtual bool doFilter(const dive *d) const = 0;
|
||||
void clearFilter();
|
||||
void selectAll();
|
||||
void invertSelection();
|
||||
std::vector<char> checkState;
|
||||
bool anyChecked;
|
||||
bool negate;
|
||||
public
|
||||
|
|
Loading…
Add table
Reference in a new issue