using System.IO;
using System.Windows.Threading;
namespace DeskCore;
///
/// Beobachtet Desktop und Kategorie-Ordner. Meldet Änderungen gebündelt (statt bei jedem einzelnen Ereignis)
/// und sammelt neu auf dem Desktop aufgetauchte Elemente für die automatische Sortierung.
///
public sealed class Watcher : IDisposable
{
private readonly List _watchers = [];
private readonly DispatcherTimer _changedTimer;
private readonly DispatcherTimer _newTimer;
private readonly HashSet _newOnDesktop = new(StringComparer.OrdinalIgnoreCase);
private readonly object _lock = new();
/// Irgendetwas hat sich geändert (UI-Thread).
public event Action? Changed;
/// Neue Elemente auf dem Desktop (UI-Thread, nach kurzer Wartezeit).
public event Action>? NewOnDesktop;
public Watcher(Dispatcher dispatcher)
{
_changedTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(350), DispatcherPriority.Background, (_, _) =>
{
_changedTimer!.Stop();
Changed?.Invoke();
}, dispatcher);
_changedTimer.Stop();
// Etwas länger warten: Installer legen Verknüpfungen oft in mehreren Schritten an
_newTimer = new DispatcherTimer(TimeSpan.FromSeconds(2), DispatcherPriority.Background, (_, _) =>
{
_newTimer!.Stop();
List items;
lock (_lock) { items = _newOnDesktop.ToList(); _newOnDesktop.Clear(); }
items = items.Where(p => (File.Exists(p) || Directory.Exists(p)) && !Storage.CreatedByApp(p)).ToList();
if (items.Count > 0) NewOnDesktop?.Invoke(items);
}, dispatcher);
_newTimer.Stop();
Directory.CreateDirectory(Storage.Root);
Add(Storage.UserDesktop, false, true);
Add(Storage.PublicDesktop, false, true);
Add(Storage.Root, true, false);
}
private void Add(string dir, bool subdirs, bool isDesktop)
{
if (!Directory.Exists(dir)) return;
try
{
var w = new FileSystemWatcher(dir)
{
IncludeSubdirectories = subdirs,
NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.LastWrite,
InternalBufferSize = 64 * 1024,
};
FileSystemEventHandler onChange = (_, e) =>
{
Poke();
if (isDesktop && e.ChangeType == WatcherChangeTypes.Created) NoteNew(e.FullPath);
};
w.Created += onChange;
w.Deleted += onChange;
w.Changed += onChange;
w.Renamed += (_, e) => { Poke(); if (isDesktop) NoteNew(e.FullPath); };
w.Error += (_, _) => Poke();
w.EnableRaisingEvents = true;
_watchers.Add(w);
}
catch { }
}
private void NoteNew(string path)
{
var name = Path.GetFileName(path);
if (name.StartsWith('~') || name.EndsWith(".tmp", StringComparison.OrdinalIgnoreCase) ||
name.Equals("desktop.ini", StringComparison.OrdinalIgnoreCase)) return;
lock (_lock) _newOnDesktop.Add(path);
_newTimer.Dispatcher.BeginInvoke(() => { _newTimer.Stop(); _newTimer.Start(); });
}
private void Poke() => _changedTimer.Dispatcher.BeginInvoke(() => { _changedTimer.Stop(); _changedTimer.Start(); });
public void Dispose()
{
foreach (var w in _watchers) w.Dispose();
_changedTimer.Stop();
_newTimer.Stop();
}
}
public static class AutoSorter
{
/// Erste passende Regel → Kategoriename (nur existierende Kategorien).
public static string? Match(string path)
{
var name = Storage.DisplayName(path);
var cats = Storage.Categories();
foreach (var r in AppSettings.Current.Rules)
{
if (string.IsNullOrWhiteSpace(r.Contains)) continue;
if (!name.Contains(r.Contains.Trim(), StringComparison.CurrentCultureIgnoreCase)) continue;
var cat = cats.FirstOrDefault(c => string.Equals(c, r.Category, StringComparison.OrdinalIgnoreCase));
if (cat is not null) return cat;
}
return null;
}
}