diff --git a/StationForm.Designer.cs b/StationForm.Designer.cs index 1bee475..561edae 100644 --- a/StationForm.Designer.cs +++ b/StationForm.Designer.cs @@ -1,4 +1,4 @@ -namespace PainoBar_Helper +namespace PainoBar_Helper { partial class StationForm { @@ -29,74 +29,41 @@ private void InitializeComponent() { station_list_group_box = new GroupBox(); - lstStations = new ListBox(); - textBox1 = new TextBox(); - search_label = new Label(); - refresh_button = new Button(); - btnSelectStation = new Button(); + ok_button = new Button(); btnCancelStation = new Button(); + terminal_box = new RichTextBox(); + input_label = new Label(); + command_input_box = new TextBox(); + submit_button = new Button(); station_list_group_box.SuspendLayout(); SuspendLayout(); // // station_list_group_box // - station_list_group_box.Controls.Add(lstStations); - station_list_group_box.Controls.Add(textBox1); - station_list_group_box.Controls.Add(search_label); + station_list_group_box.Controls.Add(submit_button); + station_list_group_box.Controls.Add(command_input_box); + station_list_group_box.Controls.Add(input_label); + station_list_group_box.Controls.Add(terminal_box); station_list_group_box.Location = new Point(12, 22); station_list_group_box.Name = "station_list_group_box"; - station_list_group_box.Size = new Size(391, 263); + station_list_group_box.Size = new Size(584, 333); station_list_group_box.TabIndex = 0; station_list_group_box.TabStop = false; - station_list_group_box.Text = "Available Stations"; + station_list_group_box.Text = "Terminal window"; // - // lstStations + // ok_button // - lstStations.FormattingEnabled = true; - lstStations.Location = new Point(26, 65); - lstStations.Name = "lstStations"; - lstStations.Size = new Size(332, 174); - lstStations.TabIndex = 2; - // - // textBox1 - // - textBox1.Location = new Point(85, 32); - textBox1.Name = "textBox1"; - textBox1.Size = new Size(273, 24); - textBox1.TabIndex = 1; - textBox1.TextChanged += textBox1_TextChanged; - // - // search_label - // - search_label.AutoSize = true; - search_label.Location = new Point(26, 32); - search_label.Name = "search_label"; - search_label.Size = new Size(50, 17); - search_label.TabIndex = 0; - search_label.Text = "Search:"; - // - // refresh_button - // - refresh_button.Location = new Point(13, 307); - refresh_button.Name = "refresh_button"; - refresh_button.Size = new Size(117, 23); - refresh_button.TabIndex = 1; - refresh_button.Text = "Refresh stations"; - refresh_button.UseVisualStyleBackColor = true; - // - // btnSelectStation - // - btnSelectStation.Location = new Point(205, 307); - btnSelectStation.Name = "btnSelectStation"; - btnSelectStation.Size = new Size(117, 23); - btnSelectStation.TabIndex = 2; - btnSelectStation.Text = "Select Station"; - btnSelectStation.UseVisualStyleBackColor = true; - btnSelectStation.Click += btnSelectStation_Click; + ok_button.Location = new Point(29, 389); + ok_button.Name = "ok_button"; + ok_button.Size = new Size(117, 23); + ok_button.TabIndex = 2; + ok_button.Text = "Ok"; + ok_button.UseVisualStyleBackColor = true; + ok_button.Click += btnSelectStation_Click; // // btnCancelStation // - btnCancelStation.Location = new Point(328, 307); + btnCancelStation.Location = new Point(521, 389); btnCancelStation.Name = "btnCancelStation"; btnCancelStation.Size = new Size(86, 23); btnCancelStation.TabIndex = 3; @@ -104,14 +71,50 @@ btnCancelStation.UseVisualStyleBackColor = true; btnCancelStation.Click += btnCancelStation_Click; // + // terminal_box + // + terminal_box.BackColor = SystemColors.MenuText; + terminal_box.ForeColor = SystemColors.Window; + terminal_box.Location = new Point(17, 35); + terminal_box.Name = "terminal_box"; + terminal_box.ReadOnly = true; + terminal_box.Size = new Size(551, 231); + terminal_box.TabIndex = 0; + terminal_box.Text = ""; + // + // input_label + // + input_label.AutoSize = true; + input_label.Location = new Point(28, 294); + input_label.Name = "input_label"; + input_label.Size = new Size(59, 17); + input_label.TabIndex = 1; + input_label.Text = "Command:"; + // + // command_input_box + // + command_input_box.Location = new Point(91, 290); + command_input_box.Name = "command_input_box"; + command_input_box.Size = new Size(244, 24); + command_input_box.TabIndex = 2; + // + // submit_button + // + submit_button.Location = new Point(449, 290); + submit_button.Name = "submit_button"; + submit_button.Size = new Size(75, 23); + submit_button.TabIndex = 3; + submit_button.Text = "Submit"; + submit_button.UseVisualStyleBackColor = true; + submit_button.Click += submit_button_Click; + // // StationForm // AutoScaleDimensions = new SizeF(7F, 17F); AutoScaleMode = AutoScaleMode.Font; - ClientSize = new Size(426, 351); + ClientSize = new Size(631, 434); Controls.Add(btnCancelStation); - Controls.Add(btnSelectStation); - Controls.Add(refresh_button); + Controls.Add(ok_button); Controls.Add(station_list_group_box); Name = "StationForm"; Text = "StationForm"; @@ -123,11 +126,12 @@ #endregion private GroupBox station_list_group_box; - private ListBox lstStations; - private TextBox textBox1; - private Label search_label; private Button refresh_button; - private Button btnSelectStation; + private Button ok_button; private Button btnCancelStation; + private RichTextBox terminal_box; + private Button submit_button; + private TextBox command_input_box; + private Label input_label; } -} \ No newline at end of file +} diff --git a/StationForm.cs b/StationForm.cs index 93a34dc..fcdc728 100644 --- a/StationForm.cs +++ b/StationForm.cs @@ -1,279 +1,168 @@ +using System.Text; +using System.Text; + namespace PainoBar_Helper { public partial class StationForm : Form { private readonly PianobarManager _pianobarManager; - private List _stations; - private List _filteredStations; - private bool _stationsLoaded = false; - private bool _stationEntriesStarted = false; - - private sealed class StationItem - { - public int Index { get; set; } - public string Name { get; set; } = string.Empty; - public override string ToString() => Name; - } + private readonly StringBuilder _outputBuffer = new StringBuilder(); public StationForm(PianobarManager pianobarManager) { InitializeComponent(); _pianobarManager = pianobarManager; - _stations = new List(); - _filteredStations = new List(); + + // Subscribe to output BEFORE controls are initialized + _pianobarManager.OutputReceived += PianobarManager_TerminalOutputReceived; + + // Handle form load to display buffered output + this.Load += StationForm_Load; InitializeControls(); } + private void StationForm_Load(object? sender, EventArgs e) + { + // Display any buffered output when form loads + if (terminal_box != null && _outputBuffer.Length > 0) + { + terminal_box.Text = _outputBuffer.ToString(); + terminal_box.SelectionStart = terminal_box.Text.Length; + terminal_box.ScrollToCaret(); + } + } + #region Initialization private void InitializeControls() { - // Uses textBox1 (search box) and lstStations from the Designer - // Uses btnSelectStation from the Designer - - if (lstStations != null) + // Wire up terminal output and command input + if (terminal_box != null) { - lstStations.DoubleClick += LstStations_DoubleClick; + terminal_box.ReadOnly = true; + terminal_box.Font = new System.Drawing.Font("Consolas", 9F); + terminal_box.WordWrap = false; } - if (refresh_button != null) + if (submit_button != null) { - refresh_button.Click += refresh_button_Click; + submit_button.Click += submit_button_Click; + } + + if (command_input_box != null) + { + command_input_box.KeyDown += command_input_box_KeyDown; } } - protected override void OnShown(EventArgs e) + #endregion + + #region Terminal Output + + private void PianobarManager_TerminalOutputReceived(object? sender, string e) { - base.OnShown(e); + if (string.IsNullOrEmpty(e)) + return; - if (!_stationsLoaded) - LoadStations(); - } + string lineWithNewline = e + "\n"; - private void LoadStations(bool forceRefresh = false) - { - _filteredStations.Clear(); - _stationsLoaded = false; - _stationEntriesStarted = false; + // Buffer output even before form is shown + _outputBuffer.Append(lineWithNewline); - if (forceRefresh) - _stations.Clear(); - - var knownStations = _pianobarManager.GetKnownStationEntries(); - if (knownStations.Count > 0) + // Thread-safe UI update + if (terminal_box != null) { - _stations = knownStations - .Select(s => new StationItem { Index = s.Index, Name = s.Name }) - .OrderBy(s => s.Index) - .ToList(); - - _stationEntriesStarted = true; - if (lstStations != null) - UpdateStationList(); - } - - _pianobarManager.OutputReceived -= PianobarManager_OutputReceived; - _pianobarManager.OutputReceived += PianobarManager_OutputReceived; - - // Only request station list on explicit refresh. - if (forceRefresh) - { - try + if (terminal_box.InvokeRequired) { - _pianobarManager.ShowStations(); - - if (lstStations != null && _stations.Count == 0) + terminal_box.Invoke(() => { - lstStations.Items.Clear(); - lstStations.Items.Add("Loading stations..."); - } + AppendTerminalText(lineWithNewline); + }); } - catch (Exception ex) + else { - MessageBox.Show($"Failed to load stations:\n{ex.Message}", "Error", - MessageBoxButtons.OK, MessageBoxIcon.Error); + AppendTerminalText(lineWithNewline); } } - else + } + + private void AppendTerminalText(string text) + { + if (terminal_box != null && !terminal_box.IsDisposed) { - _stationsLoaded = true; - if (_stations.Count == 0 && lstStations != null) - { - lstStations.Items.Clear(); - lstStations.Items.Add("No cached stations. Click Refresh stations."); - } + terminal_box.AppendText(text); + terminal_box.SelectionStart = terminal_box.Text.Length; + terminal_box.ScrollToCaret(); } } #endregion - #region Station Loading + #region Command Input - private void PianobarManager_OutputReceived(object? sender, string e) + public void submit_button_Click(object? sender, EventArgs e) { - var line = StripAnsiCodes(e).Trim(); - if (string.IsNullOrEmpty(line)) - return; + SubmitCommand(); + } - var match = System.Text.RegularExpressions.Regex.Match(line, @"^\s*(\d+)\)\s*(?:[qQ]\s+)?(.+?)\s*$"); - if (match.Success) + private void command_input_box_KeyDown(object? sender, KeyEventArgs e) + { + if (e.KeyCode == Keys.Enter) { - _stationEntriesStarted = true; - int stationIndex = int.Parse(match.Groups[1].Value); - string stationName = match.Groups[2].Value.Trim(); - - var existing = _stations.FirstOrDefault(s => s.Index == stationIndex); - if (existing == null) - { - _stations.Add(new StationItem { Index = stationIndex, Name = stationName }); - _stations = _stations.OrderBy(s => s.Index).ToList(); - if (!IsDisposed && IsHandleCreated) - BeginInvoke(() => UpdateStationList()); - } - else if (!string.Equals(existing.Name, stationName, StringComparison.Ordinal)) - { - existing.Name = stationName; - if (!IsDisposed && IsHandleCreated) - BeginInvoke(() => UpdateStationList()); - } - return; - } - - // Detect end of station list only after we started receiving entries - if (_stationEntriesStarted && - (line.Contains("Select station:", StringComparison.OrdinalIgnoreCase) || line == "[?]")) - { - _stationsLoaded = true; - _pianobarManager.OutputReceived -= PianobarManager_OutputReceived; - if (!IsDisposed && IsHandleCreated) - BeginInvoke(() => FinalizeStationList()); + e.SuppressKeyPress = true; + SubmitCommand(); } } - private static string StripAnsiCodes(string value) + private void SubmitCommand() { - return System.Text.RegularExpressions.Regex.Replace(value, @"\x1B\[[0-9;]*[A-Za-z]", string.Empty); - } - - private void UpdateStationList() - { - if (lstStations == null) + if (command_input_box == null || string.IsNullOrWhiteSpace(command_input_box.Text)) return; - lstStations.Items.Clear(); - - foreach (var station in _filteredStations.Any() ? _filteredStations : _stations) - { - lstStations.Items.Add(station); - } - } - - private void FinalizeStationList() - { - if (lstStations != null) - { - if (_stations.Count == 0) - { - lstStations.Items.Clear(); - lstStations.Items.Add("No stations available"); - } - } - } - - #endregion - - #region Search - - // Wire this to textBox1.TextChanged in the Designer - public void textBox1_TextChanged(object? sender, EventArgs e) - { - if (sender is not TextBox txtSearch) - return; - - string searchText = txtSearch.Text.Trim(); - - if (string.IsNullOrWhiteSpace(searchText)) - { - _filteredStations.Clear(); - } - else - { - _filteredStations = _stations - .Where(s => s.Name.IndexOf(searchText, StringComparison.OrdinalIgnoreCase) >= 0) - .ToList(); - } - - UpdateStationList(); - } - - #endregion - - #region Station Selection - - // Wire this to btnSelectStation.Click in the Designer - public async void btnSelectStation_Click(object? sender, EventArgs e) - { - await SelectCurrentStationAsync(); - } - - private async void LstStations_DoubleClick(object? sender, EventArgs e) - { - await SelectCurrentStationAsync(); - } - - private async Task SelectCurrentStationAsync() - { - if (lstStations == null) - return; - - if (lstStations.SelectedIndex < 0) - { - MessageBox.Show("Please select a station.", "No Selection", - MessageBoxButtons.OK, MessageBoxIcon.Warning); - return; - } - - if (lstStations.SelectedItem is not StationItem selectedStation) - return; + string command = command_input_box.Text.Trim(); try { - await _pianobarManager.SelectStationAsync(selectedStation.Index); - DialogResult = DialogResult.OK; - Close(); + // Echo the command to the terminal (simulating user input display) + AppendTerminalText($"> {command}\n"); + + // Send command to Pianobar with newline and flush + _pianobarManager.SendCommand(command); + + command_input_box.Clear(); } catch (Exception ex) { - MessageBox.Show($"Failed to select station:\n{ex.Message}", "Error", + MessageBox.Show($"Failed to send command:\n{ex.Message}", "Command Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } - public void refresh_button_Click(object? sender, EventArgs e) - { - LoadStations(forceRefresh: true); - } - #endregion - #region Cancel + #region Close - // Wire this to btnCancelStation.Click in the Designer (or Cancel button) public void btnCancelStation_Click(object? sender, EventArgs e) { DialogResult = DialogResult.Cancel; Close(); } + public void btnSelectStation_Click(object? sender, EventArgs e) + { + DialogResult = DialogResult.OK; + Close(); + } + #endregion #region Cleanup protected override void OnFormClosing(FormClosingEventArgs e) { - _pianobarManager.OutputReceived -= PianobarManager_OutputReceived; + _pianobarManager.OutputReceived -= PianobarManager_TerminalOutputReceived; base.OnFormClosing(e); }