diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj
index 4d2a4e50a..f6d144404 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj
@@ -8,6 +8,7 @@
true
false
en
+ warnings
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Helper/SortOptionTranlationHelper.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Helper/SortOptionTranlationHelper.cs
new file mode 100644
index 000000000..d3a6552d9
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Helper/SortOptionTranlationHelper.cs
@@ -0,0 +1,25 @@
+using Flow.Launcher.Plugin.Everything.Everything;
+using JetBrains.Annotations;
+using System;
+
+namespace Flow.Launcher.Plugin.Explorer.Helper;
+
+public static class SortOptionTranslationHelper
+{
+ [CanBeNull]
+ public static IPublicAPI API { get; internal set; }
+
+ public static string GetTranslatedName(this SortOption sortOption)
+ {
+ const string prefix = "flowlauncher_plugin_everything_sort_by_";
+
+ ArgumentNullException.ThrowIfNull(API);
+
+ var enumName = Enum.GetName(sortOption);
+ var splited = enumName.Split('_');
+ var name = string.Join('_', splited[..^1]);
+ var direction = splited[^1];
+
+ return $"{API.GetTranslation(prefix + name.ToLower())} {API.GetTranslation(prefix + direction.ToLower())}";
+ }
+}
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml
index bdd9dba17..691d61ffe 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml
@@ -24,7 +24,12 @@
General Setting
Customise Action Keywords
Quick Access Links
+ Everything Setting
+ Sort Option:
+ Launch Hidden
+ Editor Path
Index Search Excluded Paths
+ Use search result's location as executable working directory
Use Index Search For Path Search
Turning this on will return indexed directories/files faster, but if a directory/file is not indexed it will not show up. If a directory/file has been added to Index Search Excluded Path then it will still show up even if this option is on
Indexing Options
@@ -37,7 +42,11 @@
Done
Enabled
When disabled Flow will not execute this search option, and will additionally revert back to '*' to free up the action keyword
-
+ Everything
+ Windows Index
+ Direct Enumeration
+
+
Explorer
Search and manage files and folders. Explorer utilises Windows Index Search
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs
index 5c651266a..3b6096e20 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs
@@ -1,4 +1,5 @@
using Flow.Launcher.Infrastructure.Storage;
+using Flow.Launcher.Plugin.Explorer.Helper;
using Flow.Launcher.Plugin.Explorer.Search;
using Flow.Launcher.Plugin.Explorer.Search.Everything;
using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks;
@@ -16,7 +17,7 @@ namespace Flow.Launcher.Plugin.Explorer
{
public class Main : ISettingProvider, IAsyncPlugin, IContextMenu, IPluginI18n
{
- internal PluginInitContext Context { get; set; }
+ internal static PluginInitContext Context { get; set; }
internal Settings Settings;
@@ -50,6 +51,9 @@ namespace Flow.Launcher.Plugin.Explorer
contextMenu = new ContextMenu(Context, Settings, viewModel);
searchManager = new SearchManager(Settings, Context);
ResultManager.Init(Context, Settings);
+
+ SortOptionTranslationHelper.API = context.API;
+
EverythingApiDllImport.Load(Path.Combine(Context.CurrentPluginMetadata.PluginDirectory, "EverythingSDK",
Environment.Is64BitProcess ? "Everything64.dll" : "Everything86.dll"));
return Task.CompletedTask;
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs
index 462a27d7f..82eecd8fd 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs
@@ -83,17 +83,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo
{
Log.Exception(nameof(DirectoryInfoSearch), "Error occured while searching path", e);
- results.Add(
- new Result
- {
- Title = string.Format(SearchManager.Context.API.GetTranslation(
- "plugin_explorer_directoryinfosearch_error"),
- e.Message),
- Score = 501,
- IcoPath = Constants.ExplorerIconImagePath
- });
-
- return results;
+ throw;
}
// Initial ordering, this order can be updated later by UpdateResultView.MainViewModel based on history of user selection.
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs
index 99a5c35ba..255d52632 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs
@@ -53,9 +53,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search
if (string.IsNullOrEmpty(query.Search))
return QuickAccess.AccessLinkListAll(query, Settings.QuickAccessLinks);
- var quickaccessLinks = QuickAccess.AccessLinkListMatched(query, Settings.QuickAccessLinks);
+ var quickAccessLinks = QuickAccess.AccessLinkListMatched(query, Settings.QuickAccessLinks);
- results.UnionWith(quickaccessLinks);
+ results.UnionWith(quickAccessLinks);
}
IEnumerable searchResults;
@@ -164,7 +164,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
var recursiveIndicatorIndex = query.Search.IndexOf('>');
- if (recursiveIndicatorIndex > 0 && Settings.PathEnumerationEngine != Settings.PathTraversalEngineOption.Direct)
+ if (recursiveIndicatorIndex > 0 && Settings.PathEnumerationEngine != Settings.PathEnumerationEngineOption.Direct)
{
directoryResult =
await Settings.PathEnumerator.EnumerateAsync(query.Search[..recursiveIndicatorIndex],
@@ -176,7 +176,23 @@ namespace Flow.Launcher.Plugin.Explorer.Search
}
else
{
- directoryResult = DirectoryInfoSearch.TopLevelDirectorySearch(query, query.Search, token);
+ try
+ {
+ directoryResult = DirectoryInfoSearch.TopLevelDirectorySearch(query, query.Search, token);
+ }
+ catch (Exception e)
+ {
+ results.Add(
+ new Result
+ {
+ Title = string.Format(SearchManager.Context.API.GetTranslation(
+ "plugin_explorer_directoryinfosearch_error"),
+ e.Message),
+ Score = 501,
+ IcoPath = Constants.ExplorerIconImagePath
+ });
+ directoryResult = new List();
+ }
}
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs
index 20e85bbb5..96fb01d7e 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs
@@ -104,6 +104,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
///
public string QueryForAllFilesAndFolders(string userSearchString)
{
+ if (string.IsNullOrEmpty(userSearchString))
+ userSearchString = "*";
+
// Generate SQL from constructed parameters, converting the userSearchString from AQS->WHERE clause
return CreateBaseQuery().GenerateSQLFromUserQuery(userSearchString) + " AND " + QueryWhereRestrictionsForAllFilesAndFoldersSearch
+ QueryOrderByFileNameRestriction;
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs
index 4e3ea2a12..33df7b562 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs
@@ -6,6 +6,7 @@ using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
+using System.ComponentModel;
using System.Linq;
using System.Text.Json.Serialization;
@@ -15,14 +16,22 @@ namespace Flow.Launcher.Plugin.Explorer
{
public int MaxResult { get; set; } = 100;
- public ObservableCollection QuickAccessLinks { get; set; } = new ();
+ public ObservableCollection QuickAccessLinks { get; set; } = new();
// as at v1.7.0 this is to maintain backwards compatibility, need to be removed afterwards.
- public ObservableCollection QuickFolderAccessLinks { get; set; } = new ();
+ public ObservableCollection QuickFolderAccessLinks { get; set; } = new();
+
+ public ObservableCollection IndexSearchExcludedSubdirectoryPaths { get; set; } = new ObservableCollection();
+
+ public string EditorPath { get; set; } = "";
+
+
+ public bool UseLocationAsWorkingDir { get; set; } = false;
+
+ public bool ShowWindowsContextMenu { get; set; } = true;
public bool UseWindowsIndexForDirectorySearch { get; set; } = false;
- public ObservableCollection IndexSearchExcludedSubdirectoryPaths { get; set; } = new ObservableCollection();
public string SearchActionKeyword { get; set; } = Query.GlobalPluginWildcardSign;
@@ -44,76 +53,86 @@ namespace Flow.Launcher.Plugin.Explorer
public bool QuickAccessKeywordEnabled { get; set; }
+ public bool LaunchHidden { get; set; } = false;
+
+
public bool WarnWindowsSearchServiceOff { get; set; } = true;
- private IReadOnlyList _indexProviders;
- private IReadOnlyList _fileContentIndexProviders;
- private IReadOnlyList _pathEnumerables;
- public Settings()
- {
- var everythingManager = new EverythingSearchManager(this);
- var windowsIndexManager = new WindowsIndexSearchManager(this);
-
- _indexProviders = new List()
- {
- everythingManager,
- windowsIndexManager
- };
+ private EverythingSearchManager _everythingManagerInstance;
+ private WindowsIndexSearchManager _windowsIndexSearchManager;
+
+ #region SearchEngine
+
+ public PathEnumerationEngineOption PathEnumerationEngine { get; set; }
+
+ private EverythingSearchManager EverythingManagerInstance => _everythingManagerInstance ??= new EverythingSearchManager(this);
+ private WindowsIndexSearchManager WindowsIndexSearchManager => _windowsIndexSearchManager ??= new WindowsIndexSearchManager(this);
- _pathEnumerables = new List()
- {
- everythingManager,
- windowsIndexManager
- };
- _fileContentIndexProviders = new List
- {
- windowsIndexManager,
- everythingManager,
- };
- }
-
public IndexSearchEngineOption IndexSearchEngine { get; set; }
[JsonIgnore]
- public IIndexProvider IndexProvider => _indexProviders[(int)IndexSearchEngine];
-
- public PathTraversalEngineOption PathEnumerationEngine { get; set; }
+ public IIndexProvider IndexProvider => IndexSearchEngine switch
+ {
+ IndexSearchEngineOption.Everything => EverythingManagerInstance,
+ IndexSearchEngineOption.WindowsIndex => WindowsIndexSearchManager,
+ _ => throw new ArgumentOutOfRangeException(nameof(IndexSearchEngine))
+ };
+
[JsonIgnore]
- public IPathEnumerable PathEnumerator => _pathEnumerables[(int)PathEnumerationEngine];
+ public IPathEnumerable PathEnumerator => PathEnumerationEngine switch
+ {
+ PathEnumerationEngineOption.Everything => EverythingManagerInstance,
+ PathEnumerationEngineOption.WindowsIndex => WindowsIndexSearchManager,
+ _ => throw new ArgumentOutOfRangeException(nameof(PathEnumerationEngine))
+ };
public ContentIndexSearchEngineOption ContentSearchEngine { get; set; }
[JsonIgnore]
- public IContentIndexProvider ContentIndexProvider => _fileContentIndexProviders[(int)ContentSearchEngine];
-
- public enum PathTraversalEngineOption
+ public IContentIndexProvider ContentIndexProvider => ContentSearchEngine switch
{
+ ContentIndexSearchEngineOption.Everything => EverythingManagerInstance,
+ ContentIndexSearchEngineOption.WindowsIndex => WindowsIndexSearchManager,
+ _ => throw new ArgumentOutOfRangeException(nameof(ContentSearchEngine))
+ };
+
+ public enum PathEnumerationEngineOption
+ {
+ [Description("plugin_explorer_engine_everything")]
Everything,
+ [Description("plugin_explorer_engine_windows_index")]
WindowsIndex,
+ [Description("plugin_explorer_path_enumeration_engine_none")]
Direct
}
public enum IndexSearchEngineOption
{
+ [Description("plugin_explorer_engine_everything")]
Everything,
+ [Description("plugin_explorer_engine_windows_index")]
WindowsIndex
}
-
+
public enum ContentIndexSearchEngineOption
{
+ [Description("plugin_explorer_engine_everything")]
Everything,
+ [Description("plugin_explorer_engine_windows_index")]
WindowsIndex
}
-
- public bool LaunchHidden { get; set; } = false;
+
+ #endregion
+
#region Everything Settings
-
+
public string EverythingInstalledPath { get; set; }
+ [JsonIgnore]
public SortOption[] SortOptions { get; set; } = Enum.GetValues();
public SortOption SortOption { get; set; } = SortOption.NAME_ASCENDING;
-
+
public bool EnableEverythingContentSearch { get; set; } = false;
#endregion
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/EnumBindingModel.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/EnumBindingModel.cs
new file mode 100644
index 000000000..29c81a465
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/EnumBindingModel.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+namespace Flow.Launcher.Plugin.Explorer.ViewModels;
+
+public class EnumBindingModel where T : struct, Enum
+{
+ public static IReadOnlyList> CreateList()
+ {
+ return Enum.GetValues()
+ .Select(value => new EnumBindingModel
+ {
+ Value = value, LocalizationKey = GetDescriptionAttr(value)
+ })
+ .ToArray();
+ }
+
+ public EnumBindingModel From(T value)
+ {
+ var name = value.ToString();
+ var description = GetDescriptionAttr(value);
+
+ return new EnumBindingModel
+ {
+ Name = name,
+ LocalizationKey = description,
+ Value = value
+ };
+ }
+
+ private static string GetDescriptionAttr(T source)
+ {
+ var fi = source.GetType().GetField(source.ToString());
+
+ var attributes = (DescriptionAttribute[])fi?.GetCustomAttributes(
+ typeof(DescriptionAttribute), false);
+
+ return attributes is { Length: > 0 } ? attributes[0].Description : source.ToString();
+
+ }
+
+ public string Name { get; set; }
+ private string LocalizationKey { get; set; }
+ public string Description => Main.Context.API.GetTranslation(LocalizationKey);
+ public T Value { get; set; }
+}
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs
index e88452c49..08f25cd51 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs
@@ -1,27 +1,41 @@
using Flow.Launcher.Core.Plugin;
using Flow.Launcher.Infrastructure.Storage;
using Flow.Launcher.Plugin.Explorer.Search;
+using Flow.Launcher.Plugin.Explorer.Search.Everything;
+using Flow.Launcher.Plugin.Explorer.Search.Everything.Exceptions;
using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks;
using Flow.Launcher.Plugin.Explorer.Views;
+using System;
+using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
+using System.Linq;
using System.Threading.Tasks;
+using System.Windows;
using System.Windows.Forms;
using System.Windows.Input;
+using MessageBox = System.Windows.Forms.MessageBox;
namespace Flow.Launcher.Plugin.Explorer.ViewModels
{
- public class SettingsViewModel
+ public class SettingsViewModel : BaseModel
{
public Settings Settings { get; set; }
internal PluginInitContext Context { get; set; }
+ public IReadOnlyList> IndexSearchEngines { get; set; }
+ public IReadOnlyList> ContentIndexSearchEngines { get; set; }
+ public IReadOnlyList> PathEnumerationEngines { get; set; }
+
public SettingsViewModel(PluginInitContext context, Settings settings)
{
Context = context;
Settings = settings;
+
+ InitializeEngineSelection();
+ InitializeActionKeywordModels();
}
@@ -30,17 +44,84 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
Context.API.SaveSettingJsonStorage();
}
+ #region Engine Selection
+
+ private EnumBindingModel _selectedIndexSearchEngine;
+ private EnumBindingModel _selectedContentSearchEngine;
+ private EnumBindingModel _selectedPathEnumerationEngine;
+
+
+ public EnumBindingModel SelectedIndexSearchEngine
+ {
+ get => _selectedIndexSearchEngine;
+ set
+ {
+ _selectedIndexSearchEngine = value;
+ Settings.IndexSearchEngine = value.Value;
+ OnPropertyChanged();
+ }
+ }
+
+ public EnumBindingModel SelectedContentSearchEngine
+ {
+ get => _selectedContentSearchEngine;
+ set
+ {
+ _selectedContentSearchEngine = value;
+ Settings.ContentSearchEngine = value.Value;
+ OnPropertyChanged();
+ }
+ }
+
+ public EnumBindingModel SelectedPathEnumerationEngine
+ {
+ get => _selectedPathEnumerationEngine;
+ set
+ {
+ _selectedPathEnumerationEngine = value;
+ Settings.PathEnumerationEngine = value.Value;
+ OnPropertyChanged();
+ }
+ }
+
+ private void InitializeEngineSelection()
+ {
+ IndexSearchEngines = EnumBindingModel.CreateList();
+ ContentIndexSearchEngines = EnumBindingModel.CreateList();
+ PathEnumerationEngines = EnumBindingModel.CreateList();
+
+ SelectedIndexSearchEngine = IndexSearchEngines.FirstOrDefault(x => x.Value == Settings.IndexSearchEngine);
+ _selectedContentSearchEngine = ContentIndexSearchEngines.FirstOrDefault(x => x.Value == Settings.ContentSearchEngine);
+ _selectedPathEnumerationEngine = PathEnumerationEngines.FirstOrDefault(x => x.Value == Settings.PathEnumerationEngine);
+ }
+
+ #endregion
+
+
+
+ #region ActionKeyword
+
+ private void InitializeActionKeywordModels()
+ {
+ ActionKeywordsModels = new List
+ {
+ new(Settings.ActionKeyword.SearchActionKeyword,
+ Context.API.GetTranslation("plugin_explorer_actionkeywordview_search")),
+ new(Settings.ActionKeyword.FileContentSearchActionKeyword,
+ Context.API.GetTranslation("plugin_explorer_actionkeywordview_filecontentsearch")),
+ new(Settings.ActionKeyword.PathSearchActionKeyword,
+ Context.API.GetTranslation("plugin_explorer_actionkeywordview_pathsearch")),
+ new(Settings.ActionKeyword.IndexSearchActionKeyword,
+ Context.API.GetTranslation("plugin_explorer_actionkeywordview_indexsearch")),
+ new(Settings.ActionKeyword.QuickAccessActionKeyword,
+ Context.API.GetTranslation("plugin_explorer_actionkeywordview_quickaccess"))
+ };
+ }
+
+ public IReadOnlyList ActionKeywordsModels { get; set; }
- public AccessLink SelectedQuickAccessLink { get; set; }
- public AccessLink SelectedIndexSearchExcludedPath { get; set; }
public ActionKeywordModel SelectedActionKeyword { get; set; }
-
-
- public ICommand RemoveLinkCommand => new RelayCommand(RemoveLink);
- public ICommand EditLinkCommand => new RelayCommand(EditLink);
- public ICommand AddLinkCommand => new RelayCommand(AddLink);
-
public ICommand EditActionKeywordCommand => new RelayCommand(EditActionKeyword);
private void EditActionKeyword(object obj)
@@ -53,130 +134,116 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
var actionKeywordWindow = new ActionKeywordSetting(actionKeyword, Context.API);
- if (actionKeywordWindow.ShowDialog() ?? false)
+ if (!(actionKeywordWindow.ShowDialog() ?? false))
{
- if (actionKeyword.Enabled && !actionKeywordWindow.KeywordEnabled)
- {
+ return;
+ }
+
+ switch (actionKeyword.Enabled, actionKeywordWindow.KeywordEnabled)
+ {
+ case (true, false):
Context.API.RemoveActionKeyword(Context.CurrentPluginMetadata.ID, actionKeyword.Keyword);
- }
- else if (!actionKeyword.Enabled && actionKeywordWindow.KeywordEnabled)
- {
- Context.API.AddActionKeyword(Context.CurrentPluginMetadata.ID, actionKeyword.Keyword);
- }
- else if (actionKeyword.Enabled && actionKeywordWindow.KeywordEnabled)
- {
+ break;
+ case (true, true):
// same keyword will have dialog result false
Context.API.RemoveActionKeyword(Context.CurrentPluginMetadata.ID, actionKeyword.Keyword);
Context.API.AddActionKeyword(Context.CurrentPluginMetadata.ID, actionKeywordWindow.ActionKeyword);
- }
-
- (actionKeyword.Keyword, actionKeyword.Enabled) = (actionKeywordWindow.ActionKeyword, actionKeywordWindow.KeywordEnabled);
+ break;
+ case (false, true):
+ Context.API.AddActionKeyword(Context.CurrentPluginMetadata.ID, actionKeyword.Keyword);
+ break;
+ case (false, false):
+ throw new ArgumentException(
+ $"Both false in {nameof(actionKeyword)}.{nameof(actionKeyword.Enabled)} and {nameof(actionKeywordWindow)}.{nameof(actionKeywordWindow.KeywordEnabled)} should suggest that the ShowDialog() result is false");
}
+ (actionKeyword.Keyword, actionKeyword.Enabled) = (actionKeywordWindow.ActionKeyword, actionKeywordWindow.KeywordEnabled);
+
}
- private AccessLink? PromptUserSelectPath(ResultType type, string initialDirectory = null)
+ #endregion
+
+ #region AccessLinks
+
+ public AccessLink SelectedQuickAccessLink { get; set; }
+ public AccessLink SelectedIndexSearchExcludedPath { get; set; }
+
+
+
+ public ICommand RemoveLinkCommand => new RelayCommand(RemoveLink);
+ public ICommand EditLinkCommand => new RelayCommand(EditLink);
+ public ICommand AddLinkCommand => new RelayCommand(AddLink);
+
+ public void AppendLink(string containerName, AccessLink link)
{
- AccessLink newAccessLink = null;
-
- if (type is ResultType.Folder)
+ var container = containerName switch
{
- var folderBrowserDialog = new FolderBrowserDialog();
-
- if (initialDirectory is not null)
- folderBrowserDialog.InitialDirectory = initialDirectory;
-
- if (folderBrowserDialog.ShowDialog() != DialogResult.OK)
- return newAccessLink;
-
- newAccessLink = new AccessLink { Path = folderBrowserDialog.SelectedPath };
- }
- else if (type is ResultType.File)
- {
- var openFileDialog = new OpenFileDialog();
- if (initialDirectory is not null)
- openFileDialog.InitialDirectory = initialDirectory;
-
- if (openFileDialog.ShowDialog() != DialogResult.OK)
- return newAccessLink;
-
- newAccessLink = new AccessLink { Path = openFileDialog.FileName };
- }
- return newAccessLink;
+ "QuickAccessLink" => Settings.QuickAccessLinks,
+ "IndexSearchExcludedPath" => Settings.IndexSearchExcludedSubdirectoryPaths,
+ _ => throw new ArgumentException($"Unknown container name: {containerName}")
+ };
+ container.Add(link);
}
- private void EditLink(object obj)
+ private void EditLink(object commandParameter)
{
- if (obj is not string container) return;
-
- AccessLink selectedLink;
- ObservableCollection collection;
-
- switch (container)
+ (AccessLink selectedLink, ObservableCollection collection) = commandParameter switch
{
- case "QuickAccessLink":
- if (SelectedQuickAccessLink == null)
- {
- ShowUnselectedMessage();
- return;
- }
- selectedLink = SelectedQuickAccessLink;
- collection = Settings.QuickAccessLinks;
- break;
- case "IndexSearchExcludedPaths":
- if (SelectedIndexSearchExcludedPath == null)
- {
- ShowUnselectedMessage();
- return;
- }
- selectedLink = SelectedIndexSearchExcludedPath;
- collection = Settings.IndexSearchExcludedSubdirectoryPaths;
- break;
- default:
- return;
+ "QuickAccessLink" => (SelectedQuickAccessLink, Settings.QuickAccessLinks),
+ "IndexSearchExcludedPath" => (SelectedIndexSearchExcludedPath, Settings.IndexSearchExcludedSubdirectoryPaths),
+ _ => throw new ArgumentOutOfRangeException(nameof(commandParameter))
+ };
+
+ if (selectedLink is null)
+ {
+ ShowUnselectedMessage();
+ return;
}
- var link = PromptUserSelectPath(selectedLink.Type,
+ var path = PromptUserSelectPath(selectedLink.Type,
selectedLink.Type == ResultType.Folder
? selectedLink.Path
: Path.GetDirectoryName(selectedLink.Path));
- if (link is null)
+ if (path is null)
return;
collection.Remove(selectedLink);
- collection.Add(link);
+ collection.Add(new AccessLink
+ {
+ Path = path, Type = selectedLink.Type,
+ });
}
private void ShowUnselectedMessage()
{
- string warning = Context.API.GetTranslation("plugin_explorer_make_selection_warning");
+ var warning = Context.API.GetTranslation("plugin_explorer_make_selection_warning");
MessageBox.Show(warning);
}
- private void AddLink(object obj)
+
+ private void AddLink(object commandParameter)
{
- if (obj is not string container) return;
+ var container = commandParameter switch
+ {
+ "QuickAccessLink" => Settings.QuickAccessLinks,
+ "IndexSearchExcludedPaths" => Settings.IndexSearchExcludedSubdirectoryPaths,
+ _ => throw new ArgumentOutOfRangeException(nameof(commandParameter))
+ };
+
+ ArgumentNullException.ThrowIfNull(container);
var folderBrowserDialog = new FolderBrowserDialog();
if (folderBrowserDialog.ShowDialog() != DialogResult.OK)
return;
- var newAccessLink = new AccessLink { Path = folderBrowserDialog.SelectedPath };
-
-
- switch (container)
+ var newAccessLink = new AccessLink
{
- case "QuickAccessLink":
- if (SelectedQuickAccessLink == null) return;
- Settings.QuickAccessLinks.Add(newAccessLink);
- break;
- case "IndexSearchExcludedPaths":
- if (SelectedIndexSearchExcludedPath == null) return;
- Settings.IndexSearchExcludedSubdirectoryPaths.Add(newAccessLink);
- break;
- }
+ Path = folderBrowserDialog.SelectedPath
+ };
+
+ container.Add(newAccessLink);
}
private void RemoveLink(object obj)
@@ -197,11 +264,38 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
Save();
}
+ #endregion
+ private string? PromptUserSelectPath(ResultType type, string initialDirectory = null)
+ {
+ string path = null;
- internal void RemoveLinkFromQuickAccess(AccessLink selectedRow) => Settings.QuickAccessLinks.Remove(selectedRow);
+ if (type is ResultType.Folder)
+ {
+ var folderBrowserDialog = new FolderBrowserDialog();
+
+ if (initialDirectory is not null)
+ folderBrowserDialog.InitialDirectory = initialDirectory;
+
+ if (folderBrowserDialog.ShowDialog() != DialogResult.OK)
+ return path;
+
+ path = folderBrowserDialog.SelectedPath;
+ }
+ else if (type is ResultType.File)
+ {
+ var openFileDialog = new OpenFileDialog();
+ if (initialDirectory is not null)
+ openFileDialog.InitialDirectory = initialDirectory;
+
+ if (openFileDialog.ShowDialog() != DialogResult.OK)
+ return path;
+
+ path = openFileDialog.FileName;
+ }
+ return path;
+ }
- internal void RemoveAccessLinkFromExcludedIndexPaths(AccessLink selectedRow) => Settings.IndexSearchExcludedSubdirectoryPaths.Remove(selectedRow);
internal static void OpenWindowsIndexingOptions()
{
@@ -215,22 +309,69 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
Process.Start(psi);
}
- internal void UpdateActionKeyword(Settings.ActionKeyword modifiedActionKeyword, string newActionKeyword, string oldActionKeyword) =>
- PluginManager.ReplaceActionKeyword(Context.CurrentPluginMetadata.ID, oldActionKeyword, newActionKeyword);
+ public bool UseWindowsIndexForDirectorySearch
+ {
+ get => Settings.UseWindowsIndexForDirectorySearch;
+ set => Settings.UseWindowsIndexForDirectorySearch = value;
+ }
+ public ICommand OpenEditorPath => new RelayCommand(_ =>
+ {
+ var path = PromptUserSelectPath(ResultType.File, Settings.EditorPath != null ? Path.GetDirectoryName(Settings.EditorPath) : null);
+ if (path is null)
+ return;
- internal bool IsActionKeywordAlreadyAssigned(string newActionKeyword) => PluginManager.ActionKeywordRegistered(newActionKeyword);
+ EditorPath = path;
+ });
- internal bool IsNewActionKeywordGlobal(string newActionKeyword) => newActionKeyword == Query.GlobalPluginWildcardSign;
-
- public bool UseWindowsIndexForDirectorySearch {
- get
- {
- return Settings.UseWindowsIndexForDirectorySearch;
- }
+ public string EditorPath
+ {
+ get => Settings.EditorPath;
set
{
- Settings.UseWindowsIndexForDirectorySearch = value;
+ Settings.EditorPath = value;
+ OnPropertyChanged();
}
}
+
+
+ #region Everything FastSortWarning
+
+ public Visibility FastSortWarningVisibility
+ {
+ get
+ {
+ try
+ {
+ return EverythingApi.IsFastSortOption(Settings.SortOption) ? Visibility.Collapsed : Visibility.Visible;
+ }
+ catch (IPCErrorException)
+ {
+ // this error occurs if the Everything service is not running, in this instance show the warning and
+ // update the message to let user know in the settings panel.
+ return Visibility.Visible;
+ }
+ }
+ }
+ public string SortOptionWarningMessage
+ {
+ get
+ {
+ try
+ {
+ // this method is used to determine if Everything service is running because as at Everything v1.4.1
+ // the sdk does not provide a dedicated interface to determine if it is running.
+ return EverythingApi.IsFastSortOption(Settings.SortOption) ? string.Empty
+ : Context.API.GetTranslation("flowlauncher_plugin_everything_nonfastsort_warning");
+ }
+ catch (IPCErrorException)
+ {
+ return Context.API.GetTranslation("flowlauncher_plugin_everything_is_not_running");
+ }
+ }
+ }
+
+ #endregion
+
+
}
}
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/Converters/EverythingEnumNameConverter.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/Converters/EverythingEnumNameConverter.cs
new file mode 100644
index 000000000..e24b21dcd
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/Converters/EverythingEnumNameConverter.cs
@@ -0,0 +1,20 @@
+using Flow.Launcher.Plugin.Everything.Everything;
+using Flow.Launcher.Plugin.Explorer.Helper;
+using System;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace Flow.Launcher.Plugin.Explorer.Views.Converters;
+
+public class EnumNameConverter : IValueConverter
+{
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return value is SortOption option ? option.GetTranslatedName() : value;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+}
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml
index fc37577f5..bbc25a597 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml
@@ -7,6 +7,7 @@
xmlns:qa="clr-namespace:Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks"
xmlns:viewModels="clr-namespace:Flow.Launcher.Plugin.Explorer.ViewModels"
xmlns:views="clr-namespace:Flow.Launcher.Plugin.Explorer.Views"
+ xmlns:converters="clr-namespace:Flow.Launcher.Plugin.Explorer.Views.Converters"
d:DataContext="{d:DesignInstance viewModels:SettingsViewModel}"
d:DesignHeight="450"
d:DesignWidth="800"
@@ -15,7 +16,7 @@
-
+
+
@@ -61,32 +63,145 @@
-
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ItemsSource="{Binding ActionKeywordsModels}"
+ SelectedItem="{Binding SelectedActionKeyword}" />
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml.cs
index 878dc3518..facdd8308 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml.cs
@@ -6,10 +6,14 @@ using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Windows;
+using System.Windows.Controls;
using System.Windows.Forms;
+using System.Windows.Input;
+using System.Windows.Media;
using DataFormats = System.Windows.DataFormats;
using DragDropEffects = System.Windows.DragDropEffects;
using DragEventArgs = System.Windows.DragEventArgs;
+using ListView = System.Windows.Controls.ListView;
using MessageBox = System.Windows.MessageBox;
namespace Flow.Launcher.Plugin.Explorer.Views
@@ -23,6 +27,7 @@ namespace Flow.Launcher.Plugin.Explorer.Views
private List actionKeywordsListView;
+
public ExplorerSettings(SettingsViewModel viewModel)
{
DataContext = viewModel;
@@ -33,105 +38,36 @@ namespace Flow.Launcher.Plugin.Explorer.Views
DataContext = viewModel;
- lbxAccessLinks.ItemsSource = this.viewModel.Settings.QuickAccessLinks;
-
- lbxExcludedPaths.ItemsSource = this.viewModel.Settings.IndexSearchExcludedSubdirectoryPaths;
-
- actionKeywordsListView = new List
- {
- new(Settings.ActionKeyword.SearchActionKeyword,
- viewModel.Context.API.GetTranslation("plugin_explorer_actionkeywordview_search")),
- new(Settings.ActionKeyword.FileContentSearchActionKeyword,
- viewModel.Context.API.GetTranslation("plugin_explorer_actionkeywordview_filecontentsearch")),
- new(Settings.ActionKeyword.PathSearchActionKeyword,
- viewModel.Context.API.GetTranslation("plugin_explorer_actionkeywordview_pathsearch")),
- new(Settings.ActionKeyword.IndexSearchActionKeyword,
- viewModel.Context.API.GetTranslation("plugin_explorer_actionkeywordview_indexsearch")),
- new(Settings.ActionKeyword.QuickAccessActionKeyword,
- viewModel.Context.API.GetTranslation("plugin_explorer_actionkeywordview_quickaccess"))
- };
-
- lbxActionKeywords.ItemsSource = actionKeywordsListView;
-
ActionKeywordModel.Init(viewModel.Settings);
lbxAccessLinks.Items.SortDescriptions.Add(new SortDescription("Path", ListSortDirection.Ascending));
lbxExcludedPaths.Items.SortDescriptions.Add(new SortDescription("Path", ListSortDirection.Ascending));
}
-
- private void expActionKeywords_Click(object sender, RoutedEventArgs e)
+
+
+ private void AccessLinkDragDrop(string containerName, DragEventArgs e)
{
- if (expExcludedPaths.IsExpanded)
- expExcludedPaths.IsExpanded = false;
+ var files = (string[])e.Data.GetData(DataFormats.FileDrop);
- if (expAccessLinks.IsExpanded)
- expAccessLinks.IsExpanded = false;
- }
-
-
- private void expAccessLinks_Click(object sender, RoutedEventArgs e)
- {
- if (expExcludedPaths.IsExpanded)
- expExcludedPaths.IsExpanded = false;
-
- if (expActionKeywords.IsExpanded)
- expActionKeywords.IsExpanded = false;
- }
-
- private void expExcludedPaths_Click(object sender, RoutedEventArgs e)
- {
- if (expAccessLinks.IsExpanded)
- expAccessLinks.IsExpanded = false;
-
- if (expActionKeywords.IsExpanded)
- expActionKeywords.IsExpanded = false;
- }
-
-
- private void lbxAccessLinks_Drop(object sender, DragEventArgs e)
- {
- string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
-
- if (files != null && files.Count() > 0)
+ if (files == null || !files.Any())
{
- if (expAccessLinks.IsExpanded && viewModel.Settings.QuickAccessLinks == null)
- viewModel.Settings.QuickAccessLinks = new();
-
- foreach (string s in files)
+ return;
+ }
+ foreach (var s in files)
+ {
+ if (Directory.Exists(s))
{
- if (Directory.Exists(s))
+ var newFolderLink = new AccessLink
{
- var newFolderLink = new AccessLink { Path = s };
-
- AddAccessLink(newFolderLink);
- }
+ Path = s
+ };
+ viewModel.AppendLink(containerName, newFolderLink);
}
}
}
- private void AddAccessLink(AccessLink newAccessLink)
- {
- if (expAccessLinks.IsExpanded
- && !viewModel.Settings.QuickAccessLinks.Any(x => x.Path == newAccessLink.Path))
- {
- if (viewModel.Settings.QuickAccessLinks == null)
- viewModel.Settings.QuickAccessLinks = new();
-
- viewModel.Settings.QuickAccessLinks.Add(newAccessLink);
- }
-
- if (expExcludedPaths.IsExpanded
- && !viewModel.Settings.IndexSearchExcludedSubdirectoryPaths.Any(x => x.Path == newAccessLink.Path))
- {
- if (viewModel.Settings.IndexSearchExcludedSubdirectoryPaths == null)
- viewModel.Settings.IndexSearchExcludedSubdirectoryPaths = new ();
-
- viewModel.Settings.IndexSearchExcludedSubdirectoryPaths.Add(newAccessLink);
- }
- }
-
private void lbxAccessLinks_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
@@ -148,5 +84,53 @@ namespace Flow.Launcher.Plugin.Explorer.Views
{
SettingsViewModel.OpenWindowsIndexingOptions();
}
+ private void EverythingSortOptionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ if (tbFastSortWarning is not null)
+ {
+ tbFastSortWarning.Visibility = viewModel.FastSortWarningVisibility;
+ tbFastSortWarning.Text = viewModel.SortOptionWarningMessage;
+ }
+ }
+ private void SettingExpander_OnExpanded(object sender, RoutedEventArgs e)
+ {
+ if (sender is not Expander expander)
+ return;
+
+ var parentContainer = VisualTreeHelper.GetParent(expander);
+
+ if (parentContainer is not StackPanel stackPanel)
+ return;
+
+ foreach (UIElement child in stackPanel.Children)
+ {
+ if (child != expander)
+ child.Visibility = Visibility.Collapsed;
+ }
+ }
+ private void SettingExpander_OnCollapsed(object sender, RoutedEventArgs e)
+ {
+ if (sender is not Expander expander)
+ return;
+
+ var parentContainer = VisualTreeHelper.GetParent(expander);
+
+ if (parentContainer is not StackPanel stackPanel)
+ return;
+
+ foreach (UIElement child in stackPanel.Children)
+ {
+ if (child != expander)
+ child.Visibility = Visibility.Visible;
+ }
+ }
+ private void LbxAccessLinks_OnDrop(object sender, DragEventArgs e)
+ {
+ AccessLinkDragDrop("QuickAccessLink", e);
+ }
+ private void LbxExcludedPaths_OnDrop(object sender, DragEventArgs e)
+ {
+ AccessLinkDragDrop("IndexSearchExcludedPath", e);
+ }
}
}
\ No newline at end of file