using System; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using ShortcutsHelper.Models; using ShortcutsHelper.Services; using WpfApplication = System.Windows.Application; using SystemTimer = System.Timers.Timer; namespace ShortcutsHelper.ViewModels { public class MainViewModel : INotifyPropertyChanged, IDisposable { private readonly ShortcutService _shortcutService; private readonly ConfigurationService _configService; private readonly KeyCaptureService _keyCaptureService; private readonly SystemTimer _appDetectionTimer; private readonly SystemTimer _inactivityTimer; private string _currentApplication = ""; private ShortcutRecord? _selectedShortcut; private string _originalDescription = ""; private bool _isInitialized = false; private bool _isPinnedMode = true; // Por defecto está activo public ObservableCollection Shortcuts { get; } = new(); public string CurrentApplication { get => _currentApplication; private set { if (_currentApplication != value) { _currentApplication = value; OnPropertyChanged(); LoadShortcutsForCurrentApp(); } } } public ShortcutRecord? SelectedShortcut { get => _selectedShortcut; set { if (_selectedShortcut != value) { _selectedShortcut = value; OnPropertyChanged(); EnsureEmptyRows(); } } } public bool IsCapturingKeys => _keyCaptureService.IsCapturing; public bool IsPinnedMode { get => _isPinnedMode; set { if (_isPinnedMode != value) { _isPinnedMode = value; OnPropertyChanged(); // Actualizar configuración _appConfig.IsPinnedMode = value; if (value) { // Al activar Pin, guardar la aplicación actual como pinned (si no está vacía) if (!string.IsNullOrEmpty(CurrentApplication)) { _appConfig.PinnedApplication = CurrentApplication; } } else { // Al desactivar Pin, limpiar la aplicación pinned y mostrar la ventana _appConfig.PinnedApplication = ""; VisibilityChanged?.Invoke(this, true); // Mostrar ventana cuando se desactiva Pin } _configService.SaveConfiguration(_appConfig); } } } // Configuración de aplicación actual private AppConfiguration _appConfig = new(); private ApplicationSettings? _currentAppSettings; // Evento para notificar cambios de visibilidad de la ventana public event EventHandler? VisibilityChanged; public event PropertyChangedEventHandler? PropertyChanged; public MainViewModel() { _shortcutService = new ShortcutService(); _configService = new ConfigurationService(); _keyCaptureService = new KeyCaptureService(); // Configurar timers _appDetectionTimer = new SystemTimer(1000); // Cada segundo _appDetectionTimer.Elapsed += OnAppDetectionTimer; _inactivityTimer = new SystemTimer(TimeSpan.FromMinutes(5).TotalMilliseconds); // 5 minutos por defecto _inactivityTimer.Elapsed += OnInactivityTimer; // Suscribirse a eventos _keyCaptureService.KeyCaptured += OnKeyCaptured; _keyCaptureService.CaptureCancelled += OnCaptureCancelled; _shortcutService.ApplicationShortcutsChanged += OnApplicationShortcutsChanged; Initialize(); } public void Initialize() { if (_isInitialized) return; // Cargar configuración _appConfig = _configService.LoadConfiguration(); _inactivityTimer.Interval = TimeSpan.FromMinutes(_appConfig.InactivityTimeoutMinutes).TotalMilliseconds; // Restaurar estado de Pin _isPinnedMode = _appConfig.IsPinnedMode; OnPropertyChanged(nameof(IsPinnedMode)); // Cargar todos los shortcuts de una vez _shortcutService.LoadAllShortcuts(); // Si está en modo Pin y no hay aplicación pinned, establecer la primera aplicación detectada // Pero primero detectar la aplicación actual para establecer CurrentApplication string initialApp = GetActiveProcessName(); if (!string.IsNullOrWhiteSpace(initialApp) && !initialApp.Equals("ShortcutsHelper", StringComparison.OrdinalIgnoreCase)) { CurrentApplication = initialApp; LoadApplicationSettings(); // Si está en modo Pin y no hay aplicación pinned, establecer esta como pinned if (IsPinnedMode && string.IsNullOrEmpty(_appConfig.PinnedApplication)) { _appConfig.PinnedApplication = initialApp; _configService.SaveConfiguration(_appConfig); } } // Iniciar detección de aplicación _appDetectionTimer.Start(); // Iniciar timer de inactividad _inactivityTimer.Start(); _isInitialized = true; } private void OnAppDetectionTimer(object? sender, System.Timers.ElapsedEventArgs e) { DetectActiveApplication(); } private void OnInactivityTimer(object? sender, System.Timers.ElapsedEventArgs e) { // Guardar cambios por inactividad SavePendingChanges(); } private void DetectActiveApplication() { string appName = GetActiveProcessName(); if (string.IsNullOrWhiteSpace(appName)) return; // Si está en modo Pin if (IsPinnedMode) { // Si no hay aplicación pinned, establecer la aplicación actual (excepto ShortcutsHelper) if (string.IsNullOrEmpty(_appConfig.PinnedApplication) && !appName.Equals("ShortcutsHelper", StringComparison.OrdinalIgnoreCase)) { _appConfig.PinnedApplication = appName; CurrentApplication = appName; LoadApplicationSettings(); _configService.SaveConfiguration(_appConfig); return; } // Si hay aplicación pinned, manejar visibilidad if (!string.IsNullOrEmpty(_appConfig.PinnedApplication)) { bool isShortcutsHelper = appName.Equals("ShortcutsHelper", StringComparison.OrdinalIgnoreCase); bool isPinnedApp = appName.Equals(_appConfig.PinnedApplication, StringComparison.OrdinalIgnoreCase); // Mostrar ventana solo si está en ShortcutsHelper o en la aplicación pinned bool shouldBeVisible = isShortcutsHelper || isPinnedApp; VisibilityChanged?.Invoke(this, shouldBeVisible); // No cambiar la aplicación actual cuando está pinned return; } } // Modo normal: cambiar aplicación pero no mostrar ShortcutsHelper if (appName.Equals("ShortcutsHelper", StringComparison.OrdinalIgnoreCase)) return; if (appName != CurrentApplication) { SaveCurrentApplicationSettings(); CurrentApplication = appName; LoadApplicationSettings(); } } public void TogglePinMode() { IsPinnedMode = !IsPinnedMode; } private void LoadShortcutsForCurrentApp() { WpfApplication.Current.Dispatcher.Invoke(() => { Shortcuts.Clear(); var shortcuts = _shortcutService.GetShortcutsForApplication(CurrentApplication); foreach (var shortcut in shortcuts) { Shortcuts.Add(shortcut); } EnsureEmptyRows(); }); } private void EnsureEmptyRows() { WpfApplication.Current.Dispatcher.BeginInvoke(() => { var emptyRows = Shortcuts.Where(s => string.IsNullOrWhiteSpace(s.Shortcut) && string.IsNullOrWhiteSpace(s.Description)).Count(); while (emptyRows < 3) { Shortcuts.Add(new ShortcutRecord { Application = CurrentApplication }); emptyRows++; } }); } private void OnApplicationShortcutsChanged(object? sender, string applicationName) { if (applicationName == CurrentApplication) { LoadShortcutsForCurrentApp(); } } public void DeleteSelectedShortcut() { if (SelectedShortcut != null && !string.IsNullOrWhiteSpace(SelectedShortcut.Shortcut)) { _shortcutService.RemoveShortcut(CurrentApplication, SelectedShortcut.Shortcut); Shortcuts.Remove(SelectedShortcut); EnsureEmptyRows(); } } public void StartKeyCapture() { if (SelectedShortcut == null) return; if (_keyCaptureService.IsCapturing) { _keyCaptureService.StopCapturing(); return; } _originalDescription = SelectedShortcut.Description; SelectedShortcut.Description = "Presiona las teclas... (Esc para cancelar)"; _keyCaptureService.StartCapturing(); OnPropertyChanged(nameof(IsCapturingKeys)); } private void OnKeyCaptured(object? sender, string keyString) { if (SelectedShortcut != null) { SelectedShortcut.Shortcut = keyString; SelectedShortcut.Description = _originalDescription; SelectedShortcut.IsModified = true; } OnPropertyChanged(nameof(IsCapturingKeys)); } private void OnCaptureCancelled(object? sender, EventArgs e) { if (SelectedShortcut != null) { SelectedShortcut.Description = _originalDescription; } OnPropertyChanged(nameof(IsCapturingKeys)); } public void SavePendingChanges() { try { // Guardar shortcuts modificados var modifiedShortcuts = Shortcuts.Where(s => s.IsModified && !string.IsNullOrWhiteSpace(s.Shortcut)).ToList(); foreach (var shortcut in modifiedShortcuts) { _shortcutService.AddOrUpdateShortcut(shortcut); } if (_shortcutService.HasModifiedShortcuts()) { _shortcutService.SaveAllShortcuts(); } } catch (Exception ex) { Console.WriteLine($"Error al guardar cambios: {ex.Message}"); } } public ApplicationSettings GetCurrentApplicationSettings() { return _configService.GetApplicationSettings(_appConfig, CurrentApplication); } public void SaveCurrentApplicationSettings() { if (string.IsNullOrEmpty(CurrentApplication) || _currentAppSettings == null) return; _configService.UpdateApplicationSettings(_appConfig, CurrentApplication, _currentAppSettings); _configService.SaveConfiguration(_appConfig); } private void LoadApplicationSettings() { _currentAppSettings = _configService.GetApplicationSettings(_appConfig, CurrentApplication); } public void UpdateWindowSettings(double left, double top, double width, double height) { if (_currentAppSettings != null) { _currentAppSettings.Left = left; _currentAppSettings.Top = top; _currentAppSettings.Width = width; _currentAppSettings.Height = height; } } public void UpdateColumnWidths(double favoriteWidth, double shortcutWidth, double descriptionWidth) { if (_currentAppSettings != null) { _currentAppSettings.FavoriteColumnWidth = favoriteWidth; _currentAppSettings.ShortcutColumnWidth = shortcutWidth; _currentAppSettings.DescriptionColumnWidth = descriptionWidth; } } private static string GetActiveProcessName() { IntPtr hWnd = GetForegroundWindow(); if (hWnd == IntPtr.Zero) return string.Empty; GetWindowThreadProcessId(hWnd, out uint pid); try { var proc = Process.GetProcessById((int)pid); return proc.ProcessName; } catch { return string.Empty; } } protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public void Dispose() { SavePendingChanges(); SaveCurrentApplicationSettings(); _appDetectionTimer?.Stop(); _appDetectionTimer?.Dispose(); _inactivityTimer?.Stop(); _inactivityTimer?.Dispose(); _keyCaptureService?.Dispose(); } [DllImport("user32.dll")] private static extern IntPtr GetForegroundWindow(); [DllImport("user32.dll")] private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); } }