aboutsummaryrefslogtreecommitdiffstats
path: root/src/main.cpp
AgeCommit message (Collapse)AuthorFilesLines
11 daysfeat(i18n): add a language key overriding the system localeDanilo M.1-9/+19
The interface language followed the environment and nothing else, so choosing it meant setting LANG for the whole application. [general] language overrides it in both directions: it selects Italian on an English desktop, and en_US forces English on an Italian one. A short code or a full locale name both work, since Qt resolves "it" to it_IT when the QLocale is built and QTranslator::load falls back from qtmaildir_it_IT to qtmaildir_it. "system" is the default written down, so the default can be expressed rather than only reached by deleting the key. Validated on the locale NAME rather than on whether a translation loads, because those are different questions and only one is an error. QLocale accepts any string and degrades an unrecognised one to C rather than failing, so `language = itallian` loads no translation and is otherwise indistinguishable from asking for English on purpose; meanwhile `language = en_US` legitimately loads nothing, English being the source language and shipping no .qm. Checking the name separates the typo from the deliberate choice, and the typo is reported. The translator is now installed after Config is loaded, since the config is what chooses it. The cost is that config warnings are generated before the translator exists and are therefore built in English; retranslating them would mean re-running load(), and a warning about the config file is the one string a user can still act on in either language. Verified against the real loader across six configurations, under both LANG=en_US and LANG=it_IT: short and full codes select Italian, system and an absent key follow the environment, en_US forces English whatever the environment says, and a bad name reports a problem and falls back. Two mutations checked: dropping the name validation and treating "system" as a locale name each fail a test. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
11 daysfeat(i18n): wire translations and ship an Italian one (item 22)Danilo M.1-0/+31
Nothing loaded a translation before this: no QTranslator, no .ts file and no build rule, so every string was English whatever the locale said. The language now comes from the environment, LANG=it_IT.UTF-8, and any other locale runs in English as before. The audit found that the tr() discipline was largely holding, and found eight strings that could never be translated into any language. kFields[] in tagrulesdialog.cpp declared the rule-builder field labels with QT_TR_NOOP inside an anonymous namespace, where lupdate reports "tr() cannot be called without context" and extracts nothing, while the use site calls TagRulesDialog::tr() on them at runtime. From, To, Cc, Subject, Tag, Folder, Attachment and Date: the whole vocabulary of the rule builder, absent from every translation file that could ever exist. The source compiles and reads correctly; only lupdate reveals it. Q_DECLARE_TR_FUNCTIONS is not the fix for that case, though it is the fix for a free function calling tr(). Measured against lupdate: a class carrying the macro beside the array still extracts 0 strings, because the context must be attached to the literal itself. QT_TRANSLATE_NOOP names it explicitly and matches the tr() that already reads them, so the use site needed no change. Twenty configuration and keybinding warnings were not translatable either. They are user-facing, reaching the status label and the "Configuration problems" dialog. Config already had the tr() macro; KeyMap needed it. Translating the filter labels then broke startup_query, found in hand testing: a filter's name is a translated label, so `startup_query = Inbox` matched nothing where the filter shows as "In arrivo". The application opened a different view and reported the user's own working config as invalid. Resolution matches the generator as well now, which is stored in queries.json and identical in every locale; the translated name still works. The regression test installs a real QTranslator rather than a stub, since the bug lives in the gap between the stored string and the displayed one, and it writes a queries.json because the warning it asserts on is guarded by a non-empty saved-query list: without one the branch never runs and the test passes against a broken check. main.cpp's --help and --version stay bare printf, as they run before QApplication exists and no translator could serve them. Verified per the backlog's own standard, that lupdate output is the evidence rather than reading: 355 strings extracted with zero context warnings, where before there were 327 with eight; lrelease reporting 355 finished and 0 unfinished; the built .qm loaded in a standalone probe printing "From -> Da" and both Italian plural forms; and the install rule placing it where main.cpp looks. test_translations guards it and was mutation checked, failing on an emptied translation and naming the defect when QT_TRANSLATE_NOOP is reverted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
11 daysfix(startup): raise the config-problem modal outside the constructorDanilo M.1-0/+10
showWarnings() did two separable things and one of them could not be reached from a test. It set the status label, which is harmless, and it raised a QMessageBox from the MainWindow constructor, which under the offscreen platform nothing can dismiss: the constructor never returned and the suite hung with no output, reading as an infrastructure failure rather than a test one. It splits in two. applyWarnings() keeps the status label and stays in the constructor. configProblems() returns the list, and main.cpp raises the dialog after show(), which also gives it a visible parent to sit on. The distinction between warnings and problems is preserved exactly: a keybinding being ignored interrupts startup, "no sync command configured" does not. The warning path now has its first test, using the config shape that caused the original hang. Mutation checked by putting the modal back in the constructor: the test times out at 124 rather than failing, which is the behaviour this removes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04feat: use the application iconDanilo M.1-0/+8
The icon was committed in a previous session and referenced nowhere: no qrc, no .desktop entry, no setWindowIcon. It is wired up now, as a window icon, a desktop entry, and install rules placing both into hicolor and share/applications. resources.qrc belongs to the executable rather than to qtmaildir_lib. A qrc compiled into a static library registers itself from a global initialiser, and the linker discards that object because nothing references it: the build succeeded, qInitResources_resources() was present in the .a, and QFile::exists(":/icons/qtmaildir.svg") still returned false at runtime. Verified loading at 16, 32 and 64 pixels after the move. Toolbar and menu actions take icons from the system theme by their standard names, so they match the rest of the desktop rather than shipping bespoke art. A theme lacking one leaves that action as text, which still works.
2026-08-04Add GPLv2-only license and per-file headersDanilo M.1-0/+50
Confirmed with the maintainer as v2-only rather than v2-or-later. LICENSE is the official text from gnu.org. Every file under src/ and tests/ carries the matching notice. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04feat: add MainWindow wiring query, list, message, and syncDanilo M.1-3/+36
Wires the worker thread, thread list, message pane, sync process and undo stack together, and replaces the placeholder main() with real startup: custom URL schemes registered before QApplication, a libnotmuch ABI check, and config loading. Four fixes against the drafted version: - onWorkerError() only set a status label. Its own comment elsewhere claimed it reverted the optimistic update, and the spec requires that; it did not, so a rejected write left the list showing a tag the database never received. The pending change is now recorded and rolled back, and a confirmed tagsApplied clears it so a later unrelated error cannot undo a write that succeeded. - runCurrentQuery() cleared the model but left the undo stack pointing at rows that no longer exist. Undoing after a new query would have written to the database while the visible list stayed put. The stack is cleared with the model. - m_currentMessages was assigned on every thread load and never read. Removed. - buildUi() connected sync output to m_syncLog and errors to m_statusLabel before either existed. Both are constructed before the wiring now. cidPrefix generation lives here, this being its only producer in the application, and is pinned by tests: it must never contain '!' and must be distinct per message, which are the invariants the cid: namespacing rests on. A second test holds registeredActionNames() against KeyMap::knownActions(), since those two hand-maintained lists drifting either way silently breaks a user's key binding. Mutation-verified that dropping an action fails the test by name. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04build: add CMake skeleton and dependency discoveryDanilo M.1-0/+10