Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions Flow.Launcher/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
var storage = new FlowLauncherJsonStorage<Settings>();
_settings = storage.Load();
_settings.SetStorage(storage);
_settings.WMPInstalled = WindowsMediaPlayerHelper.IsWindowsMediaPlayerInstalled();

Check warning on line 63 in Flow.Launcher/App.xaml.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`WMP` is not a recognized word. (unrecognized-spelling)
}
catch (Exception e)
{
Expand Down Expand Up @@ -98,8 +98,12 @@
.AddTransient<SettingsPanePluginStoreViewModel>()
.AddTransient<SettingsPaneProxyViewModel>()
.AddTransient<SettingsPaneThemeViewModel>()
// Use transient instance for dialog view models because
// settings will change and we need to recreate them
.AddTransient<SelectBrowserViewModel>()
.AddTransient<SelectFileManagerViewModel>()
).Build();
Ioc.Default.ConfigureServices(host.Services);

Check warning on line 106 in Flow.Launcher/App.xaml.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`Ioc` is not a recognized word. (unrecognized-spelling)
}
catch (Exception e)
{
Expand All @@ -110,9 +114,9 @@
// Initialize the public API and Settings first
try
{
API = Ioc.Default.GetRequiredService<IPublicAPI>();

Check warning on line 117 in Flow.Launcher/App.xaml.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`Ioc` is not a recognized word. (unrecognized-spelling)
_settings.Initialize();
_mainVM = Ioc.Default.GetRequiredService<MainViewModel>();

Check warning on line 119 in Flow.Launcher/App.xaml.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`Ioc` is not a recognized word. (unrecognized-spelling)
}
catch (Exception e)
{
Expand Down Expand Up @@ -167,7 +171,7 @@
Current.Resources["SettingWindowFont"] = new FontFamily(_settings.SettingWindowFont);
Current.Resources["ContentControlThemeFontFamily"] = new FontFamily(_settings.SettingWindowFont);

Ioc.Default.GetRequiredService<Portable>().PreStartCleanUpAfterPortabilityUpdate();

Check warning on line 174 in Flow.Launcher/App.xaml.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

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

API.LogInfo(ClassName, "Begin Flow Launcher startup ----------------------------------------------------");
API.LogInfo(ClassName, $"Runtime info:{ErrorReporting.RuntimeInfo()}");
Expand All @@ -192,7 +196,7 @@
// Change language after all plugins are initialized because we need to update plugin title based on their api
await Ioc.Default.GetRequiredService<Internationalization>().InitializeLanguageAsync();

await imageLoadertask;

Check warning on line 199 in Flow.Launcher/App.xaml.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

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

_mainWindow = new MainWindow();

Expand All @@ -218,7 +222,7 @@
});
}

#pragma warning restore VSTHRD100 // Avoid async void methods

Check warning on line 225 in Flow.Launcher/App.xaml.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

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

/// <summary>
/// Check startup only for Release
Expand Down
11 changes: 11 additions & 0 deletions Flow.Launcher/Languages/en.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -366,13 +366,16 @@

<!-- FileManager Setting Dialog -->
<system:String x:Key="fileManagerWindow">Select File Manager</system:String>
<system:String x:Key="fileManager_learnMore">Learn more</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 file manager '{0}' could not be located at '{1}'. Would you like 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 +465,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
3 changes: 0 additions & 3 deletions Flow.Launcher/MessageBoxEx.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ private MessageBoxEx(MessageBoxButton button)
InitializeComponent();
}

public static MessageBoxResult Show(string messageBoxText)
=> Show(messageBoxText, string.Empty, MessageBoxButton.OK, MessageBoxImage.None, MessageBoxResult.OK);

public static MessageBoxResult Show(
string messageBoxText,
string caption = "",
Expand Down
84 changes: 55 additions & 29 deletions Flow.Launcher/PublicAPIInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,14 @@
using JetBrains.Annotations;
using Squirrel;
using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch;
using System.ComponentModel;

namespace Flow.Launcher
{
public class PublicAPIInstance : IPublicAPI, IRemovable
{
private static readonly string ClassName = nameof(PublicAPIInstance);

private readonly Settings _settings;
private readonly MainViewModel _mainVM;

Expand Down Expand Up @@ -69,7 +72,7 @@
_mainVM.ChangeQueryText(query, requery);
}

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

Check warning on line 75 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 RestartApp()
{
_mainVM.Hide();
Expand Down Expand Up @@ -142,7 +145,7 @@
ShellCommand.Execute(startInfo);
}

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

Check warning on line 148 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 +319,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 (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)
};
LogError(ClassName, "File Manager not found");
ShowMsgBox(
string.Format(GetTranslation("fileManagerNotFound"), ex.Message),
GetTranslation("fileManagerNotFoundTitle"),
MessageBoxButton.OK,
MessageBoxImage.Error
);
}
catch (Exception ex)
{
LogException(ClassName, "Failed to open folder", ex);
ShowMsgBox(
string.Format(GetTranslation("folderOpenError"), ex.Message),
GetTranslation("errorTitle"),
MessageBoxButton.OK,
MessageBoxImage.Error
);
}
explorer.Start();
}

private void OpenUri(Uri uri, bool? inPrivate = null)
Expand Down
7 changes: 4 additions & 3 deletions Flow.Launcher/SelectBrowserWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
xmlns:local="clr-namespace:Flow.Launcher"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.modernwpf.com/2019"
xmlns:vm="clr-namespace:Flow.Launcher.ViewModel"
Title="{DynamicResource defaultBrowserTitle}"
Width="550"
d:DataContext="{d:DesignInstance vm:SelectBrowserViewModel}"
Background="{DynamicResource PopuBGColor}"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Foreground="{DynamicResource PopupTextColor}"
ResizeMode="NoResize"
SizeToContent="Height"
Expand Down Expand Up @@ -97,11 +98,11 @@
</ComboBox>
<Button
Margin="10 0 0 0"
Click="btnAdd_Click"
Command="{Binding AddCommand}"
Content="{DynamicResource add}" />
<Button
Margin="10 0 0 0"
Click="btnDelete_Click"
Command="{Binding DeleteCommand}"
Content="{DynamicResource delete}"
IsEnabled="{Binding CustomBrowser.Editable}" />

Expand Down
61 changes: 15 additions & 46 deletions Flow.Launcher/SelectBrowserWindow.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,36 +1,18 @@
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows;
using System.Windows.Controls;
using CommunityToolkit.Mvvm.ComponentModel;
using Flow.Launcher.Infrastructure.UserSettings;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.ViewModel;

namespace Flow.Launcher
{
[INotifyPropertyChanged]
public partial class SelectBrowserWindow : Window
{
private readonly Settings _settings;
private readonly SelectBrowserViewModel _viewModel;

private int selectedCustomBrowserIndex;

public int SelectedCustomBrowserIndex
{
get => selectedCustomBrowserIndex;
set
{
selectedCustomBrowserIndex = value;
OnPropertyChanged(nameof(CustomBrowser));
}
}
public ObservableCollection<CustomBrowserViewModel> CustomBrowsers { get; set; }

public CustomBrowserViewModel CustomBrowser => CustomBrowsers[SelectedCustomBrowserIndex];
public SelectBrowserWindow(Settings settings)
public SelectBrowserWindow()
{
_settings = settings;
CustomBrowsers = new ObservableCollection<CustomBrowserViewModel>(_settings.CustomBrowserList.Select(x => x.Copy()));
SelectedCustomBrowserIndex = _settings.CustomBrowserIndex;
_viewModel = Ioc.Default.GetRequiredService<SelectBrowserViewModel>();
DataContext = _viewModel;
InitializeComponent();
}

Expand All @@ -41,33 +23,20 @@ private void btnCancel_Click(object sender, RoutedEventArgs e)

private void btnDone_Click(object sender, RoutedEventArgs e)
{
_settings.CustomBrowserList = CustomBrowsers.ToList();
_settings.CustomBrowserIndex = SelectedCustomBrowserIndex;
Close();
}

private void btnAdd_Click(object sender, RoutedEventArgs e)
{
CustomBrowsers.Add(new()
if (_viewModel.SaveSettings())
{
Name = "New Profile"
});
SelectedCustomBrowserIndex = CustomBrowsers.Count - 1;
}

private void btnDelete_Click(object sender, RoutedEventArgs e)
{
CustomBrowsers.RemoveAt(SelectedCustomBrowserIndex--);
Close();
}
}

private void btnBrowseFile_Click(object sender, RoutedEventArgs e)
{
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
var result = dlg.ShowDialog();
if (result == true)
var selectedFilePath = _viewModel.SelectFile();

if (!string.IsNullOrEmpty(selectedFilePath))
{
TextBox path = (TextBox)(((FrameworkElement)sender).Parent as FrameworkElement).FindName("PathTextBox");
path.Text = dlg.FileName;
var path = (TextBox)(((FrameworkElement)sender).Parent as FrameworkElement).FindName("PathTextBox");
path.Text = selectedFilePath;
path.Focus();
((Button)sender).Focus();
}
Expand Down
21 changes: 15 additions & 6 deletions Flow.Launcher/SelectFileManagerWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
xmlns:local="clr-namespace:Flow.Launcher"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.modernwpf.com/2019"
xmlns:vm="clr-namespace:Flow.Launcher.ViewModel"
Title="{DynamicResource fileManagerWindow}"
Width="600"
d:DataContext="{d:DesignInstance vm:SelectFileManagerViewModel}"
Background="{DynamicResource PopuBGColor}"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Foreground="{DynamicResource PopupTextColor}"
ResizeMode="NoResize"
SizeToContent="Height"
Expand Down Expand Up @@ -73,9 +74,17 @@
<TextBlock Margin="0 14 0 0" FontSize="14">
<TextBlock Text="{DynamicResource fileManager_tips2}" TextWrapping="WrapWithOverflow" />
</TextBlock>
<TextBlock Margin="0 14 0 0" VerticalAlignment="Center">
<Hyperlink NavigateUri="https://www.flowlauncher.com/docs/#/filemanager" RequestNavigate="Hyperlink_RequestNavigate">
<TextBlock FontSize="14" Text="{DynamicResource fileManager_learnMore}" />
</Hyperlink>
</TextBlock>
</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 All @@ -99,19 +108,19 @@
</ComboBox>
<Button
Margin="10 0 0 0"
Click="btnAdd_Click"
Command="{Binding AddCommand}"
Content="{DynamicResource add}" />
<Button
Margin="10 0 0 0"
Click="btnDelete_Click"
Command="{Binding DeleteCommand}"
Content="{DynamicResource delete}"
IsEnabled="{Binding CustomExplorer.Editable}" />

</StackPanel>
<Rectangle
Height="1"
Margin="0 20 0 12"
Fill="{StaticResource Color03B}" />
Fill="{StaticResource SeparatorForeground}" />
<StackPanel
Margin="0 0 0 0"
HorizontalAlignment="Stretch"
Expand Down
Loading
Loading