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

KDE's Doxygen guidelines are available online.