Application specific preferences saving now enabled.
This commit is contained in:
1 parent
f88e5b81a2
commit
60abcc37d9
23 files changed
+1004
-248
No files matched your search
@@ -0,0 +1,140 @@
|
||||
// Copyright (c) 2026 Storm Dragon
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
)
|
||||
|
||||
// Application_setting admits only settings which can be applied and restored
|
||||
// without changing devices, routing, storage, or other process-wide services.
|
||||
func Application_setting(path string) bool {
|
||||
switch path {
|
||||
case "presentation.speech.enabled", "presentation.speech.rate", "presentation.speech.pitch", "presentation.speech.volume", "presentation.speech.module", "presentation.speech.language", "presentation.speech.voice",
|
||||
"presentation.sound.enabled", "presentation.sound.volume", "presentation.progress.enabled",
|
||||
"presentation.time_and_date.time_format", "presentation.time_and_date.date_format",
|
||||
"keyboard.character_echo", "keyboard.word_echo", "keyboard.echo_on_capslock":
|
||||
return true
|
||||
case "reading.automatic", "reading.cursor_echo", "reading.mode", "reading.punctuation_level",
|
||||
"reading.raise_pitch_for_capitals", "reading.beep_for_capitals", "reading.say_cap_for_capitals",
|
||||
"reading.indentation", "reading.attributes", "reading.attribute_format", "reading.spellcheck",
|
||||
"reading.spellcheck_read_word", "reading.spellcheck_say_misspelled", "reading.spellcheck_sound",
|
||||
"reading.emoticons", "reading.repeated_symbols", "reading.repeated_symbols_min":
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Open_application_document overlays a sparse, current-schema profile on the
|
||||
// application's inherited settings. Missing files inherit everything.
|
||||
func Open_application_document(path string, base Settings) (*Document, error) {
|
||||
doc := &Document{Settings: base, path: path, applicationBase: &base, applicationFields: make(map[string]bool)}
|
||||
data, err := read_document(path)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return doc, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var fields map[string]any
|
||||
if err := toml.Unmarshal(data, &fields); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var collect func(map[string]any, string) error
|
||||
collect = func(fields map[string]any, prefix string) error {
|
||||
for name, value := range fields {
|
||||
path := prefix + name
|
||||
if table, ok := value.(map[string]any); ok {
|
||||
if err := collect(table, path+"."); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
if !Application_setting(path) {
|
||||
return fmt.Errorf("setting %s is not supported in application profiles", path)
|
||||
}
|
||||
doc.applicationFields[path] = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err := collect(fields, ""); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := toml.NewDecoder(bytes.NewReader(data)).DisallowUnknownFields().Decode(&doc.Settings); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := doc.Settings.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
doc.original, doc.existed = data, true
|
||||
return doc, nil
|
||||
}
|
||||
|
||||
// Apply_application preserves inherited session state for every omitted field.
|
||||
func (d *Document) Apply_application(base Settings) Settings {
|
||||
for path := range d.applicationFields {
|
||||
setting_value(reflect.ValueOf(&base).Elem(), path).Set(setting_value(reflect.ValueOf(d.Settings), path))
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
func (d *Document) save_application(settings Settings) error {
|
||||
fields := make(map[string]any)
|
||||
selectionChanged := false
|
||||
for _, path := range []string{"presentation.speech.module", "presentation.speech.language", "presentation.speech.voice"} {
|
||||
selectionChanged = selectionChanged || d.applicationFields[path] || !reflect.DeepEqual(setting_value(reflect.ValueOf(settings), path).Interface(), setting_value(reflect.ValueOf(*d.applicationBase), path).Interface())
|
||||
}
|
||||
var visit func(reflect.Value, reflect.Value, string)
|
||||
visit = func(value, base reflect.Value, prefix string) {
|
||||
for index := range value.NumField() {
|
||||
path := prefix + value.Type().Field(index).Tag.Get("toml")
|
||||
child, inherited := value.Field(index), base.Field(index)
|
||||
if child.Kind() == reflect.Struct {
|
||||
visit(child, inherited, path+".")
|
||||
continue
|
||||
}
|
||||
speechSelection := selectionChanged && (path == "presentation.speech.module" || path == "presentation.speech.language" || path == "presentation.speech.voice")
|
||||
if !Application_setting(path) || (!speechSelection && !d.applicationFields[path] && reflect.DeepEqual(child.Interface(), inherited.Interface())) {
|
||||
continue
|
||||
}
|
||||
parts := strings.Split(path, ".")
|
||||
table := fields
|
||||
for _, part := range parts[:len(parts)-1] {
|
||||
if table[part] == nil {
|
||||
table[part] = make(map[string]any)
|
||||
}
|
||||
table = table[part].(map[string]any)
|
||||
}
|
||||
table[parts[len(parts)-1]] = child.Interface()
|
||||
}
|
||||
}
|
||||
visit(reflect.ValueOf(settings), reflect.ValueOf(*d.applicationBase), "")
|
||||
data, err := toml.Marshal(fields)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return save_data(d.path, data)
|
||||
}
|
||||
|
||||
// Apply_application_settings copies the supported live settings only. Inherited
|
||||
// next-launch choices must never replace the running backend or layout.
|
||||
func Apply_application_settings(base Settings, source Settings) Settings {
|
||||
for _, definition := range Definitions() {
|
||||
if Application_setting(definition.Path) {
|
||||
if err := definition.Set(&base, definition.Value(source)); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
base.Presentation.Progress = source.Presentation.Progress
|
||||
return base
|
||||
}
|
||||
Reference in new issue
Block a user