Mailcommon

hierarchicalfoldermatcher.cpp
1/*
2 SPDX-FileCopyrightText: 2021 Intevation GmbH
3 SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
4
5 SPDX-License-Identifier: GPL-2.0-or-later
6*/
7
8#include "hierarchicalfoldermatcher_p.h"
9
10#include <QAbstractItemModel>
11#include <QModelIndex>
12#include <QRegularExpression>
13
14using namespace MailCommon;
15HierarchicalFolderMatcher::HierarchicalFolderMatcher() = default;
16
17bool HierarchicalFolderMatcher::isNull() const
18{
19 return filterRegExps.empty();
20}
21
22void HierarchicalFolderMatcher::setFilter(const QString &filter, Qt::CaseSensitivity caseSensitivity)
23{
24 filterRegExps.clear();
25 if (filter.isEmpty()) {
26 return;
27 }
29 const auto parts = QStringView(filter).split(QLatin1Char('/'));
30 std::transform(std::begin(parts), std::end(parts), std::back_inserter(filterRegExps), [patternOptions](const auto &part) {
31 // QRegularExpression::wildcardToRegularExpression() returns a fully anchored
32 // regular expression, but we want to check for substring matches; wrap
33 // the user's filter part into '*' to fix this
35 });
36}
37
38bool HierarchicalFolderMatcher::matches(const QAbstractItemModel *model, const QModelIndex &start, int role)
39{
40 if (!start.isValid()) {
41 return false;
42 }
43
44 const auto filterKeyColumn = start.column();
45 QModelIndex idx = start;
46 for (auto it = filterRegExps.crbegin(); it != filterRegExps.crend(); ++it) {
47 if (!idx.isValid()) {
48 // we have exceeded the model root or the column does not exist
49 return false;
50 }
51 const QString key = model->data(idx, role).toString();
52 if (!it->match(key).hasMatch()) {
53 return false;
54 }
55 idx = idx.parent().siblingAtColumn(filterKeyColumn);
56 }
57 return true;
58}
59
60QModelIndex HierarchicalFolderMatcher::findFirstMatch(const QAbstractItemModel *model, const QModelIndex &start, int role)
61{
62 // inspired by QAbstractItemModel::match(), but using our own matching
63 QModelIndex result;
64
65 const int filterKeyColumn = start.column();
66 const QModelIndex p = model->parent(start);
67 int from = start.row();
68 int to = model->rowCount(p);
69
70 // iterate twice (first from start row to last row; then from first row to before start row)
71 for (int i = 0; (i < 2) && !result.isValid(); ++i) {
72 for (int row = from; (row < to) && !result.isValid(); ++row) {
73 QModelIndex idx = model->index(row, filterKeyColumn, p);
74 if (!idx.isValid()) {
75 continue;
76 }
77 if (matches(model, idx, role)) {
78 result = idx;
79 break;
80 }
81 const auto idxAsParent = filterKeyColumn != 0 ? idx.siblingAtColumn(0) : idx;
82 if (model->hasChildren(idxAsParent)) {
83 result = findFirstMatch(model, model->index(0, filterKeyColumn, idxAsParent), role);
84 }
85 }
86 // prepare for the next iteration
87 from = 0;
88 to = start.row();
89 }
90
91 return result;
92}
Q_SCRIPTABLE Q_NOREPLY void start()
The filter dialog.
virtual QVariant data(const QModelIndex &index, int role) const const=0
virtual bool hasChildren(const QModelIndex &parent) const const
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const=0
virtual QModelIndex parent(const QModelIndex &index) const const=0
virtual int rowCount(const QModelIndex &parent) const const=0
int column() const const
bool isValid() const const
QModelIndex parent() const const
int row() const const
QModelIndex siblingAtColumn(int column) const const
QString wildcardToRegularExpression(QStringView pattern, WildcardConversionOptions options)
QList< QStringView > split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
CaseSensitivity
QFuture< void > filter(QThreadPool *pool, Sequence &sequence, KeepFunctor &&filterFunction)
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:14:01 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.