aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mainwindow.cpp27
-rw-r--r--src/notmuchworker.cpp37
-rw-r--r--src/notmuchworker.h17
3 files changed, 71 insertions, 10 deletions
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index f622036..e8b4dda 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -1314,18 +1314,16 @@ void MainWindow::showTagRulesDialog()
auto *dialog = new TagRulesDialog(this);
dialog->setAttribute(Qt::WA_DeleteOnClose);
- // The Folder row's dropdown. Config holds the account subdirectories and
- // the dialog holds no Config, so they are handed over here. Account::maildir
- // is the subtree name the Folder term compiles a path: against; the account
- // key is a config section name and would match nothing.
- QStringList folders;
- for (const Account &account : m_config.accounts()) {
- if (!account.maildir.isEmpty() && !folders.contains(account.maildir))
- folders.append(account.maildir);
- }
- dialog->setFolders(folders);
m_tagRulesDialog = dialog;
+ // The Folder row's dropdown, filled from the Maildir tree on disk rather
+ // than from config. Config names one subtree per account and nothing
+ // below it, so the dropdown offered five entries and no way to say Drafts
+ // or Sent, which is a folder a rule wants to target as often as a whole
+ // account. The answer comes back queued, after the dialog is already up;
+ // setFolders refills the rows that exist by then.
+ QMetaObject::invokeMethod(m_worker, "requestFolders", Qt::QueuedConnection);
+
connect(dialog, &TagRulesDialog::countsRequested, this, [this, dialog]() {
QMetaObject::invokeMethod(
m_worker, "requestMessageCounts", Qt::QueuedConnection,
@@ -1425,6 +1423,15 @@ void MainWindow::wireWorker()
connect(m_worker, &NotmuchWorker::messageCountsReady,
this, &MainWindow::onRuleCountsReady);
+ // The rules dialog is the only consumer, and it may have been closed while
+ // the scan was in flight. No generation counter: the tree on disk does not
+ // change under a query, so a late answer is still the right one.
+ connect(m_worker, &NotmuchWorker::foldersReady, this,
+ [this](const QStringList &folders) {
+ if (m_tagRulesDialog)
+ m_tagRulesDialog->setFolders(folders);
+ });
+
// A confirmed write clears the pending revert: without this, a later
// unrelated error would roll back a change that actually succeeded.
connect(m_worker, &NotmuchWorker::tagsApplied,
diff --git a/src/notmuchworker.cpp b/src/notmuchworker.cpp
index 94fb79c..6aae397 100644
--- a/src/notmuchworker.cpp
+++ b/src/notmuchworker.cpp
@@ -20,6 +20,9 @@
#include <notmuch.h>
+#include <QDir>
+#include <QDirIterator>
+#include <QFileInfo>
#include <QSet>
#include <cstdlib>
@@ -678,3 +681,37 @@ void NotmuchWorker::requestMessageCounts(const QStringList &queries,
emit messageCountsReady(counts, generation);
}
+
+void NotmuchWorker::requestFolders()
+{
+ if (!openReadOnly())
+ return;
+
+ const QString root = QString::fromUtf8(notmuch_database_get_path(m_db));
+ if (root.isEmpty()) {
+ emit errorOccurred(
+ QStringLiteral("notmuch reports no database path."));
+ return;
+ }
+
+ // A Maildir folder is a directory holding cur/. Testing for that rather
+ // than listing every directory keeps the plumbing (cur, new, tmp) and an
+ // account's container directory out of the list; neither is somewhere mail
+ // is filed. Hidden directories are skipped, which is what excludes
+ // .notmuch itself.
+ QStringList folders;
+ QDirIterator it(root, QDir::Dirs | QDir::NoDotAndDotDot,
+ QDirIterator::Subdirectories);
+ const QDir rootDir(root);
+ while (it.hasNext()) {
+ const QString path = it.next();
+ if (!QFileInfo::exists(path + QStringLiteral("/cur")))
+ continue;
+ folders.append(rootDir.relativeFilePath(path));
+ }
+
+ // Sorted, so the dropdown keeps one order across openings. QDirIterator
+ // walks in filesystem order, which is neither stable nor alphabetical.
+ folders.sort();
+ emit foldersReady(folders);
+}
diff --git a/src/notmuchworker.h b/src/notmuchworker.h
index b3fbed3..9736ab8 100644
--- a/src/notmuchworker.h
+++ b/src/notmuchworker.h
@@ -151,6 +151,18 @@ public slots:
/// called when the dialog is opened and never on a timer.
void requestDatabaseStats(quint64 generation);
+ /// Every Maildir folder under the database root, as paths relative to it.
+ ///
+ /// From the DISK, not from the index: a folder mbsync created and nothing
+ /// has landed in yet is still a folder a tagging rule may target, and one
+ /// derived from indexed message paths would not offer it.
+ ///
+ /// Here rather than in MainWindow because the database root is
+ /// notmuch's `database.path` and this class owns the only handle that can
+ /// answer for it. Duplicating the path into config is exactly the second
+ /// source of truth the design refuses.
+ void requestFolders();
+
signals:
void threadsReady(const QVector<ThreadSummary> &threads, quint64 generation);
void queryFinished(int totalThreads, quint64 generation);
@@ -175,6 +187,11 @@ signals:
/// renders as unknown rather than as zero.
void databaseStatsReady(const DatabaseStats &stats, quint64 generation);
+ /// Maildir folders relative to the database root, sorted. No generation:
+ /// the tree on disk does not change under a query, and the one consumer
+ /// asks once when its dialog opens.
+ void foldersReady(const QStringList &folders);
+
void errorOccurred(const QString &message);
private: