diff --git a/src/Files.App/Actions/Content/Archives/Compress/CompressIntoSevenZipAction.cs b/src/Files.App/Actions/Content/Archives/Compress/CompressIntoSevenZipAction.cs index f2453eb1c53c..57ac65200add 100644 --- a/src/Files.App/Actions/Content/Archives/Compress/CompressIntoSevenZipAction.cs +++ b/src/Files.App/Actions/Content/Archives/Compress/CompressIntoSevenZipAction.cs @@ -8,10 +8,10 @@ internal sealed partial class CompressIntoSevenZipAction : BaseCompressArchiveAc { public override string Label => string.Format(Strings.CreateNamedArchive.GetLocalizedResource(), $"{StorageArchiveService.GenerateArchiveNameFromItems(context.SelectedItems)}.7z"); - + public override string ExtendedLabel => Strings.CompressIntoSevenZip.GetLocalizedResource(); - + public override string Description => Strings.CompressIntoSevenZipDescription.GetLocalizedFormatResource(context.SelectedItems.Count); diff --git a/src/Files.App/Data/Contracts/IGeneralSettingsService.cs b/src/Files.App/Data/Contracts/IGeneralSettingsService.cs index 6540eb042dcc..e04dc303c1c1 100644 --- a/src/Files.App/Data/Contracts/IGeneralSettingsService.cs +++ b/src/Files.App/Data/Contracts/IGeneralSettingsService.cs @@ -344,7 +344,7 @@ public interface IGeneralSettingsService : IBaseSettingsService, INotifyProperty /// Gets or sets the thumbnail cache size limit in MB. /// double ThumbnailCacheSizeLimit { get; set; } - + /// Gets or sets a value indicating whether smooth scrolling is enabled. /// bool EnableSmoothScrolling { get; set; } diff --git a/src/Files.App/Data/Factories/PropertiesNavigationViewItemFactory.cs b/src/Files.App/Data/Factories/PropertiesNavigationViewItemFactory.cs index c39a8c4f341a..0b691086e42e 100644 --- a/src/Files.App/Data/Factories/PropertiesNavigationViewItemFactory.cs +++ b/src/Files.App/Data/Factories/PropertiesNavigationViewItemFactory.cs @@ -110,7 +110,7 @@ public static ObservableCollection Initialize var detailsItemEnabled = !(isFolder && !listedItem.IsArchive) && !isLibrary && !listedItem.IsRecycleBinItem; var customizationItemEnabled = !isLibrary && (isFolder && !listedItem.IsArchive || isShortcut); var compatibilityItemEnabled = FileExtensionHelpers.IsExecutableFile(listedItem is IShortcutItem sht ? sht.TargetPath : fileExt, true); - var signaturesItemEnabled = + var signaturesItemEnabled = !isFolder && !isLibrary && !listedItem.IsRecycleBinItem && diff --git a/src/Files.App/Dialogs/FilesystemOperationDialog.xaml b/src/Files.App/Dialogs/FilesystemOperationDialog.xaml index 3242597f40f2..184b7e01b33c 100644 --- a/src/Files.App/Dialogs/FilesystemOperationDialog.xaml +++ b/src/Files.App/Dialogs/FilesystemOperationDialog.xaml @@ -12,6 +12,7 @@ xmlns:vm="using:Files.App.ViewModels.Dialogs.FileSystemDialog" x:Name="RootDialog" Title="{x:Bind ViewModel.Title, Mode=OneWay}" + CloseButtonText="{x:Bind ViewModel.CloseButtonText, Mode=OneWay}" Closing="RootDialog_Closing" CornerRadius="{StaticResource OverlayCornerRadius}" DefaultButton="Primary" diff --git a/src/Files.App/Helpers/Dialog/DynamicDialogFactory.cs b/src/Files.App/Helpers/Dialog/DynamicDialogFactory.cs index e13d792a8f20..03d57da2e6d6 100644 --- a/src/Files.App/Helpers/Dialog/DynamicDialogFactory.cs +++ b/src/Files.App/Helpers/Dialog/DynamicDialogFactory.cs @@ -146,8 +146,9 @@ public static DynamicDialog GetFor_FileInUseDialog(List lockingPro TitleText = Strings.FileInUseDialog_Title.GetLocalizedResource(), SubtitleText = lockingProcess.IsEmpty() ? Strings.FileInUseDialog_Text.GetLocalizedResource() : string.Format(Strings.FileInUseByDialog_Text.GetLocalizedResource(), string.Join(", ", lockingProcess.Select(x => $"{x.AppName ?? x.Name} (PID: {x.Pid})"))), - PrimaryButtonText = "OK", - DynamicButtons = DynamicDialogButtons.Primary + PrimaryButtonText = Strings.Retry.GetLocalizedResource(), + SecondaryButtonText = Strings.Skip.GetLocalizedResource(), + DynamicButtons = DynamicDialogButtons.Primary | DynamicDialogButtons.Secondary | DynamicDialogButtons.Cancel }); return dialog; } diff --git a/src/Files.App/Services/PreviewPopupProviders/PreviewPopupService.cs b/src/Files.App/Services/PreviewPopupProviders/PreviewPopupService.cs index 3a20899e9ee4..43055c2a7946 100644 --- a/src/Files.App/Services/PreviewPopupProviders/PreviewPopupService.cs +++ b/src/Files.App/Services/PreviewPopupProviders/PreviewPopupService.cs @@ -7,7 +7,7 @@ namespace Files.App.Services.PreviewPopupProviders internal sealed partial class PreviewPopupService : ObservableObject, IPreviewPopupService { public async Task GetProviderAsync() - { + { if (await QuickLookProvider.Instance.DetectAvailability()) return await Task.FromResult(QuickLookProvider.Instance); if (await SeerProProvider.Instance.DetectAvailability()) diff --git a/src/Files.App/Services/Windows/WindowsDialogService.cs b/src/Files.App/Services/Windows/WindowsDialogService.cs index 20612523bd0a..9c05ae5e77af 100644 --- a/src/Files.App/Services/Windows/WindowsDialogService.cs +++ b/src/Files.App/Services/Windows/WindowsDialogService.cs @@ -24,7 +24,7 @@ public unsafe bool Open_FileOpenDialog(nint hWnd, bool pickFoldersOnly, string[] { using ComPtr pDialog = default; HRESULT hr = pDialog.CoCreateInstance(CLSID.CLSID_FileOpenDialog, null, CLSCTX.CLSCTX_INPROC_SERVER); - + // Handle COM creation failure gracefully if (hr.Failed) { @@ -60,7 +60,7 @@ public unsafe bool Open_FileOpenDialog(nint hWnd, bool pickFoldersOnly, string[] null, IID.IID_IShellItem, (void**)pDefaultFolderShellItem.GetAddressOf()); - + // Handle shell item creation failure gracefully if (hr.Failed) { @@ -123,7 +123,7 @@ public unsafe bool Open_FileSaveDialog(nint hWnd, bool pickFoldersOnly, string[] { using ComPtr pDialog = default; HRESULT hr = pDialog.CoCreateInstance(CLSID.CLSID_FileSaveDialog, null, CLSCTX.CLSCTX_INPROC_SERVER); - + // Handle COM creation failure gracefully if (hr.Failed) { @@ -159,7 +159,7 @@ public unsafe bool Open_FileSaveDialog(nint hWnd, bool pickFoldersOnly, string[] null, IID.IID_IShellItem, (void**)pDefaultFolderShellItem.GetAddressOf()); - + // Handle shell item creation failure gracefully if (hr.Failed) { diff --git a/src/Files.App/Services/Windows/WindowsRecentItemsService.cs b/src/Files.App/Services/Windows/WindowsRecentItemsService.cs index 7668078bd408..f1952a4bca3c 100644 --- a/src/Files.App/Services/Windows/WindowsRecentItemsService.cs +++ b/src/Files.App/Services/Windows/WindowsRecentItemsService.cs @@ -61,7 +61,7 @@ public IReadOnlyList RecentFolders public WindowsRecentItemsService() { var automaticDestinationsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Recent), "AutomaticDestinations"); - + // Only create the file system watcher if the AutomaticDestinations directory exists if (Directory.Exists(automaticDestinationsPath)) { diff --git a/src/Files.App/Utils/Global/QuickAccessManager.cs b/src/Files.App/Utils/Global/QuickAccessManager.cs index 6b2b1de36bd6..0c3e7798e5fa 100644 --- a/src/Files.App/Utils/Global/QuickAccessManager.cs +++ b/src/Files.App/Utils/Global/QuickAccessManager.cs @@ -26,7 +26,7 @@ public QuickAccessManager() public void Initialize() { var automaticDestinationsPath = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Microsoft", "Windows", "Recent", "AutomaticDestinations"); - + // Only initialize FileSystemWatcher if the directory exists // This handles cases where AppData is redirected to network locations that don't contain Windows system directories if (Directory.Exists(automaticDestinationsPath)) diff --git a/src/Files.App/Utils/Shell/LaunchHelper.cs b/src/Files.App/Utils/Shell/LaunchHelper.cs index 1ca4da412470..6242cb067dc1 100644 --- a/src/Files.App/Utils/Shell/LaunchHelper.cs +++ b/src/Files.App/Utils/Shell/LaunchHelper.cs @@ -107,7 +107,7 @@ private static async Task HandleApplicationLaunch(string application, stri string key = (string)ent.Key; // Skip USERNAME to avoid issues where files were executed as SYSTEM user (#12139) - if (string.Equals(key, "USERNAME", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(key, "USERNAME", StringComparison.OrdinalIgnoreCase)) continue; process.StartInfo.EnvironmentVariables[key] = (string)ent.Value; diff --git a/src/Files.App/Utils/Storage/Operations/FileOperationsHelpers.cs b/src/Files.App/Utils/Storage/Operations/FileOperationsHelpers.cs index 0ce4ed32fe50..ab88231a2b25 100644 --- a/src/Files.App/Utils/Storage/Operations/FileOperationsHelpers.cs +++ b/src/Files.App/Utils/Storage/Operations/FileOperationsHelpers.cs @@ -817,7 +817,7 @@ public static bool SetLinkIcon(string filePath, string? iconFile, int iconIndex) filePath = filePath.Replace("'", "''"); iconFile = iconFile.Replace("'", "''"); - if(ext == ".url") + if (ext == ".url") { psScript = $@" $path = '{filePath}' @@ -901,10 +901,10 @@ private static bool TrySetUrlShortcutIcon(string filePath, string iconFile, int int index = 0; int insertedIndex = 0; - foreach(var line in lines) + foreach (var line in lines) { var isInternetShortcutHeader = line.Trim().Equals("[InternetShortcut]", StringComparison.OrdinalIgnoreCase); - if(isInternetShortcutHeader) + if (isInternetShortcutHeader) { insertedIndex = index + 1; break; diff --git a/src/Files.App/Utils/Storage/Operations/ShellFilesystemOperations.cs b/src/Files.App/Utils/Storage/Operations/ShellFilesystemOperations.cs index c89155f375e3..dd683065df1c 100644 --- a/src/Files.App/Utils/Storage/Operations/ShellFilesystemOperations.cs +++ b/src/Files.App/Utils/Storage/Operations/ShellFilesystemOperations.cs @@ -857,10 +857,17 @@ private Task GetFileInUseDialog(IEnumerable source, IEnume ? Strings.FileInUseDialog_Text.GetLocalizedResource() : string.Format(Strings.FileInUseByDialog_Text.GetLocalizedResource(), string.Join(", ", lockingProcess.Select(x => $"{x.AppName ?? x.Name} (PID: {x.Pid})"))); - return GetFileListDialog(source, titleText, subtitleText, Strings.Retry.GetLocalizedResource(), Strings.Cancel.GetLocalizedResource()); + var sourceCount = source.Count(); + + return GetFileListDialog( + source, + titleText, + subtitleText, + Strings.Retry.GetLocalizedResource(), + sourceCount > 1 ? Strings.Skip.GetLocalizedResource() : Strings.Cancel.GetLocalizedResource()); } - private async Task GetFileListDialog(IEnumerable source, string titleText, string descriptionText = null, string primaryButtonText = null, string secondaryButtonText = null) + private async Task GetFileListDialog(IEnumerable source, string titleText, string descriptionText = null, string primaryButtonText = null, string secondaryButtonText = null, string closeButtonText = null) { var incomingItems = new List(); List binItems = null; @@ -886,7 +893,7 @@ private async Task GetFileListDialog(IEnumerable source, s } var dialogViewModel = FileSystemDialogViewModel.GetDialogViewModel( - incomingItems, titleText, descriptionText, primaryButtonText, secondaryButtonText); + incomingItems, titleText, descriptionText, primaryButtonText, secondaryButtonText, closeButtonText); var dialogService = Ioc.Default.GetRequiredService(); diff --git a/src/Files.App/Utils/Storage/StorageItems/NativeStorageFile.cs b/src/Files.App/Utils/Storage/StorageItems/NativeStorageFile.cs index 83c5e8f829d6..17e48f9dc760 100644 --- a/src/Files.App/Utils/Storage/StorageItems/NativeStorageFile.cs +++ b/src/Files.App/Utils/Storage/StorageItems/NativeStorageFile.cs @@ -55,7 +55,7 @@ private unsafe string GetDisplayTypeFromShell() { SHFILEINFOW shfi = default; var flags = SHGFI_FLAGS.SHGFI_TYPENAME | SHGFI_FLAGS.SHGFI_USEFILEATTRIBUTES; - + fixed (char* pExtension = extension) { var result = PInvoke.SHGetFileInfo( @@ -64,7 +64,7 @@ private unsafe string GetDisplayTypeFromShell() &shfi, (uint)sizeof(SHFILEINFOW), flags); - + if (result != 0 && shfi.szTypeName.Value[0] != '\0') { var typeName = shfi.szTypeName.ToString(); @@ -78,7 +78,7 @@ private unsafe string GetDisplayTypeFromShell() var itemType = Strings.File.GetLocalizedResource(); if (Name.Contains('.', StringComparison.Ordinal)) itemType = extension?.Trim('.') + " " + itemType; - + return itemType; } diff --git a/src/Files.App/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogViewModel.cs index 9de1dbedcf24..da7e88e3d0de 100644 --- a/src/Files.App/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogViewModel.cs @@ -216,7 +216,7 @@ public static FileSystemDialogViewModel GetDialogViewModel(FileSystemDialogMode return viewModel; } - public static FileSystemDialogViewModel GetDialogViewModel(List nonConflictingItems, string titleText, string descriptionText, string primaryButtonText, string secondaryButtonText) + public static FileSystemDialogViewModel GetDialogViewModel(List nonConflictingItems, string titleText, string descriptionText, string primaryButtonText, string secondaryButtonText, string closeButtonText = null) { var viewModel = new FileSystemDialogViewModel( new() @@ -230,6 +230,7 @@ public static FileSystemDialogViewModel GetDialogViewModel(List typeof(SecurityPage), PropertiesNavigationViewItemType.Customization => typeof(CustomizationPage), PropertiesNavigationViewItemType.Compatibility => typeof(CompatibilityPage), - PropertiesNavigationViewItemType.Hashes => typeof(HashesPage), - PropertiesNavigationViewItemType.Signatures => typeof(SignaturesPage), + PropertiesNavigationViewItemType.Hashes => typeof(HashesPage), + PropertiesNavigationViewItemType.Signatures => typeof(SignaturesPage), _ => typeof(GeneralPage), }; diff --git a/src/Files.App/ViewModels/Settings/GeneralViewModel.cs b/src/Files.App/ViewModels/Settings/GeneralViewModel.cs index 5e80e7c88c92..9efa8f618d7c 100644 --- a/src/Files.App/ViewModels/Settings/GeneralViewModel.cs +++ b/src/Files.App/ViewModels/Settings/GeneralViewModel.cs @@ -467,7 +467,7 @@ public bool ShowPinToStart } } } - + public bool ShowEditTagsMenu { get => UserSettingsService.GeneralSettingsService.ShowEditTagsMenu; diff --git a/src/Files.App/ViewModels/UserControls/NavigationToolbarViewModel.cs b/src/Files.App/ViewModels/UserControls/NavigationToolbarViewModel.cs index 901f23a0f0c1..b5965120913c 100644 --- a/src/Files.App/ViewModels/UserControls/NavigationToolbarViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/NavigationToolbarViewModel.cs @@ -1030,12 +1030,12 @@ public async Task PopulateOmnibarSuggestionsForPathMode() void AddNoResultsItem() { PathModeSuggestionItems.Clear(); - + // Use null-safe access to avoid NullReferenceException during app lifecycle transitions var workingDirectory = string.IsNullOrEmpty(ContentPageContext.ShellPage?.ShellViewModel?.WorkingDirectory) ? Constants.UserEnvironmentPaths.HomePath : ContentPageContext.ShellPage.ShellViewModel.WorkingDirectory; - + PathModeSuggestionItems.Add(new( workingDirectory, Strings.NavigationToolbarVisiblePathNoResults.GetLocalizedResource())); @@ -1062,7 +1062,7 @@ public async Task PopulateOmnibarSuggestionsForCommandPaletteMode() int processedCount = 0; foreach (var command in commandsToProcess) - { + { if (!command.IsExecutable) { processedCount++; diff --git a/src/Files.App/ViewModels/UserControls/ShelfViewModel.cs b/src/Files.App/ViewModels/UserControls/ShelfViewModel.cs index d6461384306e..f6aae67455b8 100644 --- a/src/Files.App/ViewModels/UserControls/ShelfViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/ShelfViewModel.cs @@ -48,49 +48,49 @@ private async void Items_CollectionChanged(object? sender, NotifyCollectionChang switch (e.Action) { case NotifyCollectionChangedAction.Add when e.NewItems is not null: - { - if (e.NewItems[0] is not ShelfItem shelfItem) - return; - - var parentPath = SystemIO.Path.GetDirectoryName(shelfItem.Inner.Id) ?? string.Empty; - if (_watchers.TryGetValue(parentPath, out var reference)) { - // Only increase the reference count if the watcher already exists - reference.ReferenceCount += 1; - return; + if (e.NewItems[0] is not ShelfItem shelfItem) + return; + + var parentPath = SystemIO.Path.GetDirectoryName(shelfItem.Inner.Id) ?? string.Empty; + if (_watchers.TryGetValue(parentPath, out var reference)) + { + // Only increase the reference count if the watcher already exists + reference.ReferenceCount += 1; + return; + } + + if (await shelfItem.Inner.GetParentAsync() is not IMutableFolder mutableFolder) + return; + + // Register new watcher + var watcher = await mutableFolder.GetFolderWatcherAsync(); + watcher.CollectionChanged += Watcher_CollectionChanged; + + _watchers.Add(parentPath, new(watcher, 1)); + break; } - if (await shelfItem.Inner.GetParentAsync() is not IMutableFolder mutableFolder) - return; - - // Register new watcher - var watcher = await mutableFolder.GetFolderWatcherAsync(); - watcher.CollectionChanged += Watcher_CollectionChanged; - - _watchers.Add(parentPath, new(watcher, 1)); - break; - } - case NotifyCollectionChangedAction.Remove when e.OldItems is not null: - { - if (e.OldItems[0] is not ShelfItem shelfItem) - return; - - var parentPath = SystemIO.Path.GetDirectoryName(shelfItem.Inner.Id) ?? string.Empty; - if (!_watchers.TryGetValue(parentPath, out var reference)) - return; - - // Decrease the reference count and remove the watcher if no references are present - reference.ReferenceCount -= 1; - if (reference.ReferenceCount < 1) { - reference.FolderWatcher.CollectionChanged -= Watcher_CollectionChanged; - reference.FolderWatcher.Dispose(); - _watchers.Remove(parentPath); + if (e.OldItems[0] is not ShelfItem shelfItem) + return; + + var parentPath = SystemIO.Path.GetDirectoryName(shelfItem.Inner.Id) ?? string.Empty; + if (!_watchers.TryGetValue(parentPath, out var reference)) + return; + + // Decrease the reference count and remove the watcher if no references are present + reference.ReferenceCount -= 1; + if (reference.ReferenceCount < 1) + { + reference.FolderWatcher.CollectionChanged -= Watcher_CollectionChanged; + reference.FolderWatcher.Dispose(); + _watchers.Remove(parentPath); + } + + break; } - - break; - } } } @@ -99,16 +99,16 @@ private async void Watcher_CollectionChanged(object? sender, NotifyCollectionCha switch (e.Action) { case NotifyCollectionChangedAction.Remove when e.OldItems is not null: - { - // Remove the matching item notified from the watcher - var item = e.OldItems.Cast().ElementAt(0); - var itemToRemove = Items.FirstOrDefault(x => x.Inner.Id == item.Id); - if (itemToRemove is null) - return; - - await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => Items.Remove(itemToRemove)); - break; - } + { + // Remove the matching item notified from the watcher + var item = e.OldItems.Cast().ElementAt(0); + var itemToRemove = Items.FirstOrDefault(x => x.Inner.Id == item.Id); + if (itemToRemove is null) + return; + + await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => Items.Remove(itemToRemove)); + break; + } } } } diff --git a/src/Files.App/Views/Layouts/DetailsLayoutPage.xaml.cs b/src/Files.App/Views/Layouts/DetailsLayoutPage.xaml.cs index ab3bb639884d..14253469729a 100644 --- a/src/Files.App/Views/Layouts/DetailsLayoutPage.xaml.cs +++ b/src/Files.App/Views/Layouts/DetailsLayoutPage.xaml.cs @@ -920,7 +920,7 @@ private void FileList_Loaded(object sender, RoutedEventArgs e) return; void OnZoomScrolled(object? s, ScrollViewerViewChangedEventArgs ve) { - scroller.ViewChanged -= OnZoomScrolled; + scroller.ViewChanged -= OnZoomScrolled; scroller.ChangeView(0, Math.Max(0, scroller.VerticalOffset - OffsetCorrection), null, true); } scroller.ViewChanged += OnZoomScrolled;