Messagelib

filter.cpp
1 /******************************************************************************
2  *
3  * SPDX-FileCopyrightText: 2008 Szymon Tomasz Stefanek <[email protected]>
4  *
5  * SPDX-License-Identifier: GPL-2.0-or-later
6  *
7  *******************************************************************************/
8 
9 #include "core/filter.h"
10 #include "core/messageitem.h"
11 
12 #include <AkonadiSearch/PIM/emailquery.h>
13 #include <AkonadiSearch/PIM/resultiterator.h>
14 #include <KRandom>
15 using namespace MessageList::Core;
16 
17 Filter::Filter(QObject *parent)
18  : QObject(parent)
19 {
20  generateRandomIdentifier();
21 }
22 
23 bool Filter::containString(const QString &searchInString) const
24 {
25  bool found = false;
26  for (const QString &str : std::as_const(mSearchList)) {
27  if (searchInString.contains(str, Qt::CaseInsensitive)) {
28  found = true;
29  } else {
30  found = false;
31  break;
32  }
33  }
34  return found;
35 }
36 
37 const QString &Filter::iconName() const
38 {
39  return mIconName;
40 }
41 
42 void Filter::setIconName(const QString &newIconName)
43 {
44  mIconName = newIconName;
45 }
46 
47 void Filter::setOptions(QuickSearchLine::SearchOptions newOptions)
48 {
49  mOptions = newOptions;
50 }
51 
52 const QString &Filter::filterName() const
53 {
54  return mFilterName;
55 }
56 
57 void Filter::setFilterName(const QString &newFilterName)
58 {
59  mFilterName = newFilterName;
60 }
61 
62 void Filter::setIdentifier(const QString &newIdentifier)
63 {
64  mIdentifier = newIdentifier;
65 }
66 
67 bool Filter::match(const MessageItem *item) const
68 {
69  if (!mStatus.isEmpty()) {
70  for (Akonadi::MessageStatus status : std::as_const(mStatus)) {
71  if (!(status & item->status())) {
72  return false;
73  }
74  }
75  }
76 
77  if (!mSearchString.isEmpty()) {
78  if (mMatchingItemIds.contains(item->itemId())) {
79  return true;
80  }
81 
82  bool searchMatches = false;
83  bool searchEveryWhere = (mOptions & QuickSearchLine::SearchEveryWhere);
84  if (containString(item->subject()) && ((mOptions & QuickSearchLine::SearchAgainstSubject) || searchEveryWhere)) {
85  searchMatches = true;
86  } else if (containString(item->sender()) && ((mOptions & QuickSearchLine::SearchAgainstFrom) || searchEveryWhere)) {
87  searchMatches = true;
88  } else if (containString(item->receiver()) && ((mOptions & QuickSearchLine::SearchAgainstTo) || searchEveryWhere)) {
89  searchMatches = true;
90  }
91  if (!searchMatches) {
92  return false;
93  }
94  }
95 
96  if (!mTagId.isEmpty()) {
97  // mTagId is a Akonadi::Tag::url
98  const bool tagMatches = item->findTag(mTagId) != nullptr;
99  if (!tagMatches) {
100  return false;
101  }
102  }
103 
104  return true;
105 }
106 
107 QVector<Akonadi::MessageStatus> Filter::status() const
108 {
109  return mStatus;
110 }
111 
112 void Filter::setStatus(const QVector<Akonadi::MessageStatus> &lstStatus)
113 {
114  mStatus = lstStatus;
115 }
116 
117 bool Filter::isEmpty() const
118 {
119  if (!mStatus.isEmpty()) {
120  return false;
121  }
122 
123  if (!mSearchString.isEmpty()) {
124  return false;
125  }
126 
127  if (!mTagId.isEmpty()) {
128  return false;
129  }
130 
131  return true;
132 }
133 
134 void Filter::clear()
135 {
136  mStatus.clear();
137  mSearchString.clear();
138  mTagId.clear();
139  mMatchingItemIds.clear();
140  mSearchList.clear();
141 }
142 
143 void Filter::setCurrentFolder(const Akonadi::Collection &folder)
144 {
145  mCurrentFolder = folder;
146 }
147 
148 const QString &Filter::searchString() const
149 {
150  return mSearchString;
151 }
152 
153 QuickSearchLine::SearchOptions Filter::currentOptions() const
154 {
155  return mOptions;
156 }
157 
158 void Filter::save(const KSharedConfig::Ptr &config, const QString &filtername, const QString &iconName, int numFilter)
159 {
160  KConfigGroup grp(config, "General");
161  int numberFilter = (numFilter == -1) ? grp.readEntry("NumberFilter").toInt() : numFilter;
162  KConfigGroup newGroup(config, QStringLiteral("Filter_%1").arg(numberFilter++));
163  newGroup.writeEntry("name", filtername);
164  if (!iconName.isEmpty()) {
165  newGroup.writeEntry("iconName", iconName);
166  }
167  newGroup.writeEntry("searchString", mSearchString);
168  newGroup.writeEntry("searchOptions", static_cast<int>(mOptions));
169  newGroup.writeEntry("tagId", mTagId);
170  newGroup.writeEntry("identifier", mIdentifier);
171  QList<qint32> lst;
172  lst.reserve(mStatus.count());
173  for (const auto s : std::as_const(mStatus)) {
174  lst << s.toQInt32();
175  }
176  newGroup.writeEntry("status", lst);
177  newGroup.sync();
178  grp.writeEntry("NumberFilter", numberFilter);
179  grp.sync();
180  config->reparseConfiguration();
181 }
182 
183 Filter *Filter::load(const KSharedConfig::Ptr &config, int filternumber)
184 {
185  KConfigGroup grp(config, "General");
186  int numberFilter = grp.readEntry("NumberFilter").toInt();
187  if (filternumber < numberFilter) {
188  KConfigGroup newGroup(config, QStringLiteral("Filter_%1").arg(filternumber));
189  return loadFromConfigGroup(newGroup);
190  }
191  return nullptr;
192 }
193 
194 Filter *Filter::loadFromConfigGroup(const KConfigGroup &newGroup)
195 {
196  auto filter = new Filter();
197  filter->setSearchString(newGroup.readEntry("searchString"), static_cast<QuickSearchLine::SearchOptions>(newGroup.readEntry("searchOptions").toInt()));
198  filter->setTagId(newGroup.readEntry("tagId"));
199  filter->setIdentifier(newGroup.readEntry("identifier"));
200  filter->setFilterName(newGroup.readEntry("name"));
201  filter->setIconName(newGroup.readEntry("iconName"));
202  QList<qint32> lst;
203  lst = newGroup.readEntry("status", QList<qint32>());
204  QVector<Akonadi::MessageStatus> messageStatusLst;
205  messageStatusLst.reserve(lst.count());
206  for (const auto s : std::as_const(lst)) {
207  Akonadi::MessageStatus status;
208  status.fromQInt32(s);
209  messageStatusLst << status;
210  }
211  filter->setStatus(messageStatusLst);
212  filter->setOptions(static_cast<QuickSearchLine::SearchOptions>(newGroup.readEntry("searchOptions").toInt()));
213  return filter;
214 }
215 
216 void Filter::setSearchString(const QString &search, QuickSearchLine::SearchOptions options)
217 {
218  const QString trimStr = search.trimmed();
219  if ((mSearchString == trimStr) && (mOptions == options)) {
220  return;
221  }
222  mOptions = options;
223  mSearchString = trimStr;
224  mMatchingItemIds.clear();
225 
226  if (mSearchString.isEmpty()) {
227  return;
228  }
229  bool needToSplitString = false;
230  QString newStr = mSearchString;
231  if (mSearchString.startsWith(QLatin1Char('"')) && mSearchString.startsWith(QLatin1Char('"'))) {
232  newStr.remove(0, 1);
233  newStr.remove(newStr.length() - 1, 1);
234  mSearchList = QStringList() << newStr;
235  } else {
236  const QStringList searchListTmp = mSearchString.split(QLatin1Char(' '), Qt::SkipEmptyParts);
237  mSearchList.clear();
238  newStr.clear();
239  for (const QString &text : searchListTmp) {
240  if (text.size() >= 3) {
241  mSearchList << text;
242  if (!newStr.isEmpty()) {
243  newStr += QLatin1Char(' ');
244  }
245  newStr += text;
246  }
247  }
248  needToSplitString = true;
249  }
250  if (!newStr.trimmed().isEmpty()) {
252  if (options & QuickSearchLine::SearchEveryWhere) {
253  query.matches(newStr);
254  query.setSplitSearchMatchString(needToSplitString);
255  } else if (options & QuickSearchLine::SearchAgainstSubject) {
256  query.subjectMatches(newStr);
257  } else if (options & QuickSearchLine::SearchAgainstBody) {
258  query.bodyMatches(newStr);
259  } else if (options & QuickSearchLine::SearchAgainstFrom) {
260  query.setFrom(newStr);
261  } else if (options & QuickSearchLine::SearchAgainstBcc) {
262  query.setBcc(QStringList() << newStr);
263  } else if (options & QuickSearchLine::SearchAgainstTo) {
264  query.setTo(QStringList() << newStr);
265  }
266 
267  // If the collection is virtual we're probably trying to filter the search collection, so we just search globally
268  if (mCurrentFolder.isValid() && !mCurrentFolder.isVirtual()) {
269  query.addCollection(mCurrentFolder.id());
270  }
271 
273  while (it.next()) {
274  mMatchingItemIds << it.id();
275  }
276  }
277  Q_EMIT finished();
278 }
279 
280 const QString &Filter::tagId() const
281 {
282  return mTagId;
283 }
284 
285 void Filter::setTagId(const QString &tagId)
286 {
287  mTagId = tagId;
288 }
289 
290 void Filter::generateRandomIdentifier()
291 {
292  mIdentifier = KRandom::randomString(16);
293 }
294 
295 QString Filter::identifier() const
296 {
297  return mIdentifier;
298 }
299 
301 {
302  d << "filtername " << t.filterName();
303  d << "identifier " << t.identifier();
304  d << "search string " << t.searchString();
305  d << "search option " << t.currentOptions();
306  d << "status " << t.status();
307  return d;
308 }
void clear()
bool sync() override
const QString & searchString() const
Returns the currently set search string.
Definition: filter.cpp:148
void subjectMatches(const QString &subjectMatch)
The MessageItem class.
Definition: messageitem.h:34
QVector< Akonadi::MessageStatus > status() const
Returns the currently set status mask.
Definition: filter.cpp:107
void reserve(int alloc)
ResultIterator exec() override
void fromQInt32(qint32 status)
void writeEntry(const QString &key, const QVariant &value, WriteConfigFlags pFlags=Normal)
const QString & receiver() const
Returns the receiver associated to this item.
Definition: item.cpp:501
The implementation independent part of the MessageList library.
Definition: aggregation.h:21
const QString & subject() const
Returns the subject associated to this Item.
Definition: item.cpp:531
QString & remove(int position, int n)
void clear()
void clear()
int count(const T &value) const const
CaseInsensitive
bool isEmpty() const const
QString trimmed() const const
const Tag * findTag(const QString &szTagId) const
Returns Tag associated to this message that has the specified id or 0 if no such tag exists...
void reserve(int size)
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
SkipEmptyParts
void bodyMatches(const QString &bodyMatch)
void matches(const QString &match)
QDataStream & operator<<(QDataStream &out, const KDateTime::Spec &spec)
const QString & sender() const
Returns the sender associated to this item.
Definition: item.cpp:486
This class is responsible of matching messages that should be displayed in the View.
Definition: filter.h:32
int length() const const
const Akonadi::MessageStatus & status() const
Returns the status associated to this Item.
Definition: item.cpp:446
KCOREADDONS_EXPORT QString randomString(int length)
T readEntry(const QString &key, const T &aDefault) const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Nov 30 2021 23:05:46 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.