283 lines
8.7 KiB
C#
283 lines
8.7 KiB
C#
namespace PainoBar_Helper
|
|
{
|
|
public partial class StationForm : Form
|
|
{
|
|
private readonly PianobarManager _pianobarManager;
|
|
private List<StationItem> _stations;
|
|
private List<StationItem> _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;
|
|
}
|
|
|
|
public StationForm(PianobarManager pianobarManager)
|
|
{
|
|
InitializeComponent();
|
|
|
|
_pianobarManager = pianobarManager;
|
|
_stations = new List<StationItem>();
|
|
_filteredStations = new List<StationItem>();
|
|
|
|
InitializeControls();
|
|
}
|
|
|
|
#region Initialization
|
|
|
|
private void InitializeControls()
|
|
{
|
|
// Uses textBox1 (search box) and lstStations from the Designer
|
|
// Uses btnSelectStation from the Designer
|
|
|
|
if (lstStations != null)
|
|
{
|
|
lstStations.DoubleClick += LstStations_DoubleClick;
|
|
}
|
|
|
|
if (refresh_button != null)
|
|
{
|
|
refresh_button.Click += refresh_button_Click;
|
|
}
|
|
}
|
|
|
|
protected override void OnShown(EventArgs e)
|
|
{
|
|
base.OnShown(e);
|
|
|
|
if (!_stationsLoaded)
|
|
LoadStations();
|
|
}
|
|
|
|
private void LoadStations(bool forceRefresh = false)
|
|
{
|
|
_filteredStations.Clear();
|
|
_stationsLoaded = false;
|
|
_stationEntriesStarted = false;
|
|
|
|
if (forceRefresh)
|
|
_stations.Clear();
|
|
|
|
var knownStations = _pianobarManager.GetKnownStationEntries();
|
|
if (knownStations.Count > 0)
|
|
{
|
|
_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
|
|
{
|
|
_pianobarManager.ShowStations();
|
|
|
|
if (lstStations != null && _stations.Count == 0)
|
|
{
|
|
lstStations.Items.Clear();
|
|
lstStations.Items.Add("Loading stations...");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show($"Failed to load stations:\n{ex.Message}", "Error",
|
|
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_stationsLoaded = true;
|
|
if (_stations.Count == 0 && lstStations != null)
|
|
{
|
|
lstStations.Items.Clear();
|
|
lstStations.Items.Add("No cached stations. Click Refresh stations.");
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Station Loading
|
|
|
|
private void PianobarManager_OutputReceived(object? sender, string e)
|
|
{
|
|
var line = StripAnsiCodes(e).Trim();
|
|
if (string.IsNullOrEmpty(line))
|
|
return;
|
|
|
|
var match = System.Text.RegularExpressions.Regex.Match(line, @"^\s*(\d+)\)\s*(?:[qQ]\s+)?(.+?)\s*$");
|
|
if (match.Success)
|
|
{
|
|
_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());
|
|
}
|
|
}
|
|
|
|
private static string StripAnsiCodes(string value)
|
|
{
|
|
return System.Text.RegularExpressions.Regex.Replace(value, @"\x1B\[[0-9;]*[A-Za-z]", string.Empty);
|
|
}
|
|
|
|
private void UpdateStationList()
|
|
{
|
|
if (lstStations == null)
|
|
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;
|
|
|
|
try
|
|
{
|
|
await _pianobarManager.SelectStationAsync(selectedStation.Index);
|
|
DialogResult = DialogResult.OK;
|
|
Close();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show($"Failed to select station:\n{ex.Message}", "Error",
|
|
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
}
|
|
|
|
public void refresh_button_Click(object? sender, EventArgs e)
|
|
{
|
|
LoadStations(forceRefresh: true);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Cancel
|
|
|
|
// Wire this to btnCancelStation.Click in the Designer (or Cancel button)
|
|
public void btnCancelStation_Click(object? sender, EventArgs e)
|
|
{
|
|
DialogResult = DialogResult.Cancel;
|
|
Close();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Cleanup
|
|
|
|
protected override void OnFormClosing(FormClosingEventArgs e)
|
|
{
|
|
_pianobarManager.OutputReceived -= PianobarManager_OutputReceived;
|
|
base.OnFormClosing(e);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|