using System.IO;
using System.Windows;
using System.Windows.Threading;
namespace DeskCore;
/// Verknüpfungen zu deinstallierten Programmen automatisch aus den Kategorien entfernen.
public partial class MainWindow
{
private DispatcherTimer? _deadScanTimer;
private DispatcherTimer? _deadScanPeriodic;
private bool _deadScanRunning;
private DispatcherTimer? _deadConfirmTimer;
private readonly Dictionary _deadSince = new(StringComparer.OrdinalIgnoreCase);
private static readonly TimeSpan ConfirmAfter = TimeSpan.FromSeconds(60);
/// Zweite Prüfung kurz nach Ablauf der Wartezeit.
private void ScheduleConfirmScan()
{
if (_deadConfirmTimer is null)
{
_deadConfirmTimer = new DispatcherTimer { Interval = ConfirmAfter + TimeSpan.FromSeconds(5) };
_deadConfirmTimer.Tick += (_, _) => { _deadConfirmTimer.Stop(); CleanupDeadShortcuts(); };
}
if (!_deadConfirmTimer.IsEnabled) _deadConfirmTimer.Start();
}
/// Einmal beim Start einrichten: Prüfung kurz nach dem Start und danach alle 10 Minuten.
public void StartDeadShortcutWatch()
{
_deadScanTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(4) };
_deadScanTimer.Tick += (_, _) => { _deadScanTimer.Stop(); CleanupDeadShortcuts(); };
// Deinstallationen über die Windows-Einstellungen ändern unsere Ordner nicht → zusätzlich regelmäßig prüfen
_deadScanPeriodic = new DispatcherTimer { Interval = TimeSpan.FromMinutes(10) };
_deadScanPeriodic.Tick += (_, _) => CleanupDeadShortcuts();
_deadScanPeriodic.Start();
ScheduleDeadShortcutScan();
}
/// Nach Änderungen (z. B. Deinstallation entfernt die Desktop-Verknüpfung) gebündelt prüfen.
public void ScheduleDeadShortcutScan()
{
if (_deadScanTimer is null) return;
_deadScanTimer.Stop();
_deadScanTimer.Start();
}
private async void CleanupDeadShortcuts()
{
if (!S.RemoveDeadShortcuts || _deadScanRunning || IsBusy) return;
_deadScanRunning = true;
try
{
var keep = S.KeptShortcuts.ToList();
var found = await Task.Run(() => Storage.FindDeadShortcuts(keep));
// Updates ersetzen ihre Programmdatei manchmal kurz → erst entfernen, wenn sie mindestens
// eine Minute lang fehlt (zweite Prüfung), sonst würde ein laufendes Update fälschlich aufgeräumt
var now = DateTime.UtcNow;
foreach (var p in _deadSince.Keys.Where(k => !found.Contains(k, StringComparer.OrdinalIgnoreCase)).ToList())
_deadSince.Remove(p);
foreach (var p in found) _deadSince.TryAdd(p, now);
var dead = found.Where(p => now - _deadSince[p] >= ConfirmAfter).ToList();
if (dead.Count < found.Count) ScheduleConfirmScan();
if (dead.Count == 0) return;
foreach (var p in dead) _deadSince.Remove(p);
var entry = new UndoEntry(dead.Count == 1
? $"„{Storage.DisplayName(dead[0])}“ entfernen (Programm deinstalliert)"
: $"{dead.Count} Verknüpfungen deinstallierter Programme entfernen");
var removed = new List();
foreach (var p in dead)
{
try
{
// Reihenfolge beim Rückgängigmachen: erst zurückholen, dann merken, dass sie bleiben soll
var path = p;
entry.Add(() =>
{
if (!S.KeptShortcuts.Contains(path, StringComparer.OrdinalIgnoreCase)) S.KeptShortcuts.Add(path);
S.Save();
return null;
});
entry.AddDelete(p, Storage.Delete(p));
removed.Add(p);
}
catch { }
}
if (removed.Count == 0) return;
Undo.Push(entry);
Refresh();
FenceManager.RefreshAll();
var text = removed.Count == 1
? $"„{Storage.DisplayName(removed[0])}“ entfernt – das Programm ist nicht mehr installiert"
: $"{removed.Count} Verknüpfungen entfernt – die Programme sind nicht mehr installiert";
if (IsVisible && IsActive) ShowToast(text, "", undoable: true);
else App.TrayIcon?.Notify("Aufgeräumt", text);
}
finally
{
_deadScanRunning = false;
}
}
private void DeadShortcutsToggle_Click(object sender, RoutedEventArgs e)
{
S.RemoveDeadShortcuts = DeadShortcutsToggle.IsChecked == true;
// beim Einschalten: früher behaltene Verknüpfungen wieder prüfen lassen
if (S.RemoveDeadShortcuts) S.KeptShortcuts.RemoveAll(p => !File.Exists(p));
S.Save();
if (S.RemoveDeadShortcuts) ScheduleDeadShortcutScan();
}
}