diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 5cde9de83a8..6b00fc5a0eb 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -9,6 +9,7 @@ using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; +using Microsoft.AspNetCore.Routing; namespace Flow.Launcher.Core.Plugin { @@ -21,7 +22,7 @@ public static class PluginManager public static List AllPlugins { get; private set; } public static readonly List GlobalPlugins = new List(); - public static readonly Dictionary NonGlobalPlugins = new Dictionary(); + public static readonly Dictionary> NonGlobalPlugins = new Dictionary>(); public static IPublicAPI API { private set; get; } @@ -54,7 +55,7 @@ public static void Save() public static void ReloadData() { - foreach(var plugin in AllPlugins) + foreach (var plugin in AllPlugins) { var reloadablePlugin = plugin.Plugin as IReloadable; reloadablePlugin?.ReloadData(); @@ -108,7 +109,7 @@ public static void InitializePlugins(IPublicAPI api) catch (Exception e) { Log.Exception(nameof(PluginManager), $"Fail to Init plugin: {pair.Metadata.Name}", e); - pair.Metadata.Disabled = true; + pair.Metadata.Disabled = true; failedPlugins.Enqueue(pair); } }); @@ -123,7 +124,17 @@ public static void InitializePlugins(IPublicAPI api) plugin.Metadata.ActionKeywords .Where(x => x != Query.GlobalPluginWildcardSign) .ToList() - .ForEach(x => NonGlobalPlugins[x] = plugin); + .ForEach(x => + { + if (NonGlobalPlugins.ContainsKey(x)) + { + NonGlobalPlugins[x].Add(plugin); + } + else + { + NonGlobalPlugins[x] = new List { plugin }; + } + }); } if (failedPlugins.Any()) @@ -142,8 +153,7 @@ public static List ValidPluginsForQuery(Query query) { if (NonGlobalPlugins.ContainsKey(query.ActionKeyword)) { - var plugin = NonGlobalPlugins[query.ActionKeyword]; - return new List { plugin }; + return NonGlobalPlugins[query.ActionKeyword]; } else { @@ -233,11 +243,6 @@ public static List GetContextMenusForPlugin(Result result) return results; } - public static bool ActionKeywordRegistered(string actionKeyword) - { - return actionKeyword != Query.GlobalPluginWildcardSign - && NonGlobalPlugins.ContainsKey(actionKeyword); - } /// /// used to add action keyword for multiple action keyword plugin @@ -252,7 +257,14 @@ public static void AddActionKeyword(string id, string newActionKeyword) } else { - NonGlobalPlugins[newActionKeyword] = plugin; + if (NonGlobalPlugins.ContainsKey(newActionKeyword)) + { + NonGlobalPlugins[newActionKeyword].Add(plugin); + } + else + { + NonGlobalPlugins[newActionKeyword] = new List { plugin }; + } } plugin.Metadata.ActionKeywords.Add(newActionKeyword); } @@ -268,15 +280,17 @@ public static void RemoveActionKeyword(string id, string oldActionkeyword) && // Plugins may have multiple ActionKeywords that are global, eg. WebSearch plugin.Metadata.ActionKeywords .Where(x => x == Query.GlobalPluginWildcardSign) - .ToList() - .Count == 1) + .Count() == 1) { GlobalPlugins.Remove(plugin); } - - if (oldActionkeyword != Query.GlobalPluginWildcardSign) - NonGlobalPlugins.Remove(oldActionkeyword); - + + if (oldActionkeyword != Query.GlobalPluginWildcardSign + && plugin.Metadata.ActionKeywords.Where(x => x == oldActionkeyword).Count() == 1) + { + NonGlobalPlugins[oldActionkeyword].Remove(plugin); + } + plugin.Metadata.ActionKeywords.Remove(oldActionkeyword); } diff --git a/Flow.Launcher.Core/Plugin/QueryBuilder.cs b/Flow.Launcher.Core/Plugin/QueryBuilder.cs index df336e14b69..5c16b9252d6 100644 --- a/Flow.Launcher.Core/Plugin/QueryBuilder.cs +++ b/Flow.Launcher.Core/Plugin/QueryBuilder.cs @@ -7,7 +7,7 @@ namespace Flow.Launcher.Core.Plugin { public static class QueryBuilder { - public static Query Build(string text, Dictionary nonGlobalPlugins) + public static Query Build(string text, Dictionary> nonGlobalPlugins) { // replace multiple white spaces with one white space var terms = text.Split(new[] { Query.TermSeperater }, StringSplitOptions.RemoveEmptyEntries); @@ -20,7 +20,8 @@ public static Query Build(string text, Dictionary nonGlobalP string actionKeyword, search; string possibleActionKeyword = terms[0]; List actionParameters; - if (nonGlobalPlugins.TryGetValue(possibleActionKeyword, out var pluginPair) && !pluginPair.Metadata.Disabled) + if (nonGlobalPlugins.TryGetValue(possibleActionKeyword, out var pluginPairs) + && pluginPairs.Any(pluginPair => !pluginPair.Metadata.Disabled)) { // use non global plugin for query actionKeyword = possibleActionKeyword; actionParameters = terms.Skip(1).ToList(); diff --git a/Flow.Launcher.Test/QueryBuilderTest.cs b/Flow.Launcher.Test/QueryBuilderTest.cs index 6090ecc65ea..abadb5f233a 100644 --- a/Flow.Launcher.Test/QueryBuilderTest.cs +++ b/Flow.Launcher.Test/QueryBuilderTest.cs @@ -10,9 +10,9 @@ public class QueryBuilderTest [Test] public void ExclusivePluginQueryTest() { - var nonGlobalPlugins = new Dictionary + var nonGlobalPlugins = new Dictionary> { - {">", new PluginPair {Metadata = new PluginMetadata {ActionKeywords = new List {">"}}}} + {">", new List{ new PluginPair {Metadata = new PluginMetadata { ActionKeywords = new List { ">" } } }}} }; Query q = QueryBuilder.Build("> file.txt file2 file3", nonGlobalPlugins); @@ -24,9 +24,9 @@ public void ExclusivePluginQueryTest() [Test] public void ExclusivePluginQueryIgnoreDisabledTest() { - var nonGlobalPlugins = new Dictionary + var nonGlobalPlugins = new Dictionary> { - {">", new PluginPair {Metadata = new PluginMetadata {ActionKeywords = new List {">"}, Disabled = true}}} + {">", new List{new PluginPair {Metadata = new PluginMetadata {ActionKeywords = new List {">"}, Disabled = true}} } } }; Query q = QueryBuilder.Build("> file.txt file2 file3", nonGlobalPlugins); @@ -37,7 +37,7 @@ public void ExclusivePluginQueryIgnoreDisabledTest() [Test] public void GenericPluginQueryTest() { - Query q = QueryBuilder.Build("file.txt file2 file3", new Dictionary()); + Query q = QueryBuilder.Build("file.txt file2 file3", new Dictionary>()); Assert.AreEqual("file.txt file2 file3", q.Search); Assert.AreEqual("", q.ActionKeyword); diff --git a/Flow.Launcher/ActionKeywords.xaml.cs b/Flow.Launcher/ActionKeywords.xaml.cs index 4a236834748..db56c13a249 100644 --- a/Flow.Launcher/ActionKeywords.xaml.cs +++ b/Flow.Launcher/ActionKeywords.xaml.cs @@ -44,16 +44,9 @@ private void btnDone_OnClick(object sender, RoutedEventArgs _) var oldActionKeyword = plugin.Metadata.ActionKeywords[0]; var newActionKeyword = tbAction.Text.Trim(); newActionKeyword = newActionKeyword.Length > 0 ? newActionKeyword : "*"; - if (!pluginViewModel.IsActionKeywordRegistered(newActionKeyword)) - { - pluginViewModel.ChangeActionKeyword(newActionKeyword, oldActionKeyword); - Close(); - } - else - { - string msg = translater.GetTranslation("newActionKeywordsHasBeenAssigned"); - MessageBox.Show(msg); - } + pluginViewModel.ChangeActionKeyword(newActionKeyword, oldActionKeyword); + Close(); + } } } diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 7a3aa9f2f7f..775b1d5125f 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -450,18 +450,18 @@ private void RemoveOldQueryResults(Query query) { if (!string.IsNullOrEmpty(keyword)) { - Results.RemoveResultsExcept(PluginManager.NonGlobalPlugins[keyword].Metadata); + Results.RemoveResultsExcept(PluginManager.NonGlobalPlugins[keyword].Select(p=>p.Metadata)); } } else { if (string.IsNullOrEmpty(keyword)) { - Results.RemoveResultsFor(PluginManager.NonGlobalPlugins[lastKeyword].Metadata); + Results.RemoveResultsFor(PluginManager.NonGlobalPlugins[lastKeyword].Select(p => p.Metadata)); } else if (lastKeyword != keyword) { - Results.RemoveResultsExcept(PluginManager.NonGlobalPlugins[keyword].Metadata); + Results.RemoveResultsExcept(PluginManager.NonGlobalPlugins[keyword].Select(p => p.Metadata)); } } } diff --git a/Flow.Launcher/ViewModel/PluginViewModel.cs b/Flow.Launcher/ViewModel/PluginViewModel.cs index eb7e0054d10..113ef076afa 100644 --- a/Flow.Launcher/ViewModel/PluginViewModel.cs +++ b/Flow.Launcher/ViewModel/PluginViewModel.cs @@ -34,6 +34,5 @@ public void ChangeActionKeyword(string newActionKeyword, string oldActionKeyword OnPropertyChanged(nameof(ActionKeywordsText)); } - public bool IsActionKeywordRegistered(string newActionKeyword) => PluginManager.ActionKeywordRegistered(newActionKeyword); } } diff --git a/Flow.Launcher/ViewModel/ResultsViewModel.cs b/Flow.Launcher/ViewModel/ResultsViewModel.cs index d3085418062..47ba78326ed 100644 --- a/Flow.Launcher/ViewModel/ResultsViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs @@ -119,14 +119,16 @@ public void Clear() Results.Clear(); } - public void RemoveResultsExcept(PluginMetadata metadata) + public void RemoveResultsExcept(IEnumerable metadatas) { - Results.RemoveAll(r => r.Result.PluginID != metadata.ID); + var ids = metadatas.Select(m => m.ID).ToHashSet(); + Results.RemoveAll(r => !ids.Contains(r.Result.PluginID)); } - public void RemoveResultsFor(PluginMetadata metadata) + public void RemoveResultsFor(IEnumerable metadatas) { - Results.RemoveAll(r => r.Result.PluginID == metadata.ID); + var ids = metadatas.Select(m => m.ID).ToHashSet(); + Results.RemoveAll(r => ids.Contains(r.Result.PluginID)); } /// diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs index 7fcd77f0775..8b1a24b38b8 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs @@ -53,8 +53,6 @@ internal void UpdateActionKeyword(string newActionKeyword, string oldActionKeywo Settings.SearchActionKeyword = newActionKeyword; } - internal bool IsActionKeywordAlreadyAssigned(string newActionKeyword) => PluginManager.ActionKeywordRegistered(newActionKeyword); - internal bool IsNewActionKeywordGlobal(string newActionKeyword) => newActionKeyword == Query.GlobalPluginWildcardSign; } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs index 2957283ad1f..237a2e425ec 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs @@ -35,7 +35,7 @@ public ActionKeywordSetting(SettingsViewModel settingsViewModel, List x.Description == currentActionKeyword.Description).FirstOrDefault().Keyword = newActionKeyword; - Close(); + settingsViewModel.UpdateActionKeyword(newActionKeyword, currentActionKeyword.Keyword); - return; - } + actionKeywordListView.Where(x => x.Description == currentActionKeyword.Description).FirstOrDefault().Keyword = newActionKeyword; - MessageBox.Show(settingsViewModel.Context.API.GetTranslation("newActionKeywordsHasBeenAssigned")); + Close(); + + return; } private void OnCancelButtonClick(object sender, RoutedEventArgs e) diff --git a/Plugins/Flow.Launcher.Plugin.PluginIndicator/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Main.cs index 98e4d527a1e..b250146e24f 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginIndicator/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Main.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using System.Security.Cryptography.X509Certificates; using Flow.Launcher.Core.Plugin; namespace Flow.Launcher.Plugin.PluginIndicator @@ -12,7 +13,9 @@ public List Query(Query query) { var results = from keyword in PluginManager.NonGlobalPlugins.Keys where keyword.StartsWith(query.Terms[0]) - let metadata = PluginManager.NonGlobalPlugins[keyword].Metadata + from metadata in + from plugin in PluginManager.NonGlobalPlugins[keyword] + select plugin.Metadata where !metadata.Disabled select new Result { diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SearchSourceSetting.xaml.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SearchSourceSetting.xaml.cs index fd0701c012a..6c75561ea6e 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SearchSourceSetting.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SearchSourceSetting.xaml.cs @@ -20,13 +20,13 @@ public partial class SearchSourceSettingWindow public SearchSourceSettingWindow(IList sources, PluginInitContext context, SearchSource old) { _oldSearchSource = old; - _viewModel = new SearchSourceViewModel {SearchSource = old.DeepCopy()}; + _viewModel = new SearchSourceViewModel { SearchSource = old.DeepCopy() }; Initilize(sources, context, Action.Edit); } public SearchSourceSettingWindow(IList sources, PluginInitContext context) { - _viewModel = new SearchSourceViewModel {SearchSource = new SearchSource()}; + _viewModel = new SearchSourceViewModel { SearchSource = new SearchSource() }; Initilize(sources, context, Action.Add); } @@ -80,41 +80,26 @@ private void OnConfirmButtonClick(object sender, RoutedEventArgs e) private void AddSearchSource() { var keyword = _searchSource.ActionKeyword; - if (!PluginManager.ActionKeywordRegistered(keyword)) - { - var id = _context.CurrentPluginMetadata.ID; - PluginManager.AddActionKeyword(id, keyword); + var id = _context.CurrentPluginMetadata.ID; + PluginManager.AddActionKeyword(id, keyword); - _searchSources.Add(_searchSource); + _searchSources.Add(_searchSource); - Close(); - } - else - { - var warning = _api.GetTranslation("newActionKeywordsHasBeenAssigned"); - MessageBox.Show(warning); - } + Close(); } private void EditSearchSource() { var newKeyword = _searchSource.ActionKeyword; var oldKeyword = _oldSearchSource.ActionKeyword; - if (!PluginManager.ActionKeywordRegistered(newKeyword) || oldKeyword == newKeyword) - { - var id = _context.CurrentPluginMetadata.ID; - PluginManager.ReplaceActionKeyword(id, oldKeyword, newKeyword); + var id = _context.CurrentPluginMetadata.ID; + PluginManager.ReplaceActionKeyword(id, oldKeyword, newKeyword); - var index = _searchSources.IndexOf(_oldSearchSource); - _searchSources[index] = _searchSource; + var index = _searchSources.IndexOf(_oldSearchSource); + _searchSources[index] = _searchSource; + + Close(); - Close(); - } - else - { - var warning = _api.GetTranslation("newActionKeywordsHasBeenAssigned"); - MessageBox.Show(warning); - } if (!string.IsNullOrEmpty(selectedNewIconImageFullPath)) { @@ -128,7 +113,7 @@ private void EditSearchSource() private void OnSelectIconClick(object sender, RoutedEventArgs e) { const string filter = "Image files (*.jpg, *.jpeg, *.gif, *.png, *.bmp) |*.jpg; *.jpeg; *.gif; *.png; *.bmp"; - var dialog = new OpenFileDialog {InitialDirectory = Main.CustomImagesDirectory, Filter = filter}; + var dialog = new OpenFileDialog { InitialDirectory = Main.CustomImagesDirectory, Filter = filter }; var result = dialog.ShowDialog(); if (result == true) @@ -139,7 +124,7 @@ private void OnSelectIconClick(object sender, RoutedEventArgs e) { if (_viewModel.ShouldProvideHint(selectedNewIconImageFullPath)) MessageBox.Show(_api.GetTranslation("flowlauncher_plugin_websearch_iconpath_hint")); - + imgPreviewIcon.Source = _viewModel.LoadPreviewIcon(selectedNewIconImageFullPath); } }