Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
037b800
Add file manager path validation and error handling in SelectFileMana…
onesounds May 20, 2025
2c7fb93
Add error handling and validation for custom file manager paths
onesounds May 20, 2025
aadfde0
Add tips for file manager usage and improve error messages
onesounds May 20, 2025
94b0f2e
Merge branch 'dev' into 250520-FixFilesConfig
onesounds May 20, 2025
f262263
Add hyperlink to learn more about file manager usage in settings dialog
onesounds May 20, 2025
3fa9259
Merge remote-tracking branch 'origin/250520-FixFilesConfig' into 2505…
onesounds May 20, 2025
c0eaf1c
Remove unused function
Jack251970 May 20, 2025
3beb4e9
Use api functions instead
Jack251970 May 20, 2025
af4375b
Improve code quality
Jack251970 May 20, 2025
d23f88d
Make sure vertically center
Jack251970 May 20, 2025
5626ab7
Log information when failed to execute
Jack251970 May 20, 2025
89fdd48
typo
jjw24 May 20, 2025
6e36b08
Use api functions instead
Jack251970 May 21, 2025
152ad2f
Remove setter for observable collection & Add blank line for code qua…
Jack251970 May 21, 2025
d0f0edb
Merge branch '250520-FixFilesConfig' of https://github.com/onesounds/…
Jack251970 May 21, 2025
6ce2cf9
Refactor with MVVM
Jack251970 May 21, 2025
5578daa
Remove files tip button
Jack251970 May 21, 2025
45b8181
Fix potential issue with index boundary in Delete command
Jack251970 May 21, 2025
9a692a8
Add whitespace
Jack251970 May 21, 2025
07f77f0
move calls to viewmodel
jjw24 May 21, 2025
e2d50cd
update wording
jjw24 May 21, 2025
41b9cd4
move code to viewmodel
jjw24 May 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Flow.Launcher.Infrastructure/UserSettings/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,8 @@ public CustomExplorerViewModel CustomExplorer
new()
{
Name = "Files",
Path = "Files",
DirectoryArgument = "-select \"%d\"",
Path = "Files-Stable",
DirectoryArgument = "\"%d\"",
FileArgument = "-select \"%f\""
}
};
Expand Down
12 changes: 12 additions & 0 deletions Flow.Launcher/Languages/en.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -366,13 +366,17 @@

<!-- FileManager Setting Dialog -->
<system:String x:Key="fileManagerWindow">Select File Manager</system:String>
<system:String x:Key="fileManager_files_btn">Do you use Files?</system:String>
<system:String x:Key="fileManager_files_tips">Depending on the version, Files may use either "Files" or "Files-stable" as its path. (This can vary depending on which version you're using.) For more details, see:</system:String>
<system:String x:Key="fileManager_tips">Please specify the file location of the file manager you using and add arguments as required. The &quot;%d&quot; represents the directory path to open for, used by the Arg for Folder field and for commands opening specific directories. The &quot;%f&quot; represents the file path to open for, used by the Arg for File field and for commands opening specific files.</system:String>
<system:String x:Key="fileManager_tips2">For example, if the file manager uses a command such as &quot;totalcmd.exe /A c:\windows&quot; to open the c:\windows directory, the File Manager Path will be totalcmd.exe, and the Arg For Folder will be /A &quot;%d&quot;. Certain file managers like QTTabBar may just require a path to be supplied, in this instance use &quot;%d&quot; as the File Manager Path and leave the rest of the fileds blank.</system:String>
<system:String x:Key="fileManager_name">File Manager</system:String>
<system:String x:Key="fileManager_profile_name">Profile Name</system:String>
<system:String x:Key="fileManager_path">File Manager Path</system:String>
<system:String x:Key="fileManager_directory_arg">Arg For Folder</system:String>
<system:String x:Key="fileManager_file_arg">Arg For File</system:String>
<system:String x:Key="fileManagerPathNotFound">The path for the file manager '{0}' could not be found: '{1}' Do you want to continue?</system:String>
<system:String x:Key="fileManagerPathError">File Manager Path Error</system:String>

<!-- DefaultBrowser Setting Dialog -->
<system:String x:Key="defaultBrowserTitle">Default Web Browser</system:String>
Expand Down Expand Up @@ -462,6 +466,14 @@
<system:String x:Key="reportWindow_upload_log">1. Upload log file: {0}</system:String>
<system:String x:Key="reportWindow_copy_below">2. Copy below exception message</system:String>

<!-- File Open Error -->
<system:String x:Key="fileManagerNotFoundTitle">File Manager Error</system:String>
<system:String x:Key="fileManagerNotFound">
The specified file manager could not be found. Please check the Custom File Manager setting under Settings >General.
</system:String>
<system:String x:Key="errorTitle">Error</system:String>
<system:String x:Key="folderOpenError">An error occurred while opening the folder. {0}</system:String>

<!-- General Notice -->
<system:String x:Key="pleaseWait">Please wait...</system:String>

Expand Down
81 changes: 52 additions & 29 deletions Flow.Launcher/PublicAPIInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@

// Must use getter to avoid accessing Application.Current.Resources.MergedDictionaries so earlier in theme constructor
private Theme _theme;
private Theme Theme => _theme ??= Ioc.Default.GetRequiredService<Theme>();

Check warning on line 45 in Flow.Launcher/PublicAPIInstance.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`Ioc` is not a recognized word. (unrecognized-spelling)

// Must use getter to avoid circular dependency
private Updater _updater;
private Updater Updater => _updater ??= Ioc.Default.GetRequiredService<Updater>();

Check warning on line 49 in Flow.Launcher/PublicAPIInstance.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`Ioc` is not a recognized word. (unrecognized-spelling)

private readonly object _saveSettingsLock = new();

Expand Down Expand Up @@ -142,7 +142,7 @@
ShellCommand.Execute(startInfo);
}

[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD100:Avoid async void methods", Justification = "<Pending>")]

Check warning on line 145 in Flow.Launcher/PublicAPIInstance.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`VSTHRD` is not a recognized word. (unrecognized-spelling)
public async void CopyToClipboard(string stringToCopy, bool directCopy = false, bool showDefaultNotification = true)
{
if (string.IsNullOrEmpty(stringToCopy))
Expand Down Expand Up @@ -316,40 +316,63 @@

public void OpenDirectory(string DirectoryPath, string FileNameOrFilePath = null)
{
using var explorer = new Process();
var explorerInfo = _settings.CustomExplorer;
var explorerPath = explorerInfo.Path.Trim().ToLowerInvariant();
var targetPath = FileNameOrFilePath is null
? DirectoryPath
: Path.IsPathRooted(FileNameOrFilePath)
? FileNameOrFilePath
: Path.Combine(DirectoryPath, FileNameOrFilePath);

if (Path.GetFileNameWithoutExtension(explorerPath) == "explorer")
try
{
// Windows File Manager
// We should ignore and pass only the path to Shell to prevent zombie explorer.exe processes
explorer.StartInfo = new ProcessStartInfo
using var explorer = new Process();
var explorerInfo = _settings.CustomExplorer;
var explorerPath = explorerInfo.Path.Trim().ToLowerInvariant();
var targetPath = FileNameOrFilePath is null
? DirectoryPath
: Path.IsPathRooted(FileNameOrFilePath)
? FileNameOrFilePath
: Path.Combine(DirectoryPath, FileNameOrFilePath);

if (Path.GetFileNameWithoutExtension(explorerPath) == "explorer")
{
FileName = targetPath, // Not explorer, Only path.
UseShellExecute = true // Must be true to open folder
};
// Windows File Manager
explorer.StartInfo = new ProcessStartInfo
{
FileName = targetPath,
UseShellExecute = true
};
}
else
{
// Custom File Manager
explorer.StartInfo = new ProcessStartInfo
{
FileName = explorerInfo.Path.Replace("%d", DirectoryPath),
UseShellExecute = true,
Arguments = FileNameOrFilePath is null
? explorerInfo.DirectoryArgument.Replace("%d", DirectoryPath)
: explorerInfo.FileArgument
.Replace("%d", DirectoryPath)
.Replace("%f", targetPath)
};
}

explorer.Start();
}
else
catch (System.ComponentModel.Win32Exception ex) when (ex.NativeErrorCode == 2)
{
// Custom File Manager
explorer.StartInfo = new ProcessStartInfo
{
FileName = explorerInfo.Path.Replace("%d", DirectoryPath),
UseShellExecute = true,
Arguments = FileNameOrFilePath is null
? explorerInfo.DirectoryArgument.Replace("%d", DirectoryPath)
: explorerInfo.FileArgument
.Replace("%d", DirectoryPath)
.Replace("%f", targetPath)
};
// File Manager not found
MessageBoxEx.Show(
string.Format(GetTranslation("fileManagerNotFound"), ex.Message),
GetTranslation("fileManagerNotFoundTitle"),
MessageBoxButton.OK,
MessageBoxImage.Error
);
}
catch (Exception ex)
{
// Other exceptions
MessageBoxEx.Show(
string.Format(GetTranslation("folderOpenError"), ex.Message),
GetTranslation("errorTitle"),
MessageBoxButton.OK,
MessageBoxImage.Error
);
}
explorer.Start();
}

private void OpenUri(Uri uri, bool? inPrivate = null)
Expand Down
15 changes: 12 additions & 3 deletions Flow.Launcher/SelectFileManagerWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,18 @@
<TextBlock Margin="0 14 0 0" FontSize="14">
<TextBlock Text="{DynamicResource fileManager_tips2}" TextWrapping="WrapWithOverflow" />
</TextBlock>
<Button
x:Name="btnTips"
Margin="0 14 0 0"
HorizontalAlignment="Left"
Click="btnTips_Click"
Content="{DynamicResource fileManager_files_btn}" />
</StackPanel>

<StackPanel Margin="14 28 0 0" Orientation="Horizontal">
<Rectangle
Height="1"
Margin="0 20 0 20"
Fill="{StaticResource SeparatorForeground}" />
<StackPanel Margin="14 0 0 0" Orientation="Horizontal">
<TextBlock
Grid.Column="1"
HorizontalAlignment="Left"
Expand Down Expand Up @@ -111,7 +120,7 @@
<Rectangle
Height="1"
Margin="0 20 0 12"
Fill="{StaticResource Color03B}" />
Fill="{StaticResource SeparatorForeground}" />
<StackPanel
Margin="0 0 0 0"
HorizontalAlignment="Stretch"
Expand Down
102 changes: 101 additions & 1 deletion Flow.Launcher/SelectFileManagerWindow.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
using System.Collections.ObjectModel;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using CommunityToolkit.Mvvm.ComponentModel;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.ViewModel;
using ModernWpf.Controls;

namespace Flow.Launcher
{
Expand Down Expand Up @@ -43,11 +48,106 @@

private void btnDone_Click(object sender, RoutedEventArgs e)
{
// Check if the selected file manager path is valid
if (!IsFileManagerValid(CustomExplorer.Path))
{
MessageBoxResult result = MessageBoxEx.Show(
string.Format((string)Application.Current.FindResource("fileManagerPathNotFound"),
CustomExplorer.Name, CustomExplorer.Path),
(string)Application.Current.FindResource("fileManagerPathError"),
MessageBoxButton.YesNo,
MessageBoxImage.Warning);

if (result == MessageBoxResult.No)
{
return;
}
}

_settings.CustomExplorerList = CustomExplorers.ToList();
_settings.CustomExplorerIndex = SelectedCustomExplorerIndex;
Close();
}

private bool IsFileManagerValid(string path)
{
if (string.Equals(path, "explorer", StringComparison.OrdinalIgnoreCase))
return true;

if (Path.IsPathRooted(path))
{
return File.Exists(path);
}

try
{
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "where",
Arguments = path,
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
}
};
process.Start();
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();

return !string.IsNullOrEmpty(output);
}
catch
{
return false;
}
}

private async void btnTips_Click(object sender, RoutedEventArgs e)
{
string tipText = (string)Application.Current.Resources["fileManager_files_tips"];
string url = "https://files.community/docs/contributing/updates";

var textBlock = new TextBlock
{
FontSize = 14,
TextWrapping = TextWrapping.Wrap,
Margin = new Thickness(0, 0, 0, 0)
};

textBlock.Inlines.Add(tipText);

Hyperlink hyperlink = new Hyperlink
{
NavigateUri = new Uri(url)
};
hyperlink.Inlines.Add(url);

Check warning on line 125 in Flow.Launcher/SelectFileManagerWindow.xaml.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`hyperlink` is not a recognized word. (unrecognized-spelling)
hyperlink.RequestNavigate += (s, args) =>

Check warning on line 126 in Flow.Launcher/SelectFileManagerWindow.xaml.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`hyperlink` is not a recognized word. (unrecognized-spelling)
{
Process.Start(new ProcessStartInfo
{
FileName = args.Uri.AbsoluteUri,
UseShellExecute = true
});
args.Handled = true;
};

textBlock.Inlines.Add(hyperlink);

Check warning on line 136 in Flow.Launcher/SelectFileManagerWindow.xaml.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`hyperlink` is not a recognized word. (unrecognized-spelling)

var tipsDialog = new ContentDialog()
{
Owner = Window.GetWindow(sender as DependencyObject),
Title = (string)Application.Current.Resources["fileManager_files_btn"],
Content = textBlock,
PrimaryButtonText = (string)Application.Current.Resources["commonOK"],
CornerRadius = new CornerRadius(8),
Style = (Style)Application.Current.Resources["ContentDialog"]
};

await tipsDialog.ShowAsync();
}

private void btnAdd_Click(object sender, RoutedEventArgs e)
{
CustomExplorers.Add(new()
Expand Down
Loading