diff options
| -rw-r--r-- | src/config.cpp | 21 | ||||
| -rw-r--r-- | src/mainwindow.cpp | 1 | ||||
| -rw-r--r-- | tests/test_config.cpp | 35 | ||||
| -rw-r--r-- | tests/test_mainwindow.cpp | 3 |
4 files changed, 56 insertions, 4 deletions
diff --git a/src/config.cpp b/src/config.cpp index 0fa3c85..390848a 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -64,7 +64,8 @@ const QStringList kQueryGenerators = { QStringLiteral("unread"), QStringLiteral("flagged"), QStringLiteral("sent"), QStringLiteral("drafts"), - QStringLiteral("trash") }; + QStringLiteral("trash"), + QStringLiteral("spam") }; /// The tag a generator matches, for the three filters that are a plain tag /// query. Empty for "sent", "drafts" and "trash", which compose from each @@ -1018,6 +1019,8 @@ QString Config::resolvedQuery(const SavedQuery &query) const return allSentQuery(); if (query.generated == QStringLiteral("trash")) return allTrashQuery(); + if (query.generated == QStringLiteral("spam")) + return allSpamQuery(); // An unknown generator was reported on load. Empty rather than the // bare stored query, which for a generated entry is empty anyway and // would otherwise run as "match everything". @@ -1106,6 +1109,10 @@ SavedQuery Config::builtinFilter(const QString &generator) // NOT flat, unlike Sent. A deleted message still belongs to its // conversation, and folding it back is what Sent had to avoid rather // than something every folder filter wants. + } else if (generator == QStringLiteral("spam")) { + filter.name = tr("Spam"); + // NOT flat, like trash: a spam message still belongs to its + // conversation. } return filter; @@ -1138,6 +1145,10 @@ QString Config::resolvedQuery(const SavedQuery &query, const QString all = allTrashQuery(); return all.isEmpty() ? matchNothingQuery() : all; } + if (query.generated == QStringLiteral("spam")) { + const QString all = allSpamQuery(); + return all.isEmpty() ? matchNothingQuery() : all; + } return QStringLiteral("tag:%1").arg(generatorTag(query.generated)); } @@ -1173,6 +1184,14 @@ QString Config::resolvedQuery(const SavedQuery &query, return trash.isEmpty() ? matchNothingQuery() : trash; } + if (query.generated == QStringLiteral("spam")) { + // The account's OWN spam query, for the reason spelled out above the + // sent case: wrapping the all-accounts query in this account's path + // works by accident of path: being hierarchical. + const QString spam = scope.spamQuery(); + return spam.isEmpty() ? matchNothingQuery() : spam; + } + // A tag filter carries no path of its own, so scoping is exactly what // scopedQuery() does. Its parentheses are load-bearing: `path:... and a or // b` binds as `(path:... and a) or b`. diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index e7cbf33..2cba6b1 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3119,6 +3119,7 @@ void MainWindow::buildSavedQueryRow(QWidget *parent, QVBoxLayout *layout) { QStringLiteral("sent"), QStringLiteral("mail-folder-sent") }, { QStringLiteral("drafts"), QStringLiteral("document-edit") }, { QStringLiteral("trash"), QStringLiteral("user-trash") }, + { QStringLiteral("spam"), QStringLiteral("mail-mark-junk") }, }; button->setIcon( QIcon::fromTheme(filterIcons.value(filter.generated))); diff --git a/tests/test_config.cpp b/tests/test_config.cpp index dd34e4f..70e3705 100644 --- a/tests/test_config.cpp +++ b/tests/test_config.cpp @@ -129,6 +129,8 @@ private slots: void theDraftsFilterIsFlatLikeSent(); void theTrashFilterComposesPerAccount(); void theTrashFilterMatchesNothingWithoutAFolder(); + void theSpamFilterComposesPerAccount(); + void theSpamFilterMatchesNothingWithoutAFolder(); void anAccountWithoutASendCommandIsReceiveOnly(); void composeSettingsDefaultWhenTheSectionIsAbsent(); void aZeroSendDelayIsHonouredRatherThanTreatedAsUnset(); @@ -1198,6 +1200,34 @@ void TestConfig::theTrashFilterMatchesNothingWithoutAFolder() QCOMPARE(config.resolvedQuery(trash, QString()), Config::matchNothingQuery()); } +void TestConfig::theSpamFilterComposesPerAccount() +{ + QTemporaryDir dir; + Config config; + config.load(writeIni(dir, QStringLiteral( + "[account.work]\nmaildir=work\nspam=Spam\n" + "\n[account.personal]\nmaildir=personal\nspam=[Provider]/Spam\n"))); + const SavedQuery spam = Config::builtinFilter(QStringLiteral("spam")); + QVERIFY(spam.isGenerated()); + QVERIFY2(!spam.flat, "spam must be threaded, like trash"); + const QString all = config.resolvedQuery(spam, QString()); + QVERIFY(all.contains(QStringLiteral("path:\"work/Spam/**\""))); + QVERIFY(all.contains(QStringLiteral("path:\"personal/[Provider]/Spam/**\""))); + const QString scoped = config.resolvedQuery(spam, QStringLiteral("work")); + QCOMPARE(scoped, QStringLiteral("path:\"work/Spam/**\"")); + QVERIFY(!scoped.contains(QStringLiteral("personal"))); +} + +void TestConfig::theSpamFilterMatchesNothingWithoutAFolder() +{ + QTemporaryDir dir; + Config config; + config.load(writeIni(dir, QStringLiteral( + "[account.work]\nmaildir=work\n"))); + const SavedQuery spam = Config::builtinFilter(QStringLiteral("spam")); + QCOMPARE(config.resolvedQuery(spam, QString()), Config::matchNothingQuery()); +} + void TestConfig::sentQueryComposesWithScopedQuery() { // A Sent view under one account must not show another account's sent mail. @@ -1628,7 +1658,7 @@ void TestConfig::everyBuiltinFilterIsAKnownGenerator() Config config; const QList<SavedQuery> filters = config.builtinFilters(); - QCOMPARE(filters.size(), 6); + QCOMPARE(filters.size(), 7); QStringList names; for (const SavedQuery &filter : filters) { @@ -1651,7 +1681,8 @@ void TestConfig::everyBuiltinFilterIsAKnownGenerator() QStringLiteral("Important"), QStringLiteral("Sent"), QStringLiteral("Drafts"), - QStringLiteral("Trash") })); + QStringLiteral("Trash"), + QStringLiteral("Spam") })); } void TestConfig::aFilterAcrossAllAccountsIsTheUnscopedQuery() diff --git a/tests/test_mainwindow.cpp b/tests/test_mainwindow.cpp index b7f7846..9a38a95 100644 --- a/tests/test_mainwindow.cpp +++ b/tests/test_mainwindow.cpp @@ -11008,12 +11008,13 @@ void TestMainWindow::everyBuiltinFilterButtonCarriesAnIconAndItsText() }); // A trash key too, or the Trash filter finds nothing and is skipped from // the row entirely (item 103), leaving no trashButton for this loop to - // find. Drafts behaves the same way since item 138. + // find. Drafts and Spam behave the same way since item 138. { QSettings s(path, QSettings::IniFormat); s.beginGroup(QStringLiteral("account.work")); s.setValue(QStringLiteral("trash"), QStringLiteral("Trash")); s.setValue(QStringLiteral("drafts"), QStringLiteral("Drafts")); + s.setValue(QStringLiteral("spam"), QStringLiteral("Spam")); s.endGroup(); } Config config; |
