526 lines
18 KiB
C#
526 lines
18 KiB
C#
namespace PainoBar_Helper
|
|
{
|
|
public partial class Main : Form
|
|
{
|
|
// Static event for Pianobar output - accessible to all forms
|
|
public static event Action<string>? OnPianobarOutputReceived;
|
|
|
|
// Static terminal history buffer for StationForm replay
|
|
public static List<string> TerminalHistory = new List<string>();
|
|
|
|
// Static reference to the main instance for accessing PianobarManager
|
|
private static Main? _instance;
|
|
|
|
private readonly ProfileManager _profileManager;
|
|
private readonly PianobarManager _pianobarManager;
|
|
private OutputManager? _outputManager;
|
|
private System.Windows.Forms.Timer? _statusTimer;
|
|
private bool _statusToggle = false;
|
|
|
|
// Connection status enum
|
|
private enum ConnectionStatus
|
|
{
|
|
Disconnected, // Red
|
|
ConnectedIdle, // Green
|
|
ConnectedActive // Blue
|
|
}
|
|
|
|
private ConnectionStatus _currentStatus = ConnectionStatus.Disconnected;
|
|
|
|
public Main()
|
|
{
|
|
InitializeComponent();
|
|
|
|
_instance = this;
|
|
|
|
_profileManager = new ProfileManager();
|
|
_pianobarManager = new PianobarManager();
|
|
|
|
InitializeMenus();
|
|
InitializeStatusBar();
|
|
InitializeEvents();
|
|
UpdateProfilesMenu();
|
|
UpdateConnectionStatus(ConnectionStatus.Disconnected);
|
|
|
|
// Set initial status
|
|
if (statusLabelRight != null)
|
|
{
|
|
var activeProfile = _profileManager.GetActiveProfile();
|
|
if (activeProfile != null)
|
|
{
|
|
statusLabelRight.Text = $"Ready - Profile: {activeProfile.Name}";
|
|
}
|
|
else if (_profileManager.Profiles.Any())
|
|
{
|
|
// If no active profile but profiles exist, set the first one as active
|
|
_profileManager.SetActiveProfile(_profileManager.Profiles.First().Name);
|
|
statusLabelRight.Text = $"Ready - Profile: {_profileManager.ActiveProfileName}";
|
|
}
|
|
else
|
|
{
|
|
statusLabelRight.Text = "Ready - No profile configured";
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sends a command to Pianobar's standard input stream.
|
|
/// </summary>
|
|
/// <param name="text">The command text to send (newline will be added automatically)</param>
|
|
public static void SendStdin(string text)
|
|
{
|
|
if (_instance?._pianobarManager != null)
|
|
{
|
|
_instance._pianobarManager.SendCommand(text);
|
|
}
|
|
}
|
|
|
|
#region Initialization
|
|
|
|
private void InitializeMenus()
|
|
{
|
|
// File menu is assumed to be created in the Designer
|
|
// This method sets up dynamic profile menu items
|
|
}
|
|
|
|
private void InitializeStatusBar()
|
|
{
|
|
// Status bar zones are assumed to be created in the Designer:
|
|
// - statusLabelLeft: Connection indicator
|
|
// - statusLabelCenter: Alternating status (Profile/Station)
|
|
// - statusLabelRight: Event feed
|
|
|
|
_statusTimer = new System.Windows.Forms.Timer();
|
|
_statusTimer.Interval = 10000; // 10 seconds
|
|
_statusTimer.Tick += StatusTimer_Tick;
|
|
_statusTimer.Start();
|
|
|
|
UpdateCenterStatus();
|
|
}
|
|
|
|
private void InitializeEvents()
|
|
{
|
|
// Pianobar events
|
|
_pianobarManager.Connected += PianobarManager_Connected;
|
|
_pianobarManager.Disconnected += PianobarManager_Disconnected;
|
|
_pianobarManager.SongChanged += PianobarManager_SongChanged;
|
|
_pianobarManager.StationChanged += PianobarManager_StationChanged;
|
|
_pianobarManager.EventOccurred += PianobarManager_EventOccurred;
|
|
_pianobarManager.OutputReceived += PianobarManager_OutputReceived;
|
|
|
|
// Profile events
|
|
_profileManager.ProfilesChanged += ProfileManager_ProfilesChanged;
|
|
_profileManager.ActiveProfileChanged += ProfileManager_ActiveProfileChanged;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Button Event Handlers
|
|
|
|
// This method should be wired to btnConnect.Click in the Designer
|
|
public void btnConnect_Click(object? sender, EventArgs e)
|
|
{
|
|
if (_pianobarManager.IsConnected)
|
|
{
|
|
MessageBox.Show("Already connected to Pianobar.", "Already Connected",
|
|
MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
return;
|
|
}
|
|
|
|
// Get pianobar.exe path from active profile or use default
|
|
var activeProfile = _profileManager.GetActiveProfile();
|
|
|
|
if (activeProfile == null)
|
|
{
|
|
var result = MessageBox.Show(
|
|
"No profile selected. Would you like to create one now?\n\n" +
|
|
"Click Yes to open Settings, or No to cancel.",
|
|
"No Profile",
|
|
MessageBoxButtons.YesNo,
|
|
MessageBoxIcon.Question);
|
|
|
|
if (result == DialogResult.Yes)
|
|
{
|
|
menuSettings_Click(sender, e);
|
|
}
|
|
return;
|
|
}
|
|
|
|
string pianobarPath = activeProfile.Settings.PianobarPath;
|
|
string? configPath = _profileManager.GetActiveConfigPath();
|
|
string? eventFilePath = _profileManager.GetEventOutputPath();
|
|
|
|
// Debug information
|
|
string debugInfo = $"Profile: {activeProfile.Name}\n" +
|
|
$"Pianobar Path: {pianobarPath}\n" +
|
|
$"Config Path: {configPath ?? "(none)"}\n" +
|
|
$"Event File: {eventFilePath ?? "(none)"}\n" +
|
|
$"File Exists: {File.Exists(pianobarPath)}";
|
|
|
|
try
|
|
{
|
|
// Update status to show we're attempting connection
|
|
if (statusLabelRight != null)
|
|
statusLabelRight.Text = $"Starting Pianobar...";
|
|
|
|
_pianobarManager.Connect(pianobarPath, configPath, eventFilePath);
|
|
|
|
// Success message will be shown by the Connected event handler
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Update status to show connection failed
|
|
if (statusLabelRight != null)
|
|
statusLabelRight.Text = "Failed to start Pianobar";
|
|
|
|
MessageBox.Show(
|
|
$"Failed to start Pianobar:\n\n{ex.Message}\n\n" +
|
|
$"Debug Information:\n{debugInfo}",
|
|
"Connection Error",
|
|
MessageBoxButtons.OK,
|
|
MessageBoxIcon.Error);
|
|
}
|
|
}
|
|
|
|
// This method should be wired to btnDisconnect.Click in the Designer
|
|
public void btnDisconnect_Click(object? sender, EventArgs e)
|
|
{
|
|
if (!_pianobarManager.IsConnected)
|
|
{
|
|
MessageBox.Show("Not connected to Pianobar.", "Not Connected",
|
|
MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
return;
|
|
}
|
|
|
|
if (statusLabelRight != null)
|
|
statusLabelRight.Text = "Disconnecting...";
|
|
|
|
// Check if we should kill the process or just detach
|
|
var activeProfile = _profileManager.GetActiveProfile();
|
|
bool killProcess = activeProfile?.Settings.KillProcessOnDisconnect ?? false;
|
|
|
|
if (killProcess)
|
|
{
|
|
_pianobarManager.DisconnectAndKill();
|
|
}
|
|
else
|
|
{
|
|
_pianobarManager.Disconnect();
|
|
}
|
|
|
|
_outputManager?.Stop();
|
|
}
|
|
|
|
// This method should be wired to btnPlayPause.Click in the Designer
|
|
public void btnPlayPause_Click(object? sender, EventArgs e)
|
|
{
|
|
if (!_pianobarManager.IsConnected)
|
|
{
|
|
MessageBox.Show("Not connected to Pianobar.", "Not Connected",
|
|
MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
_pianobarManager.PlayPause();
|
|
UpdatePlayPauseButton();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show($"Failed to toggle play/pause:\n{ex.Message}", "Command Error",
|
|
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
}
|
|
|
|
// This method should be wired to btnSkip.Click in the Designer
|
|
public void btnSkip_Click(object? sender, EventArgs e)
|
|
{
|
|
if (!_pianobarManager.IsConnected)
|
|
{
|
|
MessageBox.Show("Not connected to Pianobar.", "Not Connected",
|
|
MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
_pianobarManager.Skip();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show($"Failed to skip track:\n{ex.Message}", "Command Error",
|
|
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
}
|
|
|
|
// This method should be wired to btnStations.Click in the Designer (or menu item)
|
|
public void btnStations_Click(object? sender, EventArgs e)
|
|
{
|
|
if (!_pianobarManager.IsConnected)
|
|
{
|
|
MessageBox.Show("Not connected to Pianobar.", "Not Connected",
|
|
MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|
return;
|
|
}
|
|
|
|
var stationForm = new StationForm();
|
|
stationForm.ShowDialog(this);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Menu Event Handlers
|
|
|
|
// This method should be wired to the Settings menu item click
|
|
public void menuSettings_Click(object? sender, EventArgs e)
|
|
{
|
|
var activeProfile = _profileManager.GetActiveProfile();
|
|
var settingsForm = new SettingsForm(_profileManager, activeProfile?.Settings ?? new ProfileSettings());
|
|
if (settingsForm.ShowDialog(this) == DialogResult.OK)
|
|
{
|
|
// Settings were saved, update the UI
|
|
UpdateProfilesMenu();
|
|
|
|
// Update status to show the new active profile
|
|
var newActiveProfile = _profileManager.GetActiveProfile();
|
|
if (newActiveProfile != null && statusLabelRight != null)
|
|
{
|
|
statusLabelRight.Text = $"Profile saved: {newActiveProfile.Name}";
|
|
}
|
|
|
|
// Restart output manager if connected
|
|
if (_pianobarManager.IsConnected)
|
|
{
|
|
RestartOutputManager();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ProfileManager_ProfilesChanged(object? sender, EventArgs e)
|
|
{
|
|
UpdateProfilesMenu();
|
|
}
|
|
|
|
private void ProfileManager_ActiveProfileChanged(object? sender, EventArgs e)
|
|
{
|
|
UpdateProfilesMenu();
|
|
UpdateCenterStatus();
|
|
}
|
|
|
|
// Call this method to build the Profiles submenu dynamically
|
|
private void UpdateProfilesMenu()
|
|
{
|
|
// Assumes menuProfiles (profilesToolStripMenuItem) is a ToolStripMenuItem in your File menu
|
|
// Created in the Designer
|
|
if (profilesToolStripMenuItem == null)
|
|
return;
|
|
|
|
profilesToolStripMenuItem.DropDownItems.Clear();
|
|
|
|
foreach (var profile in _profileManager.Profiles)
|
|
{
|
|
var menuItem = new ToolStripMenuItem(profile.Name);
|
|
menuItem.Tag = profile.Name;
|
|
menuItem.Click += ProfileMenuItem_Click;
|
|
|
|
if (profile.Name == _profileManager.ActiveProfileName)
|
|
menuItem.Checked = true;
|
|
|
|
profilesToolStripMenuItem.DropDownItems.Add(menuItem);
|
|
}
|
|
}
|
|
|
|
private void ProfileMenuItem_Click(object? sender, EventArgs e)
|
|
{
|
|
if (sender is ToolStripMenuItem menuItem && menuItem.Tag is string profileName)
|
|
{
|
|
_profileManager.SetActiveProfile(profileName);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Pianobar Event Handlers
|
|
|
|
private void PianobarManager_Connected(object? sender, EventArgs e)
|
|
{
|
|
Invoke(() =>
|
|
{
|
|
var activeProfile = _profileManager.GetActiveProfile();
|
|
if (activeProfile != null)
|
|
{
|
|
_outputManager = new OutputManager(activeProfile.Settings);
|
|
_outputManager.ActiveStateChanged += OutputManager_ActiveStateChanged;
|
|
_outputManager.Start();
|
|
}
|
|
|
|
UpdateConnectionStatus(
|
|
_outputManager?.IsActive == true ? ConnectionStatus.ConnectedActive : ConnectionStatus.ConnectedIdle
|
|
);
|
|
UpdatePlayPauseButton();
|
|
});
|
|
}
|
|
|
|
private void PianobarManager_Disconnected(object? sender, EventArgs e)
|
|
{
|
|
Invoke(() =>
|
|
{
|
|
_outputManager?.Stop();
|
|
_outputManager = null;
|
|
TerminalHistory.Clear(); // Clear history on disconnect
|
|
UpdateConnectionStatus(ConnectionStatus.Disconnected);
|
|
UpdatePlayPauseButton();
|
|
});
|
|
}
|
|
|
|
private void PianobarManager_SongChanged(object? sender, SongChangedEventArgs e)
|
|
{
|
|
Invoke(() =>
|
|
{
|
|
_outputManager?.UpdateSongMetadata(e);
|
|
});
|
|
}
|
|
|
|
private void PianobarManager_StationChanged(object? sender, string e)
|
|
{
|
|
// Station change is handled via event feed
|
|
UpdateCenterStatus();
|
|
}
|
|
|
|
private void PianobarManager_EventOccurred(object? sender, PianobarEventArgs e)
|
|
{
|
|
Invoke(() =>
|
|
{
|
|
// Update right status zone with event text
|
|
if (statusLabelRight != null)
|
|
{
|
|
statusLabelRight.Text = e.EventText;
|
|
}
|
|
});
|
|
}
|
|
|
|
private void PianobarManager_OutputReceived(object? sender, string e)
|
|
{
|
|
// Add to history and forward to static event for StationForm and other subscribers
|
|
if (!string.IsNullOrEmpty(e))
|
|
{
|
|
TerminalHistory.Add(e);
|
|
OnPianobarOutputReceived?.Invoke(e);
|
|
}
|
|
}
|
|
|
|
private void OutputManager_ActiveStateChanged(object? sender, EventArgs e)
|
|
{
|
|
Invoke(() =>
|
|
{
|
|
if (_pianobarManager.IsConnected)
|
|
{
|
|
UpdateConnectionStatus(
|
|
_outputManager?.IsActive == true ? ConnectionStatus.ConnectedActive : ConnectionStatus.ConnectedIdle
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region UI Update Methods
|
|
|
|
private void UpdateConnectionStatus(ConnectionStatus status)
|
|
{
|
|
_currentStatus = status;
|
|
|
|
if (statusLabelLeft == null)
|
|
return;
|
|
|
|
switch (status)
|
|
{
|
|
case ConnectionStatus.Disconnected:
|
|
statusLabelLeft.Text = "? Disconnected";
|
|
statusLabelLeft.ForeColor = Color.Red;
|
|
break;
|
|
case ConnectionStatus.ConnectedIdle:
|
|
statusLabelLeft.Text = "? Connected (Idle)";
|
|
statusLabelLeft.ForeColor = Color.Green;
|
|
break;
|
|
case ConnectionStatus.ConnectedActive:
|
|
statusLabelLeft.Text = "? Connected (Active)";
|
|
statusLabelLeft.ForeColor = Color.Blue;
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void UpdatePlayPauseButton()
|
|
{
|
|
// Uses btnPlayPause from the Designer
|
|
if (btnPlayPause == null)
|
|
return;
|
|
|
|
if (!_pianobarManager.IsConnected)
|
|
{
|
|
btnPlayPause.Text = "Play";
|
|
return;
|
|
}
|
|
|
|
btnPlayPause.Text = _pianobarManager.IsPlaying ? "Pause" : "Play";
|
|
}
|
|
|
|
private void StatusTimer_Tick(object? sender, EventArgs e)
|
|
{
|
|
_statusToggle = !_statusToggle;
|
|
UpdateCenterStatus();
|
|
}
|
|
|
|
private void UpdateCenterStatus()
|
|
{
|
|
if (statusLabelCenter == null)
|
|
return;
|
|
|
|
if (_statusToggle)
|
|
{
|
|
// Show profile
|
|
string profileName = _profileManager.ActiveProfileName ?? "None";
|
|
statusLabelCenter.Text = $"Profile: {profileName}";
|
|
}
|
|
else
|
|
{
|
|
// Show station
|
|
string station = _pianobarManager.CurrentStation ?? "None";
|
|
statusLabelCenter.Text = $"Station: {station}";
|
|
}
|
|
}
|
|
|
|
private void RestartOutputManager()
|
|
{
|
|
_outputManager?.Stop();
|
|
|
|
var activeProfile = _profileManager.GetActiveProfile();
|
|
if (activeProfile != null && _pianobarManager.IsConnected)
|
|
{
|
|
_outputManager = new OutputManager(activeProfile.Settings);
|
|
_outputManager.ActiveStateChanged += OutputManager_ActiveStateChanged;
|
|
_outputManager.Start();
|
|
|
|
UpdateConnectionStatus(
|
|
_outputManager.IsActive ? ConnectionStatus.ConnectedActive : ConnectionStatus.ConnectedIdle
|
|
);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Cleanup
|
|
|
|
protected override void OnFormClosing(FormClosingEventArgs e)
|
|
{
|
|
_statusTimer?.Stop();
|
|
_pianobarManager?.Disconnect();
|
|
_outputManager?.Dispose();
|
|
base.OnFormClosing(e);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|