aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/config.cpp36
-rw-r--r--src/config.h23
-rw-r--r--tests/test_config.cpp73
3 files changed, 132 insertions, 0 deletions
diff --git a/src/config.cpp b/src/config.cpp
index 23c7364..534ba72 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -459,6 +459,17 @@ void Config::load(const QString &path)
account.sent =
settings.value(QStringLiteral("sent")).toString().trimmed();
+ // Optional, and a STARTING value rather than a binding: the composer's
+ // switch keeps every signature reachable whichever account is
+ // selected. Left empty when absent, so the composer can tell "this
+ // account says nothing" from "this account says none" and fall through
+ // to [compose] signature itself; resolving that here would collapse
+ // the two. Trimmed for the same reason as sent, above: a trailing
+ // space would be carried into a filename lookup and match nothing,
+ // which is invisible in a config file.
+ account.signature =
+ settings.value(QStringLiteral("signature")).toString().trimmed();
+
// Mandatory, unlike sent: Delete moves a file into this folder, so an
// account without one cannot delete at all. Trimmed for the same
// reason as sent, above.
@@ -547,6 +558,31 @@ void Config::load(const QString &path)
m_compose.sendHtml =
settings.value(QStringLiteral("send_html"), true).toBool();
+ // Trimmed for the same reason the account key is: it reaches a filename
+ // lookup, where a trailing space matches nothing invisibly.
+ m_compose.signature =
+ settings.value(QStringLiteral("signature")).toString().trimmed();
+
+ // The same shape as quote_position directly above: an absent key is
+ // silent and the struct default holds, but a PRESENT and malformed value
+ // is reported rather than silently accepted. value(key, default) alone
+ // would read "signature_position = abov" as above_quote.
+ const QString signaturePosition =
+ settings.value(QStringLiteral("signature_position"),
+ QStringLiteral("end"))
+ .toString().trimmed();
+ if (signaturePosition.compare(QStringLiteral("above_quote"),
+ Qt::CaseInsensitive) == 0) {
+ m_compose.signaturePosition = Signatures::Position::AboveQuote;
+ } else if (signaturePosition.compare(QStringLiteral("end"),
+ Qt::CaseInsensitive) == 0) {
+ m_compose.signaturePosition = Signatures::Position::End;
+ } else {
+ addProblem(tr("[compose] signature_position '%1' is not recognised; "
+ "expected end or above_quote. Using end.")
+ .arg(signaturePosition));
+ }
+
// Three numerics, all following the shape already established at
// message_zoom, toolbar_icon_size, mark_read_delay_ms and
// auto_sync_delay_ms elsewhere in this function: a QVariant, a checked
diff --git a/src/config.h b/src/config.h
index 4dcfbf1..02b4038 100644
--- a/src/config.h
+++ b/src/config.h
@@ -26,6 +26,7 @@
#include <QStringList>
#include "completionentry.h"
+#include "signatures.h"
class QSettings;
@@ -58,6 +59,18 @@ struct Account
/// one for the account that has none.
QString sent;
+ /// The signature seeded when composing from this account, by name.
+ ///
+ /// Optional, and it does not tie a signature to the account: the switch on
+ /// the composer's editor bar keeps every signature reachable whichever
+ /// account is selected. This is a STARTING value only, which is why the
+ /// user's "not tied to an account" constraint survives it (item 152).
+ ///
+ /// The fallback to [compose] signature is NOT resolved here. An account
+ /// with no key of its own carries an empty string and the composer falls
+ /// through, so the two values stay distinguishable.
+ QString signature;
+
/// The account's trash folder, relative to maildir.
///
/// MANDATORY, unlike `sent` and `drafts`. Delete moves a file into this
@@ -228,6 +241,16 @@ struct ComposeSettings
/// accounts. Falls through when it names an account that cannot send.
QString defaultAccount;
+ /// The signature seeded when the account carries none, by name. Empty
+ /// means no signature is seeded at all.
+ QString signature;
+
+ /// Where a newly inserted signature goes. End by default, which is the
+ /// user's own habit; above_quote exists because other clients offer the
+ /// choice, and the splice's quote-aware scan is needed for the guard
+ /// either way.
+ Signatures::Position signaturePosition = Signatures::Position::End;
+
qint64 attachmentWarnBytes = 26214400;
};
diff --git a/tests/test_config.cpp b/tests/test_config.cpp
index a5dce9a..17b8e1d 100644
--- a/tests/test_config.cpp
+++ b/tests/test_config.cpp
@@ -25,6 +25,7 @@
#include <QJsonObject>
#include "config.h"
#include "mailsync.h"
+#include "signatures.h"
class TestConfig : public QObject
{
@@ -134,6 +135,9 @@ private slots:
void garbageAttachmentWarnBytesIsRejectedNotZero();
void zeroOrNegativeAutosaveIntervalIsClamped();
void unrecognisedQuotePositionWarnsAndFallsBackToBelow();
+ void theSignatureKeysAreRead();
+ void anAccountSignatureOverridesTheComposeDefault();
+ void aMalformedSignaturePositionIsReportedAndFallsBack();
};
static QString writeIni(const QTemporaryDir &dir, const QString &body)
@@ -2577,5 +2581,74 @@ void TestConfig::unrecognisedQuotePositionWarnsAndFallsBackToBelow()
"an unrecognised quote_position was accepted silently");
}
+void TestConfig::theSignatureKeysAreRead()
+{
+ QTemporaryDir dir;
+ Config config;
+ config.load(writeIni(dir, QStringLiteral(
+ "[compose]\n"
+ "signature=work\n"
+ "signature_position=above_quote\n")));
+
+ QCOMPARE(config.compose().signature, QStringLiteral("work"));
+ QVERIFY2(config.compose().signaturePosition
+ == Signatures::Position::AboveQuote,
+ "signature_position=above_quote was not read");
+}
+
+void TestConfig::anAccountSignatureOverridesTheComposeDefault()
+{
+ QTemporaryDir dir;
+ Config config;
+ config.load(writeIni(dir, QStringLiteral(
+ "[compose]\n"
+ "signature=work\n"
+ "\n"
+ "[account.personal]\n"
+ "name=Test User\n"
+ "address=user@example.org\n"
+ "maildir=personal-mail\n"
+ "trash=Trash\n"
+ "signature=brief\n"
+ "\n"
+ "[account.other]\n"
+ "name=Test User\n"
+ "address=other@example.org\n"
+ "maildir=other-mail\n"
+ "trash=Trash\n")));
+
+ // The account SEEDS the choice; it does not own the signature. The key is
+ // a starting value and the switch keeps every signature reachable.
+ QCOMPARE(config.account(QStringLiteral("personal")).signature,
+ QStringLiteral("brief"));
+ // An account with no key of its own carries none, and the caller falls
+ // through to the [compose] default rather than this being resolved here.
+ QVERIFY2(config.account(QStringLiteral("other")).signature.isEmpty(),
+ "an account with no signature key must not inherit the "
+ "[compose] one: the composer resolves the fallback, not Config");
+ QCOMPARE(config.compose().signature, QStringLiteral("work"));
+}
+
+void TestConfig::aMalformedSignaturePositionIsReportedAndFallsBack()
+{
+ // Present and malformed is REPORTED, matching quote_position. A silent
+ // value(key, default) would accept "abov" as above_quote.
+ QTemporaryDir dir;
+ Config config;
+ config.load(writeIni(dir, QStringLiteral(
+ "[compose]\n"
+ "signature_position=abov\n")));
+
+ QVERIFY2(config.compose().signaturePosition == Signatures::Position::End,
+ "an unrecognised signature_position must still fall back to End");
+ bool reported = false;
+ for (const QString &problem : config.problems()) {
+ if (problem.contains(QStringLiteral("signature_position")))
+ reported = true;
+ }
+ QVERIFY2(reported,
+ "an unrecognised signature_position was accepted silently");
+}
+
QTEST_MAIN(TestConfig)
#include "test_config.moc"