| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- using System.Collections.ObjectModel;
- using System.Diagnostics;
- using System.Reactive;
- using System.Reactive.Linq;
- using DynamicData;
- using PicView.Core.FileAssociations;
- using ReactiveUI;
- namespace PicView.Core.ViewModels;
- public class FileAssociationsViewModel : ReactiveObject
- {
- private readonly ReadOnlyObservableCollection<FileTypeGroup> _fileTypeGroups;
- private readonly SourceList<FileTypeGroup> _fileTypeGroupsList = new();
- public ReadOnlyObservableCollection<FileTypeGroup> FileTypeGroups => _fileTypeGroups;
- public string? FilterText
- {
- get;
- set => this.RaiseAndSetIfChanged(ref field, value);
- } = string.Empty;
- public bool IsProcessing
- {
- get;
- set => this.RaiseAndSetIfChanged(ref field, value);
- }
- public double Opacity
- {
- get;
- set => this.RaiseAndSetIfChanged(ref field, value);
- } = 1.0;
- public ReactiveCommand<Unit, bool> ApplyCommand { get; }
- public ReactiveCommand<Unit, string> ClearFilterCommand { get; }
-
- public ReactiveCommand<Unit, Unit> UnassociateCommand { get; }
-
- public FileAssociationsViewModel()
- {
- // Create file type groups and populate with data
- InitializeFileTypes();
-
- // Setup the filtering
- var filter = this.WhenAnyValue(x => x.FilterText)
- .Throttle(TimeSpan.FromMilliseconds(200))
- .Select(BuildFilter);
-
- _fileTypeGroupsList.Connect()
- .AutoRefresh()
- .Filter(filter)
- .Bind(out _fileTypeGroups)
- .Subscribe();
- // Canexecute for ApplyCommand
- var canExecute = this.WhenAnyValue(x => x.IsProcessing)
- .Select(processing => !processing);
-
- // Initialize commands with error handling
- ApplyCommand = ReactiveCommand.CreateFromTask(
- ApplyFileAssociations,
- canExecute);
-
- // Handle errors from the Apply command
- ApplyCommand.ThrownExceptions
- .Subscribe(ex =>
- {
- IsProcessing = false;
- #if DEBUG
- Debug.WriteLine($"Error in ApplyCommand: {ex}");
- #endif
- });
-
- UnassociateCommand = ReactiveCommand.CreateFromTask(async () =>
- {
- try
- {
- IsProcessing = true;
- UnselectFileTypes();
- await FileTypeHelper.SetFileAssociations(FileTypeGroups);
- }
- finally
- {
- IsProcessing = false;
- }
- }, canExecute);
-
- UnassociateCommand.ThrownExceptions
- .Subscribe(ex =>
- {
- IsProcessing = false;
- #if DEBUG
- Debug.WriteLine($"Error in UnassociateCommand: {ex}");
- #endif
- });
-
- ClearFilterCommand = ReactiveCommand.Create(() => FilterText = string.Empty);
-
- this.WhenAnyValue(x => x.IsProcessing).Subscribe(isProcessing =>
- {
- Opacity = isProcessing ? 0.3 : 1.0;
- });
- }
-
- private Func<FileTypeGroup, bool> BuildFilter(string? filter)
- {
- if (string.IsNullOrWhiteSpace(filter))
- {
- // Reset all items to visible when filter is empty
- foreach (var group in _fileTypeGroupsList.Items)
- {
- foreach (var item in group.FileTypes)
- {
- item.IsVisible = true;
- }
- }
- return _ => true;
- }
-
- return group => {
- // Update visibility of items based on filter
- var anyVisible = false;
- foreach (var item in group.FileTypes)
- {
- item.IsVisible = item.Description.Contains(filter, StringComparison.OrdinalIgnoreCase) ||
- item.Extension.Contains(filter, StringComparison.OrdinalIgnoreCase);
- if (item.IsVisible)
- anyVisible = true;
- }
-
- // Only show groups that have at least one visible item
- return anyVisible;
- };
- }
-
- private void SyncUIStateToViewModel()
- {
- // Force property notifications to ensure all changes are processed
- foreach (var group in FileTypeGroups)
- {
- group.IsSelected = group.IsSelected;
- foreach (var fileType in group.FileTypes)
- {
- fileType.IsSelected = fileType.IsSelected;
- }
- }
- }
- private async Task<bool> ApplyFileAssociations()
- {
- try
- {
- IsProcessing = true;
-
- // Ensure all UI changes are synced to the ViewModel
- SyncUIStateToViewModel();
-
- // Now process the associations
- return await FileTypeHelper.SetFileAssociations(FileTypeGroups);
- }
- finally
- {
- IsProcessing = false;
- }
- }
- private void InitializeFileTypes()
- {
- var groups = FileTypeHelper.GetFileTypes();
-
- _fileTypeGroupsList.Edit(list =>
- {
- list.Clear();
- list.AddRange(groups);
- });
- }
-
- public void ResetFileTypesToDefault()
- {
- // Get fresh default file types
- var defaultGroups = FileTypeHelper.GetFileTypes();
-
- // Make a copy of the current groups to avoid enumeration issues
- var currentGroups = _fileTypeGroups.ToArray();
-
- // Update selection states based on the defaults
- foreach (var group in currentGroups)
- {
- var defaultGroup = defaultGroups.FirstOrDefault(g => g.Name == group.Name);
- if (defaultGroup == null)
- {
- continue;
- }
- // Update the group's selection state
- group.IsSelected = defaultGroup.IsSelected;
-
- // Update each file type's selection state
- var fileTypes = group.FileTypes.ToArray();
- foreach (var fileType in fileTypes)
- {
- var defaultType = defaultGroup.FileTypes.FirstOrDefault(dt =>
- dt.Description == fileType.Description);
-
- if (defaultType != null)
- {
- fileType.IsSelected = defaultType.IsSelected;
- }
- }
- }
- }
- public void UnselectFileTypes()
- {
- // Get fresh default file types
- var defaultGroups = FileTypeHelper.GetFileTypes();
-
- // Make a copy of the current groups to avoid enumeration issues
- var currentGroups = _fileTypeGroups.ToArray();
-
- // Update selection states based on the defaults
- foreach (var group in currentGroups)
- {
- var defaultGroup = defaultGroups.FirstOrDefault(g => g.Name == group.Name);
- if (defaultGroup == null)
- {
- continue;
- }
-
- group.IsSelected = false;
-
- // Update each file type's selection state
- var fileTypes = group.FileTypes.ToArray();
- foreach (var fileType in fileTypes)
- {
- var defaultType = defaultGroup.FileTypes.FirstOrDefault(dt =>
- dt.Description == fileType.Description);
-
- if (defaultType != null)
- {
- fileType.IsSelected = false;
- }
- }
- }
- }
- }
|