9#include "core/filter.h"
10#include "config-messagelist.h"
11#include "core/messageitem.h"
12#include "tagmanager.h"
13#include <MessageCore/StringUtil>
14#include <TextUtils/ConvertText>
17#if !FORCE_DISABLE_AKONADI_SEARCH
18#include <PIM/emailquery.h>
19#include <PIM/resultiterator.h>
26 generateRandomIdentifier();
29bool Filter::containString(
const QString &searchInString)
const
32 const QString searchInStringNormalize{TextUtils::ConvertText::normalize(searchInString)};
33 for (
const QString &str : std::as_const(mSearchList)) {
34 if (searchInStringNormalize.contains(TextUtils::ConvertText::normalize(str),
Qt::CaseInsensitive)) {
44const QString &Filter::iconName()
const
49void Filter::setIconName(
const QString &newIconName)
51 mIconName = newIconName;
54void Filter::setOptions(SearchMessageByButtons::SearchOptions newOptions)
56 mOptions = newOptions;
59const QString &Filter::filterName()
const
64void Filter::setFilterName(
const QString &newFilterName)
66 mFilterName = newFilterName;
69void Filter::setIdentifier(
const QString &newIdentifier)
71 mIdentifier = newIdentifier;
76 if (!mStatus.isEmpty()) {
84 if (!mSearchString.isEmpty()) {
85 if (mMatchingItemIds.contains(item->itemId())) {
89 bool searchMatches =
false;
90 bool searchEveryWhere = (mOptions & SearchMessageByButtons::SearchEveryWhere);
91 if (containString(item->
subject()) && ((mOptions & SearchMessageByButtons::SearchAgainstSubject) || searchEveryWhere)) {
93 }
else if (containString(item->
sender()) && ((mOptions & SearchMessageByButtons::SearchAgainstFrom) || searchEveryWhere)) {
95 }
else if (containString(item->
receiver()) && ((mOptions & SearchMessageByButtons::SearchAgainstTo) || searchEveryWhere)) {
103 if (!mTagIds.isEmpty()) {
105 for (
const auto &tag : mTagIds) {
106 const bool tagMatches = item->
findTag(tag) !=
nullptr;
128 if (!mStatus.isEmpty()) {
132 if (!mSearchString.isEmpty()) {
136 if (!mTagIds.isEmpty()) {
146 mSearchString.clear();
148 mMatchingItemIds.clear();
154 mCurrentFolder = folder;
159 const QString text = mSearchString;
161 if (mOptions & SearchMessageByButtons::SearchAgainstBody) {
162 SearchLineCommand::SearchLineInfo i;
163 i.type = SearchLineCommand::SearchLineType::Body;
164 i.argument = mSearchString;
166 infos.
append(std::move(i));
169 if (mOptions & SearchMessageByButtons::SearchAgainstSubject) {
170 SearchLineCommand::SearchLineInfo i;
171 i.type = SearchLineCommand::SearchLineType::Subject;
172 i.argument = mSearchString;
174 infos.
append(std::move(i));
177 if (mOptions & SearchMessageByButtons::SearchAgainstBcc) {
178 SearchLineCommand::SearchLineInfo i;
179 i.type = SearchLineCommand::SearchLineType::Bcc;
180 i.argument = mSearchString;
182 infos.
append(std::move(i));
185 if (mOptions & SearchMessageByButtons::SearchAgainstCc) {
186 SearchLineCommand::SearchLineInfo i;
187 i.type = SearchLineCommand::SearchLineType::Cc;
188 i.argument = mSearchString;
190 infos.
append(std::move(i));
195 if (
status.hasAttachment()) {
196 SearchLineCommand::SearchLineInfo i;
197 i.type = SearchLineCommand::SearchLineType::HasAttachment;
199 infos.
append(std::move(i));
201 }
else if (
status.hasInvitation()) {
202 SearchLineCommand::SearchLineInfo i;
203 i.type = SearchLineCommand::SearchLineType::HasInvitation;
205 infos.
append(std::move(i));
207 }
else if (
status.isImportant()) {
208 SearchLineCommand::SearchLineInfo i;
209 i.type = SearchLineCommand::SearchLineType::IsImportant;
211 infos.
append(std::move(i));
213 }
else if (
status.isEncrypted()) {
214 SearchLineCommand::SearchLineInfo i;
215 i.type = SearchLineCommand::SearchLineType::IsEncrypted;
217 infos.
append(std::move(i));
219 }
else if (
status.isReplied()) {
220 SearchLineCommand::SearchLineInfo i;
221 i.type = SearchLineCommand::SearchLineType::IsReplied;
223 infos.
append(std::move(i));
225 }
else if (
status.isForwarded()) {
226 SearchLineCommand::SearchLineInfo i;
227 i.type = SearchLineCommand::SearchLineType::IsForwarded;
229 infos.
append(std::move(i));
231 }
else if (
status.isRead()) {
232 SearchLineCommand::SearchLineInfo i;
233 i.type = SearchLineCommand::SearchLineType::IsRead;
235 infos.
append(std::move(i));
237 }
else if (
status.isIgnored()) {
238 SearchLineCommand::SearchLineInfo i;
239 i.type = SearchLineCommand::SearchLineType::IsIgnored;
241 infos.
append(std::move(i));
243 }
else if (
status.isSpam()) {
244 SearchLineCommand::SearchLineInfo i;
245 i.type = SearchLineCommand::SearchLineType::IsSpam;
247 infos.
append(std::move(i));
249 }
else if (
status.isHam()) {
250 SearchLineCommand::SearchLineInfo i;
251 i.type = SearchLineCommand::SearchLineType::IsHam;
253 infos.
append(std::move(i));
255 }
else if (
status.isQueued()) {
256 SearchLineCommand::SearchLineInfo i;
257 i.type = SearchLineCommand::SearchLineType::IsQueued;
259 infos.
append(std::move(i));
261 }
else if (
status.isSent()) {
262 SearchLineCommand::SearchLineInfo i;
263 i.type = SearchLineCommand::SearchLineType::IsSent;
265 infos.
append(std::move(i));
267 }
else if (
status.isDeleted()) {
268 SearchLineCommand::SearchLineInfo i;
269 i.type = SearchLineCommand::SearchLineType::IsDeleted;
271 infos.
append(std::move(i));
273 }
else if (
status.isWatched()) {
274 SearchLineCommand::SearchLineInfo i;
275 i.type = SearchLineCommand::SearchLineType::IsWatched;
277 infos.
append(std::move(i));
279 }
else if (
status.isToAct()) {
280 SearchLineCommand::SearchLineInfo i;
281 i.type = SearchLineCommand::SearchLineType::IsAction;
283 infos.
append(std::move(i));
290 if (mContainsOutboundMessages) {
291 mButtonGroup->button(SearchMessageByButtons::SearchAgainstTo)->setChecked(opts & SearchMessageByButtons::SearchAgainstTo);
293 mButtonGroup->button(SearchMessageByButtons::SearchAgainstTo)->setChecked(opts & SearchMessageByButtons::SearchAgainstFrom);
296 SearchLineCommand command;
297 command.parseSearchLineCommand(text);
298 infos += command.searchLineInfo();
304 return mSearchString;
307SearchMessageByButtons::SearchOptions Filter::currentOptions()
const
312void Filter::save(
const KSharedConfig::Ptr &config,
const QString &filtername,
const QString &iconName,
int numFilter)
315 int numberFilter = (numFilter == -1) ? grp.readEntry(
"NumberFilter").toInt() : numFilter;
316 KConfigGroup newGroup(config, QStringLiteral(
"Filter_%1").arg(numberFilter++));
317 newGroup.writeEntry(
"name", filtername);
319 newGroup.writeEntry(
"iconName", iconName);
321 newGroup.writeEntry(
"searchString", mSearchString);
322 newGroup.writeEntry(
"searchOptions",
static_cast<int>(mOptions));
323 newGroup.writeEntry(
"tagId", mTagIds);
324 newGroup.writeEntry(
"identifier", mIdentifier);
327 for (
const auto s : std::as_const(mStatus)) {
330 newGroup.writeEntry(
"status", lst);
332 grp.writeEntry(
"NumberFilter", numberFilter);
334 config->reparseConfiguration();
337Filter *Filter::load(
const KSharedConfig::Ptr &config,
int filternumber)
339 KConfigGroup grp(config, QStringLiteral(
"General"));
340 int numberFilter = grp.readEntry(
"NumberFilter").toInt();
341 if (filternumber < numberFilter) {
342 KConfigGroup newGroup(config, QStringLiteral(
"Filter_%1").arg(filternumber));
343 return loadFromConfigGroup(newGroup);
350 auto filter =
new Filter();
352 static_cast<SearchMessageByButtons::SearchOptions
>(newGroup.
readEntry(
"searchOptions").
toInt()));
357 const QList<qint32> lst = newGroup.
readEntry(
"status", QList<qint32>());
358 QList<Akonadi::MessageStatus> messageStatusLst;
360 for (
const auto s : std::as_const(lst)) {
361 Akonadi::MessageStatus
status;
363 messageStatusLst <<
status;
365 filter->setStatus(messageStatusLst);
366 filter->setOptions(
static_cast<SearchMessageByButtons::SearchOptions
>(newGroup.
readEntry(
"searchOptions").
toInt()));
372#if !FORCE_DISABLE_AKONADI_SEARCH
373 mMatchingItemIds.clear();
374 if (command.isEmpty()) {
377 const QList<SearchLineCommand::SearchLineInfo> infos = command.searchLineInfo();
378 QList<Akonadi::MessageStatus> lstStatus;
379 Akonadi::Search::PIM::EmailQuery
query;
380 for (
const auto &info : infos) {
382 case SearchLineCommand::Literal: {
384 const QStringList searchListTmp = info.argument.split(QLatin1Char(
' '),
Qt::SkipEmptyParts);
385 bool needToSplitString =
false;
386 for (
const QString &text : searchListTmp) {
387 if (text.size() >= 3) {
389 newStr += QLatin1Char(
' ');
394 needToSplitString =
true;
396 mSearchString = newStr;
397 query.matches(newStr);
398 query.setSplitSearchMatchString(needToSplitString);
401 case SearchLineCommand::Subject: {
402 mSearchString = info.argument;
403 query.subjectMatches(mSearchString);
406 case SearchLineCommand::Body: {
407 mSearchString = info.argument;
408 query.bodyMatches(mSearchString);
411 case SearchLineCommand::Unknown:
412 case SearchLineCommand::HasStateOrAttachment:
415 case SearchLineCommand::Larger:
416 case SearchLineCommand::Smaller:
417 case SearchLineCommand::OlderThan:
418 case SearchLineCommand::NewerThan:
419 case SearchLineCommand::Date:
420 case SearchLineCommand::Size:
422 case SearchLineCommand::Category: {
423 const QString tagUrl = TagManager::self()->tagFromName(info.argument);
424 addTagId(tagUrl.
isEmpty() ? info.argument : tagUrl);
427 case SearchLineCommand::HasAttachment: {
428 Akonadi::MessageStatus
status;
429 status.setHasAttachment(
true);
433 case SearchLineCommand::HasInvitation: {
434 Akonadi::MessageStatus
status;
435 status.setHasInvitation(
true);
439 case SearchLineCommand::IsImportant: {
440 Akonadi::MessageStatus
status;
441 status.setImportant(
true);
445 case SearchLineCommand::IsRead: {
446 Akonadi::MessageStatus
status;
451 case SearchLineCommand::IsUnRead: {
453 Akonadi::MessageStatus
status;
458 case SearchLineCommand::IsIgnored: {
459 Akonadi::MessageStatus
status;
464 case SearchLineCommand::IsHam: {
465 Akonadi::MessageStatus
status;
470 case SearchLineCommand::IsSpam: {
471 Akonadi::MessageStatus
status;
476 case SearchLineCommand::IsWatched: {
477 Akonadi::MessageStatus
status;
482 case SearchLineCommand::IsReplied: {
483 Akonadi::MessageStatus
status;
488 case SearchLineCommand::IsEncrypted: {
489 Akonadi::MessageStatus
status;
490 status.setEncrypted(
true);
494 case SearchLineCommand::IsQueued: {
495 Akonadi::MessageStatus
status;
500 case SearchLineCommand::IsAction: {
501 Akonadi::MessageStatus
status;
506 case SearchLineCommand::IsDeleted: {
507 Akonadi::MessageStatus
status;
512 case SearchLineCommand::IsSent: {
513 Akonadi::MessageStatus
status;
518 case SearchLineCommand::IsForwarded: {
519 Akonadi::MessageStatus
status;
520 status.setForwarded(
true);
524 case SearchLineCommand::To: {
525 mSearchString = info.argument;
526 query.addTo(info.argument);
529 case SearchLineCommand::Bcc: {
530 mSearchString = info.argument;
531 query.addBcc(info.argument);
534 case SearchLineCommand::From: {
535 mSearchString = info.argument;
536 query.addFrom(info.argument);
539 case SearchLineCommand::Cc: {
540 mSearchString = info.argument;
541 query.addCc(info.argument);
549 if (mCurrentFolder.isValid() && !mCurrentFolder.isVirtual()) {
550 query.addCollection(mCurrentFolder.id());
553 Akonadi::Search::PIM::ResultIterator it =
query.exec();
555 mMatchingItemIds << it.id();
563#if !FORCE_DISABLE_AKONADI_SEARCH
565 if ((mSearchString == trimStr) && (mOptions == options)) {
569 mSearchString = trimStr;
570 mMatchingItemIds.
clear();
572 if (mSearchString.isEmpty()) {
575 bool needToSplitString =
false;
576 QString newStr = mSearchString;
585 for (
const QString &text : searchListTmp) {
586 if (text.size() >= 3) {
594 needToSplitString =
true;
598 if (options & SearchMessageByButtons::SearchEveryWhere) {
599 query.matches(newStr);
600 query.setSplitSearchMatchString(needToSplitString);
601 }
else if (options & SearchMessageByButtons::SearchAgainstSubject) {
602 query.subjectMatches(newStr);
603 }
else if (options & SearchMessageByButtons::SearchAgainstBody) {
604 query.bodyMatches(newStr);
605 }
else if (options & SearchMessageByButtons::SearchAgainstFrom) {
606 query.setFrom(newStr);
607 }
else if (options & SearchMessageByButtons::SearchAgainstBcc) {
609 }
else if (options & SearchMessageByButtons::SearchAgainstCc) {
611 }
else if (options & SearchMessageByButtons::SearchAgainstTo) {
616 if (mCurrentFolder.isValid() && !mCurrentFolder.isVirtual()) {
617 query.addCollection(mCurrentFolder.id());
622 mMatchingItemIds << it.id();
639void Filter::addTagId(
const QString &tagId)
641 mTagIds.append(tagId);
644void Filter::generateRandomIdentifier()
649QString Filter::identifier()
const
656 d <<
"filtername " << t.filterName();
657 d <<
"identifier " << t.identifier();
659 d <<
"search option " << t.currentOptions();
660 d <<
"status " << t.
status();
664#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.
bool isEmpty() const
Returns true if this filter is empty (0 status mask, empty search string and empty tag) and it's usel...
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.
void setTagId(const QStringList &tagId)
Sets the id of a MessageItem::Tag that the matching messages must contain.
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 QStringList & tagId() const
Returns the currently set MessageItem::Tag id.
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()
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
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)