using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using CarManagerV3.Util; namespace CarManagerV3.Forms { public partial class SettingsForm : Form { // Settings map (Maps settings to controls and default values + optional change event handler function that takes previous and new value) private readonly Dictionary settingsMap = new(); public SettingsForm() { InitializeComponent(); initializeSettingsMap(); } private void initializeSettingsMap() { // Initialize the settings map with setting keys, associated controls, default values, and optional change event handlers settingsMap["DataLocation"] = new SettingBinding( control: tbxDataLocation, defaultValue: Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\CarManagerV3", read: () => Properties.Settings.Default.DataLocation, // strongly typed write: v => Properties.Settings.Default.DataLocation = v, // strongly typed onChange: (before, after) => { // TODO: handle path change if needed }); settingsMap["AllowPrerelease"] = new SettingBinding( control: cbxPreRelease, defaultValue: false, read: () => Properties.Settings.Default.AllowPrerelease, write: v => Properties.Settings.Default.AllowPrerelease = v); //TODO: implement refresh settings lblEnv.Text = lblEnv.Text.Replace("%E%", InstallModeDetector.IsInstalledViaMsi() ? "Installed via MSI" : "Running in portable mode"); } private void SettingsForm_Load(object sender, EventArgs e) { loadSettings(); } private void loadSettings() { foreach (var kvp in settingsMap) kvp.Value.Load(); } private void saveSettings() { bool requiresRestart = false; foreach (var kvp in settingsMap) { kvp.Value.Save(); if (kvp.Value.RequiresRestart()) requiresRestart = true; } Properties.Settings.Default.Save(); if(requiresRestart) { DialogResult result = MessageBox.Show( "Some changes you made require a restart to take effect. Do you want to restart now?", "Restart Required", MessageBoxButtons.YesNo, MessageBoxIcon.Information); if (result == DialogResult.Yes) { Application.Restart(); } else { MessageBox.Show("Please restart the application as soon as possible to ensure all changes take effect.", "Restart Recommended", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } } private void resetSettings() { DialogResult confirmReset = MessageBox.Show( "Are you sure you want to reset all settings to their default values? This action cannot be undone.", "Confirm Reset", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); if (confirmReset != DialogResult.Yes) return; foreach (var kvp in settingsMap) kvp.Value.Reset(); Properties.Settings.Default.Save(); loadSettings(); } private void btnAccept_Click(object sender, EventArgs e) { saveSettings(); this.Close(); } private void btnDiscard_Click(object sender, EventArgs e) { this.Close(); } private void btnReset_Click(object sender, EventArgs e) { resetSettings(); } } internal interface ISettingBinding { void Load(); void Save(); void Reset(); bool RequiresRestart(); } internal sealed class SettingBinding : ISettingBinding { private readonly Control control; private readonly T defaultValue; private readonly Func read; private readonly Action write; private readonly Action? onChange; private readonly bool requiresRestart; private bool changeRequiresRestart; public SettingBinding(Control control, T defaultValue, Func read, Action write, bool requiresRestart = false, Action? onChange = null) { this.control = control; this.defaultValue = defaultValue; this.read = read; this.write = write; this.onChange = onChange; this.requiresRestart = requiresRestart; this.changeRequiresRestart = false; } public void Load() { T value = read(); ApplyToControl(value); } public void Save() { T before = read(); T after = ReadFromControl(); write(after); if (onChange != null && !EqualityComparer.Default.Equals(before, after)) onChange(before, after); if (requiresRestart && !EqualityComparer.Default.Equals(before, after)) changeRequiresRestart = true; } public void Reset() => write(defaultValue); public bool RequiresRestart() => changeRequiresRestart; private void ApplyToControl(T value) { switch (control) { case TextBox tb: tb.Text = value?.ToString() ?? string.Empty; break; case CheckBox cb: cb.Checked = value is bool b ? b : Convert.ToBoolean(value); break; default: throw new NotSupportedException($"Control type '{control.GetType().Name}' not supported for {typeof(T).Name}."); } } private T ReadFromControl() { object result = control switch { TextBox tb when typeof(T) == typeof(string) => tb.Text, CheckBox cb when typeof(T) == typeof(bool) => cb.Checked, _ => throw new NotSupportedException($"Cannot read {typeof(T).Name} from control type '{control.GetType().Name}'.") }; return (T)result; } } }