MailJDTourDownloadDocsQualityTestsCoverage

Which source lines and branches the tests execute. Lines 91.5 %, branches 75.0 % (minimum 85 % and 72 %). main @ 399ffe3d · 21 October 2026 · pipeline 7238.

GCC Code Coverage Report


Directory: src/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 95.4% 290 / 0 / 304
Functions: 100.0% 36 / 0 / 36
Branches: 70.0% 470 / 0 / 671

presentation/SettingsCatalog.cpp
Line Branch Exec Source
1 #include "SettingsCatalog.h"
2 #include <QCoreApplication>
3 #include <QHash>
4 #include <QRegularExpression>
5 #include <QSet>
6 #include <algorithm>
7
8 namespace {
9 using K = SettingKind;
10 using S = SettingScope;
11
12 // The page list of §6.1 (D-5): three blocks, the areas in the order of the
13 // area tabs.
14 25 QList<SettingsPageSpec> pageTable() {
15 return {
16 {.id = "accounts", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Accounts"),
17 .keywords = "konten konto | accounts account", .glyph = 0xF0B55, .block = "main"},
18 {.id = "general", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "General"),
19 .keywords = "allgemein start | general startup", .glyph = 0xF1542, .block = "main"},
20 {.id = "appearance", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Appearance"),
21 .keywords = "aussehen darstellung design | appearance look", .glyph = 0xF0E0C, .block = "main"},
22 {.id = "mail", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Mail"),
23 .keywords = "e-mail nachrichten post | mail messages email", .glyph = 0xF01F0, .block = "areas"},
24 {.id = "calendar", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Calendar"),
25 .keywords = "kalender termine | calendar appointments events", .glyph = 0xF0E18, .block = "areas"},
26 {.id = "tasks", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Tasks"), .keywords = "aufgaben | tasks todo",
27 .glyph = 0xF0134, .block = "areas"},
28 {.id = "contacts", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Contacts"),
29 .keywords = "kontakte adressbuch | contacts address book", .glyph = 0xF0007, .block = "areas"},
30 {.id = "rules", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Rules"),
31 .keywords = "regeln filter serverregeln spam | rules filters sieve spam", .glyph = 0xF0233, .block = "areas"},
32 {.id = "sync", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Sync"),
33 .keywords = "abgleich synchronisieren synchronisation | sync synchronization", .glyph = 0xF04E6,
34 .block = "system"},
35 {.id = "privacy", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Privacy"),
36 .keywords = "datenschutz privatsphäre | privacy", .glyph = 0xF0CCC, .block = "system"},
37 {.id = "about", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "About"),
38 .keywords = "über version lizenz | about version license", .glyph = 0xF02FD, .block = "system"},
39 25 };
40 }
41
42 15 QList<SettingsGroupSpec> groupTable() {
43 return {
44 {.page = "accounts", .id = "overview", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "All accounts"),
45 .keywords = "übersicht status | overview status"},
46 {.page = "accounts", .id = "mail", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Mail"),
47 .keywords = "posteingang postausgang | incoming outgoing"},
48 {.page = "accounts", .id = "rules", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Server rules"),
49 .keywords = "sieve regelserver | sieve rule server"},
50 // Sprint 108 S108.5.4 (D-15, D-16): the addresses an account writes from.
51 {.page = "accounts", .id = "identities", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Addresses and signatures"),
52 .keywords = "absender identität alias signatur unterschrift | sender identity alias signature"},
53 {.page = "accounts", .id = "calendars", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Calendars and contacts"),
54 .keywords = "kalender adressbücher dav | calendars address books dav"},
55 {.page = "general", .id = "regional", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Language"),
56 .keywords = "sprache | language"},
57 {.page = "general", .id = "startup", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Startup and tray"),
58 .keywords = "start systemleiste | startup tray"},
59 {.page = "general", .id = "alerts", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Notifications"),
60 .keywords = "benachrichtigungen mitteilungen | notifications"},
61 // Sprint 105 S105.8.7: the place Sprint 100 reserved for the keyboard.
62 {.page = "general", .id = "keyboard", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Keyboard"),
63 .keywords = "tastatur tasten kürzel | keyboard keys shortcuts"},
64 {.page = "appearance", .id = "colors", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Theme and colors"),
65 .keywords = "farben design | colors theme"},
66 {.page = "appearance", .id = "display", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Layout and text"),
67 .keywords = "anordnung schrift | layout text"},
68 {.page = "mail", .id = "message-list", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Message list"),
69 .keywords = "nachrichtenliste liste | message list"},
70 {.page = "mail", .id = "reading", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Reading"),
71 .keywords = "lesen leseansicht | reading reader"},
72 {.page = "mail", .id = "writing", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Writing"),
73 .keywords = "schreiben verfassen | writing compose"},
74 {.page = "mail", .id = "downloads", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Downloads"),
75 .keywords = "herunterladen | downloads"}, // F100-44: one setting, no "Advanced" disclosure
76 {.page = "calendar", .id = "working-time", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Working time"),
77 .keywords = "arbeitszeit arbeitstag arbeitsbeginn feierabend wochenbeginn | working hours workday business hours"},
78 {.page = "calendar", .id = "reminders", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Reminders"),
79 .keywords = "erinnerungen alarm | reminders alarms"},
80 {.page = "calendar", .id = "invitations", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Invitations"),
81 .keywords = "einladungen zusage absage | invitations rsvp"},
82 {.page = "calendar", .id = "show", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Show in the calendar"),
83 .keywords = "anzeigen einblenden | show display"},
84 {.page = "calendar", .id = "mail-view", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "In the mail view"),
85 .keywords = "statusleiste | status bar"},
86 {.page = "calendar", .id = "subscriptions", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Subscribed calendars"),
87 .keywords = "abonnements abonniert ics webcal feiertage | subscriptions ics webcal holidays"},
88 {.page = "tasks", .id = "reminders", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Reminders"),
89 .keywords = "erinnerungen | reminders"},
90 {.page = "contacts", .id = "me", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Me"),
91 .keywords = "ich meine karte das bin ich | me my card this is me"},
92 {.page = "contacts", .id = "names", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Names"),
93 .keywords = "namen | names"},
94 {.page = "contacts", .id = "completion", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Address completion"),
95 .keywords = "vervollständigung autovervollständigung | completion autocomplete"},
96 {.page = "contacts", .id = "books", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Address books"),
97 .keywords = "adressbücher synchronisation | address books sync"},
98 {.page = "rules", .id = "accounts", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Rules per account"),
99 .keywords = "konten | accounts"},
100 {.page = "rules", .id = "behavior", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Behavior"),
101 .keywords = "verhalten | behavior"},
102 {.page = "sync", .id = "status", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Status"),
103 .keywords = "status zustand | status"},
104 {.page = "sync", .id = "background", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Background sync"),
105 .keywords = "intervall hintergrund minuten automatisch | interval background minutes automatic",
106 .lead = QT_TRANSLATE_NOOP("SettingsCatalog", "How often MailJD asks the servers for changes.")},
107 {.page = "sync", .id = "push", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Push"),
108 .keywords = "push sofort ntfy unifiedpush | push instant ntfy"},
109 {.page = "sync", .id = "installations", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Between installations"),
110 .keywords = "einstellungen geräte installationen | settings devices installations",
111 .lead = QT_TRANSLATE_NOOP("SettingsCatalog", "Keeps chosen settings equal on your other MailJD installations.")},
112 {.page = "privacy", .id = "remote-content", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Remote content"),
113 .keywords = "externe inhalte bilder tracking | remote content images tracking"},
114 {.page = "privacy", .id = "collected-addresses",
115 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Collected addresses"),
116 .keywords = "gesammelte adressen | collected addresses"},
117 {.page = "privacy", .id = "address-lookup", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Address lookup"),
118 .keywords = "adressen nachschlagen position | address lookup position"},
119 {.page = "privacy", .id = "relationship-suggestions",
120 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Suggested relationships"),
121 .keywords = "beziehungsvorschläge beziehungen graph | suggested relationships graph"},
122 {.page = "privacy", .id = "contact-map", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Contact map"),
123 .keywords = "karte weltkarte kacheln openstreetmap | map world map tiles openstreetmap"},
124 {.page = "privacy", .id = "command-history", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Command history"),
125 .keywords = "befehlsverlauf verlauf | command history"},
126 {.page = "about", .id = "mailjd", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "MailJD"),
127 .keywords = "version programm | version program"},
128 15 };
129 }
130
131 12 QList<SettingSpec> accountSettings() {
132 return {
133 {.id = "accounts/add", .group = "overview", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Add account"),
134 .keywords = "konto hinzufügen einrichten neu anlegen | add account new set up", .kind = K::Action,
135 .owner = "openSettings(\"accounts/new\")", .store = "account file", .scope = S::Profile},
136 {.id = "accounts/status", .group = "overview", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Account status"),
137 .keywords = "status verbindung fehler funktioniert | status connection error working", .kind = K::Info,
138 .owner = "backend.settings.accountOverview", .scope = S::Profile, .availability = "backend"},
139 {.id = "accounts/servers", .group = "mail",
140 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Incoming and outgoing mail"),
141 .keywords = "imap smtp server posteingang postausgang port verschlüsselung | imap smtp server incoming outgoing port encryption",
142 .kind = K::Editor, .owner = "backend.settings.saveAccountAsync", .store = "account file + keyring",
143 .scope = S::Account,
144 .availability = "backend"},
145 {.id = "accounts/sender-name", .group = "mail", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Your name"),
146 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "Shown to the people you write to."),
147 .keywords = "absender name anzeigename | sender name display name", .kind = K::Editor,
148 .owner = "backend.settings.saveAccountAsync", .store = "account file", .scope = S::Account,
149 .availability = "backend"},
150 {.id = "accounts/addresses", .group = "identities",
151 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Addresses and signatures"),
152 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "The names, addresses and signatures you write with."),
153 .keywords = "absender alias signatur unterschrift identität | sender alias signature identity", .kind = K::Editor,
154 .owner = "backend.compose.saveIdentity", .store = "identities.json", .scope = S::Account,
155 .availability = "backend"},
156 {.id = "accounts/password", .group = "mail", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Change password"),
157 .keywords = "passwort kennwort ändern anmeldung zugangsdaten | password change login credentials",
158 .kind = K::Editor,
159 .owner = "backend.settings.changePassword", .store = "keyring", .scope = S::Account, .availability = "backend"},
160 {.id = "accounts/server-rules", .group = "rules", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Rule server"),
161 .keywords = "sieve managesieve regelserver | sieve managesieve rule server", .kind = K::Editor,
162 .owner = "backend.settings.saveAccountAsync", .store = "account file", .scope = S::Account,
163 .availability = "backend"},
164 {.id = "accounts/collections", .group = "calendars",
165 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Calendars, task lists, address books"),
166 .keywords = "caldav carddav nextcloud kalenderauswahl adressbücher | caldav carddav nextcloud choose calendars",
167 .kind = K::Editor, .owner = "backend.settings.davAccounts", .key = "carddav/accounts, caldav/configs",
168 .store = "QSettings + keyring", .scope = S::Account, .syncCategory = "davAccounts", .availability = "backend"},
169 {.id = "accounts/remove", .group = "overview", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Remove account"),
170 .keywords = "konto entfernen löschen | remove delete account", .kind = K::Action,
171 .owner = "backend.settings.removeAccount", .store = "account file + keyring", .scope = S::Account,
172 .availability = "backend"},
173 12 };
174 }
175
176 12 QList<SettingSpec> preferenceSettings() {
177 return {
178 // General
179 {.id = "general/language", .group = "regional", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Language"),
180 .keywords = "sprache deutsch englisch | language german english locale", .kind = K::Choice,
181 .owner = "backend.preferences.language", .key = "i18n/language", .store = "QSettings", .defaultValue = "auto",
182 .values = "auto|en|de", .syncCategory = "general", .availability = "backend"},
183 {.id = "general/start-minimized", .group = "startup",
184 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Start minimized"),
185 .keywords = "minimiert starten autostart versteckt | start minimized hidden", .kind = K::Toggle,
186 .owner = "backend.preferences.startMinimized", .key = "tray/startMinimized", .store = "QSettings",
187 .defaultValue = "false", .availability = "backend"},
188 {.id = "general/close-to-tray", .group = "startup",
189 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Keep in the tray when closing"),
190 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "Closing the window keeps MailJD running."),
191 .keywords = "systemleiste tray schließen hintergrund | tray close background", .kind = K::Toggle,
192 .owner = "tray.closeToTray", .key = "ui/closeToTray", .store = "UiStateStore", .defaultValue = "true",
193 .availability = "tray"},
194 {.id = "general/notifications", .group = "alerts",
195 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Notify me about new mail"),
196 .keywords = "benachrichtigungen mitteilungen nicht stören | notifications new mail alerts", .kind = K::Toggle,
197 .owner = "backend.preferences.notifications", .key = "notifications/enabled", .store = "QSettings",
198 .defaultValue = "true", .availability = "backend"},
199 // Sprint 105 S105.8.7 (D-12, D-17): the keys overlay.
200 {.id = "general/hold-alt", .group = "keyboard",
201 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Hold Alt to show the keys"),
202 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "Ctrl+? shows them without holding."),
203 .keywords = "alt halten tastenübersicht tasten | hold alt keys overlay", .kind = K::Toggle,
204 .owner = "commands.overlay.holdEnabled", .key = "keys/holdAlt", .store = "QSettings", .defaultValue = "true"},
205 {.id = "general/hold-delay", .group = "keyboard",
206 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Delay before the keys appear"),
207 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "In milliseconds; 0 shows them at once."),
208 .keywords = "verzögerung millisekunden | delay milliseconds", .kind = K::Number,
209 .owner = "commands.overlay.holdDelay", .key = "keys/holdDelay", .store = "QSettings", .defaultValue = "300",
210 .values = "0…1000"},
211 {.id = "general/key-explanations", .group = "keyboard",
212 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Explanations"),
213 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "Short texts at the keys whose effect is not obvious."),
214 .keywords = "erklärungen hilfe texte | explanations help", .kind = K::Choice,
215 .owner = "commands.overlay.explanations", .key = "keys/explanations", .store = "QSettings",
216 .defaultValue = "after", .values = "after|atOnce|never"},
217 // Appearance
218 {.id = "appearance/theme", .group = "colors", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Theme"),
219 .keywords = "dunkel hell dunkelmodus farbschema | dark light dark mode", .kind = K::Choice,
220 .owner = "tokens.theme",
221 .key = "appearance/theme", .store = "UiStateStore", .defaultValue = "system", .values = "light|dark|system"},
222 {.id = "appearance/accent", .group = "colors", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Accent color"),
223 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "Shades adapt to the theme to keep text readable."),
224 .keywords = "akzentfarbe farbe hervorhebung | accent color highlight", .kind = K::Text,
225 .owner = "tokens.applyAccentColor", .key = "appearance/accentColor", .store = "UiStateStore", .values = "#RRGGBB"},
226 {.id = "appearance/layout", .group = "display", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Layout"),
227 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "Wide puts the reader beside the list."),
228 .keywords = "anordnung breit klassisch lesebereich | layout wide classic reading pane", .kind = K::Choice,
229 .owner = "workspace.layout", .key = "ui/layout", .store = "UiStateStore", .defaultValue = "classic",
230 .values = "classic|wide"},
231 {.id = "appearance/text-size", .group = "display", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Text size"),
232 .keywords = "schriftgröße schrift zoom größer | font size text zoom larger", .kind = K::Choice,
233 .owner = "tokens.fontScale", .key = "ui/fontScale", .store = "UiStateStore", .defaultValue = "1",
234 .values = "1|1.5|2"},
235 {.id = "appearance/motion", .group = "display", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Motion"),
236 .keywords = "animation bewegung reduziert | animation motion reduce", .kind = K::Choice,
237 .owner = "tokens.applyMotionPolicy", .key = "ui/motion", .store = "UiStateStore", .defaultValue = "system",
238 .values = "system|reduced|full"},
239 // Mail
240 {.id = "mail/threads", .group = "message-list",
241 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Group messages by thread"),
242 .keywords = "unterhaltungen konversation verlauf | threads conversations", .kind = K::Toggle,
243 .owner = "backend.threaded", .key = "view/threaded", .store = "UiStateStore", .defaultValue = "false",
244 .alsoIn = QT_TRANSLATE_NOOP("SettingsCatalog", "View menu"), .availability = "backend"},
245 {.id = "mail/suggestions", .group = "message-list",
246 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Folder suggestions for all mails"),
247 .keywords = "ordnervorschläge vorschläge verschieben | folder suggestions move", .kind = K::Toggle,
248 .owner = "backend.suggestionsVisible", .key = "view/suggestions", .store = "UiStateStore", .defaultValue = "false",
249 .alsoIn = QT_TRANSLATE_NOOP("SettingsCatalog", "View menu"), .availability = "backend"},
250 {.id = "mail/reading-mode", .group = "reading", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Open messages as"),
251 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "A mode you pick in the reader stays for that session."),
252 .keywords = "lesemodus anzeige text html | reading mode text html", .kind = K::Choice,
253 .owner = "backend.settings.defaultReadingMode", .key = "view/defaultMode", .store = "QSettings",
254 .defaultValue = "text", .values = "text|light|html", .syncCategory = "general", .availability = "backend"},
255 // F100-56; "spam" alone keeps finding the Rules page first (G4).
256 {.id = "mail/spam-score", .group = "reading", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Show the server filter's rating"),
257 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "How SpamAssassin or Rspamd on the server rated it, beside the date."),
258 .keywords = "spamassassin rspamd bewertung punkte | spamassassin rspamd rating score", .kind = K::Toggle,
259 .owner = "backend.preferences.showSpamScore", .key = "reader/showSpamScore", .store = "QSettings",
260 .defaultValue = "true", .syncCategory = "general", .availability = "backend"},
261 // Sprint 108 D-20: how long a sent message waits for Undo.
262 {.id = "mail/undo-send", .group = "writing", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Undo sending"),
263 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "How long a sent message waits before it leaves."),
264 .keywords = "rückgängig senden verzögerung zurückholen | undo send delay", .kind = K::Choice,
265 .owner = "backend.compose.undoDelaySeconds", .key = "compose/undoDelaySeconds", .store = "QSettings",
266 .defaultValue = "10", .values = "0|5|10|20|30", .availability = "backend"},
267 // Sprint 108 D-7: **bold**, - lists and # headings while typing.
268 {.id = "mail/markdown-typing", .group = "writing",
269 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Format while typing"),
270 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "**bold**, _italic_, - lists and # headings in formatted mails."),
271 .keywords = "markdown formatierung tippen fett liste | markdown formatting typing bold list", .kind = K::Toggle,
272 .owner = "backend.compose.markdownTyping", .key = "compose/markdownTyping", .store = "QSettings",
273 .defaultValue = "true", .availability = "backend"},
274 // Sprint 108 D-26: the one-line reply field above the shown message.
275 {.id = "mail/quick-reply", .group = "writing", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Quick reply"),
276 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "Q shows a reply field above the message; typing opens the reply."),
277 .keywords = "schnellantwort antworten feld | quick reply field", .kind = K::Toggle,
278 .owner = "backend.compose.quickReply", .key = "compose/quickReply", .store = "QSettings",
279 .defaultValue = "true", .availability = "backend"},
280 // Sprint 108 S108.9.5 (D-27): saved message texts (⋯ › Templates… in the composer).
281 {.id = "mail/templates", .group = "writing", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Templates"),
282 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "Saved texts to insert into a message. Save one in the composer: ⋯ › Templates…."),
283 .keywords = "vorlage vorlagen textbaustein | template templates snippet", .kind = K::Editor,
284 .owner = "backend.compose.templates", .store = "templates.json", .availability = "backend"},
285 {.id = "mail/compose-window", .group = "writing",
286 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Write in a separate window"),
287 .keywords = "eigenes fenster verfassen abdocken | separate window compose detach", .kind = K::Toggle,
288 .owner = "backend.compose.composeWindowed", .key = "ui/composeWindowed", .store = "UiStateStore",
289 .defaultValue = "false", .availability = "backend"},
290 {.id = "mail/max-message-size", .group = "downloads",
291 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Largest message to download"),
292 .description = QT_TRANSLATE_NOOP("SettingsCatalog",
293 "Larger messages are not downloaded. In MiB; matches the composer."),
294 .keywords = "maximale größe nachrichtengröße limit | maximum size limit", .kind = K::Number,
295 .owner = "backend.preferences.maximumMessageMiB", .key = "network/maxMessageSizeMiB", .store = "QSettings",
296 .defaultValue = "100", .values = "1…100", .availability = "backend"},
297 // Calendar
298 {.id = "calendar/workday-start", .group = "working-time",
299 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Working day starts at"),
300 .keywords = "beginn | start", .kind = K::Choice, .owner = "backend.pim.calendarWorkdayStart",
301 .key = "calendar/workdayStart", .store = "UiStateStore", .defaultValue = "8", .values = "0…22",
302 .availability = "backend"},
303 {.id = "calendar/workday-end", .group = "working-time",
304 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Working day ends at"),
305 .keywords = "ende feierabend | end", .kind = K::Choice, .owner = "backend.pim.calendarWorkdayEnd",
306 .key = "calendar/workdayEnd", .store = "UiStateStore", .defaultValue = "18", .values = "1…24",
307 .availability = "backend"},
308 // Sprint 108 D-22: the days *Suggest times* offers.
309 {.id = "calendar/working-days", .group = "working-time",
310 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Working days"),
311 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "The days the composer suggests meeting times on."),
312 .keywords = "arbeitstage werktage wochentage | working days weekdays", .kind = K::Choice,
313 .owner = "backend.pim.calendarWorkdays", .key = "calendar/workdays", .store = "UiStateStore",
314 .defaultValue = "1,2,3,4,5", .values = "1…7", .availability = "backend"},
315 {.id = "calendar/first-day", .group = "working-time",
316 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Week starts on"),
317 .keywords = "wochenbeginn montag sonntag | first day monday sunday", .kind = K::Choice,
318 .owner = "backend.pim.calendarFirstDayOfWeek", .key = "calendar/firstDayOfWeek", .store = "UiStateStore",
319 .defaultValue = "0", .values = "0|1|7|6", .availability = "backend"},
320 {.id = "calendar/remind", .group = "reminders",
321 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Remind me of appointments"),
322 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "Also covers tasks with a reminder."),
323 .keywords = "erinnern benachrichtigung doppelt | remind notify twice", .kind = K::Toggle,
324 .owner = "backend.pim.remindersEnabled", .key = "calendar/remindersEnabled", .store = "QSettings",
325 .defaultValue = "true", .availability = "backend"},
326 {.id = "calendar/default-reminder", .group = "reminders",
327 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Default reminder"),
328 .keywords = "standarderinnerung vorlauf | default reminder lead time", .kind = K::Choice,
329 .owner = "backend.pim.defaultReminder", .key = "calendar/defaultReminder", .store = "QSettings",
330 .defaultValue = "15", .values = "-1|0|5|10|15|30|60|1440", .availability = "backend"},
331 {.id = "calendar/invitation-replies", .group = "invitations",
332 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Invitations and replies"),
333 .description = QT_TRANSLATE_NOOP("SettingsCatalog",
334 "Automatic: a scheduling calendar server sends them, otherwise MailJD mails them."),
335 .keywords = "antworten itip imip planung | replies itip imip scheduling", .kind = K::Choice,
336 .owner = "backend.invitation.replyMode", .key = "calendar/imipReplies", .store = "QSettings",
337 .defaultValue = "auto", .values = "auto|client|server", .availability = "backend"},
338 {.id = "calendar/show-tasks", .group = "show",
339 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Tasks with a due date"),
340 .keywords = "aufgaben fällig | tasks due", .kind = K::Toggle, .owner = "backend.pim.calendar.showTasks",
341 .key = "calendar/showTasks", .store = "QSettings", .defaultValue = "false",
342 .alsoIn = QT_TRANSLATE_NOOP("SettingsCatalog", "View menu"), .availability = "backend"},
343 {.id = "calendar/show-birthdays", .group = "show",
344 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Birthdays of contacts"),
345 .keywords = "geburtstage | birthdays", .kind = K::Toggle, .owner = "backend.pim.calendar.showBirthdays",
346 .key = "calendar/showBirthdays", .store = "QSettings", .defaultValue = "false",
347 .alsoIn = QT_TRANSLATE_NOOP("SettingsCatalog", "View menu"), .availability = "backend"},
348 {.id = "calendar/next-up", .group = "mail-view",
349 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Next appointment in status bar"),
350 .keywords = "nächster termin | next appointment", .kind = K::Toggle, .owner = "backend.pim.nextUp.enabled",
351 .key = "calendar/nextUpInStatusBar", .store = "QSettings", .defaultValue = "true",
352 .alsoIn = QT_TRANSLATE_NOOP("SettingsCatalog", "View menu"), .availability = "backend"},
353 {.id = "calendar/subscription-list", .group = "subscriptions",
354 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Calendar subscriptions"),
355 .keywords = "abonnieren aktualisierung | subscribe update", .kind = K::List,
356 .owner = "backend.pim.subscriptionList", .key = "calendar/subscriptions", .store = "QSettings",
357 .alsoIn = QT_TRANSLATE_NOOP("SettingsCatalog", "calendar sidebar"), .availability = "backend"},
358 // Tasks
359 {.id = "tasks/default-reminder", .group = "reminders",
360 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Default reminder for new tasks"),
361 .keywords = "aufgabenerinnerung fälligkeit | task reminder due", .kind = K::Choice,
362 .owner = "backend.pim.taskActions.defaultReminder", .key = "calendar/taskDefaultReminder", .store = "UiStateStore",
363 .defaultValue = "0", .values = "0|1", .availability = "backend"},
364 // Contacts
365 {.id = "contacts/name-order", .group = "names", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Sort names by"),
366 .keywords = "namensreihenfolge nachname vorname sortieren | name order surname first name sort", .kind = K::Choice,
367 .owner = "backend.pim.contacts.sortByFamilyName", .key = "contacts/sortByFamilyName", .store = "QSettings",
368 .defaultValue = "given", .values = "given|family",
369 .alsoIn = QT_TRANSLATE_NOOP("SettingsCatalog", "contacts View menu"),
370 .availability = "backend"},
371 // Sprint 103 D-6, F103-29: the card that is the user (per profile, never synced).
372 {.id = "contacts/my-card", .group = "me", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "My card"),
373 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "Relationships are seen from it; the graph starts there."),
374 .keywords = "ich meine karte das bin ich eigene | me my card this is me own", .kind = K::Action,
375 .owner = "backend.pim.contactActions.setMe", .key = "contacts/meCard", .store = "QSettings",
376 .alsoIn = QT_TRANSLATE_NOOP("SettingsCatalog", "a contact's menu"), .availability = "backend"},
377 // S97.5.11, kept by S100.5.7: every CardDAV address book with its state.
378 {.id = "contacts/book-states", .group = "books", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Synchronization"),
379 .keywords = "nicht synchronisiert fehler jetzt abgleichen | not synchronized error sync now", .kind = K::List,
380 .owner = "backend.pim.contactNavigation.books", .key = "carddav/accounts", .store = "QSettings",
381 .alsoIn = QT_TRANSLATE_NOOP("SettingsCatalog", "contacts sidebar"), .availability = "backend"},
382 {.id = "contacts/collected", .group = "completion",
383 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Remembered addresses"),
384 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "Set on the Privacy page."),
385 .keywords = "gesammelte adressen merken | collected addresses remember", .kind = K::Link,
386 .target = "privacy/collect-addresses"},
387 // Rules
388 {.id = "rules/manage", .group = "accounts",
389 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Rules and out of office"),
390 .keywords = "abwesenheit abwesend urlaub abwesenheitsnotiz verwalten | out of office vacation autoreply manage",
391 .kind = K::List, .owner = "backend.rules", .store = "server", .scope = S::Account, .availability = "backend"},
392 {.id = "rules/apply-to-existing", .group = "behavior",
393 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "New rules sort existing mail"),
394 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "Matching messages already in the folder move too."),
395 .keywords = "bestehende nachrichten anwenden | existing mail apply", .kind = K::Toggle,
396 .owner = "backend.preferences.applyRulesToExisting", .key = "rules/applyToExisting", .store = "QSettings",
397 .defaultValue = "false", .availability = "backend"},
398 {.id = "rules/suggestions", .group = "behavior",
399 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Suggest rules from my moves"),
400 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "After three messages from one sender go to one folder."),
401 .keywords = "regelvorschläge vorschlagen | rule suggestions suggest", .kind = K::Toggle,
402 .owner = "backend.preferences.ruleSuggestions", .key = "rules/suggestions", .store = "QSettings",
403 .defaultValue = "true", .availability = "backend"},
404 12 };
405 }
406
407 12 QList<SettingSpec> systemSettings() {
408 return {
409 // Sync
410 {.id = "sync/now", .group = "status", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Sync now"),
411 .keywords = "jetzt abgleichen aktualisieren | sync now refresh", .kind = K::Action,
412 .owner = "backend.pim.sync.syncNow", .scope = S::Profile, .availability = "backend"},
413 {.id = "sync/calendar-interval", .group = "background",
414 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Calendars and tasks"),
415 .keywords = "kalenderintervall | calendar interval", .kind = K::Choice,
416 .owner = "backend.settings.caldavSyncIntervalMinutes", .key = "caldav/syncIntervalMin", .store = "QSettings",
417 .defaultValue = "15", .values = "0|5|15|30|60|240", .syncCategory = "general", .availability = "backend"},
418 {.id = "sync/contact-interval", .group = "background", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Contacts"),
419 .keywords = "kontaktintervall adressbücher | contact interval address books", .kind = K::Choice,
420 .owner = "backend.preferences.contactIntervalMinutes", .key = "carddav/syncIntervalMin", .store = "QSettings",
421 .defaultValue = "0", .values = "0|5|15|30|60|240", .syncCategory = "general", .availability = "backend"},
422 {.id = "sync/use-push", .group = "push",
423 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Push when the server offers it"),
424 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "Changes arrive at once; the interval stays as a safety net."),
425 .keywords = "sofort echtzeit | instant real time", .kind = K::Toggle, .owner = "backend.pim.push.enabled",
426 .key = "davpush/enabled", .store = "QSettings", .defaultValue = "true", .availability = "backend"},
427 {.id = "sync/push-system", .group = "push",
428 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Use the system push service"),
429 .keywords = "kunifiedpush systemdienst | kunifiedpush system service", .kind = K::Toggle,
430 .owner = "backend.pim.push.useSystemService", .key = "davpush/useSystemService", .store = "QSettings",
431 .defaultValue = "true", .availability = "backend"},
432 {.id = "sync/push-service", .group = "push", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Push service"),
433 .keywords = "ntfy dienst server zugangstoken | ntfy service server token", .kind = K::Editor,
434 .owner = "backend.pim.push.saveService", .key = "davpush/receiver", .store = "QSettings + keyring",
435 .availability = "backend"},
436 {.id = "sync/push-collections", .group = "push",
437 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Push per collection"),
438 .keywords = "pro kalender adressbuch einzeln | per calendar address book", .kind = K::List,
439 .owner = "backend.pim.push.calendars", .key = "davpush/policy", .store = "QSettings", .scope = S::Account,
440 .availability = "backend"},
441 {.id = "sync/settings-sync", .group = "installations",
442 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Share settings via a mail folder"),
443 .keywords = "einstellungen synchronisieren geräte | settings sync devices", .kind = K::Editor,
444 .owner = "backend.settings.settingsSync", .key = "sync/enabled, sync/accountId, sync/folder, sync/categories",
445 .store = "QSettings", .scope = S::Profile, .availability = "backend"},
446 // Privacy
447 {.id = "privacy/remote-images", .group = "remote-content",
448 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Images from the internet"),
449 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "Blocked images can be loaded per message."),
450 .keywords = "bilder externe inhalte nachladen verfolgung | images remote content load tracking", .kind = K::Choice,
451 .owner = "backend.settings.imagePolicyMode", .key = "view/externalContent", .store = "QSettings",
452 .defaultValue = "block", .values = "block|load", .syncCategory = "general", .availability = "backend"},
453 {.id = "privacy/allowed-domains", .group = "remote-content",
454 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Always load from these domains"),
455 .keywords = "erlaubte domains ausnahmen whitelist | allowed domains exceptions whitelist", .kind = K::List,
456 .owner = "backend.settings.imageWhitelist", .key = "external whitelist", .store = "mail cache",
457 .scope = S::Profile,
458 .availability = "backend"},
459 {.id = "privacy/collect-addresses", .group = "collected-addresses",
460 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Remember addresses I write to"),
461 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "For the address completion; they stay on this computer."),
462 .keywords = "adressen merken sammeln | remember addresses collect", .kind = K::Toggle,
463 .owner = "backend.pim.contactActions.collectAddresses", .key = "contacts/collectAddresses", .store = "QSettings",
464 .defaultValue = "true", .availability = "backend"},
465 {.id = "privacy/forget-collected", .group = "collected-addresses",
466 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Forget collected addresses"),
467 .keywords = "vergessen löschen | forget delete", .kind = K::Action,
468 .owner = "backend.pim.contactActions.forgetCollected",
469 .store = "contact store", .scope = S::Profile, .availability = "backend"},
470 // Sprint 103 S103.1.9 (D-10): online address lookup, off by default.
471 {.id = "privacy/place-lookup", .group = "address-lookup",
472 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Look up street addresses online"),
473 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "Exact map positions; only the address is sent, never names."),
474 .keywords = "adresse nachschlagen geocoding nominatim position | address lookup geocoding nominatim position",
475 .kind = K::Toggle, .owner = "backend.pim.contactMap.onlineLookup", .key = "contacts/placeLookup",
476 .store = "QSettings", .defaultValue = "false", .availability = "backend"},
477 {.id = "privacy/lookup-server", .group = "address-lookup",
478 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Lookup server"),
479 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "A Nominatim server (https); empty is OpenStreetMap."),
480 .keywords = "nominatim server adresse | nominatim server address", .kind = K::Text,
481 .owner = "backend.pim.contactMap.lookupServer", .key = "contacts/placeLookupServer", .store = "QSettings",
482 .values = "https://…", .availability = "backend"},
483 // Sprint 103 S103.5.8 (I-8): suggestions from local data, off by default.
484 {.id = "privacy/graph-suggestions", .group = "relationship-suggestions",
485 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Suggest relationships"),
486 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "From mails and appointments you share; worked out here."),
487 .keywords = "beziehungsvorschläge beziehungen graph zusammen | suggested relationships graph together", .kind = K::Toggle,
488 .owner = "backend.pim.contactGraph.suggestions", .key = "contacts/graphSuggestions", .store = "QSettings",
489 .defaultValue = "false", .alsoIn = QT_TRANSLATE_NOOP("SettingsCatalog", "the graph's legend"),
490 .availability = "backend"},
491 {.id = "privacy/forget-dismissed", .group = "relationship-suggestions",
492 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Forget dismissed suggestions"),
493 .keywords = "beziehungsvorschläge abgelehnt vergessen | suggested dismissed forget", .kind = K::Action,
494 .owner = "backend.pim.contactGraph.forgetDismissedSuggestions", .store = "contact store",
495 .availability = "backend"},
496 // Sprint 103 S103.4.10 (D-11): online map tiles, off by default.
497 {.id = "privacy/map-tiles", .group = "contact-map",
498 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Map tiles from the internet"),
499 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "Streets on the contact map; the server sees the areas you view."),
500 .keywords = "kartenkacheln strassen online openstreetmap | map tiles streets online openstreetmap", .kind = K::Toggle,
501 .owner = "backend.pim.contactMap.onlineTiles", .key = "contacts/mapTiles", .store = "QSettings",
502 .defaultValue = "false", .availability = "backend"},
503 // F103-24: the public styles of resources/contacts/map-styles.json, or an own server.
504 {.id = "privacy/map-tile-provider", .group = "contact-map",
505 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Map provider"),
506 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "Community servers need no key; the others need a free API key."),
507 .keywords = "kartenanbieter anbieter carto thunderforest stadia maptiler | map provider carto thunderforest stadia maptiler",
508 .kind = K::Choice, .owner = "backend.pim.contactMap.tileProvider", .key = "contacts/mapTileStyle",
509 .store = "QSettings (the style's provider)",
510 .defaultValue = "osm", .values = "osm|carto|thunderforest|stadia|maptiler|custom", .availability = "backend"},
511 {.id = "privacy/map-tile-style", .group = "contact-map",
512 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Map style"),
513 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "Each provider sees the areas you view."),
514 .keywords = "kartenstil design aussehen topografie dunkel hell satellit | map style design look topographic dark light satellite",
515 .kind = K::Choice, .owner = "backend.pim.contactMap.tileStyle", .key = "contacts/mapTileStyle", .store = "QSettings",
516 .defaultValue = "osm",
517 // The style ids of resources/contacts/map-styles.json.
518 .values = "osm|osm-de|osm-fr|osm-humanitarian|cyclosm|opentopomap|opnvkarte|carto-voyager|"
519 "carto-light|carto-dark|thunderforest-cycle|thunderforest-transport|"
520 "thunderforest-transport-dark|thunderforest-landscape|thunderforest-outdoors|"
521 "thunderforest-atlas|stadia-smooth|stadia-smooth-dark|stadia-outdoors|stadia-osm-bright|"
522 "stadia-toner|stadia-terrain|stadia-watercolor|maptiler-streets|maptiler-outdoor|"
523 "maptiler-topo|maptiler-winter|maptiler-satellite|maptiler-hybrid|custom",
524 .availability = "backend"},
525 {.id = "privacy/map-tile-key", .group = "contact-map",
526 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "API key"),
527 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "Kept on this computer only and sent with every map tile request."),
528 .keywords = "api schlüssel key zugangsschlüssel token | api key token", .kind = K::Text,
529 .owner = "backend.pim.contactMap.setTileKey", .key = "contacts/mapTileKeys/<provider>", .store = "QSettings",
530 .availability = "backend"},
531 {.id = "privacy/map-tile-server", .group = "contact-map",
532 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Tile server"),
533 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "https with {z}, {x} and {y}; empty is OpenStreetMap."),
534 .keywords = "kachelserver server adresse | tile server address", .kind = K::Text,
535 .owner = "backend.pim.contactMap.tileServer", .key = "contacts/mapTileServer", .store = "QSettings",
536 .values = "https://…/{z}/{x}/{y}.png", .availability = "backend"},
537 {.id = "privacy/clear-map-tiles", .group = "contact-map",
538 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Clear map tile cache"),
539 .keywords = "kachel cache leeren löschen | tile cache clear delete", .kind = K::Action,
540 .owner = "backend.pim.contactMap.clearTileCache", .store = "tile cache", .availability = "backend"},
541 {.id = "privacy/clear-command-history", .group = "command-history",
542 .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Clear command history"),
543 .description = QT_TRANSLATE_NOOP("SettingsCatalog", "The last 50 commands; searches and texts are never kept."),
544 .keywords = "befehlsverlauf befehlsleiste löschen | command bar history clear", .kind = K::Action,
545 .owner = "commands.clearHistory", .key = "commandbar/history", .store = "UiStateStore"},
546 // About
547 {.id = "about/version", .group = "mailjd", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Version"),
548 .keywords = "versionsnummer commit | version number commit", .kind = K::Info, .owner = "about.version"},
549 {.id = "about/license", .group = "mailjd", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "License"),
550 .keywords = "lizenz gpl freie software | license gpl free software", .kind = K::Info, .owner = "about"},
551 {.id = "about/notices", .group = "mailjd", .label = QT_TRANSLATE_NOOP("SettingsCatalog", "Third-party notices"),
552 .keywords = "drittanbieter hinweise bibliotheken | third party notices libraries", .kind = K::Action,
553 .owner = "about.thirdPartyNotices"},
554 12 };
555 }
556
557 2854 const char *kindName(SettingKind kind) {
558
9/10
✓ Branch 2 → 3 taken 926 times.
✓ Branch 2 → 4 taken 895 times.
✓ Branch 2 → 5 taken 88 times.
✓ Branch 2 → 6 taken 152 times.
✓ Branch 2 → 7 taken 69 times.
✓ Branch 2 → 8 taken 233 times.
✓ Branch 2 → 9 taken 401 times.
✓ Branch 2 → 10 taken 31 times.
✓ Branch 2 → 11 taken 59 times.
✗ Branch 2 → 12 not taken.
2854 switch (kind) {
559 926 case K::Toggle: return "toggle";
560 895 case K::Choice: return "choice";
561 88 case K::Number: return "number";
562 152 case K::Text: return "text";
563 69 case K::List: return "list";
564 233 case K::Action: return "action";
565 401 case K::Editor: return "editor";
566 31 case K::Link: return "link";
567 59 case K::Info: return "info";
568 }
569 ✗ return "";
570 }
571
572 3 const char *scopeName(SettingScope scope) {
573
3/4
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 4 taken 1 time.
✓ Branch 2 → 5 taken 1 time.
✗ Branch 2 → 6 not taken.
3 switch (scope) {
574 1 case S::Application: return "application";
575 1 case S::Profile: return "profile";
576 1 case S::Account: return "account";
577 }
578 ✗ return "";
579 }
580
581 // Words of a folded text, split at everything that is not a letter or digit.
582 111331 QStringList words(const QString &folded) {
583
4/7
✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 15 taken 111329 times.
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 15 not taken.
✓ Branch 9 → 10 taken 2 times.
✗ Branch 26 → 27 not taken.
✗ Branch 26 → 28 not taken.
111333 static const QRegularExpression separator(QStringLiteral("[^\\p{L}\\p{N}]+"));
584
1/1
✓ Branch 16 → 17 taken 111331 times.
111331 return folded.split(separator, Qt::SkipEmptyParts);
585 }
586
587 54445 bool anyWordStartsWith(const QStringList &candidates, const QString &prefix) {
588 54445 return std::any_of(candidates.cbegin(), candidates.cend(),
589 305101 [&](const QString &candidate) { return candidate.startsWith(prefix); });
590 }
591
592 // One searchable place: its own label/keywords/description and the names
593 // of the places around it.
594 struct Candidate {
595 QString place, kind, label, path;
596 QStringList labels; // folded labels (translated and English)
597 QStringList labelWords;
598 QStringList keywordWords;
599 QStringList contextWords; // group and page names
600 QStringList descriptionWords;
601 int order = 0;
602 int kindRank = 0; // ties: page, then group, then setting (the broader place first)
603 };
604
605 13796 int wordScore(const Candidate &candidate, const QString &word) {
606
2/2
✓ Branch 5 → 6 taken 61 times.
✓ Branch 5 → 7 taken 13735 times.
13796 if (std::any_of(candidate.labels.cbegin(), candidate.labels.cend(),
607 14272 [&](const QString &label) { return label.startsWith(word); }))
608 61 return 100;
609
2/2
✓ Branch 8 → 9 taken 50 times.
✓ Branch 8 → 10 taken 13685 times.
13735 if (anyWordStartsWith(candidate.labelWords, word)) return 80;
610
2/2
✓ Branch 11 → 12 taken 153 times.
✓ Branch 11 → 13 taken 13532 times.
13685 if (anyWordStartsWith(candidate.keywordWords, word)) return 60;
611
2/2
✓ Branch 14 → 15 taken 39 times.
✓ Branch 14 → 16 taken 13493 times.
13532 if (anyWordStartsWith(candidate.contextWords, word)) return 40;
612
2/2
✓ Branch 17 → 18 taken 9 times.
✓ Branch 17 → 19 taken 13484 times.
13493 if (anyWordStartsWith(candidate.descriptionWords, word)) return 30;
613 13484 return 0;
614 }
615
616 13734 void addLabel(Candidate &candidate, const char *source) {
617 41202 for (const QString &label : {SettingsCatalog::fold(SettingsCatalog::text(source)),
618
6/10
✓ Branch 2 → 3 taken 13734 times.
✓ Branch 3 → 4 taken 13734 times.
✓ Branch 5 → 6 taken 13734 times.
✓ Branch 6 → 7 taken 13734 times.
✓ Branch 21 → 12 taken 27468 times.
✓ Branch 21 → 22 taken 13734 times.
✗ Branch 36 → 37 not taken.
✗ Branch 36 → 40 not taken.
✗ Branch 38 → 39 not taken.
✗ Branch 38 → 40 not taken.
82404 SettingsCatalog::fold(QString::fromUtf8(source))}) {
619
2/2
✓ Branch 13 → 14 taken 13258 times.
✓ Branch 13 → 15 taken 14210 times.
27468 if (candidate.labels.contains(label)) continue;
620
1/1
✓ Branch 15 → 16 taken 14210 times.
14210 candidate.labels << label;
621
2/2
✓ Branch 16 → 17 taken 14210 times.
✓ Branch 17 → 18 taken 14210 times.
14210 candidate.labelWords << words(label);
622 41202 }
623 13734 }
624
625 41638 void addWords(QStringList &target, const char *source, bool translate) {
626
4/4
✓ Branch 3 → 4 taken 41638 times.
✓ Branch 4 → 5 taken 41638 times.
✓ Branch 5 → 6 taken 41638 times.
✓ Branch 6 → 7 taken 41638 times.
41638 target << words(SettingsCatalog::fold(QString::fromUtf8(source)));
627
5/6
✓ Branch 10 → 11 taken 41638 times.
✗ Branch 10 → 19 not taken.
✓ Branch 11 → 12 taken 41638 times.
✓ Branch 12 → 13 taken 41638 times.
✓ Branch 13 → 14 taken 41638 times.
✓ Branch 14 → 15 taken 41638 times.
41638 if (translate) target << words(SettingsCatalog::fold(SettingsCatalog::text(source)));
628 41638 }
629
630 109 QList<Candidate> candidates() {
631 109 QList<Candidate> result;
632 109 int order = 0;
633
3/3
✓ Branch 2 → 3 taken 109 times.
✓ Branch 22 → 5 taken 1199 times.
✓ Branch 22 → 23 taken 109 times.
1308 for (const auto &page : SettingsCatalog::pages()) {
634 1199 Candidate candidate{.place = QString::fromLatin1(page.id), .kind = QStringLiteral("page"),
635
3/9
✓ Branch 7 → 8 taken 1199 times.
✓ Branch 11 → 12 taken 1199 times.
✓ Branch 12 → 13 taken 1199 times.
✗ Branch 127 → 128 not taken.
✗ Branch 127 → 129 not taken.
✗ Branch 130 → 131 not taken.
✗ Branch 130 → 132 not taken.
✗ Branch 136 → 137 not taken.
✗ Branch 136 → 138 not taken.
2398 .label = SettingsCatalog::text(page.label), .path = SettingsCatalog::text(page.label)};
636
1/1
✓ Branch 14 → 15 taken 1199 times.
1199 addLabel(candidate, page.label);
637
1/1
✓ Branch 15 → 16 taken 1199 times.
1199 candidate.keywordWords = SettingsCatalog::keywordWords(page.keywords);
638 1199 candidate.order = order++;
639 1199 candidate.kindRank = 0;
640
1/1
✓ Branch 18 → 19 taken 1199 times.
1199 result << candidate;
641 1199 }
642
3/3
✓ Branch 23 → 24 taken 109 times.
✓ Branch 68 → 26 taken 4251 times.
✓ Branch 68 → 69 taken 109 times.
4360 for (const auto &group : SettingsCatalog::groups()) {
643
2/2
✓ Branch 28 → 29 taken 4251 times.
✓ Branch 29 → 30 taken 4251 times.
4251 const auto *page = SettingsCatalog::findPage(QString::fromLatin1(group.page));
644
3/3
✓ Branch 32 → 33 taken 4251 times.
✓ Branch 35 → 36 taken 4251 times.
✓ Branch 36 → 37 taken 4251 times.
8502 Candidate candidate{.place = QString::fromLatin1(group.page) + '/' + QString::fromLatin1(group.id),
645 4251 .kind = QStringLiteral("group"), .label = SettingsCatalog::text(group.label),
646
6/12
✓ Branch 37 → 38 taken 4251 times.
✓ Branch 41 → 42 taken 4251 times.
✓ Branch 42 → 43 taken 4251 times.
✓ Branch 46 → 47 taken 4251 times.
✓ Branch 47 → 48 taken 4251 times.
✓ Branch 48 → 49 taken 4251 times.
✗ Branch 164 → 165 not taken.
✗ Branch 164 → 166 not taken.
✗ Branch 167 → 168 not taken.
✗ Branch 167 → 169 not taken.
✗ Branch 173 → 174 not taken.
✗ Branch 173 → 175 not taken.
12753 .path = SettingsCatalog::text(page->label) + QStringLiteral(" › ") + SettingsCatalog::text(group.label)};
647
1/1
✓ Branch 58 → 59 taken 4251 times.
4251 addLabel(candidate, group.label);
648
1/1
✓ Branch 59 → 60 taken 4251 times.
4251 candidate.keywordWords = SettingsCatalog::keywordWords(group.keywords);
649
1/1
✓ Branch 62 → 63 taken 4251 times.
4251 addWords(candidate.contextWords, page->label, true);
650
1/1
✓ Branch 63 → 64 taken 4251 times.
4251 addWords(candidate.descriptionWords, group.lead, true);
651 4251 candidate.order = order++;
652 4251 candidate.kindRank = 1;
653
1/1
✓ Branch 64 → 65 taken 4251 times.
4251 result << candidate;
654 4251 }
655
3/3
✓ Branch 69 → 70 taken 109 times.
✓ Branch 124 → 72 taken 8284 times.
✓ Branch 124 → 125 taken 109 times.
8393 for (const auto &setting : SettingsCatalog::settings()) {
656
1/1
✓ Branch 74 → 75 taken 8284 times.
8284 const QString id = QString::fromLatin1(setting.id);
657
2/2
✓ Branch 77 → 78 taken 8284 times.
✓ Branch 78 → 79 taken 8284 times.
8284 const auto *page = SettingsCatalog::findPage(id.section('/', 0, 0));
658
5/5
✓ Branch 81 → 82 taken 8284 times.
✓ Branch 85 → 86 taken 8284 times.
✓ Branch 86 → 87 taken 8284 times.
✓ Branch 87 → 88 taken 8284 times.
✓ Branch 88 → 89 taken 8284 times.
16568 const auto *group = SettingsCatalog::findGroup(id.section('/', 0, 0) + '/' + QString::fromLatin1(setting.group));
659 ✗ Candidate candidate{.place = id, .kind = QStringLiteral("setting"), .label = SettingsCatalog::text(setting.label),
660
5/11
✓ Branch 97 → 98 taken 8284 times.
✓ Branch 98 → 99 taken 8284 times.
✓ Branch 102 → 103 taken 8284 times.
✓ Branch 103 → 104 taken 8284 times.
✓ Branch 104 → 105 taken 8284 times.
✗ Branch 230 → 231 not taken.
✗ Branch 230 → 232 not taken.
✗ Branch 233 → 234 not taken.
✗ Branch 233 → 235 not taken.
✗ Branch 239 → 240 not taken.
✗ Branch 239 → 241 not taken.
24852 .path = SettingsCatalog::text(page->label) + QStringLiteral(" › ") + SettingsCatalog::text(group->label)};
661
1/1
✓ Branch 111 → 112 taken 8284 times.
8284 addLabel(candidate, setting.label);
662
1/1
✓ Branch 112 → 113 taken 8284 times.
8284 candidate.keywordWords = SettingsCatalog::keywordWords(setting.keywords);
663
1/1
✓ Branch 115 → 116 taken 8284 times.
8284 addWords(candidate.contextWords, page->label, true);
664
1/1
✓ Branch 116 → 117 taken 8284 times.
8284 addWords(candidate.contextWords, group->label, true);
665
1/1
✓ Branch 117 → 118 taken 8284 times.
8284 addWords(candidate.descriptionWords, setting.description, true);
666
1/1
✓ Branch 118 → 119 taken 8284 times.
8284 addWords(candidate.descriptionWords, setting.alsoIn, true);
667 8284 candidate.order = order++;
668 8284 candidate.kindRank = 2;
669
1/1
✓ Branch 119 → 120 taken 8284 times.
8284 result << candidate;
670 8284 }
671 109 return result;
672 ✗ }
673 } // namespace
674
675 18460 const QList<SettingsPageSpec> &SettingsCatalog::pages() {
676
4/7
✓ Branch 2 → 3 taken 25 times.
✓ Branch 2 → 8 taken 18435 times.
✓ Branch 4 → 5 taken 25 times.
✗ Branch 4 → 8 not taken.
✓ Branch 5 → 6 taken 25 times.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
18460 static const QList<SettingsPageSpec> table = pageTable();
677 18460 return table;
678 }
679
680 13558 const QList<SettingsGroupSpec> &SettingsCatalog::groups() {
681
4/7
✓ Branch 2 → 3 taken 15 times.
✓ Branch 2 → 8 taken 13543 times.
✓ Branch 4 → 5 taken 15 times.
✗ Branch 4 → 8 not taken.
✓ Branch 5 → 6 taken 15 times.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
13558 static const QList<SettingsGroupSpec> table = groupTable();
682 13558 return table;
683 }
684
685 5770 const QList<SettingSpec> &SettingsCatalog::settings() {
686
8/11
✓ Branch 2 → 3 taken 12 times.
✓ Branch 2 → 17 taken 5758 times.
✓ Branch 4 → 5 taken 12 times.
✗ Branch 4 → 17 not taken.
✓ Branch 5 → 6 taken 12 times.
✓ Branch 6 → 7 taken 12 times.
✓ Branch 7 → 8 taken 12 times.
✓ Branch 8 → 9 taken 12 times.
✓ Branch 9 → 10 taken 12 times.
✗ Branch 31 → 32 not taken.
✗ Branch 31 → 33 not taken.
5770 static const QList<SettingSpec> table = accountSettings() + preferenceSettings() + systemSettings();
687 5770 return table;
688 }
689
690 17516 const SettingsPageSpec *SettingsCatalog::findPage(const QString &id) {
691
3/3
✓ Branch 2 → 3 taken 17516 times.
✓ Branch 12 → 5 taken 112215 times.
✓ Branch 12 → 13 taken 2129 times.
114344 for (const auto &page : pages())
692
2/2
✓ Branch 8 → 9 taken 15387 times.
✓ Branch 8 → 10 taken 96828 times.
112215 if (id == QLatin1String(page.id)) return &page;
693 2129 return nullptr;
694 }
695
696 12611 const SettingsGroupSpec *SettingsCatalog::findGroup(const QString &place) {
697
2/2
✓ Branch 4 → 5 taken 12611 times.
✓ Branch 7 → 8 taken 12611 times.
12611 const QString page = place.section('/', 0, 0), id = place.section('/', 1);
698
3/3
✓ Branch 8 → 9 taken 12611 times.
✓ Branch 24 → 11 taken 298686 times.
✓ Branch 24 → 25 taken 2180 times.
300866 for (const auto &group : groups())
699
6/6
✓ Branch 14 → 15 taken 36713 times.
✓ Branch 14 → 19 taken 261973 times.
✓ Branch 17 → 18 taken 10431 times.
✓ Branch 17 → 19 taken 26282 times.
✓ Branch 20 → 21 taken 10431 times.
✓ Branch 20 → 22 taken 288255 times.
298686 if (page == QLatin1String(group.page) && id == QLatin1String(group.id)) return &group;
700 2180 return nullptr;
701 12611 }
702
703 5614 const SettingSpec *SettingsCatalog::findSetting(const QString &id) {
704
3/3
✓ Branch 2 → 3 taken 5614 times.
✓ Branch 12 → 5 taken 218336 times.
✓ Branch 12 → 13 taken 135 times.
218471 for (const auto &setting : settings())
705
2/2
✓ Branch 8 → 9 taken 5479 times.
✓ Branch 8 → 10 taken 212857 times.
218336 if (id == QLatin1String(setting.id)) return &setting;
706 135 return nullptr;
707 }
708
709 127715 QString SettingsCatalog::text(const char *source) {
710
3/4
✓ Branch 2 → 3 taken 127715 times.
✗ Branch 2 → 5 not taken.
✓ Branch 3 → 4 taken 104215 times.
✓ Branch 3 → 5 taken 23500 times.
127715 return source && *source ? QCoreApplication::translate("SettingsCatalog", source) : QString{};
711 }
712
713 13735 QStringList SettingsCatalog::keywordWords(const char *keywords) {
714
3/3
✓ Branch 3 → 4 taken 13735 times.
✓ Branch 4 → 5 taken 13735 times.
✓ Branch 5 → 6 taken 13735 times.
13735 return words(fold(QString::fromUtf8(keywords)));
715 }
716
717 124590 QString SettingsCatalog::fold(const QString &text) {
718
3/3
✓ Branch 2 → 3 taken 124590 times.
✓ Branch 7 → 8 taken 124590 times.
✓ Branch 8 → 9 taken 124590 times.
249180 QString decomposed = text.toLower().replace(QChar(0x00DF), QStringLiteral("ss")).normalized(QString::NormalizationForm_D);
719 124590 QString result;
720
1/1
✓ Branch 13 → 14 taken 124590 times.
124590 result.reserve(decomposed.size());
721
4/4
✓ Branch 14 → 15 taken 124590 times.
✓ Branch 15 → 16 taken 124590 times.
✓ Branch 21 → 17 taken 1985341 times.
✓ Branch 21 → 22 taken 124590 times.
2109931 for (const QChar c : decomposed)
722
3/3
✓ Branch 18 → 19 taken 1980327 times.
✓ Branch 18 → 20 taken 5014 times.
✓ Branch 19 → 20 taken 1980327 times.
1985341 if (c.category() != QChar::Mark_NonSpacing) result += c;
723 124590 return result;
724 124590 }
725
726 110 QList<SettingsCatalog::SearchHit> SettingsCatalog::search(const QString &query, int limit) {
727
2/2
✓ Branch 2 → 3 taken 110 times.
✓ Branch 3 → 4 taken 110 times.
110 const QStringList queryWords = words(fold(query));
728
2/2
✓ Branch 6 → 7 taken 1 time.
✓ Branch 6 → 8 taken 109 times.
110 if (queryWords.isEmpty()) return {};
729 struct Scored { int score; const Candidate *candidate; };
730
1/1
✓ Branch 8 → 9 taken 109 times.
109 const QList<Candidate> all = candidates(); // translations follow the current language
731 109 QList<Scored> scored;
732
2/2
✓ Branch 26 → 11 taken 13734 times.
✓ Branch 26 → 27 taken 109 times.
13843 for (const auto &candidate : all) {
733 13734 int total = 0;
734 13734 bool every = true;
735
2/2
✓ Branch 20 → 14 taken 13796 times.
✓ Branch 20 → 21 taken 250 times.
14046 for (const auto &word : queryWords) {
736
1/1
✓ Branch 15 → 16 taken 13796 times.
13796 const int score = wordScore(candidate, word);
737
2/2
✓ Branch 16 → 17 taken 13484 times.
✓ Branch 16 → 18 taken 312 times.
13796 if (score == 0) { every = false; break; }
738 312 total += score;
739 }
740
3/3
✓ Branch 21 → 22 taken 250 times.
✓ Branch 21 → 24 taken 13484 times.
✓ Branch 22 → 23 taken 250 times.
13734 if (every) scored.append({total, &candidate});
741 }
742
3/3
✓ Branch 27 → 28 taken 109 times.
✓ Branch 28 → 29 taken 109 times.
✓ Branch 29 → 30 taken 109 times.
109 std::stable_sort(scored.begin(), scored.end(), [](const Scored &a, const Scored &b) {
743
2/2
✓ Branch 2 → 3 taken 102 times.
✓ Branch 2 → 4 taken 187 times.
289 if (a.score != b.score) return a.score > b.score;
744
2/2
✓ Branch 4 → 5 taken 71 times.
✓ Branch 4 → 6 taken 116 times.
187 if (a.candidate->kindRank != b.candidate->kindRank) return a.candidate->kindRank < b.candidate->kindRank;
745 116 return a.candidate->order < b.candidate->order;
746 });
747 109 QList<SearchHit> hits;
748
4/4
✓ Branch 30 → 31 taken 109 times.
✓ Branch 31 → 32 taken 109 times.
✓ Branch 45 → 33 taken 233 times.
✓ Branch 45 → 46 taken 108 times.
341 for (const auto &entry : scored) {
749
2/2
✓ Branch 35 → 36 taken 1 time.
✓ Branch 35 → 37 taken 232 times.
233 if (hits.size() >= limit) break;
750 232 hits.append({entry.candidate->place, entry.candidate->kind, entry.candidate->label, entry.candidate->path, entry.score});
751 }
752 109 return hits;
753 342 }
754
755 1 QStringList SettingsCatalog::lint() {
756
3/7
✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 15 not taken.
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 15 not taken.
✓ Branch 9 → 10 taken 1 time.
✗ Branch 319 → 320 not taken.
✗ Branch 319 → 321 not taken.
2 static const QRegularExpression idPattern(QStringLiteral("^[a-z]+(-[a-z]+)*$"));
757 1 QStringList problems;
758 1 QSet<QString> places;
759 126 const auto keywordsBothLanguages = [](const char *keywords) {
760
1/1
✓ Branch 3 → 4 taken 126 times.
126 const QString text = QString::fromUtf8(keywords);
761
1/1
✓ Branch 7 → 8 taken 126 times.
126 const int bar = text.indexOf(QStringLiteral(" | "));
762
11/26
✓ Branch 10 → 11 taken 126 times.
✗ Branch 10 → 20 not taken.
✓ Branch 11 → 12 taken 126 times.
✓ Branch 12 → 13 taken 126 times.
✓ Branch 14 → 15 taken 126 times.
✗ Branch 14 → 20 not taken.
✓ Branch 15 → 16 taken 126 times.
✓ Branch 16 → 17 taken 126 times.
✓ Branch 18 → 19 taken 126 times.
✗ Branch 18 → 20 not taken.
✓ Branch 22 → 23 taken 126 times.
✗ Branch 22 → 24 not taken.
✓ Branch 24 → 25 taken 126 times.
✗ Branch 24 → 26 not taken.
✓ Branch 26 → 27 taken 126 times.
✗ Branch 26 → 28 not taken.
✓ Branch 28 → 29 taken 126 times.
✗ Branch 28 → 30 not taken.
✗ Branch 40 → 41 not taken.
✗ Branch 40 → 42 not taken.
✗ Branch 44 → 45 not taken.
✗ Branch 44 → 46 not taken.
✗ Branch 48 → 49 not taken.
✗ Branch 48 → 50 not taken.
✗ Branch 52 → 53 not taken.
✗ Branch 52 → 54 not taken.
252 return bar > 0 && !text.left(bar).trimmed().isEmpty() && !text.mid(bar + 3).trimmed().isEmpty();
763 126 };
764
3/3
✓ Branch 16 → 17 taken 1 time.
✓ Branch 78 → 19 taken 11 times.
✓ Branch 78 → 79 taken 1 time.
12 for (const auto &page : pages()) {
765
1/1
✓ Branch 21 → 22 taken 11 times.
11 const QString id = QString::fromLatin1(page.id);
766
3/6
✓ Branch 23 → 24 taken 11 times.
✓ Branch 24 → 25 taken 11 times.
✗ Branch 26 → 27 not taken.
✓ Branch 26 → 31 taken 11 times.
✗ Branch 27 → 28 not taken.
✗ Branch 28 → 29 not taken.
11 if (!idPattern.match(id).hasMatch()) problems << id + ": page ids are lowercase words joined by '-'";
767
1/4
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 37 taken 11 times.
✗ Branch 33 → 34 not taken.
✗ Branch 34 → 35 not taken.
11 if (places.contains(id)) problems << id + ": duplicate place";
768
1/1
✓ Branch 37 → 38 taken 11 times.
11 places.insert(id);
769
2/6
✓ Branch 38 → 39 taken 11 times.
✗ Branch 38 → 40 not taken.
✗ Branch 39 → 40 not taken.
✓ Branch 39 → 44 taken 11 times.
✗ Branch 40 → 41 not taken.
✗ Branch 41 → 42 not taken.
11 if (!*page.label || !page.glyph) problems << id + ": a page needs a label and a glyph";
770
2/5
✓ Branch 44 → 45 taken 11 times.
✗ Branch 45 → 46 not taken.
✓ Branch 45 → 50 taken 11 times.
✗ Branch 46 → 47 not taken.
✗ Branch 47 → 48 not taken.
11 if (!keywordsBothLanguages(page.keywords)) problems << id + ": keywords need German and English words";
771
8/14
✓ Branch 51 → 52 taken 11 times.
✓ Branch 53 → 54 taken 8 times.
✓ Branch 53 → 63 taken 3 times.
✓ Branch 55 → 56 taken 8 times.
✓ Branch 57 → 58 taken 3 times.
✓ Branch 57 → 63 taken 5 times.
✓ Branch 68 → 69 taken 11 times.
✗ Branch 68 → 70 not taken.
✗ Branch 70 → 71 not taken.
✓ Branch 70 → 75 taken 11 times.
✗ Branch 346 → 347 not taken.
✗ Branch 346 → 348 not taken.
✗ Branch 352 → 353 not taken.
✗ Branch 352 → 354 not taken.
25 if (QString::fromLatin1(page.block) != "main" && QString::fromLatin1(page.block) != "areas" &&
772
6/9
✓ Branch 59 → 60 taken 3 times.
✗ Branch 61 → 62 not taken.
✓ Branch 61 → 63 taken 3 times.
✓ Branch 64 → 65 taken 3 times.
✓ Branch 64 → 66 taken 8 times.
✓ Branch 66 → 67 taken 8 times.
✓ Branch 66 → 68 taken 3 times.
✗ Branch 340 → 341 not taken.
✗ Branch 340 → 342 not taken.
14 QString::fromLatin1(page.block) != "system")
773 ✗ problems << id + ": unknown block";
774 11 }
775 1 int leads = 0;
776
3/3
✓ Branch 79 → 80 taken 1 time.
✓ Branch 137 → 82 taken 39 times.
✓ Branch 137 → 138 taken 1 time.
40 for (const auto &group : groups()) {
777
4/4
✓ Branch 84 → 85 taken 39 times.
✓ Branch 87 → 88 taken 39 times.
✓ Branch 88 → 89 taken 39 times.
✓ Branch 89 → 90 taken 39 times.
39 const QString place = QString::fromLatin1(group.page) + '/' + QString::fromLatin1(group.id);
778
3/6
✓ Branch 94 → 95 taken 39 times.
✓ Branch 95 → 96 taken 39 times.
✗ Branch 97 → 98 not taken.
✓ Branch 97 → 102 taken 39 times.
✗ Branch 98 → 99 not taken.
✗ Branch 99 → 100 not taken.
39 if (!findPage(QString::fromLatin1(group.page))) problems << place + ": unknown page";
779
4/7
✓ Branch 104 → 105 taken 39 times.
✓ Branch 105 → 106 taken 39 times.
✓ Branch 106 → 107 taken 39 times.
✗ Branch 109 → 110 not taken.
✓ Branch 109 → 114 taken 39 times.
✗ Branch 110 → 111 not taken.
✗ Branch 111 → 112 not taken.
39 if (!idPattern.match(QString::fromLatin1(group.id)).hasMatch()) problems << place + ": bad group id";
780
1/4
✗ Branch 115 → 116 not taken.
✓ Branch 115 → 120 taken 39 times.
✗ Branch 116 → 117 not taken.
✗ Branch 117 → 118 not taken.
39 if (places.contains(place)) problems << place + ": duplicate place";
781
1/1
✓ Branch 120 → 121 taken 39 times.
39 places.insert(place);
782
1/4
✗ Branch 121 → 122 not taken.
✓ Branch 121 → 126 taken 39 times.
✗ Branch 122 → 123 not taken.
✗ Branch 123 → 124 not taken.
39 if (!*group.label) problems << place + ": a group needs a label";
783
2/5
✓ Branch 126 → 127 taken 39 times.
✗ Branch 127 → 128 not taken.
✓ Branch 127 → 132 taken 39 times.
✗ Branch 128 → 129 not taken.
✗ Branch 129 → 130 not taken.
39 if (!keywordsBothLanguages(group.keywords)) problems << place + ": keywords need German and English words";
784
2/2
✓ Branch 132 → 133 taken 2 times.
✓ Branch 132 → 134 taken 37 times.
39 if (*group.lead) ++leads;
785 39 }
786
1/3
✗ Branch 138 → 139 not taken.
✓ Branch 138 → 146 taken 1 time.
✗ Branch 142 → 143 not taken.
1 if (leads > 6) problems << QStringLiteral("more than six group leads (§6.3)");
787 1 QHash<QString, QSet<QString>> labelsPerPage;
788
3/3
✓ Branch 146 → 147 taken 1 time.
✓ Branch 307 → 149 taken 76 times.
✓ Branch 307 → 308 taken 1 time.
77 for (const auto &setting : settings()) {
789
1/1
✓ Branch 151 → 152 taken 76 times.
76 const QString id = QString::fromLatin1(setting.id);
790
1/1
✓ Branch 154 → 155 taken 76 times.
76 const QString page = id.section('/', 0, 0);
791
2/5
✓ Branch 155 → 156 taken 76 times.
✗ Branch 156 → 157 not taken.
✓ Branch 156 → 161 taken 76 times.
✗ Branch 157 → 158 not taken.
✗ Branch 158 → 159 not taken.
76 if (!findPage(page)) problems << id + ": unknown page";
792
9/18
✓ Branch 164 → 165 taken 76 times.
✓ Branch 165 → 166 taken 76 times.
✓ Branch 166 → 167 taken 76 times.
✓ Branch 167 → 168 taken 76 times.
✗ Branch 167 → 171 not taken.
✓ Branch 169 → 170 taken 76 times.
✗ Branch 170 → 171 not taken.
✓ Branch 170 → 172 taken 76 times.
✓ Branch 173 → 174 taken 76 times.
✗ Branch 173 → 175 not taken.
✓ Branch 175 → 176 taken 76 times.
✗ Branch 175 → 177 not taken.
✗ Branch 177 → 178 not taken.
✓ Branch 177 → 182 taken 76 times.
✗ Branch 422 → 423 not taken.
✗ Branch 422 → 424 not taken.
✗ Branch 426 → 427 not taken.
✗ Branch 426 → 428 not taken.
152 if (!idPattern.match(id.section('/', 1)).hasMatch() || id.count('/') != 1)
793 ✗ problems << id + ": a setting id is <page>/<lowercase-words>";
794
5/8
✓ Branch 183 → 184 taken 76 times.
✓ Branch 185 → 186 taken 76 times.
✓ Branch 186 → 187 taken 76 times.
✓ Branch 187 → 188 taken 76 times.
✗ Branch 191 → 192 not taken.
✓ Branch 191 → 196 taken 76 times.
✗ Branch 192 → 193 not taken.
✗ Branch 193 → 194 not taken.
76 if (!findGroup(page + '/' + QString::fromLatin1(setting.group))) problems << id + ": unknown group";
795
1/4
✗ Branch 197 → 198 not taken.
✓ Branch 197 → 202 taken 76 times.
✗ Branch 198 → 199 not taken.
✗ Branch 199 → 200 not taken.
76 if (places.contains(id)) problems << id + ": duplicate place";
796
1/1
✓ Branch 202 → 203 taken 76 times.
76 places.insert(id);
797
1/1
✓ Branch 204 → 205 taken 76 times.
76 const QString label = QString::fromUtf8(setting.label);
798
1/4
✗ Branch 206 → 207 not taken.
✓ Branch 206 → 211 taken 76 times.
✗ Branch 207 → 208 not taken.
✗ Branch 208 → 209 not taken.
76 if (label.isEmpty()) problems << id + ": a setting needs a label";
799
1/4
✗ Branch 212 → 213 not taken.
✓ Branch 212 → 217 taken 76 times.
✗ Branch 213 → 214 not taken.
✗ Branch 214 → 215 not taken.
76 if (label.size() > 40) problems << id + ": label longer than 40 characters";
800
2/5
✓ Branch 218 → 219 taken 76 times.
✗ Branch 221 → 222 not taken.
✓ Branch 221 → 226 taken 76 times.
✗ Branch 222 → 223 not taken.
✗ Branch 223 → 224 not taken.
76 if (QString::fromUtf8(setting.description).size() > 90) problems << id + ": description longer than 90 characters";
801
2/5
✓ Branch 226 → 227 taken 76 times.
✗ Branch 228 → 229 not taken.
✓ Branch 228 → 233 taken 76 times.
✗ Branch 229 → 230 not taken.
✗ Branch 230 → 231 not taken.
76 if (labelsPerPage[page].contains(label)) problems << id + ": two settings with the same label on one page";
802
2/2
✓ Branch 233 → 234 taken 76 times.
✓ Branch 234 → 235 taken 76 times.
76 labelsPerPage[page].insert(label);
803
2/5
✓ Branch 235 → 236 taken 76 times.
✗ Branch 236 → 237 not taken.
✓ Branch 236 → 241 taken 76 times.
✗ Branch 237 → 238 not taken.
✗ Branch 238 → 239 not taken.
76 if (!keywordsBothLanguages(setting.keywords)) problems << id + ": keywords need German and English words";
804
6/6
✓ Branch 241 → 242 taken 54 times.
✓ Branch 241 → 245 taken 22 times.
✓ Branch 242 → 243 taken 33 times.
✓ Branch 242 → 245 taken 21 times.
✓ Branch 243 → 244 taken 31 times.
✓ Branch 243 → 245 taken 2 times.
107 const bool stored = setting.kind == K::Toggle || setting.kind == K::Choice || setting.kind == K::Number ||
805
2/2
✓ Branch 244 → 245 taken 4 times.
✓ Branch 244 → 246 taken 27 times.
31 setting.kind == K::Text;
806
5/8
✓ Branch 247 → 248 taken 49 times.
✓ Branch 247 → 255 taken 27 times.
✓ Branch 248 → 249 taken 49 times.
✗ Branch 248 → 251 not taken.
✓ Branch 249 → 250 taken 49 times.
✗ Branch 249 → 251 not taken.
✗ Branch 250 → 251 not taken.
✓ Branch 250 → 255 taken 49 times.
76 if (stored && (!*setting.key || !*setting.store || !*setting.owner))
807 ✗ problems << id + ": a stored setting names its owner, key and store";
808
5/6
✓ Branch 255 → 256 taken 55 times.
✓ Branch 255 → 257 taken 21 times.
✓ Branch 256 → 257 taken 2 times.
✓ Branch 256 → 262 taken 53 times.
✗ Branch 257 → 258 not taken.
✓ Branch 257 → 262 taken 23 times.
76 if ((setting.kind == K::Choice || setting.kind == K::Number) && !*setting.values)
809 ✗ problems << id + ": choices and numbers name their values";
810
6/9
✓ Branch 262 → 263 taken 22 times.
✓ Branch 262 → 272 taken 54 times.
✓ Branch 264 → 265 taken 22 times.
✓ Branch 266 → 267 taken 10 times.
✓ Branch 266 → 272 taken 12 times.
✗ Branch 277 → 278 not taken.
✓ Branch 277 → 282 taken 76 times.
✗ Branch 485 → 486 not taken.
✗ Branch 485 → 487 not taken.
162 if (setting.kind == K::Toggle && QString::fromLatin1(setting.defaultValue) != "true" &&
811
6/9
✓ Branch 268 → 269 taken 10 times.
✗ Branch 270 → 271 not taken.
✓ Branch 270 → 272 taken 10 times.
✓ Branch 273 → 274 taken 10 times.
✓ Branch 273 → 275 taken 66 times.
✓ Branch 275 → 276 taken 22 times.
✓ Branch 275 → 277 taken 54 times.
✗ Branch 479 → 480 not taken.
✗ Branch 479 → 481 not taken.
86 QString::fromLatin1(setting.defaultValue) != "false")
812 ✗ problems << id + ": a toggle defaults to true or false";
813
8/12
✓ Branch 282 → 283 taken 1 time.
✓ Branch 282 → 288 taken 75 times.
✓ Branch 284 → 285 taken 1 time.
✓ Branch 285 → 286 taken 1 time.
✗ Branch 286 → 287 not taken.
✓ Branch 286 → 288 taken 1 time.
✓ Branch 289 → 290 taken 1 time.
✓ Branch 289 → 291 taken 75 times.
✗ Branch 291 → 292 not taken.
✓ Branch 291 → 296 taken 76 times.
✗ Branch 494 → 495 not taken.
✗ Branch 494 → 496 not taken.
76 if (setting.kind == K::Link && !findSetting(QString::fromLatin1(setting.target)))
814 ✗ problems << id + ": a link names an existing setting";
815
3/6
✓ Branch 296 → 297 taken 75 times.
✓ Branch 296 → 302 taken 1 time.
✗ Branch 297 → 298 not taken.
✓ Branch 297 → 302 taken 75 times.
✗ Branch 298 → 299 not taken.
✗ Branch 299 → 300 not taken.
76 if (setting.kind != K::Link && *setting.target) problems << id + ": only links have a target";
816 76 }
817 1 return problems;
818 1 }
819
820 1 QString SettingsCatalog::referenceSnapshot() {
821 1 QString result = QStringLiteral(
822 "# Settings reference\n\n"
823 "Generated from `SettingsCatalog::referenceSnapshot()` (`src/presentation/SettingsCatalog.cpp`);\n"
824 "`test_settings_catalog` fails when this file is stale. Every setting has one home\n"
825 "(page › group); *Also in* lists other places that change the same value; *Synchronised*\n"
826 "names the category of settings sync between installations (empty = this computer only).\n"
827 "The place (`page/name`) opens the setting: `settings <place>` in the command bar.\n"
828 "How to open, search and leave the settings: [user guide](user/CONFIGURATION.md#settings).\n");
829
3/3
✓ Branch 6 → 7 taken 1 time.
✓ Branch 183 → 9 taken 11 times.
✓ Branch 183 → 184 taken 1 time.
12 for (const auto &page : pages()) {
830
4/4
✓ Branch 14 → 15 taken 11 times.
✓ Branch 16 → 17 taken 11 times.
✓ Branch 17 → 18 taken 11 times.
✓ Branch 18 → 19 taken 11 times.
11 result += QStringLiteral("\n## %1 (`%2`)\n").arg(QString::fromUtf8(page.label), QString::fromLatin1(page.id));
831
3/3
✓ Branch 24 → 25 taken 11 times.
✓ Branch 180 → 27 taken 429 times.
✓ Branch 180 → 181 taken 11 times.
440 for (const auto &group : groups()) {
832
2/2
✓ Branch 31 → 32 taken 390 times.
✓ Branch 31 → 33 taken 39 times.
429 if (QLatin1String(group.page) != QLatin1String(page.id)) continue;
833
2/2
✓ Branch 42 → 43 taken 39 times.
✓ Branch 43 → 44 taken 39 times.
156 result += QStringLiteral("\n### %1%2\n\n").arg(QString::fromUtf8(group.label),
834
3/7
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 40 taken 39 times.
✓ Branch 44 → 45 taken 39 times.
✗ Branch 48 → 49 not taken.
✓ Branch 48 → 50 taken 39 times.
✗ Branch 262 → 263 not taken.
✗ Branch 262 → 264 not taken.
117 group.advanced ? QStringLiteral(" (Advanced)") : QString{});
835
5/5
✓ Branch 52 → 53 taken 2 times.
✓ Branch 52 → 65 taken 37 times.
✓ Branch 57 → 58 taken 2 times.
✓ Branch 58 → 59 taken 2 times.
✓ Branch 59 → 60 taken 2 times.
41 if (*group.lead) result += QString::fromUtf8(group.lead) + QStringLiteral("\n\n");
836
1/1
✓ Branch 68 → 69 taken 39 times.
39 result += QStringLiteral("| Place | Setting | Kind | Default | Values | Stored in | Also in | Synchronised |\n"
837 "|---|---|---|---|---|---|---|---|\n");
838
3/3
✓ Branch 71 → 72 taken 39 times.
✓ Branch 176 → 74 taken 2964 times.
✓ Branch 176 → 177 taken 39 times.
3003 for (const auto &setting : settings()) {
839
1/1
✓ Branch 76 → 77 taken 2964 times.
2964 const QString id = QString::fromLatin1(setting.id);
840
8/11
✓ Branch 80 → 81 taken 2964 times.
✓ Branch 82 → 83 taken 337 times.
✓ Branch 82 → 87 taken 2627 times.
✓ Branch 86 → 87 taken 261 times.
✓ Branch 86 → 88 taken 76 times.
✓ Branch 89 → 90 taken 2964 times.
✗ Branch 89 → 91 not taken.
✓ Branch 91 → 92 taken 2888 times.
✓ Branch 91 → 93 taken 76 times.
✗ Branch 296 → 297 not taken.
✗ Branch 296 → 298 not taken.
5928 if (id.section('/', 0, 0) != QLatin1String(page.id) || QLatin1String(setting.group) != QLatin1String(group.id))
841 2888 continue;
842
1/1
✓ Branch 94 → 95 taken 76 times.
76 QString label = QString::fromUtf8(setting.label);
843
5/5
✓ Branch 95 → 96 taken 34 times.
✓ Branch 95 → 108 taken 42 times.
✓ Branch 97 → 98 taken 34 times.
✓ Branch 101 → 102 taken 34 times.
✓ Branch 102 → 103 taken 34 times.
110 if (*setting.description) label += QStringLiteral(" — ") + QString::fromUtf8(setting.description);
844
8/16
✓ Branch 113 → 114 taken 58 times.
✓ Branch 115 → 116 taken 58 times.
✓ Branch 121 → 122 taken 58 times.
✓ Branch 121 → 123 taken 18 times.
✓ Branch 123 → 124 taken 58 times.
✓ Branch 123 → 125 taken 18 times.
✓ Branch 125 → 126 taken 58 times.
✓ Branch 125 → 127 taken 18 times.
✗ Branch 318 → 319 not taken.
✗ Branch 318 → 320 not taken.
✗ Branch 323 → 324 not taken.
✗ Branch 323 → 325 not taken.
✗ Branch 328 → 329 not taken.
✗ Branch 328 → 330 not taken.
✗ Branch 332 → 333 not taken.
✗ Branch 332 → 334 not taken.
250 const QString key = *setting.key ? QStringLiteral("`%1` (%2)").arg(QString::fromUtf8(setting.key), QString::fromUtf8(setting.store))
845
6/6
✓ Branch 108 → 109 taken 58 times.
✓ Branch 108 → 117 taken 18 times.
✓ Branch 116 → 119 taken 58 times.
✓ Branch 118 → 119 taken 18 times.
✓ Branch 119 → 120 taken 58 times.
✓ Branch 119 → 121 taken 18 times.
250 : QString::fromUtf8(setting.store);
846 152 result += QStringLiteral("| `%1` | %2 | %3 | %4 | %5 | %6 | %7 | %8 |\n")
847
3/3
✓ Branch 140 → 141 taken 76 times.
✓ Branch 149 → 150 taken 76 times.
✓ Branch 150 → 151 taken 76 times.
228 .arg(id, label, QString::fromLatin1(kindName(setting.kind)),
848
9/13
✓ Branch 141 → 142 taken 45 times.
✓ Branch 141 → 146 taken 31 times.
✓ Branch 143 → 144 taken 45 times.
✓ Branch 144 → 145 taken 45 times.
✓ Branch 145 → 147 taken 45 times.
✓ Branch 155 → 156 taken 45 times.
✓ Branch 155 → 157 taken 31 times.
✓ Branch 157 → 158 taken 45 times.
✓ Branch 157 → 159 taken 31 times.
✗ Branch 345 → 346 not taken.
✗ Branch 345 → 347 not taken.
✗ Branch 349 → 350 not taken.
✗ Branch 349 → 351 not taken.
152 *setting.defaultValue ? "`" + QString::fromUtf8(setting.defaultValue) + "`" : QString{},
849
1/1
✓ Branch 135 → 136 taken 76 times.
228 QString::fromUtf8(setting.values).replace('|', QStringLiteral(" ∣ ")), key,
850
3/3
✓ Branch 131 → 132 taken 76 times.
✓ Branch 133 → 134 taken 76 times.
✓ Branch 151 → 152 taken 76 times.
228 QString::fromUtf8(setting.alsoIn), QString::fromLatin1(setting.syncCategory));
851 2964 }
852 }
853 }
854
1/1
✓ Branch 187 → 188 taken 1 time.
1 result += QStringLiteral("\n## Scopes\n\n");
855
2/2
✓ Branch 234 → 192 taken 3 times.
✓ Branch 234 → 235 taken 1 time.
4 for (const auto scope : {S::Application, S::Profile, S::Account}) {
856 3 QStringList ids;
857
3/3
✓ Branch 192 → 193 taken 3 times.
✓ Branch 212 → 195 taken 228 times.
✓ Branch 212 → 213 taken 3 times.
231 for (const auto &setting : settings())
858
5/5
✓ Branch 196 → 197 taken 76 times.
✓ Branch 196 → 210 taken 152 times.
✓ Branch 202 → 203 taken 76 times.
✓ Branch 203 → 204 taken 76 times.
✓ Branch 204 → 205 taken 76 times.
380 if (setting.scope == scope) ids << QStringLiteral("`%1`").arg(QString::fromLatin1(setting.id));
859
4/4
✓ Branch 219 → 220 taken 3 times.
✓ Branch 222 → 223 taken 3 times.
✓ Branch 223 → 224 taken 3 times.
✓ Branch 224 → 225 taken 3 times.
6 result += QStringLiteral("- **%1**: %2\n").arg(QString::fromLatin1(scopeName(scope)), ids.join(QStringLiteral(", ")));
860 3 }
861 1 return result;
862 ✗ }
863
864 831 QVariantList SettingsCatalog::pageList() const {
865 831 QVariantList result;
866
3/3
✓ Branch 2 → 3 taken 831 times.
✓ Branch 28 → 5 taken 9141 times.
✓ Branch 28 → 29 taken 831 times.
9972 for (const auto &page : pages())
867
1/1
✓ Branch 7 → 8 taken 9141 times.
27423 result << QVariantMap{{"id", QString::fromLatin1(page.id)}, {"label", text(page.label)},
868
4/6
✓ Branch 13 → 14 taken 9141 times.
✓ Branch 17 → 18 taken 9141 times.
✓ Branch 21 → 22 taken 36564 times.
✓ Branch 21 → 23 taken 9141 times.
✗ Branch 36 → 37 not taken.
✗ Branch 36 → 38 not taken.
73128 {"glyph", page.glyph}, {"block", QString::fromLatin1(page.block)}};
869 831 return result;
870 27423 }
871
872 825 QVariantList SettingsCatalog::groupList(const QString &page) const {
873 825 QVariantList result;
874
3/3
✓ Branch 2 → 3 taken 825 times.
✓ Branch 40 → 5 taken 32175 times.
✓ Branch 40 → 41 taken 825 times.
33000 for (const auto &group : groups())
875
2/2
✓ Branch 8 → 9 taken 3639 times.
✓ Branch 8 → 38 taken 28536 times.
32175 if (page == QLatin1String(group.page))
876
4/4
✓ Branch 10 → 11 taken 3639 times.
✓ Branch 13 → 14 taken 3639 times.
✓ Branch 15 → 16 taken 3639 times.
✓ Branch 16 → 17 taken 3639 times.
14556 result << QVariantMap{{"id", QString::fromLatin1(group.id)}, {"place", page + '/' + QString::fromLatin1(group.id)},
877
3/5
✓ Branch 25 → 26 taken 3639 times.
✓ Branch 29 → 30 taken 18195 times.
✓ Branch 29 → 31 taken 3639 times.
✗ Branch 48 → 49 not taken.
✗ Branch 48 → 50 not taken.
29112 {"label", text(group.label)}, {"lead", text(group.lead)}, {"advanced", group.advanced}};
878 825 return result;
879 10917 }
880
881 2778 QVariantMap SettingsCatalog::setting(const QString &id) const {
882 2778 const auto *spec = findSetting(id);
883
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 2778 times.
2778 if (!spec) return {};
884
1/1
✓ Branch 7 → 8 taken 2778 times.
5556 return {{"id", id}, {"group", QString::fromLatin1(spec->group)}, {"label", text(spec->label)},
885
1/1
✓ Branch 15 → 16 taken 2778 times.
5556 {"description", text(spec->description)}, {"kind", QString::fromLatin1(kindName(spec->kind))},
886
1/1
✓ Branch 18 → 19 taken 2778 times.
5556 {"syncCategory", QString::fromLatin1(spec->syncCategory)}, {"alsoIn", text(spec->alsoIn)},
887
2/2
✓ Branch 23 → 24 taken 2778 times.
✓ Branch 26 → 27 taken 2778 times.
8334 {"availability", QString::fromLatin1(spec->availability)}, {"target", QString::fromLatin1(spec->target)},
888
3/5
✓ Branch 29 → 30 taken 2778 times.
✓ Branch 33 → 34 taken 27780 times.
✓ Branch 33 → 35 taken 2778 times.
✗ Branch 47 → 48 not taken.
✗ Branch 47 → 49 not taken.
36114 {"defaultValue", QString::fromUtf8(spec->defaultValue)}};
889 19446 }
890
891 2376 QVariantMap SettingsCatalog::place(const QString &id) const {
892
2/2
✓ Branch 3 → 4 taken 249 times.
✓ Branch 3 → 21 taken 2127 times.
2376 if (const auto *page = findPage(id))
893 249 return {{"id", id}, {"kind", "page"}, {"page", id}, {"group", QString()}, {"label", text(page->label)},
894
2/4
✓ Branch 15 → 16 taken 1494 times.
✓ Branch 15 → 17 taken 249 times.
✗ Branch 109 → 110 not taken.
✗ Branch 109 → 111 not taken.
1992 {"path", text(page->label)}};
895
1/2
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 56 taken 2127 times.
2127 if (const auto *group = findGroup(id)) {
896 ✗ const auto *page = findPage(QString::fromLatin1(group->page));
897 ✗ return {{"id", id}, {"kind", "group"}, {"page", QString::fromLatin1(group->page)}, {"group", id},
898 ✗ {"label", text(group->label)}, {"path", text(page->label) + QStringLiteral(" › ") + text(group->label)}};
899 }
900
2/2
✓ Branch 57 → 58 taken 2061 times.
✓ Branch 57 → 106 taken 66 times.
2127 if (const auto *setting = findSetting(id)) {
901
1/1
✓ Branch 60 → 61 taken 2061 times.
2061 const QString page = id.section('/', 0, 0);
902
4/4
✓ Branch 62 → 63 taken 2061 times.
✓ Branch 64 → 65 taken 2061 times.
✓ Branch 65 → 66 taken 2061 times.
✓ Branch 66 → 67 taken 2061 times.
2061 const auto *group = findGroup(page + '/' + QString::fromLatin1(setting->group));
903
3/3
✓ Branch 74 → 75 taken 2061 times.
✓ Branch 76 → 77 taken 2061 times.
✓ Branch 77 → 78 taken 2061 times.
4122 return {{"id", id}, {"kind", "setting"}, {"page", page}, {"group", page + '/' + QString::fromLatin1(setting->group)},
904 2061 {"label", text(setting->label)},
905
6/8
✓ Branch 85 → 86 taken 2061 times.
✓ Branch 86 → 87 taken 2061 times.
✓ Branch 87 → 88 taken 2061 times.
✓ Branch 88 → 89 taken 2061 times.
✓ Branch 92 → 93 taken 12366 times.
✓ Branch 92 → 94 taken 2061 times.
✗ Branch 180 → 181 not taken.
✗ Branch 180 → 182 not taken.
16488 {"path", text(findPage(page)->label) + QStringLiteral(" › ") + text(group->label)}};
906 2061 }
907 66 return {};
908 6432 }
909
910 4 QVariantList SettingsCatalog::searchPlaces(const QString &query) const {
911 4 QVariantList result;
912
5/5
✓ Branch 2 → 3 taken 4 times.
✓ Branch 3 → 4 taken 4 times.
✓ Branch 4 → 5 taken 4 times.
✓ Branch 21 → 6 taken 12 times.
✓ Branch 21 → 22 taken 4 times.
16 for (const auto &hit : search(query))
913
3/5
✓ Branch 13 → 14 taken 12 times.
✓ Branch 17 → 18 taken 48 times.
✓ Branch 17 → 19 taken 12 times.
✗ Branch 30 → 31 not taken.
✗ Branch 30 → 32 not taken.
64 result << QVariantMap{{"place", hit.place}, {"kind", hit.kind}, {"label", hit.label}, {"path", hit.path}};
914 4 return result;
915 12 }
916
917
4/4
✓ Branch 2 → 3 taken 2288 times.
✓ Branch 4 → 5 taken 2288 times.
✓ Branch 5 → 6 taken 2288 times.
✓ Branch 6 → 7 taken 2288 times.
2288 QString SettingsCatalog::label(const QString &id) const { return place(id).value("label").toString(); }
918
919 655 QString SettingsCatalog::description(const QString &id) const {
920 655 const auto *spec = findSetting(id);
921
2/2
✓ Branch 3 → 4 taken 628 times.
✓ Branch 3 → 5 taken 27 times.
655 return spec ? text(spec->description) : QString{};
922 }
923