Files
Taskbar-Launcher/Services/LauncherController.cs
2026-08-26 22:43:58 -04:00

368 lines
11 KiB
C#

using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using TaskbarLauncher.Models;
using TaskbarLauncher.Views;
namespace TaskbarLauncher.Services;
public sealed class LauncherController : IDisposable
{
private const int MaxRecentItems = 8;
private readonly HotkeyService hotkeyService = new();
private NotifyIcon? notifyIcon;
private LauncherPopup? popup;
private MainWindow? settingsWindow;
private AboutWindow? aboutWindow;
public LauncherConfig Config { get; private set; } = new();
public void Start()
{
Config = ConfigService.Load();
ThemeService.Apply(Config.ThemeScheme);
CreateTrayIcon();
RegisterHotkey();
}
public void ReloadConfig()
{
Config = ConfigService.Load();
ThemeService.Apply(Config.ThemeScheme);
RegisterHotkey();
popup?.Close();
popup = null;
}
public void ShowSettings()
{
settingsWindow ??= new MainWindow(this);
settingsWindow.Show();
settingsWindow.Activate();
}
public void ShowSettings(LauncherItem itemToEdit)
{
settingsWindow ??= new MainWindow(this);
settingsWindow.SelectItemForEdit(itemToEdit);
}
public void ShowAbout()
{
if (aboutWindow?.IsVisible == true)
{
aboutWindow.Activate();
return;
}
aboutWindow = new AboutWindow();
aboutWindow.Closed += (_, _) => aboutWindow = null;
aboutWindow.Show();
aboutWindow.Activate();
}
public void TogglePopup()
{
if (popup?.IsVisible == true)
{
popup.Close();
popup = null;
return;
}
popup = new LauncherPopup(Config, LaunchItem, OpenAllItems, ShowSettings, ShowSettings, OpenContainingFolderFromPopup);
popup.Closed += (_, _) => popup = null;
popup.ShowNearTaskbar();
}
public void Dispose()
{
hotkeyService.Dispose();
notifyIcon?.Dispose();
}
private void RegisterHotkey()
{
hotkeyService.Pressed -= HotkeyPressed;
hotkeyService.Register(Config.Hotkey);
hotkeyService.Pressed += HotkeyPressed;
}
private void HotkeyPressed(object? sender, EventArgs e)
{
System.Windows.Application.Current.Dispatcher.Invoke(TogglePopup);
}
private void CreateTrayIcon()
{
notifyIcon = new NotifyIcon
{
Text = "Taskbar Launcher",
Icon = LoadTrayIcon(),
Visible = true,
ContextMenuStrip = new ContextMenuStrip()
};
notifyIcon.DoubleClick += (_, _) => TogglePopup();
notifyIcon.ContextMenuStrip.Items.Add("Open Launcher", null, (_, _) => TogglePopup());
notifyIcon.ContextMenuStrip.Items.Add("Settings", null, (_, _) => ShowSettings());
notifyIcon.ContextMenuStrip.Items.Add("About", null, (_, _) => ShowAbout());
notifyIcon.ContextMenuStrip.Items.Add("Reload Config", null, (_, _) => ReloadConfig());
notifyIcon.ContextMenuStrip.Items.Add("Open Config Folder", null, (_, _) => ConfigService.OpenConfigFolder());
notifyIcon.ContextMenuStrip.Items.Add("Exit", null, (_, _) => System.Windows.Application.Current.Shutdown());
}
private static Icon LoadTrayIcon()
{
try
{
Uri iconUri = new("pack://application:,,,/Assets/TrayIcon.ico", UriKind.Absolute);
System.Windows.Resources.StreamResourceInfo? resource = System.Windows.Application.GetResourceStream(iconUri);
if (resource?.Stream is null)
{
return SystemIcons.Application;
}
using Icon icon = new(resource.Stream);
return (Icon)icon.Clone();
}
catch
{
return SystemIcons.Application;
}
}
private void LaunchItem(LauncherItem item)
{
if (TryLaunchItem(item, out string? error))
{
RecordRecentItem(item);
popup?.Close();
return;
}
if (!string.IsNullOrWhiteSpace(error))
{
System.Windows.MessageBox.Show(error, "Taskbar Launcher");
}
}
private void RecordRecentItem(LauncherItem item)
{
if (item.Type == LauncherItemType.Menu || item.IsDisabled || string.IsNullOrWhiteSpace(item.Target))
{
return;
}
Config.RecentItems.RemoveAll(recent => IsSameRecentItem(recent, item));
Config.RecentItems.Insert(0, CreateRecentItem(item));
if (Config.RecentItems.Count > MaxRecentItems)
{
Config.RecentItems.RemoveRange(MaxRecentItems, Config.RecentItems.Count - MaxRecentItems);
}
ConfigService.Save(Config);
}
private static bool IsSameRecentItem(LauncherItem recent, LauncherItem item)
{
return string.Equals(recent.Target, item.Target, StringComparison.OrdinalIgnoreCase) &&
string.Equals(recent.Arguments ?? "", item.Arguments ?? "", StringComparison.OrdinalIgnoreCase);
}
private static LauncherItem CreateRecentItem(LauncherItem item)
{
return new LauncherItem
{
Title = item.Title,
Type = item.Type,
Target = item.Target,
Arguments = item.Arguments,
WorkingFolder = item.WorkingFolder,
Notes = item.Notes,
IconPath = item.IconPath,
IsFavorite = false,
IsDisabled = false,
RunAsAdmin = item.RunAsAdmin,
StartMinimized = item.StartMinimized,
DisplayMode = item.DisplayMode,
Children = []
};
}
private void OpenAllItems(LauncherItem menuItem)
{
List<string> failures = [];
int launchedCount = 0;
foreach (LauncherItem child in menuItem.Children)
{
if (child.Type == LauncherItemType.Menu || child.IsDisabled || string.IsNullOrWhiteSpace(child.Target))
{
continue;
}
if (TryLaunchItem(child, out string? error))
{
launchedCount++;
RecordRecentItem(child);
}
else if (!string.IsNullOrWhiteSpace(error))
{
failures.Add(error);
}
}
popup?.Close();
if (failures.Count > 0)
{
string message = $"Opened {launchedCount} item(s), but {failures.Count} item(s) failed:\n\n" +
string.Join("\n\n", failures.Take(5));
System.Windows.MessageBox.Show(message, "Taskbar Launcher");
}
}
private void OpenContainingFolderFromPopup(LauncherItem item)
{
if (!OpenContainingFolder(item, out string? error) && !string.IsNullOrWhiteSpace(error))
{
System.Windows.MessageBox.Show(error, "Taskbar Launcher");
}
}
public static bool OpenContainingFolder(LauncherItem item, out string? error)
{
error = null;
if (item.Type == LauncherItemType.Menu || string.IsNullOrWhiteSpace(item.Target))
{
return false;
}
string target = Environment.ExpandEnvironmentVariables(item.Target);
string? folderPath = Directory.Exists(target)
? target
: Path.GetDirectoryName(target);
if (string.IsNullOrWhiteSpace(folderPath) || !Directory.Exists(folderPath))
{
error = $"{item.Title}: Containing folder was not found.";
return false;
}
try
{
Process.Start(new ProcessStartInfo
{
FileName = folderPath,
UseShellExecute = true
});
return true;
}
catch (Exception ex)
{
error = $"{item.Title}: {ex.Message}";
return false;
}
}
private static bool TryLaunchItem(LauncherItem item, out string? error)
{
error = null;
if (item.Type == LauncherItemType.Menu || item.IsDisabled || string.IsNullOrWhiteSpace(item.Target))
{
return false;
}
string target = Environment.ExpandEnvironmentVariables(item.Target);
bool runAsAdmin = item.RunAsAdmin && CanRunAsAdmin(item.Type);
string launchTarget = runAsAdmin ? ResolveExecutableTarget(target) : target;
string workingDirectory = GetWorkingDirectory(item, target);
try
{
ProcessStartInfo startInfo = new()
{
FileName = launchTarget,
Arguments = item.Arguments ?? "",
UseShellExecute = true,
WorkingDirectory = workingDirectory
};
if (runAsAdmin)
{
startInfo.Verb = "runas";
}
if (item.StartMinimized && CanUseLaunchOptions(item.Type))
{
startInfo.WindowStyle = ProcessWindowStyle.Minimized;
}
Process.Start(startInfo);
return true;
}
catch (Exception ex)
{
error = $"{item.Title}: {ex.Message}";
return false;
}
}
public static bool CanRunAsAdmin(LauncherItemType itemType)
{
return itemType is LauncherItemType.App or LauncherItemType.File;
}
public static bool CanUseLaunchOptions(LauncherItemType itemType)
{
return itemType is LauncherItemType.App or LauncherItemType.File;
}
private static string GetWorkingDirectory(LauncherItem item, string target)
{
if (CanUseLaunchOptions(item.Type) && !string.IsNullOrWhiteSpace(item.WorkingFolder))
{
string configuredFolder = Environment.ExpandEnvironmentVariables(item.WorkingFolder.Trim());
if (Directory.Exists(configuredFolder))
{
return configuredFolder;
}
}
return Directory.Exists(target) ? target : ConfigService.AppFolder;
}
private static string ResolveExecutableTarget(string target)
{
if (Path.IsPathRooted(target) || Path.GetDirectoryName(target) is { Length: > 0 })
{
return target;
}
string[] searchFolders =
[
Environment.CurrentDirectory,
Environment.GetFolderPath(Environment.SpecialFolder.Windows),
Environment.GetFolderPath(Environment.SpecialFolder.System),
.. (Environment.GetEnvironmentVariable("PATH") ?? "")
.Split(Path.PathSeparator, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
];
foreach (string folder in searchFolders.Where(Directory.Exists).Distinct(StringComparer.OrdinalIgnoreCase))
{
string candidate = Path.Combine(folder, target);
if (File.Exists(candidate))
{
return candidate;
}
}
return target;
}
}