diff --git a/Flow.Launcher.Infrastructure/UserSettings/CustomExplorerViewModel.cs b/Flow.Launcher.Infrastructure/UserSettings/CustomExplorerViewModel.cs new file mode 100644 index 00000000000..7806debe125 --- /dev/null +++ b/Flow.Launcher.Infrastructure/UserSettings/CustomExplorerViewModel.cs @@ -0,0 +1,30 @@ +using Flow.Launcher.Plugin; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Flow.Launcher.ViewModel +{ + public class CustomExplorerViewModel : BaseModel + { + public string Name { get; set; } + public string Path { get; set; } + public string FileArgument { get; set; } = "\"%d\""; + public string DirectoryArgument { get; set; } = "\"%d\""; + public bool Editable { get; init; } = true; + + public CustomExplorerViewModel Copy() + { + return new CustomExplorerViewModel + { + Name = Name, + Path = Path, + FileArgument = FileArgument, + DirectoryArgument = DirectoryArgument, + Editable = Editable + }; + } + } +} diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 5f9082fe935..34e86a3edf5 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -1,10 +1,12 @@ using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Drawing; using System.Text.Json.Serialization; using Flow.Launcher.Plugin; using Flow.Launcher.Plugin.SharedModels; using Flow.Launcher; +using Flow.Launcher.ViewModel; namespace Flow.Launcher.Infrastructure.UserSettings { @@ -37,6 +39,49 @@ public string Language public string ResultFontStretch { get; set; } public bool UseGlyphIcons { get; set; } = true; + public int CustomExplorerIndex { get; set; } = 0; + + [JsonIgnore] + public CustomExplorerViewModel CustomExplorer + { + get => CustomExplorerList[CustomExplorerIndex < CustomExplorerList.Count ? CustomExplorerIndex : 0]; + set => CustomExplorerList[CustomExplorerIndex] = value; + } + + public List CustomExplorerList { get; set; } = new() + { + new() + { + Name = "Explorer", + Path = "explorer", + DirectoryArgument = "\"%d\"", + FileArgument = "/select, \"%f\"", + Editable = false + }, + new() + { + Name = "Total Commander", + Path = @"C:\Program Files\totalcmd\TOTALCMD64.exe", + DirectoryArgument = "/O /A /S /T \"%d\"", + FileArgument = "/O /A /S /T \"%f\"" + }, + new() + { + Name = "Directory Opus", + Path = @"C:\Program Files\GPSoftware\Directory Opus\dopusrt.exe", + DirectoryArgument = "/cmd Go \"%d\" NEW", + FileArgument = "/cmd Go \"%f\" NEW" + + }, + new() + { + Name = "Files", + Path = "Files", + DirectoryArgument = "-select \"%d\"", + FileArgument = "-select \"%f\"" + } + }; + /// /// when false Alphabet static service will always return empty results diff --git a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs index 974eafa9646..3abdaf01f4e 100644 --- a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs @@ -190,5 +190,12 @@ public interface IPublicAPI /// Type for Serialization /// void SaveSettingJsonStorage() where T : new(); + + /// + /// Open directory in an explorer configured by user via Flow's Settings. The default is Windows Explorer + /// + /// Directory Path to open + /// Extra FileName Info + public void OpenDirectory(string DirectoryPath, string FileName = null); } } diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 1cb203515a3..5ca6bdbfd7a 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -1,7 +1,8 @@ - - + + Failed to register hotkey: {0} Could not start {0} Invalid Flow Launcher plugin file format @@ -15,7 +16,7 @@ Exit Close - + Flow Launcher Settings General Portable Mode @@ -33,6 +34,8 @@ Maximum results shown Ignore hotkeys in fullscreen mode Disable Flow Launcher activation when a full screen application is active (Recommended for games). + Default File Manager + Select the file manager to use when opening the folder. Python Directory Auto Update Select @@ -44,7 +47,7 @@ Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for translating Chinese Shadow effect is not allowed while current theme has blur effect enabled - + Plugins Find more plugins On @@ -62,12 +65,12 @@ Query time: - + Plugin Store Refresh Install - + Theme Theme Gallery How to create a theme @@ -81,7 +84,7 @@ Theme Folder Open Theme Folder - + Hotkey Flow Launcher Hotkey Enter shortcut to show/hide Flow Launcher. @@ -102,7 +105,7 @@ Use Segoe Fluent Icons Use Segoe Fluent Icons for query results where supported - + HTTP Proxy Enable HTTP Proxy HTTP Server @@ -118,7 +121,7 @@ Proxy configured correctly Proxy connection failed - + About Website Version @@ -127,18 +130,28 @@ New version {0} is available, would you like to restart Flow Launcher to use the update? Check updates failed, please check your connection and proxy settings to api.github.com. - Download updates failed, please check your connection and proxy settings to github-cloud.s3.amazonaws.com, + Download updates failed, please check your connection and proxy settings to github-cloud.s3.amazonaws.com, or go to https://github.com/Flow-Launcher/Flow.Launcher/releases to download updates manually. Release Notes Usage Tips: - + + Select File Manager + Please specify the file location of the file manager you using and add arguments if necessary. The default arguments is "%d", and a path is entered at that location. For example, If a command is required such as "totalcmd.exe /A c:\windows", argument is /A "%d". + "%f" is an argument that represent the file path. It is used to emphasize the file/folder name when opening a specific file location in 3rd party file manager. This argument is only available in the "Arg for File" item. If the file manager does not have that function, you can use "%d". + File Manager + Profile Name + File Manager Path + Arg For Folder + Arg For File + + Change Priority Greater the number, the higher the result will be ranked. Try setting it as 5. If you want the results to be lower than any other plugin's, provide a negative number Please provide an valid integer for Priority! - + Old Action Keyword New Action Keyword Cancel @@ -148,9 +161,9 @@ This new Action Keyword is already assigned to another plugin, please choose a different one Success Completed successfully - Enter the action keyword you need to start the plug-in. Use * if you don't want to specify an action keyword. In the case, The plug-in works without keywords. + Enter the action keyword you need to start the plug-in. Use * if you don't want to specify an action keyword. In the case, The plug-in works without keywords. - + Custom Query Hotkey Press the custom hotkey to automatically insert the specified query. Preview @@ -158,10 +171,10 @@ Invalid plugin hotkey Update - + Hotkey Unavailable - + Version Time Please tell us how application crashed so we can fix it @@ -177,16 +190,18 @@ Failed to send report Flow Launcher got an error - + Please wait... - + Checking for new update You already have the latest Flow Launcher version Update found Updating... - Flow Launcher was not able to move your user profile data to the new update version. - Please manually move your profile data folder from {0} to {1} + + Flow Launcher was not able to move your user profile data to the new update version. + Please manually move your profile data folder from {0} to {1} + New Update New Flow Launcher release {0} is now available An error occurred while trying to install software updates diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 71f6f1be44c..e90a53fc3a1 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -22,6 +22,8 @@ using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.Storage; using System.Collections.Concurrent; +using Flow.Launcher.Plugin.SharedCommands; +using System.Diagnostics; namespace Flow.Launcher { @@ -158,7 +160,7 @@ public void SavePluginSettings() if (!_pluginJsonStorages.ContainsKey(type)) _pluginJsonStorages[type] = new PluginJsonStorage(); - return ((PluginJsonStorage) _pluginJsonStorages[type]).Load(); + return ((PluginJsonStorage)_pluginJsonStorages[type]).Load(); } public void SaveSettingJsonStorage() where T : new() @@ -167,7 +169,7 @@ public void SavePluginSettings() if (!_pluginJsonStorages.ContainsKey(type)) _pluginJsonStorages[type] = new PluginJsonStorage(); - ((PluginJsonStorage) _pluginJsonStorages[type]).Save(); + ((PluginJsonStorage)_pluginJsonStorages[type]).Save(); } public void SaveJsonStorage(T settings) where T : new() @@ -175,7 +177,22 @@ public void SavePluginSettings() var type = typeof(T); _pluginJsonStorages[type] = new PluginJsonStorage(settings); - ((PluginJsonStorage) _pluginJsonStorages[type]).Save(); + ((PluginJsonStorage)_pluginJsonStorages[type]).Save(); + } + + public void OpenDirectory(string DirectoryPath, string FileName = null) + { + using Process explorer = new Process(); + var explorerInfo = _settingsVM.Settings.CustomExplorer; + explorer.StartInfo = new ProcessStartInfo + { + FileName = explorerInfo.Path, + Arguments = FileName is null ? + explorerInfo.DirectoryArgument.Replace("%d", DirectoryPath) : + explorerInfo.FileArgument.Replace("%d", DirectoryPath).Replace("%f", + Path.IsPathRooted(FileName) ? FileName : Path.Combine(DirectoryPath, FileName)) + }; + explorer.Start(); } public event FlowLauncherGlobalKeyboardEventHandler GlobalKeyboardEvent; @@ -188,7 +205,7 @@ private bool KListener_hookedKeyboardCallback(KeyEvent keyevent, int vkcode, Spe { if (GlobalKeyboardEvent != null) { - return GlobalKeyboardEvent((int) keyevent, vkcode, state); + return GlobalKeyboardEvent((int)keyevent, vkcode, state); } return true; diff --git a/Flow.Launcher/SelectFileManagerWindow.xaml b/Flow.Launcher/SelectFileManagerWindow.xaml new file mode 100644 index 00000000000..eba794c96ca --- /dev/null +++ b/Flow.Launcher/SelectFileManagerWindow.xaml @@ -0,0 +1,244 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Flow.Launcher/SelectFileManagerWindow.xaml.cs b/Flow.Launcher/SelectFileManagerWindow.xaml.cs new file mode 100644 index 00000000000..4f6fb343911 --- /dev/null +++ b/Flow.Launcher/SelectFileManagerWindow.xaml.cs @@ -0,0 +1,88 @@ +using Flow.Launcher.Infrastructure.UserSettings; +using Flow.Launcher.ViewModel; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; + +namespace Flow.Launcher +{ + public partial class SelectFileManagerWindow : Window, INotifyPropertyChanged + { + private int selectedCustomExplorerIndex; + + public event PropertyChangedEventHandler PropertyChanged; + + public Settings Settings { get; } + + public int SelectedCustomExplorerIndex + { + get => selectedCustomExplorerIndex; set + { + selectedCustomExplorerIndex = value; + PropertyChanged?.Invoke(this, new(nameof(CustomExplorer))); + } + } + public ObservableCollection CustomExplorers { get; set; } + + public CustomExplorerViewModel CustomExplorer => CustomExplorers[SelectedCustomExplorerIndex]; + public SelectFileManagerWindow(Settings settings) + { + Settings = settings; + CustomExplorers = new ObservableCollection(Settings.CustomExplorerList.Select(x => x.Copy())); + SelectedCustomExplorerIndex = Settings.CustomExplorerIndex; + InitializeComponent(); + } + + private void btnCancel_Click(object sender, RoutedEventArgs e) + { + Close(); + } + + private void btnDone_Click(object sender, RoutedEventArgs e) + { + Settings.CustomExplorerList = CustomExplorers.ToList(); + Settings.CustomExplorerIndex = SelectedCustomExplorerIndex; + Close(); + } + + private void btnAdd_Click(object sender, RoutedEventArgs e) + { + CustomExplorers.Add(new() + { + Name = "New Profile" + }); + SelectedCustomExplorerIndex = CustomExplorers.Count - 1; + } + + private void btnDelete_Click(object sender, RoutedEventArgs e) + { + CustomExplorers.RemoveAt(SelectedCustomExplorerIndex--); + } + + private void btnBrowseFile_Click(object sender, RoutedEventArgs e) + { + Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog(); + Nullable result = dlg.ShowDialog(); + + if (result == true) + { + TextBox path = (TextBox)(((FrameworkElement)sender).Parent as FrameworkElement).FindName("PathTextBox"); + path.Text = dlg.FileName; + path.Focus(); + ((Button)sender).Focus(); + } + } + } +} diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index e69b26f6345..2b5fd23972c 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -688,6 +688,35 @@ + + + + + + + +