Warning, file /pim/mailcommon/src/folder/hierarchicalfoldermatcher.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2021 Intevation GmbH 0003 SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "hierarchicalfoldermatcher_p.h" 0009 0010 #include <QAbstractItemModel> 0011 #include <QModelIndex> 0012 #include <QRegularExpression> 0013 0014 using namespace MailCommon; 0015 HierarchicalFolderMatcher::HierarchicalFolderMatcher() = default; 0016 0017 bool HierarchicalFolderMatcher::isNull() const 0018 { 0019 return filterRegExps.empty(); 0020 } 0021 0022 void HierarchicalFolderMatcher::setFilter(const QString &filter, Qt::CaseSensitivity caseSensitivity) 0023 { 0024 filterRegExps.clear(); 0025 if (filter.isEmpty()) { 0026 return; 0027 } 0028 const auto patternOptions = caseSensitivity == Qt::CaseInsensitive ? QRegularExpression::CaseInsensitiveOption : QRegularExpression::NoPatternOption; 0029 const auto parts = filter.split(QLatin1Char('/')); 0030 std::transform(std::begin(parts), std::end(parts), std::back_inserter(filterRegExps), [patternOptions](const auto &part) { 0031 // QRegularExpression::wildcardToRegularExpression() returns a fully anchored 0032 // regular expression, but we want to check for substring matches; wrap 0033 // the user's filter part into '*' to fix this 0034 return QRegularExpression{QRegularExpression::wildcardToRegularExpression(QLatin1Char('*') + part + QLatin1Char('*')), patternOptions}; 0035 }); 0036 } 0037 0038 bool HierarchicalFolderMatcher::matches(const QAbstractItemModel *model, const QModelIndex &start, int role) 0039 { 0040 if (!start.isValid()) { 0041 return false; 0042 } 0043 0044 const auto filterKeyColumn = start.column(); 0045 QModelIndex idx = start; 0046 for (auto it = filterRegExps.crbegin(); it != filterRegExps.crend(); ++it) { 0047 if (!idx.isValid()) { 0048 // we have exceeded the model root or the column does not exist 0049 return false; 0050 } 0051 const QString key = model->data(idx, role).toString(); 0052 if (!it->match(key).hasMatch()) { 0053 return false; 0054 } 0055 idx = idx.parent().siblingAtColumn(filterKeyColumn); 0056 } 0057 return true; 0058 } 0059 0060 QModelIndex HierarchicalFolderMatcher::findFirstMatch(const QAbstractItemModel *model, const QModelIndex &start, int role) 0061 { 0062 // inspired by QAbstractItemModel::match(), but using our own matching 0063 QModelIndex result; 0064 0065 const int filterKeyColumn = start.column(); 0066 const QModelIndex p = model->parent(start); 0067 int from = start.row(); 0068 int to = model->rowCount(p); 0069 0070 // iterate twice (first from start row to last row; then from first row to before start row) 0071 for (int i = 0; (i < 2) && !result.isValid(); ++i) { 0072 for (int row = from; (row < to) && !result.isValid(); ++row) { 0073 QModelIndex idx = model->index(row, filterKeyColumn, p); 0074 if (!idx.isValid()) { 0075 continue; 0076 } 0077 if (matches(model, idx, role)) { 0078 result = idx; 0079 break; 0080 } 0081 const auto idxAsParent = filterKeyColumn != 0 ? idx.siblingAtColumn(0) : idx; 0082 if (model->hasChildren(idxAsParent)) { 0083 result = findFirstMatch(model, model->index(0, filterKeyColumn, idxAsParent), role); 0084 } 0085 } 0086 // prepare for the next iteration 0087 from = 0; 0088 to = start.row(); 0089 } 0090 0091 return result; 0092 }