Further Refactor UI Code and Implement Engine Selection and Everything Setting UI

This commit is contained in:
Hongtao Zhang 2022-07-03 19:50:02 -05:00
parent 70e9a0ca84
commit d36d15c887
No known key found for this signature in database
GPG key ID: 75F655B91C7AC9BB
13 changed files with 714 additions and 329 deletions

View file

@ -8,6 +8,7 @@
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
<Nullable>warnings</Nullable>
<ApplicationIcon />
<StartupObject />
</PropertyGroup>

View file

@ -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())}";
}
}

View file

@ -24,7 +24,12 @@
<system:String x:Key="plugin_explorer_generalsetting_header">General Setting</system:String>
<system:String x:Key="plugin_explorer_manageactionkeywords_header">Customise Action Keywords</system:String>
<system:String x:Key="plugin_explorer_quickaccesslinks_header">Quick Access Links</system:String>
<system:String x:Key="plugin_explorer_everything_setting_header">Everything Setting</system:String>
<system:String x:Key="plugin_explorer_everything_sort_option">Sort Option:</system:String>
<system:String x:Key="plugin_explorer_launch_hidden">Launch Hidden</system:String>
<system:String x:Key="plugin_explorer_editor_path">Editor Path</system:String>
<system:String x:Key="plugin_explorer_indexsearchexcludedpaths_header">Index Search Excluded Paths</system:String>
<system:String x:Key="plugin_explorer_use_location_as_working_dir">Use search result's location as executable working directory</system:String>
<system:String x:Key="plugin_explorer_usewindowsindexfordirectorysearch">Use Index Search For Path Search</system:String>
<system:String x:Key="plugin_explorer_usewindowsindexfordirectorysearch_tooltip">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</system:String>
<system:String x:Key="plugin_explorer_manageindexoptions">Indexing Options</system:String>
@ -37,7 +42,11 @@
<system:String x:Key="plugin_explorer_actionkeyword_done">Done</system:String>
<system:String x:Key="plugin_explorer_actionkeyword_enabled">Enabled</system:String>
<system:String x:Key="plugin_explorer_actionkeyword_enabled_tooltip">When disabled Flow will not execute this search option, and will additionally revert back to '*' to free up the action keyword</system:String>
<system:String x:Key="plugin_explorer_engine_everything">Everything</system:String>
<system:String x:Key="plugin_explorer_engine_windows_index">Windows Index</system:String>
<system:String x:Key="plugin_explorer_path_enumeration_engine_none">Direct Enumeration</system:String>
<!--Plugin Infos-->
<system:String x:Key="plugin_explorer_plugin_name">Explorer</system:String>
<system:String x:Key="plugin_explorer_plugin_description">Search and manage files and folders. Explorer utilises Windows Index Search</system:String>

View file

@ -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;

View file

@ -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.

View file

@ -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<SearchResult> 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<SearchResult>();
}
}

View file

@ -104,6 +104,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
///</summary>
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;

View file

@ -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<AccessLink> QuickAccessLinks { get; set; } = new ();
public ObservableCollection<AccessLink> QuickAccessLinks { get; set; } = new();
// as at v1.7.0 this is to maintain backwards compatibility, need to be removed afterwards.
public ObservableCollection<AccessLink> QuickFolderAccessLinks { get; set; } = new ();
public ObservableCollection<AccessLink> QuickFolderAccessLinks { get; set; } = new();
public ObservableCollection<AccessLink> IndexSearchExcludedSubdirectoryPaths { get; set; } = new ObservableCollection<AccessLink>();
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<AccessLink> IndexSearchExcludedSubdirectoryPaths { get; set; } = new ObservableCollection<AccessLink>();
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<IIndexProvider> _indexProviders;
private IReadOnlyList<IContentIndexProvider> _fileContentIndexProviders;
private IReadOnlyList<IPathEnumerable> _pathEnumerables;
public Settings()
{
var everythingManager = new EverythingSearchManager(this);
var windowsIndexManager = new WindowsIndexSearchManager(this);
_indexProviders = new List<IIndexProvider>()
{
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<IPathEnumerable>()
{
everythingManager,
windowsIndexManager
};
_fileContentIndexProviders = new List<IContentIndexProvider>
{
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<SortOption>();
public SortOption SortOption { get; set; } = SortOption.NAME_ASCENDING;
public bool EnableEverythingContentSearch { get; set; } = false;
#endregion

View file

@ -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<T> where T : struct, Enum
{
public static IReadOnlyList<EnumBindingModel<T>> CreateList()
{
return Enum.GetValues<T>()
.Select(value => new EnumBindingModel<T>
{
Value = value, LocalizationKey = GetDescriptionAttr(value)
})
.ToArray();
}
public EnumBindingModel<T> From(T value)
{
var name = value.ToString();
var description = GetDescriptionAttr(value);
return new EnumBindingModel<T>
{
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; }
}

View file

@ -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<EnumBindingModel<Settings.IndexSearchEngineOption>> IndexSearchEngines { get; set; }
public IReadOnlyList<EnumBindingModel<Settings.ContentIndexSearchEngineOption>> ContentIndexSearchEngines { get; set; }
public IReadOnlyList<EnumBindingModel<Settings.PathEnumerationEngineOption>> 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<Settings>();
}
#region Engine Selection
private EnumBindingModel<Settings.IndexSearchEngineOption> _selectedIndexSearchEngine;
private EnumBindingModel<Settings.ContentIndexSearchEngineOption> _selectedContentSearchEngine;
private EnumBindingModel<Settings.PathEnumerationEngineOption> _selectedPathEnumerationEngine;
public EnumBindingModel<Settings.IndexSearchEngineOption> SelectedIndexSearchEngine
{
get => _selectedIndexSearchEngine;
set
{
_selectedIndexSearchEngine = value;
Settings.IndexSearchEngine = value.Value;
OnPropertyChanged();
}
}
public EnumBindingModel<Settings.ContentIndexSearchEngineOption> SelectedContentSearchEngine
{
get => _selectedContentSearchEngine;
set
{
_selectedContentSearchEngine = value;
Settings.ContentSearchEngine = value.Value;
OnPropertyChanged();
}
}
public EnumBindingModel<Settings.PathEnumerationEngineOption> SelectedPathEnumerationEngine
{
get => _selectedPathEnumerationEngine;
set
{
_selectedPathEnumerationEngine = value;
Settings.PathEnumerationEngine = value.Value;
OnPropertyChanged();
}
}
private void InitializeEngineSelection()
{
IndexSearchEngines = EnumBindingModel<Settings.IndexSearchEngineOption>.CreateList();
ContentIndexSearchEngines = EnumBindingModel<Settings.ContentIndexSearchEngineOption>.CreateList();
PathEnumerationEngines = EnumBindingModel<Settings.PathEnumerationEngineOption>.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<ActionKeywordModel>
{
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<ActionKeywordModel> 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<AccessLink> collection;
switch (container)
(AccessLink selectedLink, ObservableCollection<AccessLink> 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
}
}

View file

@ -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();
}
}

View file

@ -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 @@
<DataTemplate x:Key="ListViewTemplateAccessLinks" DataType="qa:AccessLink">
<TextBlock Margin="0,5,0,5" Text="{Binding Path, Mode=OneTime}" />
</DataTemplate>
<DataTemplate x:Key="ListViewActionKeywords" DataType="views:ActionKeywordView">
<DataTemplate x:Key="ListViewActionKeywords" DataType="{x:Type views:ActionKeywordModel}">
<Grid>
<TextBlock
Margin="0,5,0,0"
@ -53,6 +54,7 @@
</TextBlock>
</Grid>
</DataTemplate>
<converters:EnumNameConverter x:Key="EnumNameConverter" />
</UserControl.Resources>
<Grid Margin="10">
<Grid.RowDefinitions>
@ -61,32 +63,145 @@
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Margin="20,35,0,0">
<StackPanel>
<Expander
Header="{DynamicResource plugin_explorer_generalsetting_header}">
<Expander
Margin="15"
Header="{DynamicResource plugin_explorer_generalsetting_header}"
Expanded="SettingExpander_OnExpanded"
Collapsed="SettingExpander_OnCollapsed">
<StackPanel Orientation="Vertical">
<StackPanel.Resources>
<DataTemplate x:Key="EnumBindingModelItemTemplate">
<TextBlock Text="{Binding Description}" />
</DataTemplate>
</StackPanel.Resources>
<CheckBox
Name="UseWindowsIndexForDirectorySearch"
Margin="12,10,0,0"
Content="{DynamicResource plugin_explorer_usewindowsindexfordirectorysearch}"
IsChecked="{Binding UseWindowsIndexForDirectorySearch}"
ToolTip="{DynamicResource plugin_explorer_usewindowsindexfordirectorysearch_tooltip}" />
<ComboBox
Width="200"
ItemsSource="{Binding Settings.SortOptions, Mode=OneWay}"
SelectedItem="{Binding Settings.SortOption}" />
<CheckBox
Margin="12,10,0,0"
HorizontalAlignment="Left"
Content="{DynamicResource plugin_explorer_use_location_as_working_dir}"
IsChecked="{Binding Settings.UseLocationAsWorkingDir}" />
<CheckBox
Margin="12,10,0,0"
HorizontalAlignment="Left"
Content="{DynamicResource plugin_explorer_launch_hidden}"
IsChecked="{Binding Settings.LaunchHidden}" />
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="10,6,6,6"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{DynamicResource plugin_explorer_editor_path}" />
<TextBox
Margin="15"
HorizontalAlignment="Left"
VerticalAlignment="Center"
TextWrapping="NoWrap"
MaxWidth="250"
Text="{Binding EditorPath}" />
<Button
MinWidth="50"
HorizontalAlignment="Left"
Margin="15"
Command="{Binding OpenEditorPath}"
Content="..." />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Index Search Engine" />
<ComboBox
Margin="15"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Width="250"
ItemsSource="{Binding IndexSearchEngines }"
SelectedItem="{Binding SelectedIndexSearchEngine}"
ItemTemplate="{StaticResource EnumBindingModelItemTemplate}">
</ComboBox>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Content Search Engine" />
<ComboBox
Margin="15"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Width="250"
ItemsSource="{Binding ContentIndexSearchEngines }"
SelectedItem="{Binding SelectedContentSearchEngine}"
ItemTemplate="{StaticResource EnumBindingModelItemTemplate}">
</ComboBox>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Path Enumeration Engine" />
<ComboBox
Margin="15"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Width="250"
ItemsSource="{Binding PathEnumerationEngines }"
SelectedItem="{Binding SelectedPathEnumerationEngine}"
ItemTemplate="{StaticResource EnumBindingModelItemTemplate}">
</ComboBox>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button
Margin="15"
Content="Open Window Index Option"
Click="btnOpenIndexingOptions_Click"/>
</StackPanel>
</StackPanel>
</Expander>
<Expander
Name="expActionKeywords"
Margin="15"
Header="{DynamicResource plugin_explorer_everything_setting_header}"
Expanded="SettingExpander_OnExpanded"
Collapsed="SettingExpander_OnCollapsed">
<DockPanel>
<TextBlock
Text="{DynamicResource plugin_explorer_everything_sort_option}"
Margin="15" />
<ComboBox
DockPanel.Dock="Left"
Width="200"
VerticalAlignment="Center"
ItemsSource="{Binding Settings.SortOptions, Mode=OneWay}"
SelectedItem="{Binding Settings.SortOption}"
SelectionChanged="EverythingSortOptionChanged">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Converter={StaticResource EnumNameConverter}}" />
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock
Name="tbFastSortWarning"
DockPanel.Dock="Right"
Margin="0 0 10 0"
VerticalAlignment="Center"
Foreground="Orange"
Text="{Binding SortOptionWarningMessage, Mode=OneTime}"
TextAlignment="Left"
TextWrapping="Wrap"
Visibility="{Binding FastSortWarningVisibility, Mode=OneTime}" />
</DockPanel>
</Expander>
<Expander
Margin="15"
Height="auto"
Expanded="expActionKeywords_Click"
Expanded="SettingExpander_OnExpanded"
Collapsed="SettingExpander_OnCollapsed"
Header="{DynamicResource plugin_explorer_manageactionkeywords_header}">
<DockPanel HorizontalAlignment="Stretch">
<ListView
x:Name="lbxActionKeywords"
DockPanel.Dock="Top"
ItemTemplate="{StaticResource ListViewActionKeywords}"
SelectedItem="{Binding SelectedActionKeyword}"/>
ItemsSource="{Binding ActionKeywordsModels}"
SelectedItem="{Binding SelectedActionKeyword}" />
<Button
MinWidth="100"
Margin="10"
@ -95,81 +210,89 @@
</DockPanel>
</Expander>
<Expander
Margin="15"
Expanded="SettingExpander_OnExpanded"
Collapsed="SettingExpander_OnCollapsed"
Name="expAccessLinks"
Margin="0,10,0,0"
Header="{DynamicResource plugin_explorer_quickaccesslinks_header}">
<DockPanel HorizontalAlignment="Stretch">
<ListView
x:Name="lbxAccessLinks"
Height="200"
AllowDrop="True"
DockPanel.Dock="Top"
ItemTemplate="{StaticResource ListViewTemplateAccessLinks}"
ItemsSource="{Binding Settings.QuickAccessLinks}"
SelectedItem="{Binding SelectedQuickAccessLink}" />
<StackPanel
HorizontalAlignment="Right"
DockPanel.Dock="Bottom"
Orientation="Horizontal">
<Button
MinWidth="100"
Margin="10"
Command="{Binding RemoveLinkCommand}"
CommandParameter="QuickAccessLink"
Content="{DynamicResource plugin_explorer_delete}" />
<Button
MinWidth="100"
Margin="10"
Command="{Binding EditLinkCommand}"
CommandParameter="QuickAccessLink"
Content="{DynamicResource plugin_explorer_edit}" />
<Button
MinWidth="100"
Margin="10"
Command="{Binding AddLinkCommand}"
CommandParameter="QuickAccessLink"
Content="{DynamicResource plugin_explorer_add}" />
</StackPanel>
</DockPanel>
<ScrollViewer>
<DockPanel HorizontalAlignment="Stretch">
<ListView
x:Name="lbxAccessLinks"
Height="200"
AllowDrop="True"
DragEnter="lbxAccessLinks_DragEnter"
Drop="LbxAccessLinks_OnDrop"
DockPanel.Dock="Top"
ItemTemplate="{StaticResource ListViewTemplateAccessLinks}"
ItemsSource="{Binding Settings.QuickAccessLinks}"
SelectedItem="{Binding SelectedQuickAccessLink}" />
<StackPanel
HorizontalAlignment="Right"
DockPanel.Dock="Bottom"
Orientation="Horizontal">
<Button
MinWidth="100"
Margin="10"
Command="{Binding RemoveLinkCommand}"
CommandParameter="QuickAccessLink"
Content="{DynamicResource plugin_explorer_delete}" />
<Button
MinWidth="100"
Margin="10"
Command="{Binding EditLinkCommand}"
CommandParameter="QuickAccessLink"
Content="{DynamicResource plugin_explorer_edit}" />
<Button
MinWidth="100"
Margin="10"
Command="{Binding AddLinkCommand}"
CommandParameter="QuickAccessLink"
Content="{DynamicResource plugin_explorer_add}" />
</StackPanel>
</DockPanel></ScrollViewer>
</Expander>
<Expander
x:Name="expExcludedPaths"
Margin="0,10,0,0"
Margin="15"
Expanded="SettingExpander_OnExpanded"
Collapsed="SettingExpander_OnCollapsed"
Header="{DynamicResource plugin_explorer_indexsearchexcludedpaths_header}">
<DockPanel HorizontalAlignment="Stretch">
<ListView
Name="lbxExcludedPaths"
AllowDrop="True"
DockPanel.Dock="Top"
DragEnter="lbxAccessLinks_DragEnter"
Drop="lbxAccessLinks_Drop"
ItemTemplate="{StaticResource ListViewActionKeywords}"
ItemsSource="{Binding Settings.IndexSearchExcludedSubdirectoryPaths}"
SelectedItem="{Binding SelectedIndexSearchExcludedPath}" />
<StackPanel
HorizontalAlignment="Right"
DockPanel.Dock="Bottom"
Orientation="Horizontal">
<Button
MinWidth="100"
Margin="10"
Command="{Binding RemoveLinkCommand}"
CommandParameter="IndexSearchExcludedPaths"
Content="{DynamicResource plugin_explorer_delete}" />
<Button
MinWidth="100"
Margin="10"
Command="{Binding EditLinkCommand}"
CommandParameter="IndexSearchExcludedPaths"
Content="{DynamicResource plugin_explorer_edit}" />
<Button
MinWidth="100"
Margin="10"
Command="{Binding AddLinkCommand}"
CommandParameter="IndexSearchExcludedPaths"
Content="{DynamicResource plugin_explorer_add}" />
</StackPanel>
</DockPanel>
<ScrollViewer>
<DockPanel HorizontalAlignment="Stretch">
<ListView
Name="lbxExcludedPaths"
AllowDrop="True"
DockPanel.Dock="Top"
DragEnter="lbxAccessLinks_DragEnter"
Drop="LbxExcludedPaths_OnDrop"
ItemTemplate="{StaticResource ListViewActionKeywords}"
ItemsSource="{Binding Settings.IndexSearchExcludedSubdirectoryPaths}"
SelectedItem="{Binding SelectedIndexSearchExcludedPath}" />
<StackPanel
HorizontalAlignment="Right"
DockPanel.Dock="Bottom"
Orientation="Horizontal">
<Button
MinWidth="100"
Margin="10"
Command="{Binding RemoveLinkCommand}"
CommandParameter="IndexSearchExcludedPaths"
Content="{DynamicResource plugin_explorer_delete}" />
<Button
MinWidth="100"
Margin="10"
Command="{Binding EditLinkCommand}"
CommandParameter="IndexSearchExcludedPaths"
Content="{DynamicResource plugin_explorer_edit}" />
<Button
MinWidth="100"
Margin="10"
Command="{Binding AddLinkCommand}"
CommandParameter="IndexSearchExcludedPaths"
Content="{DynamicResource plugin_explorer_add}" />
</StackPanel>
</DockPanel>
</ScrollViewer>
</Expander>
</StackPanel>
</StackPanel>

View file

@ -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<ActionKeywordModel> 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<ActionKeywordModel>
{
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);
}
}
}