File indexing completed on 2024-05-12 15:59:51
0001 /* 0002 * SPDX-FileCopyrightText: 2019 Agata Cacko <cacko.azh@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "KisResourceSearchBoxFilter.h" 0008 0009 0010 #include <QRegExp> 0011 #include <QRegularExpression> 0012 #include <QList> 0013 #include <QSet> 0014 #include <kis_debug.h> 0015 0016 class Q_DECL_HIDDEN KisResourceSearchBoxFilter::Private 0017 { 0018 public: 0019 Private() 0020 : searchTokenizer("\\s*,+\\s*") 0021 {} 0022 0023 QRegularExpression searchTokenizer; 0024 0025 QChar excludeBegin {'!'}; 0026 QChar tagBegin {'#'}; 0027 QChar exactMatchBeginEnd {'"'}; 0028 0029 QSet<QString> tagExactMatchesIncluded; 0030 QSet<QString> tagExactMatchesExcluded; 0031 QSet<QString> resourceExactMatchesIncluded; 0032 QSet<QString> resourceExactMatchesExcluded; 0033 0034 QList<QString> resourceNamesPartialIncluded; 0035 QList<QString> resourceNamesPartialExcluded; 0036 QList<QString> tagsPartialIncluded; 0037 QList<QString> tagsPartialExcluded; 0038 0039 QString filter; 0040 }; 0041 0042 0043 KisResourceSearchBoxFilter::KisResourceSearchBoxFilter() 0044 : m_d(new Private()) 0045 { 0046 } 0047 0048 KisResourceSearchBoxFilter::~KisResourceSearchBoxFilter() 0049 { 0050 0051 } 0052 0053 bool checkDelimetersAndCut(const QChar& begin, const QChar& end, QString& token) { 0054 if (token.startsWith(begin) && token.endsWith(end)) { 0055 token.remove(0, 1); 0056 token = token.left(token.length() - 1); 0057 return true; 0058 } else { 0059 return false; 0060 } 0061 } 0062 0063 bool checkDelimetersAndCut(const QChar& beginEnd, QString& token) { 0064 return checkDelimetersAndCut(beginEnd, beginEnd, token); 0065 } 0066 0067 bool checkPrefixAndCut(QChar& prefix, QString& token) { 0068 if (token.startsWith(prefix)) { 0069 token.remove(0, 1); 0070 return true; 0071 } else { 0072 return false; 0073 } 0074 } 0075 0076 void KisResourceSearchBoxFilter::setFilter(const QString& filter) 0077 { 0078 m_d->filter = QString(filter); 0079 initializeFilterData(); 0080 } 0081 0082 0083 bool KisResourceSearchBoxFilter::matchesResource(const QString &_resourceName, const QStringList &tagList) 0084 { 0085 // exact matches 0086 QString resourceName = _resourceName.toLower(); 0087 0088 if (m_d->resourceExactMatchesIncluded.count() > 0 0089 && !m_d->resourceExactMatchesIncluded.contains(resourceName)) { 0090 return false; 0091 } 0092 if (m_d->resourceExactMatchesExcluded.contains(resourceName)) { 0093 return false; 0094 } 0095 0096 // partial name matches 0097 if (m_d->resourceNamesPartialIncluded.count() > 0) { 0098 Q_FOREACH(const QString& partialName, m_d->resourceNamesPartialIncluded) { 0099 if (!resourceName.contains(partialName) && tagList.filter(partialName, Qt::CaseInsensitive).isEmpty()) { 0100 return false; 0101 } 0102 } 0103 } 0104 0105 Q_FOREACH(const QString& partialName, m_d->resourceNamesPartialExcluded) { 0106 if (resourceName.contains(partialName) || tagList.filter(partialName, Qt::CaseInsensitive).size() > 0) { 0107 return false; 0108 } 0109 } 0110 0111 // Tag partial matches 0112 if (m_d->tagsPartialIncluded.count() > 0 ) { 0113 Q_FOREACH(const QString& partialTag, m_d->tagsPartialIncluded) { 0114 if (tagList.filter(partialTag, Qt::CaseInsensitive).isEmpty()) { 0115 return false; 0116 } 0117 } 0118 } 0119 0120 if (m_d->tagsPartialExcluded.count() > 0) { 0121 Q_FOREACH(const QString& partialTag, m_d->tagsPartialExcluded) { 0122 if (tagList.filter(partialTag, Qt::CaseInsensitive).size() > 0) { 0123 return false; 0124 } 0125 } 0126 } 0127 0128 // Tag exact matches 0129 if (m_d->tagExactMatchesIncluded.count() > 0) { 0130 Q_FOREACH(const QString& tagName, m_d->tagExactMatchesIncluded) { 0131 if (!tagList.contains(tagName, Qt::CaseInsensitive)) { 0132 return false; 0133 } 0134 } 0135 } 0136 0137 if (m_d->tagExactMatchesExcluded.count() > 0) { 0138 Q_FOREACH(const QString excludedTag, m_d->tagExactMatchesExcluded) { 0139 if (tagList.contains(excludedTag, Qt::CaseInsensitive)) { 0140 return false; 0141 } 0142 } 0143 } 0144 0145 return true; 0146 } 0147 0148 bool KisResourceSearchBoxFilter::isEmpty() 0149 { 0150 return m_d->filter.isEmpty(); 0151 } 0152 0153 void KisResourceSearchBoxFilter::clearFilterData() 0154 { 0155 m_d->resourceExactMatchesIncluded.clear(); 0156 m_d->resourceExactMatchesExcluded.clear(); 0157 m_d->tagExactMatchesIncluded.clear(); 0158 m_d->tagExactMatchesExcluded.clear(); 0159 0160 m_d->resourceNamesPartialIncluded.clear(); 0161 m_d->resourceNamesPartialExcluded.clear(); 0162 m_d->tagsPartialIncluded.clear(); 0163 m_d->tagsPartialExcluded.clear(); 0164 } 0165 0166 void KisResourceSearchBoxFilter::initializeFilterData() 0167 { 0168 clearFilterData(); 0169 0170 QString tempFilter(m_d->filter); 0171 0172 QStringList tokens = tempFilter.split(m_d->searchTokenizer, QString::SkipEmptyParts); 0173 Q_FOREACH(const QString& token, tokens) { 0174 QString workingToken(token.toLower()); 0175 const bool included = !checkPrefixAndCut(m_d->excludeBegin, workingToken); 0176 0177 if (checkPrefixAndCut(m_d->tagBegin, workingToken)) { 0178 if (checkDelimetersAndCut(m_d->exactMatchBeginEnd, workingToken)) { 0179 if (included) { 0180 0181 m_d->tagExactMatchesIncluded.insert(workingToken); 0182 } else { 0183 0184 m_d->tagExactMatchesExcluded.insert(workingToken); 0185 } 0186 } else { 0187 if (included) { 0188 0189 m_d->tagsPartialIncluded.append(workingToken); 0190 } else { 0191 0192 m_d->tagsPartialExcluded.append(workingToken); 0193 } 0194 } 0195 } else if (checkDelimetersAndCut(m_d->exactMatchBeginEnd, workingToken)) { 0196 if (included) { 0197 0198 m_d->resourceExactMatchesIncluded.insert(workingToken); 0199 } else { 0200 0201 m_d->resourceExactMatchesExcluded.insert(workingToken); 0202 } 0203 0204 } else { 0205 if (included) { 0206 0207 m_d->resourceNamesPartialIncluded.append(workingToken); 0208 } else { 0209 0210 m_d->resourceNamesPartialExcluded.append(workingToken); 0211 } 0212 } 0213 } 0214 }