9#include "core/filter.h"
10#include "config-messagelist.h"
11#include "core/messageitem.h"
12#include <MessageCore/StringUtil>
13#include <TextUtils/ConvertText>
16#if !FORCE_DISABLE_AKONADI_SEARCH
17#include <PIM/emailquery.h>
18#include <PIM/resultiterator.h>
25 generateRandomIdentifier();
28bool Filter::containString(
const QString &searchInString)
const
31 const QString searchInStringNormalize{TextUtils::ConvertText::normalize(searchInString)};
32 for (
const QString &str : std::as_const(mSearchList)) {
33 if (searchInStringNormalize.contains(TextUtils::ConvertText::normalize(str),
Qt::CaseInsensitive)) {
43const QString &Filter::iconName()
const
48void Filter::setIconName(
const QString &newIconName)
50 mIconName = newIconName;
53void Filter::setOptions(SearchMessageByButtons::SearchOptions newOptions)
55 mOptions = newOptions;
58const QString &Filter::filterName()
const
63void Filter::setFilterName(
const QString &newFilterName)
65 mFilterName = newFilterName;
68void Filter::setIdentifier(
const QString &newIdentifier)
70 mIdentifier = newIdentifier;
75 if (!mStatus.isEmpty()) {
83 if (!mSearchString.isEmpty()) {
84 if (mMatchingItemIds.contains(item->itemId())) {
88 bool searchMatches =
false;
89 bool searchEveryWhere = (mOptions & SearchMessageByButtons::SearchEveryWhere);
90 if (containString(item->
subject()) && ((mOptions & SearchMessageByButtons::SearchAgainstSubject) || searchEveryWhere)) {
92 }
else if (containString(item->
sender()) && ((mOptions & SearchMessageByButtons::SearchAgainstFrom) || searchEveryWhere)) {
94 }
else if (containString(item->
receiver()) && ((mOptions & SearchMessageByButtons::SearchAgainstTo) || searchEveryWhere)) {
102 if (!mTagId.isEmpty()) {
104 const bool tagMatches = item->
findTag(mTagId) !=
nullptr;
125 if (!mStatus.isEmpty()) {
129 if (!mSearchString.isEmpty()) {
133 if (!mTagId.isEmpty()) {
143 mSearchString.clear();
145 mMatchingItemIds.clear();
151 mCurrentFolder = folder;
156 const QString text = mSearchString;
158 if (mOptions & SearchMessageByButtons::SearchAgainstBody) {
159 SearchLineCommand::SearchLineInfo i;
160 i.type = SearchLineCommand::SearchLineType::Body;
161 i.argument = mSearchString;
163 infos.
append(std::move(i));
166 if (mOptions & SearchMessageByButtons::SearchAgainstSubject) {
167 SearchLineCommand::SearchLineInfo i;
168 i.type = SearchLineCommand::SearchLineType::Subject;
169 i.argument = mSearchString;
171 infos.
append(std::move(i));
174 if (mOptions & SearchMessageByButtons::SearchAgainstBcc) {
175 SearchLineCommand::SearchLineInfo i;
176 i.type = SearchLineCommand::SearchLineType::Bcc;
177 i.argument = mSearchString;
179 infos.
append(std::move(i));
182 if (mOptions & SearchMessageByButtons::SearchAgainstCc) {
183 SearchLineCommand::SearchLineInfo i;
184 i.type = SearchLineCommand::SearchLineType::Cc;
185 i.argument = mSearchString;
187 infos.
append(std::move(i));
192 if (
status.hasAttachment()) {
193 SearchLineCommand::SearchLineInfo i;
194 i.type = SearchLineCommand::SearchLineType::HasAttachment;
196 infos.
append(std::move(i));
198 }
else if (
status.hasInvitation()) {
199 SearchLineCommand::SearchLineInfo i;
200 i.type = SearchLineCommand::SearchLineType::HasInvitation;
202 infos.
append(std::move(i));
204 }
else if (
status.isImportant()) {
205 SearchLineCommand::SearchLineInfo i;
206 i.type = SearchLineCommand::SearchLineType::IsImportant;
208 infos.
append(std::move(i));
210 }
else if (
status.isEncrypted()) {
211 SearchLineCommand::SearchLineInfo i;
212 i.type = SearchLineCommand::SearchLineType::IsEncrypted;
214 infos.
append(std::move(i));
216 }
else if (
status.isReplied()) {
217 SearchLineCommand::SearchLineInfo i;
218 i.type = SearchLineCommand::SearchLineType::IsReplied;
220 infos.
append(std::move(i));
222 }
else if (
status.isForwarded()) {
223 SearchLineCommand::SearchLineInfo i;
224 i.type = SearchLineCommand::SearchLineType::IsForwarded;
226 infos.
append(std::move(i));
228 }
else if (
status.isRead()) {
229 SearchLineCommand::SearchLineInfo i;
230 i.type = SearchLineCommand::SearchLineType::IsRead;
232 infos.
append(std::move(i));
234 }
else if (
status.isIgnored()) {
235 SearchLineCommand::SearchLineInfo i;
236 i.type = SearchLineCommand::SearchLineType::IsIgnored;
238 infos.
append(std::move(i));
240 }
else if (
status.isSpam()) {
241 SearchLineCommand::SearchLineInfo i;
242 i.type = SearchLineCommand::SearchLineType::IsSpam;
244 infos.
append(std::move(i));
246 }
else if (
status.isHam()) {
247 SearchLineCommand::SearchLineInfo i;
248 i.type = SearchLineCommand::SearchLineType::IsHam;
250 infos.
append(std::move(i));
252 }
else if (
status.isQueued()) {
253 SearchLineCommand::SearchLineInfo i;
254 i.type = SearchLineCommand::SearchLineType::IsQueued;
256 infos.
append(std::move(i));
258 }
else if (
status.isSent()) {
259 SearchLineCommand::SearchLineInfo i;
260 i.type = SearchLineCommand::SearchLineType::IsSent;
262 infos.
append(std::move(i));
264 }
else if (
status.isDeleted()) {
265 SearchLineCommand::SearchLineInfo i;
266 i.type = SearchLineCommand::SearchLineType::IsDeleted;
268 infos.
append(std::move(i));
270 }
else if (
status.isWatched()) {
271 SearchLineCommand::SearchLineInfo i;
272 i.type = SearchLineCommand::SearchLineType::IsWatched;
274 infos.
append(std::move(i));
276 }
else if (
status.isToAct()) {
277 SearchLineCommand::SearchLineInfo i;
278 i.type = SearchLineCommand::SearchLineType::IsAction;
280 infos.
append(std::move(i));
287 if (mContainsOutboundMessages) {
288 mButtonGroup->button(SearchMessageByButtons::SearchAgainstTo)->setChecked(opts & SearchMessageByButtons::SearchAgainstTo);
290 mButtonGroup->button(SearchMessageByButtons::SearchAgainstTo)->setChecked(opts & SearchMessageByButtons::SearchAgainstFrom);
293 SearchLineCommand command;
294 command.parseSearchLineCommand(text);
295 infos += command.searchLineInfo();
301 return mSearchString;
304SearchMessageByButtons::SearchOptions Filter::currentOptions()
const
309void Filter::save(
const KSharedConfig::Ptr &config,
const QString &filtername,
const QString &iconName,
int numFilter)
312 int numberFilter = (numFilter == -1) ? grp.readEntry(
"NumberFilter").toInt() : numFilter;
313 KConfigGroup newGroup(config, QStringLiteral(
"Filter_%1").arg(numberFilter++));
314 newGroup.writeEntry(
"name", filtername);
316 newGroup.writeEntry(
"iconName", iconName);
318 newGroup.writeEntry(
"searchString", mSearchString);
319 newGroup.writeEntry(
"searchOptions",
static_cast<int>(mOptions));
320 newGroup.writeEntry(
"tagId", mTagId);
321 newGroup.writeEntry(
"identifier", mIdentifier);
324 for (
const auto s : std::as_const(mStatus)) {
327 newGroup.writeEntry(
"status", lst);
329 grp.writeEntry(
"NumberFilter", numberFilter);
331 config->reparseConfiguration();
334Filter *Filter::load(
const KSharedConfig::Ptr &config,
int filternumber)
336 KConfigGroup grp(config, QStringLiteral(
"General"));
337 int numberFilter = grp.readEntry(
"NumberFilter").toInt();
338 if (filternumber < numberFilter) {
339 KConfigGroup newGroup(config, QStringLiteral(
"Filter_%1").arg(filternumber));
340 return loadFromConfigGroup(newGroup);
347 auto filter =
new Filter();
349 static_cast<SearchMessageByButtons::SearchOptions
>(newGroup.
readEntry(
"searchOptions").
toInt()));
354 const QList<qint32> lst = newGroup.
readEntry(
"status", QList<qint32>());
355 QList<Akonadi::MessageStatus> messageStatusLst;
357 for (
const auto s : std::as_const(lst)) {
358 Akonadi::MessageStatus
status;
360 messageStatusLst <<
status;
362 filter->setStatus(messageStatusLst);
363 filter->setOptions(
static_cast<SearchMessageByButtons::SearchOptions
>(newGroup.
readEntry(
"searchOptions").
toInt()));
369#if !FORCE_DISABLE_AKONADI_SEARCH
370 mMatchingItemIds.clear();
371 if (command.isEmpty()) {
374 const QList<SearchLineCommand::SearchLineInfo> infos = command.searchLineInfo();
375 QList<Akonadi::MessageStatus> lstStatus;
376 Akonadi::Search::PIM::EmailQuery
query;
377 for (
const auto &info : infos) {
379 case SearchLineCommand::Literal: {
381 const QStringList searchListTmp = info.argument.split(QLatin1Char(
' '),
Qt::SkipEmptyParts);
382 bool needToSplitString =
false;
383 for (
const QString &text : searchListTmp) {
384 if (text.size() >= 3) {
386 newStr += QLatin1Char(
' ');
391 needToSplitString =
true;
393 mSearchString = newStr;
394 query.matches(newStr);
395 query.setSplitSearchMatchString(needToSplitString);
398 case SearchLineCommand::Subject: {
399 mSearchString = info.argument;
400 query.subjectMatches(mSearchString);
403 case SearchLineCommand::Body: {
404 mSearchString = info.argument;
405 query.bodyMatches(mSearchString);
408 case SearchLineCommand::Unknown:
409 case SearchLineCommand::HasStateOrAttachment:
412 case SearchLineCommand::Larger:
413 case SearchLineCommand::Smaller:
414 case SearchLineCommand::OlderThan:
415 case SearchLineCommand::NewerThan:
416 case SearchLineCommand::Date:
417 case SearchLineCommand::Size:
418 case SearchLineCommand::Category:
421 case SearchLineCommand::HasAttachment: {
422 Akonadi::MessageStatus
status;
423 status.setHasAttachment(
true);
427 case SearchLineCommand::HasInvitation: {
428 Akonadi::MessageStatus
status;
429 status.setHasInvitation(
true);
433 case SearchLineCommand::IsImportant: {
434 Akonadi::MessageStatus
status;
435 status.setImportant(
true);
439 case SearchLineCommand::IsRead: {
440 Akonadi::MessageStatus
status;
445 case SearchLineCommand::IsUnRead: {
447 Akonadi::MessageStatus
status;
452 case SearchLineCommand::IsIgnored: {
453 Akonadi::MessageStatus
status;
458 case SearchLineCommand::IsHam: {
459 Akonadi::MessageStatus
status;
464 case SearchLineCommand::IsSpam: {
465 Akonadi::MessageStatus
status;
470 case SearchLineCommand::IsWatched: {
471 Akonadi::MessageStatus
status;
476 case SearchLineCommand::IsReplied: {
477 Akonadi::MessageStatus
status;
482 case SearchLineCommand::IsEncrypted: {
483 Akonadi::MessageStatus
status;
484 status.setEncrypted(
true);
488 case SearchLineCommand::IsQueued: {
489 Akonadi::MessageStatus
status;
494 case SearchLineCommand::IsAction: {
495 Akonadi::MessageStatus
status;
500 case SearchLineCommand::IsDeleted: {
501 Akonadi::MessageStatus
status;
506 case SearchLineCommand::IsSent: {
507 Akonadi::MessageStatus
status;
512 case SearchLineCommand::IsForwarded: {
513 Akonadi::MessageStatus
status;
514 status.setForwarded(
true);
518 case SearchLineCommand::To: {
519 mSearchString = info.argument;
520 query.addTo(info.argument);
523 case SearchLineCommand::Bcc: {
524 mSearchString = info.argument;
525 query.addBcc(info.argument);
528 case SearchLineCommand::From: {
529 mSearchString = info.argument;
530 query.addFrom(info.argument);
533 case SearchLineCommand::Cc: {
534 mSearchString = info.argument;
535 query.addCc(info.argument);
543 if (mCurrentFolder.isValid() && !mCurrentFolder.isVirtual()) {
544 query.addCollection(mCurrentFolder.id());
547 Akonadi::Search::PIM::ResultIterator it =
query.exec();
549 mMatchingItemIds << it.id();
557#if !FORCE_DISABLE_AKONADI_SEARCH
559 if ((mSearchString == trimStr) && (mOptions == options)) {
563 mSearchString = trimStr;
564 mMatchingItemIds.
clear();
566 if (mSearchString.isEmpty()) {
569 bool needToSplitString =
false;
570 QString newStr = mSearchString;
579 for (
const QString &text : searchListTmp) {
580 if (text.size() >= 3) {
588 needToSplitString =
true;
592 if (options & SearchMessageByButtons::SearchEveryWhere) {
593 query.matches(newStr);
594 query.setSplitSearchMatchString(needToSplitString);
595 }
else if (options & SearchMessageByButtons::SearchAgainstSubject) {
596 query.subjectMatches(newStr);
597 }
else if (options & SearchMessageByButtons::SearchAgainstBody) {
598 query.bodyMatches(newStr);
599 }
else if (options & SearchMessageByButtons::SearchAgainstFrom) {
600 query.setFrom(newStr);
601 }
else if (options & SearchMessageByButtons::SearchAgainstBcc) {
603 }
else if (options & SearchMessageByButtons::SearchAgainstCc) {
605 }
else if (options & SearchMessageByButtons::SearchAgainstTo) {
610 if (mCurrentFolder.isValid() && !mCurrentFolder.isVirtual()) {
611 query.addCollection(mCurrentFolder.id());
616 mMatchingItemIds << it.id();
633void Filter::generateRandomIdentifier()
638QString Filter::identifier()
const
645 d <<
"filtername " << t.filterName();
646 d <<
"identifier " << t.identifier();
648 d <<
"search option " << t.currentOptions();
649 d <<
"status " << t.
status();
653#include "moc_filter.cpp"
QString readEntry(const char *key, const char *aDefault=nullptr) const
This class is responsible of matching messages that should be displayed in the View.
const QString & tagId() const
Returns the currently set MessageItem::Tag id.
bool isEmpty() const
Returns true if this filter is empty (0 status mask, empty search string and empty tag) and it's usel...
void setTagId(const QString &tagId)
Sets the id of a MessageItem::Tag that the matching messages must contain.
QList< Akonadi::MessageStatus > status() const
Returns the currently set status mask.
void clear()
Clears this filter (sets status to 0, search string and tag id to empty strings)
const QString & searchString() const
Returns the currently set search string.
bool match(const MessageItem *item) const
Returns true if the specified parameters match this filter and false otherwise.
void setStatus(const QList< Akonadi::MessageStatus > &lstStatus)
Sets the status mask for this filter.
void setCurrentFolder(const Akonadi::Collection &collection)
Sets the current folder of this filter.
void setSearchString(const QString &search, SearchMessageByButtons::SearchOptions options)
Sets the search string for this filter.
const QString & receiver() const
Returns the receiver associated to this item.
const Akonadi::MessageStatus & status() const
Returns the status associated to this Item.
const QString & sender() const
Returns the sender associated to this item.
const QString & subject() const
Returns the subject associated to this Item.
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.
Q_SCRIPTABLE CaptureState status()
std::optional< QSqlQuery > query(const QString &queryStatement)
KCOREADDONS_EXPORT QString randomString(int length)
The implementation independent part of the MessageList library.
void append(QList< T > &&value)
qsizetype count() const const
void reserve(qsizetype size)
bool isEmpty() const const
qsizetype length() const const
QString & remove(QChar ch, Qt::CaseSensitivity cs)
int toInt(bool *ok, int base) const const
QString trimmed() const const
QFuture< void > filter(QThreadPool *pool, Sequence &sequence, KeepFunctor &&filterFunction)