Mailcommon

hierarchicalfoldermatcher.cpp
1 /*
2  SPDX-FileCopyrightText: 2021 Intevation GmbH
3  SPDX-FileContributor: Ingo Klöcker <[email protected]>
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 
14 using namespace MailCommon;
15 HierarchicalFolderMatcher::HierarchicalFolderMatcher() = default;
16 
17 bool HierarchicalFolderMatcher::isNull() const
18 {
19  return filterRegExps.empty();
20 }
21 
22 void HierarchicalFolderMatcher::setFilter(const QString &filter, Qt::CaseSensitivity caseSensitivity)
23 {
24  filterRegExps.clear();
25  if (filter.isEmpty()) {
26  return;
27  }
29  const auto parts = 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 
38 bool 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 
60 QModelIndex 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 }
virtual bool hasChildren(const QModelIndex &parent) const const
QString wildcardToRegularExpression(const QString &pattern)
virtual int rowCount(const QModelIndex &parent) const const=0
CaseSensitivity
virtual QVariant data(const QModelIndex &index, int role) const const=0
Q_SCRIPTABLE Q_NOREPLY void start()
virtual QModelIndex parent(const QModelIndex &index) const const=0
QFuture< void > filter(Sequence &sequence, KeepFunctor filterFunction)
bool isValid() const const
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const=0
QModelIndex parent() const const
The filter dialog.
QString toString() const const
QModelIndex siblingAtColumn(int column) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Sat Sep 24 2022 03:58:15 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.