Files
DeskCore/MainWindow.Cleanup.cs
T
2026-10-03 19:51:56 +00:00

115 lines
5.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.IO;
using System.Windows;
using System.Windows.Threading;
namespace DeskCore;
/// <summary>Verknüpfungen zu deinstallierten Programmen automatisch aus den Kategorien entfernen.</summary>
public partial class MainWindow
{
private DispatcherTimer? _deadScanTimer;
private DispatcherTimer? _deadScanPeriodic;
private bool _deadScanRunning;
private DispatcherTimer? _deadConfirmTimer;
private readonly Dictionary<string, DateTime> _deadSince = new(StringComparer.OrdinalIgnoreCase);
private static readonly TimeSpan ConfirmAfter = TimeSpan.FromSeconds(60);
/// <summary>Zweite Prüfung kurz nach Ablauf der Wartezeit.</summary>
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();
}
/// <summary>Einmal beim Start einrichten: Prüfung kurz nach dem Start und danach alle 10 Minuten.</summary>
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();
}
/// <summary>Nach Änderungen (z. B. Deinstallation entfernt die Desktop-Verknüpfung) gebündelt prüfen.</summary>
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<string>();
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();
}
}