// Copyright (C) 2026 Danilo M. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. import QtQuick import "Toml.js" as T // One config.toml's draft, the store qtmaildir's Setting and BoolSetting // edit. Fields hand it text; it writes the type of the key's default, the // type sb-builder checks on load: a list is words split on spaces, a number // is a number, a switch is true or false. QtObject { id: st // The file as last read or written, the draft's start, and the draft. property string text: "" property string base: "" property string draft: "" readonly property bool dirty: st.draft !== st.base // {section: {key: value}}, top-level keys under "". property var defaults: ({}) property var invalid: ({}) readonly property bool valid: Object.keys(st.invalid).length === 0 property string error: "" // Assigned only when the set changes, as Qtm does: a Repeater bound to a // fresh array per keystroke rebuilds the row being typed in. property var manifests: [] onDraftChanged: { const k = T.entries(st.draft, "manifests").map(e => e.key); if (JSON.stringify(k) !== JSON.stringify(st.manifests)) st.manifests = k; } function fallback(section, key) { return (st.defaults[section] || {})[key]; } function show(v) { return v === undefined ? undefined : Array.isArray(v) ? v.join(" ") : String(v); } function placeholder(section, key) { return st.show(st.fallback(section, key)) ?? ""; } function get(section, key) { return st.show(T.get(st.draft, section, key)); } // The effective value, typed: the file's, else the default. function value(section, key) { return T.get(st.draft, section, key) ?? st.fallback(section, key); } function entries(section) { return T.entries(st.draft, section); } function put(section, key, v) { try { st.draft = T.set(st.draft, section, key, v); } catch (e) { st.error = e.message; } } // A field's text; null or "" unsets. function set(section, key, t) { if (t === null || t === "") return st.put(section, key, null); const d = st.fallback(section, key); st.put(section, key, Array.isArray(d) ? t.split(/\s+/).filter(x => x !== "") : typeof d === "number" ? Number(t) : typeof d === "boolean" ? t === "true" : t); } function remove(section, key) { st.draft = T.remove(st.draft, section, key); } function markInvalid(id, bad) { if (!!st.invalid[id] === bad) return; const next = Object.assign({}, st.invalid); if (bad) next[id] = true; else delete next[id]; st.invalid = next; } // The file as read: a clean draft follows it, a dirty one is kept and // its save is refused. function loaded(t) { st.text = t; if (!st.dirty) { st.base = t; st.draft = t; } } function discard() { st.error = ""; st.invalid = ({}); st.base = st.text; st.draft = st.text; } }