ShortcutsHelper/ViewModels/MainViewModel.cs

412 lines
14 KiB
C#

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<ShortcutRecord> 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<bool>? 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);
}
}