diff --git a/Doc/Default Icons/app_missing_img.png b/Doc/Default Icons/app_missing_img.png new file mode 100644 index 000000000..b07eb4262 Binary files /dev/null and b/Doc/Default Icons/app_missing_img.png differ diff --git a/Doc/Default Icons/app_missing_img_01.png b/Doc/Default Icons/app_missing_img_01.png new file mode 100644 index 000000000..2cc466ed0 Binary files /dev/null and b/Doc/Default Icons/app_missing_img_01.png differ diff --git a/Doc/Default Icons/app_missing_img_01.svg b/Doc/Default Icons/app_missing_img_01.svg new file mode 100644 index 000000000..88b84858a --- /dev/null +++ b/Doc/Default Icons/app_missing_img_01.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Doc/Default Icons/app_missing_img_02.png b/Doc/Default Icons/app_missing_img_02.png new file mode 100644 index 000000000..285a68890 Binary files /dev/null and b/Doc/Default Icons/app_missing_img_02.png differ diff --git a/Doc/Default Icons/app_missing_img_02.svg b/Doc/Default Icons/app_missing_img_02.svg new file mode 100644 index 000000000..190ecf469 --- /dev/null +++ b/Doc/Default Icons/app_missing_img_02.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Doc/Default Icons/app_missing_img_03.png b/Doc/Default Icons/app_missing_img_03.png new file mode 100644 index 000000000..11d5466f0 Binary files /dev/null and b/Doc/Default Icons/app_missing_img_03.png differ diff --git a/Doc/Default Icons/app_missing_img_03.svg b/Doc/Default Icons/app_missing_img_03.svg new file mode 100644 index 000000000..904380091 --- /dev/null +++ b/Doc/Default Icons/app_missing_img_03.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Doc/Default Icons/app_missing_img_buttons.png b/Doc/Default Icons/app_missing_img_buttons.png new file mode 100644 index 000000000..62de71f21 Binary files /dev/null and b/Doc/Default Icons/app_missing_img_buttons.png differ diff --git a/Doc/Default Icons/app_missing_img_buttons.svg b/Doc/Default Icons/app_missing_img_buttons.svg new file mode 100644 index 000000000..b9d5e1259 --- /dev/null +++ b/Doc/Default Icons/app_missing_img_buttons.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Doc/Default Icons/app_missing_img_fluent.png b/Doc/Default Icons/app_missing_img_fluent.png new file mode 100644 index 000000000..eafd1bb35 Binary files /dev/null and b/Doc/Default Icons/app_missing_img_fluent.png differ diff --git a/Doc/Default Icons/app_missing_img_fluent.svg b/Doc/Default Icons/app_missing_img_fluent.svg new file mode 100644 index 000000000..7a201cdc3 --- /dev/null +++ b/Doc/Default Icons/app_missing_img_fluent.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Doc/Default Icons/app_missing_img_huge.png b/Doc/Default Icons/app_missing_img_huge.png new file mode 100644 index 000000000..70aea308d Binary files /dev/null and b/Doc/Default Icons/app_missing_img_huge.png differ diff --git a/Doc/Default Icons/app_missing_img_huge.svg b/Doc/Default Icons/app_missing_img_huge.svg new file mode 100644 index 000000000..874a9a0ca --- /dev/null +++ b/Doc/Default Icons/app_missing_img_huge.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Doc/Default Icons/app_missing_img_lightblue.png b/Doc/Default Icons/app_missing_img_lightblue.png new file mode 100644 index 000000000..3b262fc9b Binary files /dev/null and b/Doc/Default Icons/app_missing_img_lightblue.png differ diff --git a/Doc/Default Icons/app_missing_img_lightblue.svg b/Doc/Default Icons/app_missing_img_lightblue.svg new file mode 100644 index 000000000..da713b413 --- /dev/null +++ b/Doc/Default Icons/app_missing_img_lightblue.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Doc/Default Icons/app_missing_img_lightblue_buttons.png b/Doc/Default Icons/app_missing_img_lightblue_buttons.png new file mode 100644 index 000000000..17de0a6fe Binary files /dev/null and b/Doc/Default Icons/app_missing_img_lightblue_buttons.png differ diff --git a/Doc/Default Icons/app_missing_img_lightblue_buttons.svg b/Doc/Default Icons/app_missing_img_lightblue_buttons.svg new file mode 100644 index 000000000..7251fb9f6 --- /dev/null +++ b/Doc/Default Icons/app_missing_img_lightblue_buttons.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Doc/Default Icons/app_missing_img_minimal.png b/Doc/Default Icons/app_missing_img_minimal.png new file mode 100644 index 000000000..e66ea6d6e Binary files /dev/null and b/Doc/Default Icons/app_missing_img_minimal.png differ diff --git a/Doc/Default Icons/app_missing_img_minimal.svg b/Doc/Default Icons/app_missing_img_minimal.svg new file mode 100644 index 000000000..d2b6d22fe --- /dev/null +++ b/Doc/Default Icons/app_missing_img_minimal.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Doc/Default Icons/app_missing_img_minimal_buttons.png b/Doc/Default Icons/app_missing_img_minimal_buttons.png new file mode 100644 index 000000000..d4f240909 Binary files /dev/null and b/Doc/Default Icons/app_missing_img_minimal_buttons.png differ diff --git a/Doc/Default Icons/app_missing_img_minimal_buttons.svg b/Doc/Default Icons/app_missing_img_minimal_buttons.svg new file mode 100644 index 000000000..131ffadf8 --- /dev/null +++ b/Doc/Default Icons/app_missing_img_minimal_buttons.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Doc/app_missing_img.png b/Doc/app_missing_img.png new file mode 100644 index 000000000..11d5466f0 Binary files /dev/null and b/Doc/app_missing_img.png differ diff --git a/Flow.Launcher.Core/Configuration/Portable.cs b/Flow.Launcher.Core/Configuration/Portable.cs index 44e4434be..5bca087b8 100644 --- a/Flow.Launcher.Core/Configuration/Portable.cs +++ b/Flow.Launcher.Core/Configuration/Portable.cs @@ -95,7 +95,7 @@ namespace Flow.Launcher.Core.Configuration public void MoveUserDataFolder(string fromLocation, string toLocation) { - FilesFolders.Copy(fromLocation, toLocation); + FilesFolders.CopyAll(fromLocation, toLocation); VerifyUserDataAfterMove(fromLocation, toLocation); } diff --git a/Flow.Launcher.Core/Plugin/QueryBuilder.cs b/Flow.Launcher.Core/Plugin/QueryBuilder.cs index 9ad6466e9..df336e14b 100644 --- a/Flow.Launcher.Core/Plugin/QueryBuilder.cs +++ b/Flow.Launcher.Core/Plugin/QueryBuilder.cs @@ -29,7 +29,6 @@ namespace Flow.Launcher.Core.Plugin else { // non action keyword actionKeyword = string.Empty; - actionParameters = terms.ToList(); search = rawQuery; } @@ -38,10 +37,7 @@ namespace Flow.Launcher.Core.Plugin Terms = terms, RawQuery = rawQuery, ActionKeyword = actionKeyword, - Search = search, - // Obsolete value initialisation - ActionName = actionKeyword, - ActionParameters = actionParameters + Search = search }; return query; diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index 99d48275a..20df23e40 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -91,7 +91,7 @@ namespace Flow.Launcher.Core if (DataLocation.PortableDataLocationInUse()) { var targetDestination = updateManager.RootAppDirectory + $"\\app-{newReleaseVersion.ToString()}\\{DataLocation.PortableFolderName}"; - FilesFolders.Copy(DataLocation.PortableDataPath, targetDestination); + FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination); if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination)) MessageBox.Show("Flow Launcher was not able to move your user profile data to the new update version. Please manually " + $"move your profile data folder from {DataLocation.PortableDataPath} to {targetDestination}"); diff --git a/Flow.Launcher.Infrastructure/Alphabet.cs b/Flow.Launcher.Infrastructure/Alphabet.cs deleted file mode 100644 index e0eb60de4..000000000 --- a/Flow.Launcher.Infrastructure/Alphabet.cs +++ /dev/null @@ -1,199 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using hyjiacan.util.p4n; -using hyjiacan.util.p4n.format; -using JetBrains.Annotations; -using Flow.Launcher.Infrastructure.Logger; -using Flow.Launcher.Infrastructure.Storage; -using Flow.Launcher.Infrastructure.UserSettings; - -namespace Flow.Launcher.Infrastructure -{ - public interface IAlphabet - { - string Translate(string stringToTranslate); - } - - public class Alphabet : IAlphabet - { - private readonly HanyuPinyinOutputFormat Format = new HanyuPinyinOutputFormat(); - private ConcurrentDictionary PinyinCache; - private BinaryStorage> _pinyinStorage; - private Settings _settings; - - public void Initialize([NotNull] Settings settings) - { - _settings = settings ?? throw new ArgumentNullException(nameof(settings)); - InitializePinyinHelpers(); - } - - private void InitializePinyinHelpers() - { - Format.setToneType(HanyuPinyinToneType.WITHOUT_TONE); - - Stopwatch.Normal("|Flow Launcher.Infrastructure.Alphabet.Initialize|Preload pinyin cache", () => - { - _pinyinStorage = new BinaryStorage>("Pinyin"); - - lock(_pinyinStorage) - { - var loaded = _pinyinStorage.TryLoad(new Dictionary()); - - PinyinCache = new ConcurrentDictionary(loaded); - } - - // force pinyin library static constructor initialize - PinyinHelper.toHanyuPinyinStringArray('T', Format); - }); - Log.Info($"|Flow Launcher.Infrastructure.Alphabet.Initialize|Number of preload pinyin combination<{PinyinCache.Count}>"); - } - - public string Translate(string str) - { - return ConvertChineseCharactersToPinyin(str); - } - - public string ConvertChineseCharactersToPinyin(string source) - { - if (!_settings.ShouldUsePinyin) - return source; - - if (string.IsNullOrEmpty(source)) - return source; - - if (!ContainsChinese(source)) - return source; - - var combination = PinyinCombination(source); - - var pinyinArray=combination.Select(x => string.Join("", x)); - var acronymArray = combination.Select(Acronym).Distinct(); - - var joinedSingleStringCombination = new StringBuilder(); - var all = acronymArray.Concat(pinyinArray); - all.ToList().ForEach(x => joinedSingleStringCombination.Append(x)); - - return joinedSingleStringCombination.ToString(); - } - - public void Save() - { - if (!_settings.ShouldUsePinyin) - { - return; - } - - lock(_pinyinStorage) - { - _pinyinStorage.Save(PinyinCache.ToDictionary(i => i.Key, i => i.Value)); - } - } - - private static string[] EmptyStringArray = new string[0]; - private static string[][] Empty2DStringArray = new string[0][]; - - [Obsolete("Not accurate, eg 音乐 will not return yinyue but returns yinle ")] - /// - /// replace chinese character with pinyin, non chinese character won't be modified - /// should be word or sentence, instead of single character. e.g. 微软 - /// - public string[] Pinyin(string word) - { - if (!_settings.ShouldUsePinyin) - { - return EmptyStringArray; - } - - var pinyin = word.Select(c => - { - var pinyins = PinyinHelper.toHanyuPinyinStringArray(c); - var result = pinyins == null ? c.ToString() : pinyins[0]; - return result; - }).ToArray(); - return pinyin; - } - - /// - /// replace chinese character with pinyin, non chinese character won't be modified - /// Because we don't have words dictionary, so we can only return all possiblie pinyin combination - /// e.g. 音乐 will return yinyue and yinle - /// should be word or sentence, instead of single character. e.g. 微软 - /// - public string[][] PinyinCombination(string characters) - { - if (!_settings.ShouldUsePinyin || string.IsNullOrEmpty(characters)) - { - return Empty2DStringArray; - } - - if (!PinyinCache.ContainsKey(characters)) - { - var allPinyins = new List(); - foreach (var c in characters) - { - var pinyins = PinyinHelper.toHanyuPinyinStringArray(c, Format); - if (pinyins != null) - { - var r = pinyins.Distinct().ToArray(); - allPinyins.Add(r); - } - else - { - var r = new[] { c.ToString() }; - allPinyins.Add(r); - } - } - - var combination = allPinyins.Aggregate(Combination).Select(c => c.Split(';')).ToArray(); - PinyinCache[characters] = combination; - return combination; - } - else - { - return PinyinCache[characters]; - } - } - - public string Acronym(string[] pinyin) - { - var acronym = string.Join("", pinyin.Select(p => p[0])); - return acronym; - } - - public bool ContainsChinese(string word) - { - if (!_settings.ShouldUsePinyin) - { - return false; - } - - if (word.Length > 40) - { - //Skip strings that are too long string for Pinyin conversion. - return false; - } - - var chinese = word.Select(PinyinHelper.toHanyuPinyinStringArray) - .Any(p => p != null); - return chinese; - } - - private string[] Combination(string[] array1, string[] array2) - { - if (!_settings.ShouldUsePinyin) - { - return EmptyStringArray; - } - - var combination = ( - from a1 in array1 - from a2 in array2 - select $"{a1};{a2}" - ).ToArray(); - return combination; - } - } -} diff --git a/Flow.Launcher.Infrastructure/Constant.cs b/Flow.Launcher.Infrastructure/Constant.cs index a8c72fb12..c6a3b48f3 100644 --- a/Flow.Launcher.Infrastructure/Constant.cs +++ b/Flow.Launcher.Infrastructure/Constant.cs @@ -23,8 +23,10 @@ namespace Flow.Launcher.Infrastructure public static readonly string Version = FileVersionInfo.GetVersionInfo(Assembly.Location.NonNull()).ProductVersion; public static readonly int ThumbnailSize = 64; - public static readonly string DefaultIcon = Path.Combine(ProgramDirectory, "Images", "app.png"); - public static readonly string ErrorIcon = Path.Combine(ProgramDirectory, "Images", "app_error.png"); + private static readonly string ImagesDirectory = Path.Combine(ProgramDirectory, "Images"); + public static readonly string DefaultIcon = Path.Combine(ImagesDirectory, "app.png"); + public static readonly string ErrorIcon = Path.Combine(ImagesDirectory, "app_error.png"); + public static readonly string MissingImgIcon = Path.Combine(ImagesDirectory, "app_missing_img.png"); public static string PythonPath; diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj index 410d11536..d834bd2d3 100644 --- a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj +++ b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj @@ -53,10 +53,10 @@ - + diff --git a/Flow.Launcher.Infrastructure/FuzzyMatcher.cs b/Flow.Launcher.Infrastructure/FuzzyMatcher.cs deleted file mode 100644 index 3df719b28..000000000 --- a/Flow.Launcher.Infrastructure/FuzzyMatcher.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; - -namespace Flow.Launcher.Infrastructure -{ - [Obsolete("This class is obsolete and should not be used. Please use the static function StringMatcher.FuzzySearch")] - public class FuzzyMatcher - { - private string query; - private MatchOption opt; - - private FuzzyMatcher(string query, MatchOption opt) - { - this.query = query.Trim(); - this.opt = opt; - } - - public static FuzzyMatcher Create(string query) - { - return new FuzzyMatcher(query, new MatchOption()); - } - - public static FuzzyMatcher Create(string query, MatchOption opt) - { - return new FuzzyMatcher(query, opt); - } - - public MatchResult Evaluate(string str) - { - return StringMatcher.Instance.FuzzyMatch(query, str, opt); - } - } -} diff --git a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs index 29e13a2de..4ae62dacd 100644 --- a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs +++ b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs @@ -37,7 +37,7 @@ namespace Flow.Launcher.Infrastructure.Image ImageCache.Usage = LoadStorageToConcurrentDictionary(); - foreach (var icon in new[] { Constant.DefaultIcon, Constant.ErrorIcon }) + foreach (var icon in new[] { Constant.DefaultIcon, Constant.MissingImgIcon }) { ImageSource img = new BitmapImage(new Uri(icon)); img.Freeze(); @@ -106,7 +106,7 @@ namespace Flow.Launcher.Infrastructure.Image { if (string.IsNullOrEmpty(path)) { - return new ImageResult(ImageCache[Constant.ErrorIcon], ImageType.Error); + return new ImageResult(_imageCache[Constant.MissingImgIcon], ImageType.Error); } if (ImageCache.ContainsKey(path)) { @@ -139,8 +139,8 @@ namespace Flow.Launcher.Infrastructure.Image Log.Exception($"|ImageLoader.Load|Failed to get thumbnail for {path} on first try", e); Log.Exception($"|ImageLoader.Load|Failed to get thumbnail for {path} on second try", e2); - ImageSource image = ImageCache[Constant.ErrorIcon]; - ImageCache[path] = image; + ImageSource image = _imageCache[Constant.MissingImgIcon]; + _imageCache[path] = image; imageResult = new ImageResult(image, ImageType.Error); } } @@ -191,8 +191,8 @@ namespace Flow.Launcher.Infrastructure.Image } else { - image = ImageCache[Constant.ErrorIcon]; - path = Constant.ErrorIcon; + image = _imageCache[Constant.MissingImgIcon]; + path = Constant.MissingImgIcon; } if (type != ImageType.Error) diff --git a/Flow.Launcher.Infrastructure/PinyinAlphabet.cs b/Flow.Launcher.Infrastructure/PinyinAlphabet.cs new file mode 100644 index 000000000..38f1ab879 --- /dev/null +++ b/Flow.Launcher.Infrastructure/PinyinAlphabet.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using JetBrains.Annotations; +using Flow.Launcher.Infrastructure.Logger; +using Flow.Launcher.Infrastructure.Storage; +using Flow.Launcher.Infrastructure.UserSettings; +using ToolGood.Words.Pinyin; +using System.Threading.Tasks; + +namespace Flow.Launcher.Infrastructure +{ + public interface IAlphabet + { + string Translate(string stringToTranslate); + } + + public class PinyinAlphabet : IAlphabet + { + private ConcurrentDictionary _pinyinCache = new ConcurrentDictionary(); + private Settings _settings; + + public void Initialize([NotNull] Settings settings) + { + _settings = settings ?? throw new ArgumentNullException(nameof(settings)); + } + + + public string Translate(string content) + { + if (_settings.ShouldUsePinyin) + { + if (!_pinyinCache.ContainsKey(content)) + { + if (WordsHelper.HasChinese(content)) + { + var result = WordsHelper.GetPinyin(content, ";"); + result = GetFirstPinyinChar(result) + result.Replace(";", ""); + _pinyinCache[content] = result; + return result; + } + else + { + return content; + } + } + else + { + return _pinyinCache[content]; + } + } + else + { + return content; + } + } + + private string GetFirstPinyinChar(string content) + { + return string.Concat(content.Split(';').Select(x => x.First())); + } + } +} \ No newline at end of file diff --git a/Flow.Launcher.Infrastructure/StringMatcher.cs b/Flow.Launcher.Infrastructure/StringMatcher.cs index 79ccfd7af..2a4270fb4 100644 --- a/Flow.Launcher.Infrastructure/StringMatcher.cs +++ b/Flow.Launcher.Infrastructure/StringMatcher.cs @@ -21,18 +21,6 @@ namespace Flow.Launcher.Infrastructure public static StringMatcher Instance { get; internal set; } - [Obsolete("This method is obsolete and should not be used. Please use the static function StringMatcher.FuzzySearch")] - public static int Score(string source, string target) - { - return FuzzySearch(target, source).Score; - } - - [Obsolete("This method is obsolete and should not be used. Please use the static function StringMatcher.FuzzySearch")] - public static bool IsMatch(string source, string target) - { - return Score(source, target) > 0; - } - public static MatchResult FuzzySearch(string query, string stringToCompare) { return Instance.FuzzyMatch(query, stringToCompare); @@ -323,18 +311,6 @@ namespace Flow.Launcher.Infrastructure public class MatchOption { - /// - /// prefix of match char, use for highlight - /// - [Obsolete("this is never used")] - public string Prefix { get; set; } = ""; - - /// - /// suffix of match char, use for highlight - /// - [Obsolete("this is never used")] - public string Suffix { get; set; } = ""; - public bool IgnoreCase { get; set; } = true; } } diff --git a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs index 310c1e33a..e93c341dc 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs @@ -30,5 +30,6 @@ namespace Flow.Launcher.Infrastructure.UserSettings } public static readonly string PluginsDirectory = Path.Combine(DataDirectory(), Constant.Plugins); + public static readonly string PluginSettingsDirectory = Path.Combine(DataDirectory(), "Settings", Constant.Plugins); } } diff --git a/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs b/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs index 4c47ca425..ccd9beb86 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs @@ -15,6 +15,16 @@ namespace Flow.Launcher.Infrastructure.UserSettings if (Plugins.ContainsKey(metadata.ID)) { var settings = Plugins[metadata.ID]; + + // TODO: Remove. This is one off for 1.2.0 release. + // Introduced a new action keyword in Explorer, so need to update plugin setting in the UserData folder. + // This kind of plugin meta update should be handled by a dedicated method trigger by version bump. + if (metadata.ID == "572be03c74c642baae319fc283e561a8" && metadata.ActionKeywords.Count != settings.ActionKeywords.Count) + settings.ActionKeywords = metadata.ActionKeywords; + + if (string.IsNullOrEmpty(settings.Version)) + settings.Version = metadata.Version; + if (settings.ActionKeywords?.Count > 0) { metadata.ActionKeywords = settings.ActionKeywords; @@ -28,6 +38,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings { ID = metadata.ID, Name = metadata.Name, + Version = metadata.Version, ActionKeywords = metadata.ActionKeywords, Disabled = metadata.Disabled }; @@ -39,6 +50,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings { public string ID { get; set; } public string Name { get; set; } + public string Version { get; set; } public List ActionKeywords { get; set; } // a reference of the action keywords from plugin manager /// diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 4e1a8d7d8..837fe3b71 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -70,12 +70,6 @@ namespace Flow.Launcher.Infrastructure.UserSettings public PluginsSettings PluginSettings { get; set; } = new PluginsSettings(); public ObservableCollection CustomPluginHotkeys { get; set; } = new ObservableCollection(); - [Obsolete] - public double Opacity { get; set; } = 1; - - [Obsolete] - public OpacityMode OpacityMode { get; set; } = OpacityMode.Normal; - public bool DontPromptUpdateMsg { get; set; } public bool EnableUpdateLog { get; set; } @@ -108,12 +102,4 @@ namespace Flow.Launcher.Infrastructure.UserSettings Empty, Preserved } - - [Obsolete] - public enum OpacityMode - { - Normal = 0, - LayeredWindow = 1, - DWM = 2 - } } \ No newline at end of file diff --git a/Flow.Launcher.Plugin/Feature.cs b/Flow.Launcher.Plugin/Feature.cs index 62da96c57..81839d816 100644 --- a/Flow.Launcher.Plugin/Feature.cs +++ b/Flow.Launcher.Plugin/Feature.cs @@ -11,23 +11,6 @@ namespace Flow.Launcher.Plugin List LoadContextMenus(Result selectedResult); } - [Obsolete("If a plugin has a action keyword, then it is exclusive. This interface will be remove in v1.3.0")] - public interface IExclusiveQuery : IFeatures - { - [Obsolete("If a plugin has a action keyword, then it is exclusive. This method will be remove in v1.3.0")] - bool IsExclusiveQuery(Query query); - } - - /// - /// Represent plugin query will be executed in UI thread directly. Don't do long-running operation in Query method if you implement this interface - /// This will improve the performance of instant search like websearch or cmd plugin - /// - [Obsolete("Flow Launcher is fast enough now, executed on ui thread is no longer needed")] - public interface IInstantQuery : IFeatures - { - bool IsInstantQuery(string query); - } - /// /// Represent plugins that support internationalization /// diff --git a/Flow.Launcher.Plugin/Features/IContextMenu.cs b/Flow.Launcher.Plugin/Features/IContextMenu.cs deleted file mode 100644 index f60e02a69..000000000 --- a/Flow.Launcher.Plugin/Features/IContextMenu.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace Flow.Launcher.Plugin.Features -{ - [Obsolete("Delete Flow.Launcher.Plugin.Features using directive, " + - "and use Flow.Launcher.Plugin.Feature.IContextMenu instead, " + - "this method will be removed in v1.3.0")] - public interface IContextMenu { } -} \ No newline at end of file diff --git a/Flow.Launcher.Plugin/Features/IExclusiveQuery.cs b/Flow.Launcher.Plugin/Features/IExclusiveQuery.cs deleted file mode 100644 index 57eddd93e..000000000 --- a/Flow.Launcher.Plugin/Features/IExclusiveQuery.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace Flow.Launcher.Plugin.Features -{ - [Obsolete("Delete Flow.Launcher.Plugin.Features using directive, " + - "and use Flow.Launcher.Plugin.Feature.IInstantQuery instead, " + - "this method will be removed in v1.3.0")] - public interface IExclusiveQuery { } -} diff --git a/Flow.Launcher.Plugin/Features/IInstantQuery.cs b/Flow.Launcher.Plugin/Features/IInstantQuery.cs deleted file mode 100644 index 465640ae9..000000000 --- a/Flow.Launcher.Plugin/Features/IInstantQuery.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace Flow.Launcher.Plugin.Features -{ - [Obsolete("Delete Flow.Launcher.Plugin.Features using directive, " + - "and use Flow.Launcher.Plugin.Feature.IInstantQuery instead, " + - "this method will be removed in v1.3.0")] - public interface IInstantQuery { } -} \ No newline at end of file diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index 0c69a6b9b..5dda76bc4 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -14,10 +14,10 @@ - 1.1.0 - 1.1.0 - 1.1.0 - 1.1.0 + 1.2.1 + 1.2.1 + 1.2.1 + 1.2.1 Flow.Launcher.Plugin Flow-Launcher MIT diff --git a/Flow.Launcher.Plugin/IPublicAPI.cs b/Flow.Launcher.Plugin/IPublicAPI.cs index 2e754a0ff..681973905 100644 --- a/Flow.Launcher.Plugin/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/IPublicAPI.cs @@ -8,15 +8,6 @@ namespace Flow.Launcher.Plugin /// public interface IPublicAPI { - /// - /// Push result to query box - /// - /// - /// - /// - [Obsolete("This method will be removed in Flow Launcher 1.3")] - void PushResults(Query query, PluginMetadata plugin, List results); - /// /// Change Flow.Launcher query /// @@ -27,42 +18,11 @@ namespace Flow.Launcher.Plugin /// void ChangeQuery(string query, bool requery = false); - /// - /// Just change the query text, this won't raise search - /// - /// - [Obsolete] - void ChangeQueryText(string query, bool selectAll = false); - - /// - /// Close Flow Launcher - /// - [Obsolete] - void CloseApp(); - /// /// Restart Flow Launcher /// void RestartApp(); - /// - /// Restart Flow Launcher - /// - [Obsolete("Use RestartApp instead. This method will be removed in Flow Launcher 1.3")] - void RestarApp(); - - /// - /// Hide Flow Launcher - /// - [Obsolete] - void HideApp(); - - /// - /// Show Flow Launcher - /// - [Obsolete] - void ShowApp(); - /// /// Save all Flow Launcher settings /// @@ -103,18 +63,6 @@ namespace Flow.Launcher.Plugin /// void OpenSettingDialog(); - /// - /// Show loading animation - /// - [Obsolete("automatically start")] - void StartLoadingBar(); - - /// - /// Stop loading animation - /// - [Obsolete("automatically stop")] - void StopLoadingBar(); - /// /// Install Flow Launcher plugin /// diff --git a/Flow.Launcher.Plugin/PluginMetadata.cs b/Flow.Launcher.Plugin/PluginMetadata.cs index fb615459b..d81b442e2 100644 --- a/Flow.Launcher.Plugin/PluginMetadata.cs +++ b/Flow.Launcher.Plugin/PluginMetadata.cs @@ -43,9 +43,6 @@ namespace Flow.Launcher.Plugin return Name; } - [Obsolete("Use IcoPath")] - public string FullIcoPath => IcoPath; - /// /// Init time include both plugin load time and init time /// diff --git a/Flow.Launcher.Plugin/Query.cs b/Flow.Launcher.Plugin/Query.cs index 33310f2aa..1eb5c9c14 100644 --- a/Flow.Launcher.Plugin/Query.cs +++ b/Flow.Launcher.Plugin/Query.cs @@ -94,14 +94,5 @@ namespace Flow.Launcher.Plugin } public override string ToString() => RawQuery; - - [Obsolete("Use ActionKeyword, this property will be removed in v1.3.0")] - public string ActionName { get; internal set; } - - [Obsolete("Use Search instead, this property will be removed in v1.3.0")] - public List ActionParameters { get; internal set; } - - [Obsolete("Use Search instead, this method will be removed in v1.3.0")] - public string GetAllRemainingParameter() => Search; } } diff --git a/Flow.Launcher.Plugin/Result.cs b/Flow.Launcher.Plugin/Result.cs index d210ba1d8..70be1e9dc 100644 --- a/Flow.Launcher.Plugin/Result.cs +++ b/Flow.Launcher.Plugin/Result.cs @@ -104,21 +104,6 @@ namespace Flow.Launcher.Plugin return Title + SubTitle; } - - /// - /// Context menus associate with this result - /// - [Obsolete("Use IContextMenu instead")] - public List ContextMenu { get; set; } - - [Obsolete("Use Object initializer instead")] - public Result(string Title, string IcoPath, string SubTitle = null) - { - this.Title = Title; - this.IcoPath = IcoPath; - this.SubTitle = SubTitle; - } - public Result() { } /// diff --git a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs index 98cd777aa..27cd1a558 100644 --- a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs +++ b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs @@ -11,7 +11,13 @@ namespace Flow.Launcher.Plugin.SharedCommands private const string FileExplorerProgramEXE = "explorer.exe"; - public static void Copy(this string sourcePath, string targetPath) + /// + /// Copies the folder and all of its files and folders + /// including subfolders to the target location + /// + /// + /// + public static void CopyAll(this string sourcePath, string targetPath) { // Get the subdirectories for the specified directory. DirectoryInfo dir = new DirectoryInfo(sourcePath); @@ -44,7 +50,7 @@ namespace Flow.Launcher.Plugin.SharedCommands foreach (DirectoryInfo subdir in dirs) { string temppath = Path.Combine(targetPath, subdir.Name); - Copy(subdir.FullName, temppath); + CopyAll(subdir.FullName, temppath); } } catch (Exception e) @@ -108,7 +114,7 @@ namespace Flow.Launcher.Plugin.SharedCommands return Directory.Exists(path); } - public static bool FileExits(this string filePath) + public static bool FileExists(this string filePath) { return File.Exists(filePath); } @@ -118,7 +124,7 @@ namespace Flow.Launcher.Plugin.SharedCommands var psi = new ProcessStartInfo { FileName = FileExplorerProgramName, UseShellExecute = true, Arguments = fileOrFolderPath }; try { - if (LocationExists(fileOrFolderPath) || FileExits(fileOrFolderPath)) + if (LocationExists(fileOrFolderPath) || FileExists(fileOrFolderPath)) Process.Start(psi); } catch (Exception e) @@ -172,7 +178,7 @@ namespace Flow.Launcher.Plugin.SharedCommands /// /// Gets the previous level directory from a path string. /// Checks that previous level directory exists and returns it - /// as a path string, or empty string if doesn't exit + /// as a path string, or empty string if doesn't exist /// public static string GetPreviousExistingDirectory(Func locationExists, string path) { diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 0a1dece4f..c91144825 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -58,8 +58,8 @@ namespace Flow.Launcher.Test.Plugins $"Actual: {result}{Environment.NewLine}"); } - [TestCase("C:\\", "SELECT TOP 100 System.FileName, System.ItemPathDisplay, System.ItemType FROM SystemIndex WHERE directory='file:C:\\'")] - [TestCase("C:\\SomeFolder\\", "SELECT TOP 100 System.FileName, System.ItemPathDisplay, System.ItemType FROM SystemIndex WHERE directory='file:C:\\SomeFolder\\'")] + [TestCase("C:\\", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\'")] + [TestCase("C:\\SomeFolder\\", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\SomeFolder\\'")] public void GivenWindowsIndexSearch_WhenSearchTypeIsTopLevelDirectorySearch_ThenQueryShouldUseExpectedString(string folderPath, string expectedString) { // Given @@ -74,7 +74,7 @@ namespace Flow.Launcher.Test.Plugins $"Actual string was: {queryString}{Environment.NewLine}"); } - [TestCase("C:\\SomeFolder\\flow.launcher.sln", "SELECT TOP 100 System.FileName, System.ItemPathDisplay, System.ItemType " + + [TestCase("C:\\SomeFolder\\flow.launcher.sln", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType " + "FROM SystemIndex WHERE (System.FileName LIKE 'flow.launcher.sln%' " + "OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033))" + " AND directory='file:C:\\SomeFolder'")] @@ -126,7 +126,7 @@ namespace Flow.Launcher.Test.Plugins $"Actual string was: {resultString}{Environment.NewLine}"); } - [TestCase("flow.launcher.sln", "SELECT TOP 100 \"System.FileName\", \"System.ItemPathDisplay\", \"System.ItemType\" " + + [TestCase("flow.launcher.sln", "SELECT TOP 100 \"System.FileName\", \"System.ItemUrl\", \"System.ItemType\" " + "FROM \"SystemIndex\" WHERE (System.FileName LIKE 'flow.launcher.sln%' " + "OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033)) AND scope='file:'")] public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryShouldUseExpectedString( @@ -184,6 +184,55 @@ namespace Flow.Launcher.Test.Plugins $"Actual number of results is {results.Count} {Environment.NewLine}"); } + [TestCase(@"some words", @"FREETEXT('some words')")] + public void GivenWindowsIndexSearch_WhenQueryWhereRestrictionsIsForFileContentSearch_ThenShouldReturnFreeTextString( + string querySearchString, string expectedString) + { + // Given + var queryConstructor = new QueryConstructor(new Settings()); + + //When + var resultString = queryConstructor.QueryWhereRestrictionsForFileContentSearch(querySearchString); + + // Then + Assert.IsTrue(resultString == expectedString, + $"Expected QueryWhereRestrictions string: {expectedString}{Environment.NewLine} " + + $"Actual string was: {resultString}{Environment.NewLine}"); + } + + [TestCase("some words", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType " + + "FROM SystemIndex WHERE FREETEXT('some words') AND scope='file:'")] + public void GivenWindowsIndexSearch_WhenSearchForFileContent_ThenQueryShouldUseExpectedString( + string userSearchString, string expectedString) + { + // Given + var queryConstructor = new QueryConstructor(new Settings()); + + //When + var resultString = queryConstructor.QueryForFileContentSearch(userSearchString); + + // Then + Assert.IsTrue(resultString == expectedString, + $"Expected query string: {expectedString}{Environment.NewLine} " + + $"Actual string was: {resultString}{Environment.NewLine}"); + } + + public void GivenQuery_WhenActionKeywordForFileContentSearchExists_ThenFileContentSearchRequiredShouldReturnTrue() + { + // Given + var query = new Query { ActionKeyword = "doc:", Search = "search term" }; + + var searchManager = new SearchManager(new Settings(), new PluginInitContext()); + + // When + var result = searchManager.IsFileContentSearch(query.ActionKeyword); + + // Then + Assert.IsTrue(result, + $"Expected True for file content search. {Environment.NewLine} " + + $"Actual result was: {result}{Environment.NewLine}"); + } + [TestCase(@"c:\\", false)] [TestCase(@"i:\", true)] [TestCase(@"\c:\", false)] diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 985d4da87..59bdbc896 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Text; using System.Threading.Tasks; using System.Timers; using System.Windows; @@ -28,7 +29,7 @@ namespace Flow.Launcher private SettingWindowViewModel _settingsVM; private readonly Updater _updater = new Updater(Flow.Launcher.Properties.Settings.Default.GithubRepo); private readonly Portable _portable = new Portable(); - private readonly Alphabet _alphabet = new Alphabet(); + private readonly PinyinAlphabet _alphabet = new PinyinAlphabet(); private StringMatcher _stringMatcher; [STAThread] @@ -85,6 +86,8 @@ namespace Flow.Launcher Http.Proxy = _settings.Proxy; + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + RegisterExitEvents(); AutoStartup(); diff --git a/Flow.Launcher/Images/app_missing_img.png b/Flow.Launcher/Images/app_missing_img.png new file mode 100644 index 000000000..11d5466f0 Binary files /dev/null and b/Flow.Launcher/Images/app_missing_img.png differ diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 069071f39..adb49b65d 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -111,7 +111,7 @@ Done Can't find specified plugin New Action Keyword can't be empty - New Action Keywords have been assigned to another plugin, please assign other new action keyword + This new Action Keyword is already assigned to another plugin, please choose a different one Success Use * if you don't want to specify an action keyword diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 530e6443c..0cc5a0e5d 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -21,11 +21,11 @@ namespace Flow.Launcher { private readonly SettingWindowViewModel _settingsVM; private readonly MainViewModel _mainVM; - private readonly Alphabet _alphabet; + private readonly PinyinAlphabet _alphabet; #region Constructor - public PublicAPIInstance(SettingWindowViewModel settingsVM, MainViewModel mainVM, Alphabet alphabet) + public PublicAPIInstance(SettingWindowViewModel settingsVM, MainViewModel mainVM, PinyinAlphabet alphabet) { _settingsVM = settingsVM; _mainVM = mainVM; @@ -48,12 +48,6 @@ namespace Flow.Launcher _mainVM.ChangeQueryText(query); } - [Obsolete] - public void CloseApp() - { - Application.Current.MainWindow.Close(); - } - public void RestartApp() { _mainVM.MainWindowVisibility = Visibility.Hidden; @@ -82,7 +76,6 @@ namespace Flow.Launcher _settingsVM.Save(); PluginManager.Save(); ImageLoader.Save(); - _alphabet.Save(); } public void ReloadAllPluginData() @@ -90,18 +83,6 @@ namespace Flow.Launcher PluginManager.ReloadData(); } - [Obsolete] - public void HideApp() - { - _mainVM.MainWindowVisibility = Visibility.Hidden; - } - - [Obsolete] - public void ShowApp() - { - _mainVM.MainWindowVisibility = Visibility.Visible; - } - public void ShowMsg(string title, string subTitle = "", string iconPath = "") { ShowMsg(title, subTitle, iconPath, true); @@ -151,21 +132,6 @@ namespace Flow.Launcher public event FlowLauncherGlobalKeyboardEventHandler GlobalKeyboardEvent; - [Obsolete("This will be removed in Flow Launcher 1.3")] - public void PushResults(Query query, PluginMetadata plugin, List results) - { - results.ForEach(o => - { - o.PluginDirectory = plugin.PluginDirectory; - o.PluginID = plugin.ID; - o.OriginQuery = query; - }); - Task.Run(() => - { - _mainVM.UpdateResultView(results, plugin, query); - }); - } - #endregion #region Private Methods diff --git a/Flow.Launcher/ViewModel/ResultViewModel.cs b/Flow.Launcher/ViewModel/ResultViewModel.cs index 4392234eb..a4fe2ede4 100644 --- a/Flow.Launcher/ViewModel/ResultViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultViewModel.cs @@ -51,7 +51,7 @@ namespace Flow.Launcher.ViewModel catch (Exception e) { Log.Exception($"|ResultViewModel.Image|IcoPath is empty and exception when calling Icon() for result <{Result.Title}> of plugin <{Result.PluginDirectory}>", e); - imagePath = Constant.ErrorIcon; + imagePath = Constant.MissingImgIcon; } } diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Bookmark.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Bookmark.cs index 1149042dd..790c03686 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Bookmark.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Bookmark.cs @@ -19,10 +19,8 @@ namespace Flow.Launcher.Plugin.BrowserBookmark set { m_Name = value; - PinyinName = m_Name.Unidecode(); } } - public string PinyinName { get; private set; } public string Url { get; set; } public string Source { get; set; } public int Score { get; set; } diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Commands/Bookmarks.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Commands/Bookmarks.cs index 7c2db8bf9..c7013aa67 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Commands/Bookmarks.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Commands/Bookmarks.cs @@ -6,19 +6,19 @@ namespace Flow.Launcher.Plugin.BrowserBookmark.Commands { internal static class Bookmarks { - internal static bool MatchProgram(Bookmark bookmark, string queryString) + internal static MatchResult MatchProgram(Bookmark bookmark, string queryString) { - if (StringMatcher.FuzzySearch(queryString, bookmark.Name).IsSearchPrecisionScoreMet()) return true; - if (StringMatcher.FuzzySearch(queryString, bookmark.PinyinName).IsSearchPrecisionScoreMet()) return true; - if (StringMatcher.FuzzySearch(queryString, bookmark.Url).IsSearchPrecisionScoreMet()) return true; + var match = StringMatcher.FuzzySearch(queryString, bookmark.Name); + if (match.IsSearchPrecisionScoreMet()) + return match; - return false; + return StringMatcher.FuzzySearch(queryString, bookmark.Url); } internal static List LoadAllBookmarks() { var allbookmarks = new List(); - + var chromeBookmarks = new ChromeBookmarks(); var mozBookmarks = new FirefoxBookmarks(); var edgeBookmarks = new EdgeBookmarks(); diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/EdgeBookmarks.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/EdgeBookmarks.cs index 12b80c08a..376808549 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/EdgeBookmarks.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/EdgeBookmarks.cs @@ -18,7 +18,7 @@ namespace Flow.Launcher.Plugin.BrowserBookmark return bookmarks; } - private void ParseEdgeBookmarks(String path, string source) + private void ParseEdgeBookmarks(string path, string source) { if (!File.Exists(path)) return; @@ -72,12 +72,13 @@ namespace Flow.Launcher.Plugin.BrowserBookmark private void LoadEdgeBookmarks() { - String platformPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + string platformPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); LoadEdgeBookmarks(Path.Combine(platformPath, @"Microsoft\Edge\User Data"), "Microsoft Edge"); + LoadEdgeBookmarks(Path.Combine(platformPath, @"Microsoft\Edge Dev\User Data"), "Microsoft Edge Dev"); LoadEdgeBookmarks(Path.Combine(platformPath, @"Microsoft\Edge SxS\User Data"), "Microsoft Edge Canary"); } - private String DecodeUnicode(String dataStr) + private string DecodeUnicode(string dataStr) { Regex reg = new Regex(@"(?i)\\[uU]([0-9a-f]{4})"); return reg.Replace(dataStr, m => ((char)Convert.ToInt32(m.Groups[1].Value, 16)).ToString()); diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs index 5d74ed4ad..47493654f 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs @@ -12,7 +12,7 @@ namespace Flow.Launcher.Plugin.BrowserBookmark public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, ISavable { private PluginInitContext context; - + private List cachedBookmarks = new List(); private readonly Settings _settings; @@ -33,40 +33,60 @@ namespace Flow.Launcher.Plugin.BrowserBookmark public List Query(Query query) { - string param = query.GetAllRemainingParameter().TrimStart(); + string param = query.Search.TrimStart(); // Should top results be returned? (true if no search parameters have been passed) var topResults = string.IsNullOrEmpty(param); - - var returnList = cachedBookmarks; + if (!topResults) { // Since we mixed chrome and firefox bookmarks, we should order them again - returnList = cachedBookmarks.Where(o => Bookmarks.MatchProgram(o, param)).ToList(); - returnList = returnList.OrderByDescending(o => o.Score).ToList(); - } - - return returnList.Select(c => new Result() - { - Title = c.Name, - SubTitle = c.Url, - IcoPath = @"Images\bookmark.png", - Score = 5, - Action = (e) => + var returnList = cachedBookmarks.Select(c => new Result() { - if (_settings.OpenInNewBrowserWindow) + Title = c.Name, + SubTitle = c.Url, + IcoPath = @"Images\bookmark.png", + Score = Bookmarks.MatchProgram(c, param).Score, + Action = _ => { - c.Url.NewBrowserWindow(_settings.BrowserPath); - } - else - { - c.Url.NewTabInBrowser(_settings.BrowserPath); - } + if (_settings.OpenInNewBrowserWindow) + { + c.Url.NewBrowserWindow(_settings.BrowserPath); + } + else + { + c.Url.NewTabInBrowser(_settings.BrowserPath); + } - return true; - } - }).ToList(); + return true; + } + }).Where(r => r.Score > 0); + return returnList.ToList(); + } + else + { + return cachedBookmarks.Select(c => new Result() + { + Title = c.Name, + SubTitle = c.Url, + IcoPath = @"Images\bookmark.png", + Score = 5, + Action = _ => + { + if (_settings.OpenInNewBrowserWindow) + { + c.Url.NewBrowserWindow(_settings.BrowserPath); + } + else + { + c.Url.NewTabInBrowser(_settings.BrowserPath); + } + + return true; + } + }).ToList(); + } } public void ReloadData() diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json index 8676a3e5b..7bb662b36 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json @@ -4,7 +4,7 @@ "Name": "Browser Bookmarks", "Description": "Search your browser bookmarks", "Author": "qianlifeng, Ioannis G.", - "Version": "1.2.0", + "Version": "1.2.1", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.browserBookmark.dll", diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs index 1235a18e7..949911229 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs @@ -34,7 +34,7 @@ namespace Flow.Launcher.Plugin.Caculator { MagesEngine = new Engine(); } - + public void Init(PluginInitContext context) { Context = context; @@ -78,16 +78,16 @@ namespace Flow.Launcher.Plugin.Caculator { try { - Clipboard.SetText(newResult); + Clipboard.SetDataObject(newResult); return true; } - catch (ExternalException) + catch (ExternalException e) { MessageBox.Show("Copy failed, please try later"); return false; } } - } + } }; } } @@ -111,7 +111,7 @@ namespace Flow.Launcher.Plugin.Caculator { return false; } - + if (!IsBracketComplete(query.Search)) { return false; @@ -164,7 +164,7 @@ namespace Flow.Launcher.Plugin.Caculator return leftBracketCount == 0; } - + public string GetTranslatedPluginTitle() { return Context.API.GetTranslation("flowlauncher_plugin_caculator_plugin_name"); diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json b/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json index 284a2893d..316788884 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json @@ -4,7 +4,7 @@ "Name": "Calculator", "Description": "Provide mathematical calculations.(Try 5*3-2 in Flow Launcher)", "Author": "cxfksword", - "Version": "1.0.0", + "Version": "1.0.1", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Caculator.dll", diff --git a/Plugins/Flow.Launcher.Plugin.Everything b/Plugins/Flow.Launcher.Plugin.Everything new file mode 160000 index 000000000..6d5b687e2 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Everything @@ -0,0 +1 @@ +Subproject commit 6d5b687e240a6abdc5623d8a8e09501f3994b0d3 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 bd2a047eb..efa5339b4 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj @@ -104,6 +104,7 @@ + diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml index 01bd1a60f..2fb16e0e1 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml @@ -1,22 +1,26 @@ - + Please make a selection first Please select a folder link Are you sure you want to delete {0}? Are you sure you want to permanently delete this {0}? Deletion successful Successfully deleted the {0} + Assigning the global action keyword could bring up too many results during search. Please choose a specific action keyword Delete Edit Add + Customise Action Keywords Quick Folder Access Paths Index Search Excluded Paths Indexing Options - + Search Activation: + File Content Search: Explorer diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs index db2eaa722..38939e244 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs @@ -22,6 +22,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search internal const char AllFilesFolderSearchWildcard = '>'; + internal const string DefaultContentSearchActionKeyword = "doc:"; + internal const char DirectorySeperator = '\\'; internal const string WindowsIndexingOptions = "srchadmin.dll"; diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs index c576d6ee8..6a870f149 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs @@ -22,11 +22,20 @@ namespace Flow.Launcher.Plugin.Explorer.Search foreach (DictionaryEntry special in Environment.GetEnvironmentVariables()) { - if (Directory.Exists(special.Value.ToString())) + var path = special.Value.ToString(); + if (Directory.Exists(path)) { + // we add a trailing slash to the path to make sure drive paths become valid absolute paths. + // for example, if %systemdrive% is C: we turn it to C:\ + path = path.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar; + + // if we don't have an absolute path, we use Path.GetFullPath to get one. + // for example, if %homepath% is \Users\John we turn it to C:\Users\John + path = Path.IsPathFullyQualified(path) ? path : Path.GetFullPath(path); + // Variables are returned with a mixture of all upper/lower case. // Call ToLower() to make the results look consistent - envStringPaths.Add(special.Key.ToString().ToLower(), special.Value.ToString()); + envStringPaths.Add(special.Key.ToString().ToLower(), path); } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/QuickFolderAccess.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/QuickFolderAccess.cs index ebde039d6..8bd19956e 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/QuickFolderAccess.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/QuickFolderAccess.cs @@ -6,14 +6,10 @@ namespace Flow.Launcher.Plugin.Explorer.Search.FolderLinks { public class QuickFolderAccess { - internal List FolderList(Query query, List folderLinks, PluginInitContext context) + internal List FolderListMatched(Query query, List folderLinks, PluginInitContext context) { if (string.IsNullOrEmpty(query.Search)) - return folderLinks - .Select(item => - new ResultManager(context) - .CreateFolderResult(item.Nickname, item.Path, item.Path, query)) - .ToList(); + return new List(); string search = query.Search.ToLower(); @@ -24,5 +20,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search.FolderLinks .CreateFolderResult(item.Nickname, item.Path, item.Path, query)) .ToList(); } + + internal List FolderListAll(Query query, List folderLinks, PluginInitContext context) + => folderLinks + .Select(item => + new ResultManager(context).CreateFolderResult(item.Nickname, item.Path, item.Path, query)) + .ToList(); } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index f814962ba..5b50b7fad 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -34,13 +34,19 @@ namespace Flow.Launcher.Plugin.Explorer.Search var querySearch = query.Search; - var quickFolderLinks = quickFolderAccess.FolderList(query, settings.QuickFolderAccessLinks, context); + if (IsFileContentSearch(query.ActionKeyword)) + return WindowsIndexFileContentSearch(query, querySearch); + + // This allows the user to type the assigned action keyword and only see the list of quick folder links + if (settings.QuickFolderAccessLinks.Count > 0 + && query.ActionKeyword == settings.SearchActionKeyword + && string.IsNullOrEmpty(query.Search)) + return quickFolderAccess.FolderListAll(query, settings.QuickFolderAccessLinks, context); + + var quickFolderLinks = quickFolderAccess.FolderListMatched(query, settings.QuickFolderAccessLinks, context); if (quickFolderLinks.Count > 0) - return quickFolderLinks; - - if (string.IsNullOrEmpty(querySearch)) - return results; + results.AddRange(quickFolderLinks); var isEnvironmentVariable = EnvironmentVariables.IsEnvironmentVariableSearch(querySearch); @@ -51,7 +57,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search var isEnvironmentVariablePath = querySearch.Substring(1).Contains("%\\"); if (!FilesFolders.IsLocationPathString(querySearch) && !isEnvironmentVariablePath) - return WindowsIndexFilesAndFoldersSearch(query, querySearch); + { + results.AddRange(WindowsIndexFilesAndFoldersSearch(query, querySearch)); + + return results; + } var locationPath = querySearch; @@ -74,6 +84,24 @@ namespace Flow.Launcher.Plugin.Explorer.Search return results; } + private List WindowsIndexFileContentSearch(Query query, string querySearchString) + { + var queryConstructor = new QueryConstructor(settings); + + if (string.IsNullOrEmpty(querySearchString)) + return new List(); + + return indexSearch.WindowsIndexSearch(querySearchString, + queryConstructor.CreateQueryHelper().ConnectionString, + queryConstructor.QueryForFileContentSearch, + query); + } + + public bool IsFileContentSearch(string actionKeyword) + { + return actionKeyword == settings.FileContentSearchActionKeyword; + } + private List DirectoryInfoClassSearch(Query query, string querySearch) { var directoryInfoSearch = new DirectoryInfoSearch(context); @@ -116,15 +144,17 @@ namespace Flow.Launcher.Plugin.Explorer.Search private bool UseWindowsIndexForDirectorySearch(string locationPath) { + var pathToDirectory = FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath); + if (!settings.UseWindowsIndexForDirectorySearch) return false; if (settings.IndexSearchExcludedSubdirectoryPaths - .Any(x => FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath) + .Any(x => FilesFolders.ReturnPreviousDirectoryIfIncompleteString(pathToDirectory) .StartsWith(x.Path, StringComparison.OrdinalIgnoreCase))) return false; - return indexSearch.PathIsIndexed(locationPath); + return indexSearch.PathIsIndexed(pathToDirectory); } } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs index c1793b42b..4f9325c77 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs @@ -21,7 +21,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex private readonly ResultManager resultManager; // Reserved keywords in oleDB - private readonly string reservedStringPattern = @"^[\/\\\$\%]+$"; + private readonly string reservedStringPattern = @"^[\/\\\$\%_]+$"; internal IndexSearch(PluginInitContext context) { @@ -51,17 +51,24 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex { if (dataReaderResults.GetValue(0) != DBNull.Value && dataReaderResults.GetValue(1) != DBNull.Value) { + // # is URI syntax for the fragment component, need to be encoded so LocalPath returns complete path + var encodedFragmentPath = dataReaderResults + .GetString(1) + .Replace("#", "%23", StringComparison.OrdinalIgnoreCase); + + var path = new Uri(encodedFragmentPath).LocalPath; + if (dataReaderResults.GetString(2) == "Directory") { folderResults.Add(resultManager.CreateFolderResult( dataReaderResults.GetString(0), - dataReaderResults.GetString(1), - dataReaderResults.GetString(1), + path, + path, query, true, true)); } else { - fileResults.Add(resultManager.CreateFileResult(dataReaderResults.GetString(1), query, true, true)); + fileResults.Add(resultManager.CreateFileResult(path, query, true, true)); } } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs index f4480eea7..5718fdb0a 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs @@ -21,7 +21,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex baseQuery.QueryMaxResults = settings.MaxResult; // Set list of columns we want to display, getting the path presently - baseQuery.QuerySelectColumns = "System.FileName, System.ItemPathDisplay, System.ItemType"; + baseQuery.QuerySelectColumns = "System.FileName, System.ItemUrl, System.ItemType"; // Filter based on file name baseQuery.QueryContentProperties = "System.FileName"; @@ -117,5 +117,23 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex { return $"scope='file:'"; } + + /// + /// Search will be performed on all indexed file contents for the specified search keywords. + /// + public string QueryForFileContentSearch(string userSearchString) + { + string query = "SELECT TOP " + settings.MaxResult + $" {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE "; + + return query + QueryWhereRestrictionsForFileContentSearch(userSearchString) + " AND " + QueryWhereRestrictionsForAllFilesAndFoldersSearch(); + } + + /// + /// Set the required WHERE clause restriction to search within file content. + /// + public string QueryWhereRestrictionsForFileContentSearch(string searchQuery) + { + return $"FREETEXT('{searchQuery}')"; + } } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs index d4bfdbbfc..5b12870c8 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs @@ -1,4 +1,5 @@ -using Flow.Launcher.Plugin.Explorer.Search.FolderLinks; +using Flow.Launcher.Plugin.Explorer.Search; +using Flow.Launcher.Plugin.Explorer.Search.FolderLinks; using Newtonsoft.Json; using System.Collections.Generic; @@ -17,5 +18,11 @@ namespace Flow.Launcher.Plugin.Explorer [JsonProperty] public List IndexSearchExcludedSubdirectoryPaths { get; set; } = new List(); + + [JsonProperty] + public string SearchActionKeyword { get; set; } = Query.GlobalPluginWildcardSign; + + [JsonProperty] + public string FileContentSearchActionKeyword { get; set; } = Constants.DefaultContentSearchActionKeyword; } } \ 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 a5e81716a..7fcd77f07 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs @@ -1,4 +1,5 @@ -using Flow.Launcher.Infrastructure.Storage; +using Flow.Launcher.Core.Plugin; +using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Plugin.Explorer.Search; using Flow.Launcher.Plugin.Explorer.Search.FolderLinks; using System.Diagnostics; @@ -40,5 +41,20 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels Process.Start(psi); } + + internal void UpdateActionKeyword(string newActionKeyword, string oldActionKeyword) + { + PluginManager.ReplaceActionKeyword(Context.CurrentPluginMetadata.ID, oldActionKeyword, newActionKeyword); + + if (Settings.FileContentSearchActionKeyword == oldActionKeyword) + Settings.FileContentSearchActionKeyword = newActionKeyword; + + if (Settings.SearchActionKeyword == oldActionKeyword) + 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 b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml new file mode 100644 index 000000000..0e1c7e872 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + +