diff --git a/Program.cs b/Program.cs index 112e2c2..69da252 100644 --- a/Program.cs +++ b/Program.cs @@ -143,6 +143,24 @@ internal static class Program return view; } }); + uiHost.RegisterView(new PolyTrader.Core.Modularity.ModuleView + { + Id = "core.settings", + Title = "Server Settings", + Group = "Core", + Order = 20, + PreferredWidth = 720, + PreferredHeight = 720, + CreateControl = () => + { + var view = new PolyTraderSharp.Ui.Views.SettingsView(); + view.Initialize( + viewServices.GetRequiredService(), + viewServices.GetRequiredService(), + viewServices.GetRequiredService()); + return view; + } + }); // TODO Phase 5-UI: registrierte IPolyTraderModule durchlaufen und module.RegisterUi(uiHost) aufrufen. var launcher = AppHost.Services.GetRequiredService(); diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs index f72d07d..386f5ab 100644 --- a/Properties/Resources.Designer.cs +++ b/Properties/Resources.Designer.cs @@ -1,11 +1,9 @@ -using MongoDB.Driver; -using PolyTraderSharp.Extensions; -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // Dieser Code wurde von einem Tool generiert. // Laufzeitversion:4.0.30319.42000 // -// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn // der Code erneut generiert wird. // //------------------------------------------------------------------------------ @@ -18,10 +16,10 @@ namespace PolyTraderSharp.Properties { /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. /// // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert - // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert. - // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen + // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert. + // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { @@ -35,7 +33,7 @@ namespace PolyTraderSharp.Properties { } /// - /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. + /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { @@ -49,7 +47,7 @@ namespace PolyTraderSharp.Properties { } /// - /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle + /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] @@ -172,6 +170,16 @@ namespace PolyTraderSharp.Properties { } } + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap setting_tools { + get { + object obj = ResourceManager.GetObject("setting_tools", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. /// diff --git a/Properties/Resources.resx b/Properties/Resources.resx index 042b0f6..c6f6195 100644 --- a/Properties/Resources.resx +++ b/Properties/Resources.resx @@ -118,6 +118,9 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + ..\Resources\stop.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Resources\diskette.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a @@ -163,7 +166,7 @@ ..\Resources\accept_button.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - ..\Resources\stop.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Resources\setting_tools.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a \ No newline at end of file diff --git a/Resources/setting_tools.png b/Resources/setting_tools.png new file mode 100644 index 0000000..2a1e727 Binary files /dev/null and b/Resources/setting_tools.png differ diff --git a/Ui/LauncherForm.Designer.cs b/Ui/LauncherForm.Designer.cs index a1e8c99..ef6838c 100644 --- a/Ui/LauncherForm.Designer.cs +++ b/Ui/LauncherForm.Designer.cs @@ -17,102 +17,140 @@ namespace PolyTraderSharp.Ui private void InitializeComponent() { - this.menuStrip = new System.Windows.Forms.MenuStrip(); - this.miDatei = new System.Windows.Forms.ToolStripMenuItem(); - this.miLegacy = new System.Windows.Forms.ToolStripMenuItem(); - this.miBeenden = new System.Windows.Forms.ToolStripMenuItem(); - this.lstViews = new System.Windows.Forms.ListView(); - this.colName = new System.Windows.Forms.ColumnHeader(); - this.pnlBottom = new System.Windows.Forms.Panel(); - this.btnOpen = new System.Windows.Forms.Button(); - this.menuStrip.SuspendLayout(); - this.pnlBottom.SuspendLayout(); - this.SuspendLayout(); + menuStrip = new MenuStrip(); + miDatei = new ToolStripMenuItem(); + miLegacy = new ToolStripMenuItem(); + miBeenden = new ToolStripMenuItem(); + toolstrip_windows = new ToolStrip(); + btn_settings = new ToolStripButton(); + btn_terminal = new ToolStripButton(); + toolstrip_quickbar = new ToolStrip(); + statusStrip_info = new StatusStrip(); + lbl_trading = new ToolStripStatusLabel(); + lbl_modules = new ToolStripStatusLabel(); + lbl_ratelimit = new ToolStripStatusLabel(); + menuStrip.SuspendLayout(); + toolstrip_windows.SuspendLayout(); + statusStrip_info.SuspendLayout(); + SuspendLayout(); // // menuStrip // - this.menuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.miDatei}); - this.menuStrip.Location = new System.Drawing.Point(0, 0); - this.menuStrip.Name = "menuStrip"; - this.menuStrip.Size = new System.Drawing.Size(360, 24); - this.menuStrip.TabIndex = 0; + menuStrip.ImageScalingSize = new Size(24, 24); + menuStrip.Items.AddRange(new ToolStripItem[] { miDatei }); + menuStrip.Location = new Point(0, 0); + menuStrip.Name = "menuStrip"; + menuStrip.Padding = new Padding(9, 3, 0, 3); + menuStrip.Size = new Size(2599, 35); + menuStrip.TabIndex = 0; // // miDatei // - this.miDatei.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.miLegacy, - this.miBeenden}); - this.miDatei.Name = "miDatei"; - this.miDatei.Text = "Datei"; + miDatei.DropDownItems.AddRange(new ToolStripItem[] { miLegacy, miBeenden }); + miDatei.Name = "miDatei"; + miDatei.Size = new Size(69, 29); + miDatei.Text = "Datei"; // // miLegacy // - this.miLegacy.Name = "miLegacy"; - this.miLegacy.Text = "Legacy-UI (alte Tabs)"; + miLegacy.Name = "miLegacy"; + miLegacy.Size = new Size(275, 34); + miLegacy.Text = "Legacy-UI (alte Tabs)"; // // miBeenden // - this.miBeenden.Name = "miBeenden"; - this.miBeenden.Text = "Beenden"; + miBeenden.Name = "miBeenden"; + miBeenden.Size = new Size(275, 34); + miBeenden.Text = "Beenden"; // - // lstViews + // toolstrip_windows // - this.lstViews.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { - this.colName}); - this.lstViews.Dock = System.Windows.Forms.DockStyle.Fill; - this.lstViews.FullRowSelect = true; - this.lstViews.HideSelection = false; - this.lstViews.Location = new System.Drawing.Point(0, 24); - this.lstViews.MultiSelect = false; - this.lstViews.Name = "lstViews"; - this.lstViews.Size = new System.Drawing.Size(360, 420); - this.lstViews.TabIndex = 1; - this.lstViews.UseCompatibleStateImageBehavior = false; - this.lstViews.View = System.Windows.Forms.View.Details; + toolstrip_windows.AutoSize = false; + toolstrip_windows.ImageScalingSize = new Size(64, 64); + toolstrip_windows.Items.AddRange(new ToolStripItem[] { btn_settings, btn_terminal }); + toolstrip_windows.Location = new Point(0, 35); + toolstrip_windows.Name = "toolstrip_windows"; + toolstrip_windows.Size = new Size(2599, 70); + toolstrip_windows.TabIndex = 3; + toolstrip_windows.Text = "toolStrip1"; // - // colName + // btn_settings // - this.colName.Text = "Ansicht"; - this.colName.Width = 330; + btn_settings.BackColor = SystemColors.Control; + btn_settings.Image = Properties.Resources.setting_tools; + btn_settings.ImageScaling = ToolStripItemImageScaling.None; + btn_settings.ImageTransparentColor = Color.Magenta; + btn_settings.Name = "btn_settings"; + btn_settings.Size = new Size(112, 65); + btn_settings.Text = "Settings"; + btn_settings.TextImageRelation = TextImageRelation.ImageAboveText; // - // pnlBottom + // btn_terminal // - this.pnlBottom.Controls.Add(this.btnOpen); - this.pnlBottom.Dock = System.Windows.Forms.DockStyle.Bottom; - this.pnlBottom.Location = new System.Drawing.Point(0, 444); - this.pnlBottom.Name = "pnlBottom"; - this.pnlBottom.Size = new System.Drawing.Size(360, 44); - this.pnlBottom.TabIndex = 2; + btn_terminal.DisplayStyle = ToolStripItemDisplayStyle.Text; + btn_terminal.Name = "btn_terminal"; + btn_terminal.Size = new Size(112, 65); + btn_terminal.Text = "Terminal"; // - // btnOpen + // toolstrip_quickbar // - this.btnOpen.Location = new System.Drawing.Point(12, 8); - this.btnOpen.Name = "btnOpen"; - this.btnOpen.Size = new System.Drawing.Size(336, 30); - this.btnOpen.TabIndex = 0; - this.btnOpen.Text = "Öffnen"; - this.btnOpen.UseVisualStyleBackColor = true; + toolstrip_quickbar.ImageScalingSize = new Size(24, 24); + toolstrip_quickbar.Location = new Point(0, 105); + toolstrip_quickbar.Name = "toolstrip_quickbar"; + toolstrip_quickbar.Size = new Size(2599, 25); + toolstrip_quickbar.TabIndex = 4; + toolstrip_quickbar.Text = "toolStrip2"; + // + // statusStrip_info + // + statusStrip_info.ImageScalingSize = new Size(24, 24); + statusStrip_info.Items.AddRange(new ToolStripItem[] { lbl_trading, lbl_modules, lbl_ratelimit }); + statusStrip_info.Location = new Point(0, 924); + statusStrip_info.Name = "statusStrip_info"; + statusStrip_info.Size = new Size(2599, 22); + statusStrip_info.TabIndex = 5; + statusStrip_info.Text = "statusStrip1"; + // + // lbl_trading + // + lbl_trading.Name = "lbl_trading"; + lbl_trading.Size = new Size(100, 16); + lbl_trading.Text = "Trading: —"; + // + // lbl_modules + // + lbl_modules.Name = "lbl_modules"; + lbl_modules.Size = new Size(90, 16); + lbl_modules.Text = "Module: —"; + // + // lbl_ratelimit + // + lbl_ratelimit.Name = "lbl_ratelimit"; + lbl_ratelimit.Size = new Size(80, 16); + lbl_ratelimit.Text = "API: —"; // // LauncherForm // - this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(360, 488); - this.Controls.Add(this.lstViews); - this.Controls.Add(this.pnlBottom); - this.Controls.Add(this.menuStrip); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; - this.MainMenuStrip = this.menuStrip; - this.MaximizeBox = false; - this.Name = "LauncherForm"; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "PolyTrader"; - this.menuStrip.ResumeLayout(false); - this.menuStrip.PerformLayout(); - this.pnlBottom.ResumeLayout(false); - this.ResumeLayout(false); - this.PerformLayout(); + AutoScaleDimensions = new SizeF(10F, 25F); + AutoScaleMode = AutoScaleMode.Font; + ClientSize = new Size(2599, 946); + Controls.Add(statusStrip_info); + Controls.Add(toolstrip_quickbar); + Controls.Add(toolstrip_windows); + Controls.Add(menuStrip); + MainMenuStrip = menuStrip; + Margin = new Padding(4, 5, 4, 5); + Name = "LauncherForm"; + StartPosition = FormStartPosition.CenterScreen; + Text = "PolyTrader"; + menuStrip.ResumeLayout(false); + menuStrip.PerformLayout(); + toolstrip_windows.ResumeLayout(false); + toolstrip_windows.PerformLayout(); + statusStrip_info.ResumeLayout(false); + statusStrip_info.PerformLayout(); + ResumeLayout(false); + PerformLayout(); } #endregion @@ -121,9 +159,13 @@ namespace PolyTraderSharp.Ui private System.Windows.Forms.ToolStripMenuItem miDatei; private System.Windows.Forms.ToolStripMenuItem miLegacy; private System.Windows.Forms.ToolStripMenuItem miBeenden; - private System.Windows.Forms.ListView lstViews; - private System.Windows.Forms.ColumnHeader colName; - private System.Windows.Forms.Panel pnlBottom; - private System.Windows.Forms.Button btnOpen; + private ToolStrip toolstrip_windows; + private ToolStripButton btn_settings; + private ToolStripButton btn_terminal; + private ToolStrip toolstrip_quickbar; + private StatusStrip statusStrip_info; + private ToolStripStatusLabel lbl_trading; + private ToolStripStatusLabel lbl_modules; + private ToolStripStatusLabel lbl_ratelimit; } } diff --git a/Ui/LauncherForm.cs b/Ui/LauncherForm.cs index 32320d4..80b4ec5 100644 --- a/Ui/LauncherForm.cs +++ b/Ui/LauncherForm.cs @@ -7,58 +7,60 @@ using PolyTrader.Core.Modularity; namespace PolyTraderSharp.Ui { /// - /// Schlanke „Startleiste" der PolyTrader.App. Das Layout (ListView, Menü, Button) liegt - /// im Designer (LauncherForm.Designer.cs). Zur Laufzeit werden ausschließlich die - /// Listeneinträge (Daten) aus den registrierten Views befüllt — keine Steuerelemente. - /// Kennt selbst kein Modul. + /// „Startleiste" der PolyTrader.App. Fenster werden über die im Designer platzierten + /// Buttons in toolstrip_windows geöffnet bzw. in den Vordergrund geholt. + /// toolstrip_quickbar ist für Schnellaktionen (z.B. Trading an/aus) reserviert, + /// statusStrip_info zeigt Kernkennzahlen. Der Launcher kennt selbst kein Modul — + /// Buttons werden per stabiler View-ID an registrierte Views gebunden. /// public partial class LauncherForm : Form { private readonly ShellUiHost _uiHost; private readonly IServiceProvider _services; + private readonly TradingState _state; + private readonly System.Windows.Forms.Timer _statusTimer = new() { Interval = 1000 }; private frm_main? _legacyForm; public LauncherForm(ShellUiHost uiHost, IServiceProvider services) { _uiHost = uiHost; _services = services; + _state = services.GetRequiredService(); InitializeComponent(); - PopulateViews(); - lstViews.DoubleClick += (_, _) => OpenSelected(); - btnOpen.Click += (_, _) => OpenSelected(); + btn_settings.Click += (_, _) => OpenView("core.settings"); + btn_terminal.Click += (_, _) => OpenView("core.terminal"); miLegacy.Click += (_, _) => OpenLegacy(); miBeenden.Click += (_, _) => Close(); + + _statusTimer.Tick += (_, _) => UpdateStatus(); + _statusTimer.Start(); + UpdateStatus(); } - private void PopulateViews() + /// Öffnet die Ansicht mit der angegebenen ID (bzw. fokussiert das offene Fenster). + private void OpenView(string viewId) { - lstViews.BeginUpdate(); - lstViews.Groups.Clear(); - lstViews.Items.Clear(); - - foreach (var group in _uiHost.Views - .GroupBy(v => string.IsNullOrEmpty(v.Group) ? "Allgemein" : v.Group!) - .OrderBy(g => g.Key)) - { - var lvGroup = new ListViewGroup(group.Key) { Header = group.Key }; - lstViews.Groups.Add(lvGroup); - - foreach (var view in group.OrderBy(v => v.Order).ThenBy(v => v.Title)) - { - lstViews.Items.Add(new ListViewItem(view.Title, lvGroup) { Tag = view }); - } - } - - lstViews.EndUpdate(); - } - - private void OpenSelected() - { - if (lstViews.SelectedItems.Count == 0) return; - if (lstViews.SelectedItems[0].Tag is ModuleView view) + var view = _uiHost.Views.FirstOrDefault(v => v.Id == viewId); + if (view != null) _uiHost.OpenView(view); + else + MessageBox.Show($"Ansicht '{viewId}' ist (noch) nicht registriert.", "Hinweis", + MessageBoxButtons.OK, MessageBoxIcon.Information); + } + + private void UpdateStatus() + { + string trading = _state.GlobalTradingPaused + ? "Pausiert" + : $"Live={_state.LiveTradingMode} / Demo={_state.DemoTradingMode}"; + lbl_trading.Text = $"Trading: {trading}"; + + int moduleCount = _services.GetServices().Count(); + lbl_modules.Text = $"Module: {moduleCount}"; + + lbl_ratelimit.Text = $"API: {(_state.IsAlchemyHealthy ? "WSS aktiv" : "Polling")}"; } private void OpenLegacy() diff --git a/Ui/LauncherForm.resx b/Ui/LauncherForm.resx index 1af7de1..585575f 100644 --- a/Ui/LauncherForm.resx +++ b/Ui/LauncherForm.resx @@ -1,17 +1,17 @@  - @@ -117,4 +117,19 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 585, 0 + + + 733, 0 + + + 376, 0 + + + 944, 0 + + + 25 + \ No newline at end of file diff --git a/Ui/Views/SettingsView.Designer.cs b/Ui/Views/SettingsView.Designer.cs new file mode 100644 index 0000000..4031f18 --- /dev/null +++ b/Ui/Views/SettingsView.Designer.cs @@ -0,0 +1,82 @@ +namespace PolyTraderSharp.Ui.Views +{ + partial class SettingsView + { + private System.ComponentModel.IContainer components = null; + + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Vom Komponenten-Designer generierter Code + + private void InitializeComponent() + { + this.pnlTop = new System.Windows.Forms.Panel(); + this.btnReload = new System.Windows.Forms.Button(); + this.btnSave = new System.Windows.Forms.Button(); + this.propertyGrid = new System.Windows.Forms.PropertyGrid(); + this.pnlTop.SuspendLayout(); + this.SuspendLayout(); + // + // pnlTop + // + this.pnlTop.Controls.Add(this.btnReload); + this.pnlTop.Controls.Add(this.btnSave); + this.pnlTop.Dock = System.Windows.Forms.DockStyle.Top; + this.pnlTop.Location = new System.Drawing.Point(0, 0); + this.pnlTop.Name = "pnlTop"; + this.pnlTop.Size = new System.Drawing.Size(700, 44); + this.pnlTop.TabIndex = 0; + // + // btnReload + // + this.btnReload.Location = new System.Drawing.Point(140, 8); + this.btnReload.Name = "btnReload"; + this.btnReload.Size = new System.Drawing.Size(120, 28); + this.btnReload.TabIndex = 1; + this.btnReload.Text = "Neu laden"; + this.btnReload.UseVisualStyleBackColor = true; + // + // btnSave + // + this.btnSave.Location = new System.Drawing.Point(12, 8); + this.btnSave.Name = "btnSave"; + this.btnSave.Size = new System.Drawing.Size(120, 28); + this.btnSave.TabIndex = 0; + this.btnSave.Text = "Speichern"; + this.btnSave.UseVisualStyleBackColor = true; + // + // propertyGrid + // + this.propertyGrid.Dock = System.Windows.Forms.DockStyle.Fill; + this.propertyGrid.Location = new System.Drawing.Point(0, 44); + this.propertyGrid.Name = "propertyGrid"; + this.propertyGrid.Size = new System.Drawing.Size(700, 556); + this.propertyGrid.TabIndex = 1; + // + // SettingsView + // + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.propertyGrid); + this.Controls.Add(this.pnlTop); + this.Name = "SettingsView"; + this.Size = new System.Drawing.Size(700, 600); + this.pnlTop.ResumeLayout(false); + this.ResumeLayout(false); + } + + #endregion + + private System.Windows.Forms.Panel pnlTop; + private System.Windows.Forms.Button btnSave; + private System.Windows.Forms.Button btnReload; + private System.Windows.Forms.PropertyGrid propertyGrid; + } +} diff --git a/Ui/Views/SettingsView.cs b/Ui/Views/SettingsView.cs new file mode 100644 index 0000000..0292e7b --- /dev/null +++ b/Ui/Views/SettingsView.cs @@ -0,0 +1,61 @@ +using System; +using System.Windows.Forms; +using PolyTraderSharp.Models; +using PolyTraderSharp.Services; + +namespace PolyTraderSharp.Ui.Views +{ + /// + /// Core-Settings-Ansicht: bearbeitet die ServerSettings über ein PropertyGrid. + /// Designbar (siehe SettingsView.Designer.cs); Laufzeit-Abhängigkeiten via Initialize. + /// Verhalten entspricht dem bisherigen Settings-Tab (Speichern -> XML + Services neu laden). + /// + public partial class SettingsView : UserControl + { + private const string SettingsPath = "server_settings.xml"; + + private ServerSettings _settings = new(); + private ThreemaService? _threema; + private MullvadVpnService? _vpn; + private TerminalLogger? _logger; + + public SettingsView() + { + InitializeComponent(); + btnSave.Click += (_, _) => Save(); + btnReload.Click += (_, _) => Reload(); + } + + public void Initialize(ThreemaService threema, MullvadVpnService vpn, TerminalLogger logger) + { + _threema = threema; + _vpn = vpn; + _logger = logger; + Reload(); + } + + private void Reload() + { + _settings = ServerSettings.Load(SettingsPath); + propertyGrid.SelectedObject = _settings; + } + + private void Save() + { + try + { + _settings.Save(SettingsPath); + _threema?.ReloadSettings(); + _vpn?.ReloadSettings(); + _logger?.Info("Server-Einstellungen gespeichert und Services neu geladen."); + MessageBox.Show("Server-Einstellungen gespeichert.", "Erfolg", + MessageBoxButtons.OK, MessageBoxIcon.Information); + } + catch (Exception ex) + { + MessageBox.Show($"Fehler beim Speichern: {ex.Message}", "Fehler", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + } +}