feat: Add Print Service Tray application with configuration and queue management

- Implemented PrintQueueService with job counting and snapshot retrieval.
- Created Form1 as the main entry point for the tray application.
- Developed ConfigurationForm for managing printer configurations and document types.
- Added models for printer configuration, queue status requests, and responses.
- Established ConfigurationService for loading and saving printer configurations.
- Introduced ServiceClient for IPC communication with the print service.
- Built TrayApplicationContext for managing the system tray icon and status updates.
- Added TODO.md for tracking remaining work and future enhancements.
This commit is contained in:
Jason
2026-05-29 10:43:48 -05:00
parent cce9248089
commit a08da0217d
17 changed files with 1541 additions and 4 deletions
@@ -0,0 +1,136 @@
using System.Text.Json;
using PrintServiceTray.Models;
namespace PrintServiceTray.Services;
public class ConfigurationService
{
private readonly string _globalConfigPath;
private readonly string _localConfigPath;
public ConfigurationService()
{
// Global config: next to CLI executable (C:\LAAPC or wherever installed)
var cliPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
"LAAPC"
);
_globalConfigPath = Path.Combine(cliPath, "printer-config.json");
// Local config: user's AppData
var appDataPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"LAAPC"
);
Directory.CreateDirectory(appDataPath);
_localConfigPath = Path.Combine(appDataPath, "printer-config.json");
}
/// <summary>
/// Load configuration (local overrides global)
/// </summary>
public PrinterConfiguration Load()
{
var config = new PrinterConfiguration();
// Load global first
if (File.Exists(_globalConfigPath))
{
try
{
var json = File.ReadAllText(_globalConfigPath);
var globalConfig = JsonSerializer.Deserialize<PrinterConfiguration>(json);
if (globalConfig != null)
{
config = globalConfig;
}
}
catch { }
}
// Load local and merge/override
if (File.Exists(_localConfigPath))
{
try
{
var json = File.ReadAllText(_localConfigPath);
var localConfig = JsonSerializer.Deserialize<PrinterConfiguration>(json);
if (localConfig != null)
{
// Merge: local overrides global for matching document types
foreach (var kvp in localConfig.DocumentTypes)
{
config.DocumentTypes[kvp.Key] = kvp.Value;
}
}
}
catch { }
}
return config;
}
/// <summary>
/// Save configuration to global or local
/// </summary>
public void Save(PrinterConfiguration config, bool saveGlobal)
{
var path = saveGlobal ? _globalConfigPath : _localConfigPath;
// Ensure directory exists
var directory = Path.GetDirectoryName(path);
if (!string.IsNullOrEmpty(directory))
{
Directory.CreateDirectory(directory);
}
var options = new JsonSerializerOptions
{
WriteIndented = true
};
var json = JsonSerializer.Serialize(config, options);
File.WriteAllText(path, json);
}
/// <summary>
/// Get all installed printers
/// </summary>
public List<string> GetInstalledPrinters()
{
var printers = new List<string>();
try
{
foreach (string printerName in System.Drawing.Printing.PrinterSettings.InstalledPrinters)
{
printers.Add(printerName);
}
}
catch { }
return printers;
}
/// <summary>
/// Get available trays for a printer
/// </summary>
public List<(int Number, string Name)> GetPrinterTrays(string printerName)
{
var trays = new List<(int, string)>();
try
{
var printerSettings = new System.Drawing.Printing.PrinterSettings
{
PrinterName = printerName
};
foreach (System.Drawing.Printing.PaperSource source in printerSettings.PaperSources)
{
trays.Add((source.RawKind, source.SourceName));
}
}
catch { }
return trays;
}
public string GlobalConfigPath => _globalConfigPath;
public string LocalConfigPath => _localConfigPath;
}