diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/notmuchworker.cpp | 66 | ||||
| -rw-r--r-- | src/notmuchworker.h | 27 |
2 files changed, 93 insertions, 0 deletions
diff --git a/src/notmuchworker.cpp b/src/notmuchworker.cpp index 6a0694f..62174c9 100644 --- a/src/notmuchworker.cpp +++ b/src/notmuchworker.cpp @@ -776,6 +776,72 @@ void NotmuchWorker::moveMessages(const QStringList &messageIds, emit messagesMovedFrom(origins, destFolder); } +void NotmuchWorker::resolveThreadMessages(const QStringList &threadIds, + const QString &requestTag) +{ + if (threadIds.isEmpty()) + return; + + if (!openReadOnly()) + return; + + // One combined query, for the reason applyTagsToThreads() gives: a query + // per thread reopens the same Xapian cursor once per selected row. + QStringList terms; + terms.reserve(threadIds.size()); + for (const QString &id : threadIds) + terms.append(QStringLiteral("thread:%1").arg(id)); + + const QString query = terms.join(QStringLiteral(" or ")); + NmQuery nmQuery(notmuch_query_create(m_db, query.toUtf8().constData())); + if (!nmQuery) { + emit errorOccurred(QStringLiteral("Cannot resolve selected threads")); + return; + } + + notmuch_messages_t *raw = nullptr; + if (notmuch_query_search_messages(nmQuery.get(), &raw) + != NOTMUCH_STATUS_SUCCESS) { + emit errorOccurred(QStringLiteral("Cannot resolve selected threads")); + return; + } + + // Paths are reported RELATIVE to the database root, matching + // ThreadSummary::firstMessagePath: the UI knows accounts only by their + // maildir, itself a database-relative prefix. + const QString dbRoot = + QDir(QString::fromUtf8(notmuch_database_get_path(m_db))).absolutePath(); + + QStringList messageIds; + QStringList paths; + QStringList tags; + NmMessages messages(raw); + for (; notmuch_messages_valid(messages.get()); + notmuch_messages_move_to_next(messages.get())) { + NmMessage message(notmuch_messages_get(messages.get())); + if (!message) + continue; + const char *rawName = notmuch_message_get_filename(message.get()); + if (!rawName) + continue; + messageIds.append( + QString::fromUtf8(notmuch_message_get_message_id(message.get()))); + paths.append( + QDir(dbRoot).relativeFilePath(QString::fromUtf8(rawName))); + // Joined by a TAB, not a space. A notmuch tag may absolutely contain + // a space: a Maildir folder named "Inbox/SlackBuilds users" produces + // `deleted-from:Inbox/SlackBuilds users`, and splitting that on spaces + // truncated the folder to "Inbox/SlackBuilds". Restore then moved the + // messages into a folder of that name, CREATING it, so four real + // messages ended up in a directory mbsync does not sync and the user + // could not find them. A tab cannot appear in a tag, because notmuch's + // own dump/restore format is whitespace-delimited by line. + tags.append(tagsOf(message.get()).join(QLatin1Char('\t'))); + } + + emit threadMessagesResolved(messageIds, paths, tags, requestTag); +} + void NotmuchWorker::requestAllTags(quint64 generation) { if (!openReadOnly()) diff --git a/src/notmuchworker.h b/src/notmuchworker.h index d1bec59..dfd4303 100644 --- a/src/notmuchworker.h +++ b/src/notmuchworker.h @@ -143,6 +143,21 @@ public slots: const QStringList &remove, const QString &description); + /// Resolves whole threads to the message ids and file paths they contain. + /// + /// Delete thread MOVES every message, and a move needs message ids, which + /// the UI does not hold for a thread it never expanded. The resolution + /// happens here for the same reason applyTagsToThreads() does it here: + /// the database handle lives on this thread, and one combined query beats + /// reopening the cursor per thread. + /// + /// Paths come back beside the ids because the destination is per ACCOUNT + /// and the UI resolves an account from a message's path. Without them the + /// caller would know which messages to move and not where any of them + /// belongs. + void resolveThreadMessages(const QStringList &threadIds, + const QString &requestTag); + /// Every tag in the database, sorted. Feeds query bar completion, which /// cannot offer tag names it has no way to enumerate. Called at startup, /// after a sync, and after a tag mutation introduces an unknown tag. @@ -222,6 +237,18 @@ signals: /// from here can be passed straight back to move a message home. void messagesMovedFrom(const QMap<QString, QString> &originByMessageId, const QString &destFolder); + /// The answer to resolveThreadMessages(), as parallel lists: `messageIds` + /// and the database-relative `paths` of the same messages, in the same + /// order. `requestTag` is echoed back so a caller can tell which request + /// this answers. + /// `tags` carries each message's tags joined by a space, in the same + /// order. Needed because Restore reads a message's `deleted-from:` tag to + /// decide where to send it, and an unexpanded thread's messages have no + /// node in the model to read tags from. + void threadMessagesResolved(const QStringList &messageIds, + const QStringList &paths, + const QStringList &tags, + const QString &requestTag); void allTagsReady(const QStringList &tags, quint64 generation); /// One entry per requested query, in the order they were asked for. A query |
