diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj
index 4a7bc20e3..566e96502 100644
--- a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj
+++ b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj
@@ -58,7 +58,7 @@
-
+
diff --git a/Flow.Launcher.Test/Plugins/ProgramTest.cs b/Flow.Launcher.Test/Plugins/ProgramTest.cs
index a0d2243ce..e3a05f484 100644
--- a/Flow.Launcher.Test/Plugins/ProgramTest.cs
+++ b/Flow.Launcher.Test/Plugins/ProgramTest.cs
@@ -19,7 +19,7 @@ namespace Flow.Launcher.Test.Plugins
var app = new UWP.Application();
// Act
- var result = app.FormattedPriReferenceValue(packageName, rawPriReferenceValue);
+ var result = UWP.Application.FormattedPriReferenceValue(packageName, rawPriReferenceValue);
// Assert
Assert.IsTrue(result == expectedFormat,
diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs
index 892e18ddd..fc58e40df 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs
@@ -1,4 +1,4 @@
-using Flow.Launcher.Plugin.BrowserBookmark.Models;
+using Flow.Launcher.Plugin.BrowserBookmark.Models;
using System;
using System.Collections.Generic;
using System.Data.SQLite;
@@ -130,4 +130,4 @@ namespace Flow.Launcher.Plugin.BrowserBookmark
}
}
}
-}
\ No newline at end of file
+}
diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj
index 8454b11e6..5cff7e2e5 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj
@@ -3,7 +3,8 @@
Library
net6.0-windows
- true
+ win-x64
+ true
{9B130CC5-14FB-41FF-B310-0A95B6894C37}
Properties
Flow.Launcher.Plugin.BrowserBookmark
@@ -23,7 +24,7 @@
4
false
-
+
pdbonly
true
@@ -39,15 +40,6 @@
PreserveNewest
-
-
-
- Always
-
-
- Always
-
-
@@ -64,8 +56,8 @@
-
-
+
+
diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/x64/SQLite.Interop.dll b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/x64/SQLite.Interop.dll
deleted file mode 100644
index 877b4d78e..000000000
Binary files a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/x64/SQLite.Interop.dll and /dev/null differ
diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/x86/SQLite.Interop.dll b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/x86/SQLite.Interop.dll
deleted file mode 100644
index ccb5e03b6..000000000
Binary files a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/x86/SQLite.Interop.dll and /dev/null differ
diff --git a/Plugins/Flow.Launcher.Plugin.Program/AddProgramSource.xaml b/Plugins/Flow.Launcher.Plugin.Program/AddProgramSource.xaml
index b125c35f1..49bb8df67 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/AddProgramSource.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/AddProgramSource.xaml
@@ -10,8 +10,7 @@
Foreground="{DynamicResource PopupTextColor}"
ResizeMode="NoResize"
SizeToContent="WidthAndHeight"
- WindowStartupLocation="CenterScreen"
- mc:Ignorable="d">
+ WindowStartupLocation="CenterScreen">
@@ -20,7 +19,6 @@
-
@@ -54,42 +52,78 @@
-
-
+
+
-
-
+
+
+
+
+
+
+
+
+
+
-
+
-
-
+
+
+
+
+
+
+ Margin="10,0"
+ VerticalAlignment="Center" />
@@ -97,19 +131,17 @@
-
-
-
+
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.Program/AddProgramSource.xaml.cs b/Plugins/Flow.Launcher.Plugin.Program/AddProgramSource.xaml.cs
index 045d363b3..e88c9b988 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/AddProgramSource.xaml.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/AddProgramSource.xaml.cs
@@ -9,11 +9,12 @@ namespace Flow.Launcher.Plugin.Program
///
/// Interaction logic for AddProgramSource.xaml
///
- public partial class AddProgramSource
+ public partial class AddProgramSource : Window
{
private PluginInitContext _context;
- private Settings.ProgramSource _editing;
+ private ProgramSource _editing;
private Settings _settings;
+ private bool update;
public AddProgramSource(PluginInitContext context, Settings settings)
{
@@ -21,14 +22,19 @@ namespace Flow.Launcher.Plugin.Program
_context = context;
_settings = settings;
Directory.Focus();
+ Chkbox.IsChecked = true;
+ update = false;
+ btnAdd.Content = _context.API.GetTranslation("flowlauncher_plugin_program_add");
}
- public AddProgramSource(Settings.ProgramSource edit, Settings settings)
+ public AddProgramSource(PluginInitContext context, Settings settings, ProgramSource source)
{
- _editing = edit;
- _settings = settings;
-
InitializeComponent();
+ _context = context;
+ _editing = source;
+ _settings = settings;
+ update = true;
+ Chkbox.IsChecked = _editing.Enabled;
Directory.Text = _editing.Location;
}
@@ -47,34 +53,54 @@ namespace Flow.Launcher.Plugin.Program
Close();
}
- private void ButtonAdd_OnClick(object sender, RoutedEventArgs e)
+ private void BtnAdd_OnClick(object sender, RoutedEventArgs e)
{
- string s = Directory.Text;
- if (!System.IO.Directory.Exists(s))
+ string path = Directory.Text;
+ bool modified = false;
+ if (!System.IO.Directory.Exists(path))
{
System.Windows.MessageBox.Show(_context.API.GetTranslation("flowlauncher_plugin_program_invalid_path"));
return;
}
- if (_editing == null)
+ if (!update)
{
- if (!ProgramSetting.ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == Directory.Text))
+ if (!ProgramSetting.ProgramSettingDisplayList.Any(x => x.UniqueIdentifier.Equals(path, System.StringComparison.OrdinalIgnoreCase)))
{
- var source = new ProgramSource
- {
- Location = Directory.Text,
- UniqueIdentifier = Directory.Text
- };
-
+ var source = new ProgramSource(path);
+ modified = true;
_settings.ProgramSources.Insert(0, source);
ProgramSetting.ProgramSettingDisplayList.Add(source);
}
+ else
+ {
+ System.Windows.MessageBox.Show(_context.API.GetTranslation("flowlauncher_plugin_program_duplicate_program_source"));
+ return;
+ }
}
else
{
- _editing.Location = Directory.Text;
+ // Separate checks to avoid changing UniqueIdentifier of UWP
+ if (!_editing.Location.Equals(path, System.StringComparison.OrdinalIgnoreCase))
+ {
+ if (ProgramSetting.ProgramSettingDisplayList
+ .Any(x => x.UniqueIdentifier.Equals(path, System.StringComparison.OrdinalIgnoreCase)))
+ {
+ // Check if the new location is used
+ // No need to check win32 or uwp, just override them
+ System.Windows.MessageBox.Show(_context.API.GetTranslation("flowlauncher_plugin_program_duplicate_program_source"));
+ return;
+ }
+ modified = true;
+ _editing.Location = path; // Changes UniqueIdentifier internally
+ }
+ if (_editing.Enabled != Chkbox.IsChecked)
+ {
+ modified = true;
+ _editing.Enabled = Chkbox.IsChecked ?? true;
+ }
}
- DialogResult = true;
+ DialogResult = modified;
Close();
}
}
diff --git a/Plugins/Flow.Launcher.Plugin.Program/FileChangeWatcher.cs b/Plugins/Flow.Launcher.Plugin.Program/FileChangeWatcher.cs
deleted file mode 100644
index 4ede37dec..000000000
--- a/Plugins/Flow.Launcher.Plugin.Program/FileChangeWatcher.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using System.Collections.Generic;
-using System.IO;
-using System.Threading.Tasks;
-using Flow.Launcher.Infrastructure.Logger;
-using Flow.Launcher.Plugin.Program.Programs;
-
-namespace Flow.Launcher.Plugin.Program
-{
- //internal static class FileChangeWatcher
- //{
- // private static readonly List WatchedPath = new List();
- // // todo remove previous watcher events
- // public static void AddAll(List sources, string[] suffixes)
- // {
- // foreach (var s in sources)
- // {
- // if (Directory.Exists(s.Location))
- // {
- // AddWatch(s.Location, suffixes);
- // }
- // }
- // }
-
- // public static void AddWatch(string path, string[] programSuffixes, bool includingSubDirectory = true)
- // {
- // if (WatchedPath.Contains(path)) return;
- // if (!Directory.Exists(path))
- // {
- // Log.Warn($"|FileChangeWatcher|{path} doesn't exist");
- // return;
- // }
-
- // WatchedPath.Add(path);
- // foreach (string fileType in programSuffixes)
- // {
- // FileSystemWatcher watcher = new FileSystemWatcher
- // {
- // Path = path,
- // IncludeSubdirectories = includingSubDirectory,
- // Filter = $"*.{fileType}",
- // EnableRaisingEvents = true
- // };
- // watcher.Changed += FileChanged;
- // watcher.Created += FileChanged;
- // watcher.Deleted += FileChanged;
- // watcher.Renamed += FileChanged;
- // }
- // }
-
- // private static void FileChanged(object source, FileSystemEventArgs e)
- // {
- // Task.Run(() =>
- // {
- // Main.IndexPrograms();
- // });
- // }
- //}
-}
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml
index 3132db36b..1c6c97792 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml
@@ -10,16 +10,21 @@
Add
Name
Enable
+ Enabled
Disable
Location
All Programs
File Type
Reindex
Indexing
- Index Start Menu
+ Index Sources
+ Options
+ Start Menu
When enabled, Flow will load programs from the start menu
- Index Registry
+ Registry
When enabled, Flow will load programs from the registry
+ PATH
+ When enabled, Flow will load programs from the PATH environment variable
Hide app path
For executable files such as UWP or lnk, hide the file path from being visible
Search in Program Description
@@ -34,8 +39,12 @@
Please select a program source
Are you sure you want to delete the selected program sources?
+ Another program source with the same location alreaday exists.
- OK
+ Program Source
+ Edit directory and status of this program source.
+
+ Update
Program Plugin will only index files with selected suffixes and .url files with selected protocols.
Successfully updated file suffixes
File suffixes can't be empty
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Logger/ProgramLogger.cs b/Plugins/Flow.Launcher.Plugin.Program/Logger/ProgramLogger.cs
index cbf4960a3..1ae6d8a29 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Logger/ProgramLogger.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/Logger/ProgramLogger.cs
@@ -1,13 +1,8 @@
using NLog;
-using NLog.Config;
-using NLog.Targets;
using System;
-using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Security;
-using Flow.Launcher.Infrastructure;
-using Flow.Launcher.Infrastructure.UserSettings;
namespace Flow.Launcher.Plugin.Program.Logger
{
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs
index f0a53ed77..c212bf8f4 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs
@@ -1,7 +1,5 @@
using System;
-using System.Collections.Concurrent;
using System.Collections.Generic;
-using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using System.Threading;
@@ -11,9 +9,8 @@ using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.Storage;
using Flow.Launcher.Plugin.Program.Programs;
using Flow.Launcher.Plugin.Program.Views;
+using Flow.Launcher.Plugin.Program.Views.Models;
using Microsoft.Extensions.Caching.Memory;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.Primitives;
using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch;
namespace Flow.Launcher.Plugin.Program
@@ -82,13 +79,9 @@ namespace Flow.Launcher.Plugin.Program
Stopwatch.Normal("|Flow.Launcher.Plugin.Program.Main|Preload programs cost", () =>
{
_win32Storage = new BinaryStorage("Win32");
- _win32s = _win32Storage.TryLoad(new Win32[]
- {
- });
+ _win32s = _win32Storage.TryLoad(Array.Empty());
_uwpStorage = new BinaryStorage("UWP");
- _uwps = _uwpStorage.TryLoad(new UWP.Application[]
- {
- });
+ _uwps = _uwpStorage.TryLoad(Array.Empty());
});
Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload win32 programs <{_win32s.Length}>");
Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload uwps <{_uwps.Length}>");
@@ -102,7 +95,7 @@ namespace Flow.Launcher.Plugin.Program
var b = Task.Run(() =>
{
- Stopwatch.Normal("|Flow.Launcher.Plugin.Program.Main|Win32Program index cost", IndexUwpPrograms);
+ Stopwatch.Normal("|Flow.Launcher.Plugin.Program.Main|UWPPRogram index cost", IndexUwpPrograms);
});
if (cacheEmpty)
@@ -123,18 +116,23 @@ namespace Flow.Launcher.Plugin.Program
{
var windows10 = new Version(10, 0);
var support = Environment.OSVersion.Version.Major >= windows10.Major;
- var applications = support ? UWP.All() : new UWP.Application[]
- {
- };
+ var applications = support ? UWP.All() : Array.Empty();
_uwps = applications;
ResetCache();
}
public static async Task IndexProgramsAsync()
{
- var t1 = Task.Run(IndexWin32Programs);
- var t2 = Task.Run(IndexUwpPrograms);
- await Task.WhenAll(t1, t2).ConfigureAwait(false);
+ var a = Task.Run(() =>
+ {
+ Stopwatch.Normal("|Flow.Launcher.Plugin.Program.Main|Win32Program index cost", IndexWin32Programs);
+ });
+
+ var b = Task.Run(() =>
+ {
+ Stopwatch.Normal("|Flow.Launcher.Plugin.Program.Main|UWPProgram index cost", IndexUwpPrograms);
+ });
+ await Task.WhenAll(a, b).ConfigureAwait(false);
_settings.LastIndexTime = DateTime.Today;
}
@@ -190,29 +188,33 @@ namespace Flow.Launcher.Plugin.Program
return menuOptions;
}
- private void DisableProgram(IProgram programToDelete)
+ private static void DisableProgram(IProgram programToDelete)
{
if (_settings.DisabledProgramSources.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier))
return;
if (_uwps.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier))
- _uwps.FirstOrDefault(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier)
- .Enabled = false;
-
- if (_win32s.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier))
- _win32s.FirstOrDefault(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier)
- .Enabled = false;
-
- _settings.DisabledProgramSources
- .Add(
- new Settings.DisabledProgramSource
- {
- Name = programToDelete.Name,
- Location = programToDelete.Location,
- UniqueIdentifier = programToDelete.UniqueIdentifier,
- Enabled = false
- }
- );
+ {
+ var program = _uwps.First(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier);
+ program.Enabled = false;
+ _settings.DisabledProgramSources.Add(new ProgramSource(program));
+ _ = Task.Run(() =>
+ {
+ IndexUwpPrograms();
+ _settings.LastIndexTime = DateTime.Today;
+ });
+ }
+ else if (_win32s.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier))
+ {
+ var program = _win32s.First(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier);
+ program.Enabled = false;
+ _settings.DisabledProgramSources.Add(new ProgramSource(program));
+ _ = Task.Run(() =>
+ {
+ IndexWin32Programs();
+ _settings.LastIndexTime = DateTime.Today;
+ });
+ }
}
public static void StartProcess(Func runProcess, ProcessStartInfo info)
@@ -233,6 +235,7 @@ namespace Flow.Launcher.Plugin.Program
{
await IndexProgramsAsync();
}
+
public void Dispose()
{
Win32.Dispose();
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs
index 4b8423ed6..790a70392 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs
@@ -1,8 +1,6 @@
using System;
-using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-using System.Text.RegularExpressions;
namespace Flow.Launcher.Plugin.Program.Programs
{
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/AppxPackageHelper.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/AppxPackageHelper.cs
index e48f30757..62416609e 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Programs/AppxPackageHelper.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/AppxPackageHelper.cs
@@ -1,20 +1,18 @@
using System;
using System.Collections.Generic;
-using System.Text;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
-using Windows.Storage;
namespace Flow.Launcher.Plugin.Program.Programs
{
public class AppxPackageHelper
{
// This function returns a list of attributes of applications
- public List getAppsFromManifest(IStream stream)
+ public static List GetAppsFromManifest(IStream stream)
{
+ IAppxFactory appxFactory = (IAppxFactory)new AppxFactory();
List apps = new List();
- var appxFactory = new AppxFactory();
- var reader = ((IAppxFactory)appxFactory).CreateManifestReader(stream);
+ var reader = appxFactory.CreateManifestReader(stream);
var manifestApps = reader.GetApplications();
while (manifestApps.GetHasCurrent())
{
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs
index d17048dcb..72b7b0a91 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs
@@ -1,11 +1,8 @@
using System;
-using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
-using System.IO;
using Accessibility;
using System.Runtime.InteropServices.ComTypes;
-using System.Security.Policy;
namespace Flow.Launcher.Plugin.Program.Programs
{
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs
index 316aaaac3..84aa93664 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs
@@ -18,7 +18,6 @@ using Flow.Launcher.Plugin.Program.Logger;
using Rect = System.Windows.Rect;
using Flow.Launcher.Plugin.SharedModels;
using Flow.Launcher.Infrastructure.Logger;
-using System.Runtime.Versioning;
using System.Threading.Channels;
namespace Flow.Launcher.Plugin.Program.Programs
@@ -42,39 +41,27 @@ namespace Flow.Launcher.Plugin.Program.Programs
FullName = package.Id.FullName;
FamilyName = package.Id.FamilyName;
InitializeAppInfo();
- Apps = Apps.Where(a =>
- {
- var valid =
- !string.IsNullOrEmpty(a.UserModelId) &&
- !string.IsNullOrEmpty(a.DisplayName);
- return valid;
- }).ToArray();
}
private void InitializeAppInfo()
{
- AppxPackageHelper _helper = new AppxPackageHelper();
var path = Path.Combine(Location, "AppxManifest.xml");
var namespaces = XmlNamespaces(path);
InitPackageVersion(namespaces);
const uint noAttribute = 0x80;
- const Stgm exclusiveRead = Stgm.Read | Stgm.ShareExclusive;
- var hResult = SHCreateStreamOnFileEx(path, exclusiveRead, noAttribute, false, null, out IStream stream);
+ const Stgm nonExclusiveRead = Stgm.Read | Stgm.ShareDenyNone;
+ var hResult = SHCreateStreamOnFileEx(path, nonExclusiveRead, noAttribute, false, null, out IStream stream);
if (hResult == Hresult.Ok)
{
- var apps = new List();
+ List _apps = AppxPackageHelper.GetAppsFromManifest(stream);
- List _apps = _helper.getAppsFromManifest(stream);
- foreach (var _app in _apps)
- {
- var app = new Application(_app, this);
- apps.Add(app);
- }
-
- Apps = apps.Where(a => a.AppListEntry != "none").ToArray();
+ Apps = _apps.Select(x => new Application(x, this))
+ .Where(a => !string.IsNullOrEmpty(a.UserModelId)
+ && !string.IsNullOrEmpty(a.DisplayName))
+ .ToArray();
}
else
{
@@ -82,17 +69,17 @@ namespace Flow.Launcher.Plugin.Program.Programs
ProgramLogger.LogException($"|UWP|InitializeAppInfo|{path}" +
"|Error caused while trying to get the details of the UWP program", e);
- Apps = new List().ToArray();
+ Apps = Array.Empty();
}
- if (Marshal.ReleaseComObject(stream) > 0)
+ if (stream != null && Marshal.ReleaseComObject(stream) > 0)
{
Log.Error("Flow.Launcher.Plugin.Program.Programs.UWP", "AppxManifest.xml was leaked");
}
}
/// http://www.hanselman.com/blog/GetNamespacesFromAnXMLDocumentWithXPathDocumentAndLINQToXML.aspx
- private string[] XmlNamespaces(string path)
+ private static string[] XmlNamespaces(string path)
{
XDocument z = XDocument.Load(path);
if (z.Root != null)
@@ -110,9 +97,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
ProgramLogger.LogException($"|UWP|XmlNamespaces|{path}" +
$"|Error occured while trying to get the XML from {path}", new ArgumentNullException());
- return new string[]
- {
- };
+ return Array.Empty();
}
}
@@ -178,16 +163,13 @@ namespace Flow.Launcher.Plugin.Program.Programs
var updatedListWithoutDisabledApps = applications
.Where(t1 => !Main._settings.DisabledProgramSources
- .Any(x => x.UniqueIdentifier == t1.UniqueIdentifier))
- .Select(x => x);
+ .Any(x => x.UniqueIdentifier == t1.UniqueIdentifier));
return updatedListWithoutDisabledApps.ToArray();
}
else
{
- return new Application[]
- {
- };
+ return Array.Empty();
}
}
@@ -233,9 +215,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
}
else
{
- return new Package[]
- {
- };
+ return Array.Empty();
}
}
@@ -297,7 +277,6 @@ namespace Flow.Launcher.Plugin.Program.Programs
[Serializable]
public class Application : IProgram
{
- public string AppListEntry { get; set; }
public string UniqueIdentifier { get; set; }
public string DisplayName { get; set; }
public string Description { get; set; }
@@ -317,7 +296,6 @@ namespace Flow.Launcher.Plugin.Program.Programs
public Application() { }
-
public Result Result(string query, IPublicAPI api)
{
string title;
@@ -544,7 +522,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
}
}
- public string FormattedPriReferenceValue(string packageName, string rawPriReferenceValue)
+ public static string FormattedPriReferenceValue(string packageName, string rawPriReferenceValue)
{
const string prefix = "ms-resource:";
@@ -701,9 +679,15 @@ namespace Flow.Launcher.Plugin.Program.Programs
private BitmapImage ImageFromPath(string path)
{
+ // TODO: Consider using infrastructure.image.imageloader?
if (File.Exists(path))
{
- var image = new BitmapImage(new Uri(path));
+ var image = new BitmapImage();
+ image.BeginInit();
+ image.UriSource = new Uri(path);
+ image.CacheOption = BitmapCacheOption.OnLoad;
+ image.EndInit();
+ image.Freeze();
return image;
}
else
@@ -775,6 +759,23 @@ namespace Flow.Launcher.Plugin.Program.Programs
{
return $"{DisplayName}: {Description}";
}
+
+ public override bool Equals(object obj)
+ {
+ if (obj is Application other)
+ {
+ return UniqueIdentifier == other.UniqueIdentifier;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public override int GetHashCode()
+ {
+ return UniqueIdentifier.GetHashCode();
+ }
}
public enum PackageVersion
@@ -790,6 +791,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
{
Read = 0x0,
ShareExclusive = 0x10,
+ ShareDenyNone = 0x40
}
private enum Hresult : uint
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs
index fbb3ea32c..c13b32e80 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs
@@ -11,14 +11,10 @@ using Flow.Launcher.Infrastructure;
using Flow.Launcher.Plugin.Program.Logger;
using Flow.Launcher.Plugin.SharedCommands;
using Flow.Launcher.Plugin.SharedModels;
-using Flow.Launcher.Infrastructure.Logger;
-using System.Collections;
using System.Diagnostics;
-using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch;
using System.Diagnostics.CodeAnalysis;
-using System.Text.RegularExpressions;
using System.Threading.Channels;
-using Flow.Launcher.Infrastructure.Image;
+using Flow.Launcher.Plugin.Program.Views.Models;
using IniParser;
namespace Flow.Launcher.Plugin.Program.Programs
@@ -27,7 +23,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
public class Win32 : IProgram, IEquatable
{
public string Name { get; set; }
- public string UniqueIdentifier { get; set; }
+ public string UniqueIdentifier { get => _uid; set => _uid = value.ToLowerInvariant(); } // For path comparison
public string IcoPath { get; set; }
public string FullPath { get; set; }
public string LnkResolvedPath { get; set; }
@@ -41,6 +37,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
private const string ShortcutExtension = "lnk";
private const string UrlExtension = "url";
private const string ExeExtension = "exe";
+ private string _uid = string.Empty;
private static readonly Win32 Default = new Win32()
{
@@ -225,10 +222,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
ProgramLogger.LogException($"|Win32|Win32Program|{path}" +
$"|Permission denied when trying to load the program from {path}", e);
- return new Win32()
- {
- Valid = false, Enabled = false
- };
+ return Default;
}
}
@@ -248,7 +242,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
if (extension == ExeExtension && File.Exists(target))
{
program.LnkResolvedPath = program.FullPath;
- program.FullPath = Path.GetFullPath(target).ToLower();
+ program.FullPath = Path.GetFullPath(target).ToLowerInvariant();
program.ExecutableName = Path.GetFileName(target);
var description = _helper.description;
@@ -279,6 +273,14 @@ namespace Flow.Launcher.Plugin.Program.Programs
program.Valid = false;
return program;
}
+ catch (FileNotFoundException e)
+ {
+ ProgramLogger.LogException($"|Win32|LnkProgram|{path}" +
+ "|An unexpected error occurred in the calling method LnkProgram", e);
+
+ program.Valid = false;
+ return program;
+ }
#if !DEBUG //Only do a catch all in production. This is so make developer aware of any unhandled exception and add the exception handling in.
catch (Exception e)
{
@@ -291,7 +293,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
#endif
}
- private static Win32 UrlProgram(string path)
+ private static Win32 UrlProgram(string path, string[] protocols)
{
var program = Win32Program(path);
program.Valid = false;
@@ -306,9 +308,9 @@ namespace Flow.Launcher.Plugin.Program.Programs
{
return program;
}
- foreach(var protocol in Main._settings.GetProtocols())
+ foreach (var protocol in protocols)
{
- if(url.StartsWith(protocol))
+ if (url.StartsWith(protocol))
{
program.LnkResolvedPath = url;
program.Valid = true;
@@ -345,30 +347,28 @@ namespace Flow.Launcher.Plugin.Program.Programs
ProgramLogger.LogException($"|Win32|ExeProgram|{path}" +
$"|Permission denied when trying to load the program from {path}", e);
- return new Win32()
- {
- Valid = false, Enabled = false
- };
+ return Default;
}
}
- private static IEnumerable ProgramPaths(string directory, string[] suffixes)
+ private static IEnumerable ProgramPaths(string directory, string[] suffixes, bool recursive = true)
{
if (!Directory.Exists(directory))
return Enumerable.Empty();
return Directory.EnumerateFiles(directory, "*", new EnumerationOptions
{
- IgnoreInaccessible = true, RecurseSubdirectories = true
+ IgnoreInaccessible = true,
+ RecurseSubdirectories = recursive
}).Where(x => suffixes.Contains(Extension(x)));
}
private static string Extension(string path)
{
- var extension = Path.GetExtension(path)?.ToLower();
+ var extension = Path.GetExtension(path)?.ToLowerInvariant();
if (!string.IsNullOrEmpty(extension))
{
- return extension.Substring(1);
+ return extension.Substring(1); // remove dot
}
else
{
@@ -376,27 +376,20 @@ namespace Flow.Launcher.Plugin.Program.Programs
}
}
- private static IEnumerable UnregisteredPrograms(List sources, string[] suffixes)
+ private static IEnumerable UnregisteredPrograms(List sources, string[] suffixes, string[] protocols)
{
+ // Disabled custom sources are not in DisabledProgramSources
var paths = ExceptDisabledSource(sources.Where(s => Directory.Exists(s.Location) && s.Enabled)
- .SelectMany(s => ProgramPaths(s.Location, suffixes)), x => x)
- .Distinct();
-
- var programs = paths.Select(x => Extension(x) switch
- {
- ExeExtension => ExeProgram(x),
- ShortcutExtension => LnkProgram(x),
- UrlExtension => UrlProgram(x),
- _ => Win32Program(x)
- });
+ .AsParallel()
+ .SelectMany(s => ProgramPaths(s.Location, suffixes)))
+ .Distinct();
+ var programs = paths.Select(x => GetProgramFromPath(x, protocols));
return programs;
}
- private static IEnumerable StartMenuPrograms(string[] suffixes)
+ private static IEnumerable StartMenuPrograms(string[] suffixes, string[] protocols)
{
- var disabledProgramsList = Main._settings.DisabledProgramSources;
-
var directory1 = Environment.GetFolderPath(Environment.SpecialFolder.Programs);
var directory2 = Environment.GetFolderPath(Environment.SpecialFolder.CommonPrograms);
var paths1 = ProgramPaths(directory1, suffixes);
@@ -404,17 +397,35 @@ namespace Flow.Launcher.Plugin.Program.Programs
var toFilter = paths1.Concat(paths2);
+ var programs = ExceptDisabledSource(toFilter.Distinct())
+ .Select(x => GetProgramFromPath(x, protocols));
+ return programs;
+ }
+
+ private static IEnumerable PATHPrograms(string[] suffixes, string[] protocols)
+ {
+ var pathEnv = Environment.GetEnvironmentVariable("Path");
+ if (String.IsNullOrEmpty(pathEnv))
+ {
+ return Array.Empty();
+ }
+
+ var paths = pathEnv.Split(";", StringSplitOptions.RemoveEmptyEntries).DistinctBy(p => p.ToLowerInvariant());
+
+ var toFilter = paths.AsParallel().SelectMany(p => ProgramPaths(p, suffixes, recursive: false));
+
var programs = ExceptDisabledSource(toFilter.Distinct())
.Select(x => Extension(x) switch
{
ShortcutExtension => LnkProgram(x),
- UrlExtension => UrlProgram(x),
+ UrlExtension => UrlProgram(x, protocols),
+ ExeExtension => ExeProgram(x),
_ => Win32Program(x)
});
return programs;
}
- private static IEnumerable AppPathsPrograms(string[] suffixes)
+ private static IEnumerable AppPathsPrograms(string[] suffixes, string[] protocols)
{
// https://msdn.microsoft.com/en-us/library/windows/desktop/ee872121
const string appPaths = @"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths";
@@ -434,12 +445,11 @@ namespace Flow.Launcher.Plugin.Program.Programs
toFilter = toFilter.Concat(GetPathFromRegistry(rootUser));
}
-
toFilter = toFilter.Distinct().Where(p => suffixes.Contains(Extension(p)));
- var filtered = ExceptDisabledSource(toFilter);
-
- return filtered.Select(GetProgramFromPath).ToList(); // ToList due to disposing issue
+ var programs = ExceptDisabledSource(toFilter)
+ .Select(x => GetProgramFromPath(x, protocols)).Where(x => x.Valid).ToList(); // ToList due to disposing issue
+ return programs;
}
private static IEnumerable GetPathFromRegistry(RegistryKey root)
@@ -479,7 +489,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
}
}
- private static Win32 GetProgramFromPath(string path)
+ private static Win32 GetProgramFromPath(string path, string[] protocols)
{
if (string.IsNullOrEmpty(path))
return Default;
@@ -489,14 +499,18 @@ namespace Flow.Launcher.Plugin.Program.Programs
if (!File.Exists(path))
return Default;
- var entry = Win32Program(path);
-
- return entry;
+ return Extension(path) switch
+ {
+ ShortcutExtension => LnkProgram(path),
+ ExeExtension => ExeProgram(path),
+ UrlExtension => UrlProgram(path, protocols),
+ _ => Win32Program(path)
+ }; ;
}
- public static IEnumerable ExceptDisabledSource(IEnumerable sources)
+ public static IEnumerable ExceptDisabledSource(IEnumerable paths)
{
- return ExceptDisabledSource(sources, x => x);
+ return ExceptDisabledSource(paths, x => x.ToLowerInvariant());
}
public static IEnumerable ExceptDisabledSource(IEnumerable sources,
@@ -531,7 +545,8 @@ namespace Flow.Launcher.Plugin.Program.Programs
private static IEnumerable ProgramsHasher(IEnumerable programs)
{
- return programs.GroupBy(p => p.FullPath.ToLower())
+ return programs.GroupBy(p => p.FullPath.ToLowerInvariant())
+ .AsParallel()
.SelectMany(g =>
{
var temp = g.Where(g => !string.IsNullOrEmpty(g.Description)).ToList();
@@ -547,8 +562,10 @@ namespace Flow.Launcher.Plugin.Program.Programs
try
{
var programs = Enumerable.Empty();
+ var suffixes = settings.GetSuffixes();
+ var protocols = settings.GetProtocols();
- var unregistered = UnregisteredPrograms(settings.ProgramSources, settings.GetSuffixes());
+ var unregistered = UnregisteredPrograms(settings.ProgramSources, suffixes, protocols);
programs = programs.Concat(unregistered);
@@ -556,16 +573,22 @@ namespace Flow.Launcher.Plugin.Program.Programs
if (settings.EnableRegistrySource)
{
- var appPaths = AppPathsPrograms(settings.GetSuffixes());
+ var appPaths = AppPathsPrograms(suffixes, protocols);
autoIndexPrograms = autoIndexPrograms.Concat(appPaths);
}
if (settings.EnableStartMenuSource)
{
- var startMenu = StartMenuPrograms(settings.GetSuffixes());
+ var startMenu = StartMenuPrograms(suffixes, protocols);
autoIndexPrograms = autoIndexPrograms.Concat(startMenu);
}
+ if (settings.EnablePATHSource)
+ {
+ var path = PATHPrograms(settings.GetSuffixes(), protocols);
+ autoIndexPrograms = autoIndexPrograms.Concat(path);
+ }
+
autoIndexPrograms = ProgramsHasher(autoIndexPrograms);
return programs.Concat(autoIndexPrograms).Where(x => x.Valid).Distinct().ToArray();
@@ -600,6 +623,18 @@ namespace Flow.Launcher.Plugin.Program.Programs
return UniqueIdentifier == other.UniqueIdentifier;
}
+ public override bool Equals(object obj)
+ {
+ if (obj is Win32 other)
+ {
+ return UniqueIdentifier == other.UniqueIdentifier;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
private static IEnumerable GetStartMenuPaths()
{
var directory1 = Environment.GetFolderPath(Environment.SpecialFolder.Programs);
@@ -640,7 +675,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
await Task.Run(Main.IndexWin32Programs);
}
}
-
+
public static void WatchDirectory(string directory)
{
if (!Directory.Exists(directory))
@@ -653,7 +688,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
watcher.Deleted += static (_, _) => indexQueue.Writer.TryWrite(default);
watcher.EnableRaisingEvents = true;
watcher.IncludeSubdirectories = true;
-
+
Watchers.Add(watcher);
}
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Settings.cs b/Plugins/Flow.Launcher.Plugin.Program/Settings.cs
index 96328ba62..e3e8b99b9 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Settings.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/Settings.cs
@@ -1,19 +1,26 @@
using System;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
using System.Text.Json.Serialization;
-using Windows.Foundation.Metadata;
+using Flow.Launcher.Plugin.Program.Views.Models;
namespace Flow.Launcher.Plugin.Program
{
public class Settings
{
public DateTime LastIndexTime { get; set; }
- public List ProgramSources { get; set; } = new List();
- public List DisabledProgramSources { get; set; } = new List();
- [Obsolete, JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ ///
+ /// User-added program sources' directories
+ ///
+ public List ProgramSources { get; set; } = new List();
+
+ ///
+ /// Disabled single programs, not including User-added directories
+ ///
+ public List DisabledProgramSources { get; set; } = new List();
+
+ [Obsolete("Should use GetSuffixes() instead."), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string[] ProgramSuffixes { get; set; } = null;
public string[] CustomSuffixes { get; set; } = Array.Empty(); // Custom suffixes only
public string[] CustomProtocols { get; set; } = Array.Empty();
@@ -111,6 +118,8 @@ namespace Flow.Launcher.Plugin.Program
public bool EnableDescription { get; set; } = false;
public bool HideAppsPath { get; set; } = true;
public bool EnableRegistrySource { get; set; } = true;
+ public bool EnablePATHSource { get; set; } = true;
+
public string CustomizedExplorer { get; set; } = Explorer;
public string CustomizedArgs { get; set; } = ExplorerArgs;
@@ -119,25 +128,5 @@ namespace Flow.Launcher.Plugin.Program
internal const string Explorer = "explorer";
internal const string ExplorerArgs = "%s";
-
- ///
- /// Contains user added folder location contents as well as all user disabled applications
- ///
- ///
- /// Win32 class applications set UniqueIdentifier using their full file path
- /// UWP class applications set UniqueIdentifier using their Application User Model ID
- /// Custom user added program sources set UniqueIdentifier using their location
- ///
- public class ProgramSource
- {
- private string name;
-
- public string Location { get; set; }
- public string Name { get => name ?? new DirectoryInfo(Location).Name; set => name = value; }
- public bool Enabled { get; set; } = true;
- public string UniqueIdentifier { get; set; }
- }
-
- public class DisabledProgramSource : ProgramSource { }
}
}
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Views/Commands/ProgramSettingDisplay.cs b/Plugins/Flow.Launcher.Plugin.Program/Views/Commands/ProgramSettingDisplay.cs
index 3129b2b99..e4d7c323a 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Views/Commands/ProgramSettingDisplay.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/Views/Commands/ProgramSettingDisplay.cs
@@ -1,146 +1,88 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using Flow.Launcher.Plugin.Program.Views.Models;
namespace Flow.Launcher.Plugin.Program.Views.Commands
{
internal static class ProgramSettingDisplay
{
- internal static List LoadProgramSources(this List programSources)
+ internal static List LoadProgramSources()
{
- var list = new List();
-
- programSources.ForEach(x => list
- .Add(
- new ProgramSource
- {
- Enabled = x.Enabled,
- Location = x.Location,
- Name = x.Name,
- UniqueIdentifier = x.UniqueIdentifier
- }
- ));
-
// Even though these are disabled, we still want to display them so users can enable later on
- Main._settings
- .DisabledProgramSources
- .Where(t1 => !Main._settings
- .ProgramSources // program sourcces added above already, so exlcude
- .Any(x => t1.UniqueIdentifier == x.UniqueIdentifier))
- .Select(x => x)
- .ToList()
- .ForEach(x => list
- .Add(
- new ProgramSource
- {
- Enabled = x.Enabled,
- Location = x.Location,
- Name = x.Name,
- UniqueIdentifier = x.UniqueIdentifier
- }
- ));
-
- return list;
+ return Main._settings
+ .DisabledProgramSources
+ .Union(Main._settings.ProgramSources)
+ .ToList();
}
- internal static void LoadAllApplications(this List list)
+ internal static void DisplayAllPrograms()
{
- Main._win32s
- .Where(t1 => !ProgramSetting.ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier))
- .ToList()
- .ForEach(t1 => ProgramSetting.ProgramSettingDisplayList
- .Add(
- new ProgramSource
- {
- Name = t1.Name,
- Location = t1.ParentDirectory,
- UniqueIdentifier = t1.UniqueIdentifier,
- Enabled = t1.Enabled
- }
- ));
+ var win32 = Main._win32s
+ .Where(t1 => !ProgramSetting.ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier))
+ .Select(x => new ProgramSource(x));
- Main._uwps
- .Where(t1 => !ProgramSetting.ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier))
- .ToList()
- .ForEach(t1 => ProgramSetting.ProgramSettingDisplayList
- .Add(
- new ProgramSource
- {
- Name = t1.DisplayName,
- Location = t1.Package.Location,
- UniqueIdentifier = t1.UniqueIdentifier,
- Enabled = t1.Enabled
- }
- ));
+ var uwp = Main._uwps
+ .Where(t1 => !ProgramSetting.ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier))
+ .Select(x => new ProgramSource(x));
+
+ ProgramSetting.ProgramSettingDisplayList.AddRange(win32);
+ ProgramSetting.ProgramSettingDisplayList.AddRange(uwp);
}
- internal static void SetProgramSourcesStatus(this List list, List selectedProgramSourcesToDisable, bool status)
+ internal static void SetProgramSourcesStatus(List selectedProgramSourcesToDisable, bool status)
{
- ProgramSetting.ProgramSettingDisplayList
- .Where(t1 => selectedProgramSourcesToDisable.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier && t1.Enabled != status))
- .ToList()
- .ForEach(t1 => t1.Enabled = status);
+ foreach(var program in ProgramSetting.ProgramSettingDisplayList)
+ {
+ if (selectedProgramSourcesToDisable.Any(x => x.UniqueIdentifier == program.UniqueIdentifier && program.Enabled != status))
+ {
+ program.Enabled = status;
+ }
+ }
- Main._win32s
- .Where(t1 => selectedProgramSourcesToDisable.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier && t1.Enabled != status))
- .ToList()
- .ForEach(t1 => t1.Enabled = status);
+ foreach(var program in Main._win32s)
+ {
+ if (selectedProgramSourcesToDisable.Any(x => x.UniqueIdentifier == program.UniqueIdentifier && program.Enabled != status))
+ {
+ program.Enabled = status;
+ }
+ }
- Main._uwps
- .Where(t1 => selectedProgramSourcesToDisable.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier && t1.Enabled != status))
- .ToList()
- .ForEach(t1 => t1.Enabled = status);
+ foreach (var program in Main._uwps)
+ {
+ if (selectedProgramSourcesToDisable.Any(x => x.UniqueIdentifier == program.UniqueIdentifier && program.Enabled != status))
+ {
+ program.Enabled = status;
+ }
+ }
}
- internal static void StoreDisabledInSettings(this List list)
+ internal static void StoreDisabledInSettings()
{
- Main._settings.ProgramSources
- .Where(t1 => ProgramSetting.ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier && !x.Enabled))
- .ToList()
- .ForEach(t1 => t1.Enabled = false);
-
- ProgramSetting.ProgramSettingDisplayList
+ // Disabled, not in DisabledProgramSources or ProgramSources
+ var tmp = ProgramSetting.ProgramSettingDisplayList
.Where(t1 => !t1.Enabled
- && !Main._settings.DisabledProgramSources.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier))
- .ToList()
- .ForEach(x => Main._settings.DisabledProgramSources
- .Add(
- new Settings.DisabledProgramSource
- {
- Name = x.Name,
- Location = x.Location,
- UniqueIdentifier = x.UniqueIdentifier,
- Enabled = false
- }
- ));
+ && !Main._settings.DisabledProgramSources.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier)
+ && !Main._settings.ProgramSources.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier));
+
+ Main._settings.DisabledProgramSources.AddRange(tmp);
}
- internal static void RemoveDisabledFromSettings(this List list)
+ internal static void RemoveDisabledFromSettings()
{
- Main._settings.ProgramSources
- .Where(t1 => ProgramSetting.ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier && x.Enabled))
- .ToList()
- .ForEach(t1 => t1.Enabled = true);
-
- Main._settings.DisabledProgramSources
- .Where(t1 => ProgramSetting.ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier && x.Enabled))
- .ToList()
- .ForEach(x => Main._settings.DisabledProgramSources.Remove(x));
+ Main._settings.DisabledProgramSources.RemoveAll(t1 => t1.Enabled);
}
internal static bool IsReindexRequired(this List selectedItems)
{
- if (selectedItems.Where(t1 => t1.Enabled && !Main._uwps.Any(x => t1.UniqueIdentifier == x.UniqueIdentifier)).Count() > 0
- && selectedItems.Where(t1 => t1.Enabled && !Main._win32s.Any(x => t1.UniqueIdentifier == x.UniqueIdentifier)).Count() > 0)
+ // Not in cache
+ if (selectedItems.Any(t1 => t1.Enabled && !Main._uwps.Any(x => t1.UniqueIdentifier == x.UniqueIdentifier))
+ && selectedItems.Any(t1 => t1.Enabled && !Main._win32s.Any(x => t1.UniqueIdentifier == x.UniqueIdentifier)))
return true;
// ProgramSources holds list of user added directories,
// so when we enable/disable we need to reindex to show/not show the programs
// that are found in those directories.
- if (selectedItems.Where(t1 => Main._settings.ProgramSources.Any(x => t1.UniqueIdentifier == x.UniqueIdentifier)).Count() > 0)
+ if (selectedItems.Any(t1 => Main._settings.ProgramSources.Any(x => t1.UniqueIdentifier == x.UniqueIdentifier)))
return true;
return false;
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Views/Models/ProgramSource.cs b/Plugins/Flow.Launcher.Plugin.Program/Views/Models/ProgramSource.cs
index 8e3184ff7..fb32fb892 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Views/Models/ProgramSource.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/Views/Models/ProgramSource.cs
@@ -1,5 +1,86 @@
-
+using System;
+using System.IO;
+using System.Text.Json.Serialization;
+using Flow.Launcher.Plugin.Program.Programs;
+
namespace Flow.Launcher.Plugin.Program.Views.Models
{
- public class ProgramSource : Settings.ProgramSource { }
+ ///
+ /// Contains user added folder location contents as well as all user disabled applications
+ ///
+ ///
+ /// Win32 class applications set UniqueIdentifier using their full file path
+ /// UWP class applications set UniqueIdentifier using their Application User Model ID
+ /// Custom user added program sources set UniqueIdentifier using their location
+ ///
+ public class ProgramSource
+ {
+ private string name;
+
+ private string loc;
+ public string Location
+ {
+ get => loc;
+ set
+ {
+ loc = value;
+ UniqueIdentifier = value.ToLowerInvariant();
+ }
+ }
+ public string Name { get => name ?? new DirectoryInfo(Location).Name; set => name = value; }
+ public bool Enabled { get; set; } = true;
+
+ public string UniqueIdentifier { get; private set; }
+
+ [JsonConstructor]
+ public ProgramSource(string name, string location, bool enabled, string uniqueIdentifier)
+ {
+ loc = location;
+ this.name = name;
+ Enabled = enabled;
+ if (location.Equals(uniqueIdentifier, StringComparison.OrdinalIgnoreCase))
+ {
+ UniqueIdentifier = location.ToLowerInvariant(); // To make sure old config can be reset to case-insensitive
+ }
+ else
+ {
+ UniqueIdentifier = uniqueIdentifier; // For uwp apps
+ }
+ }
+
+ ///
+ /// Add source by location
+ ///
+ /// location of program source
+ /// enabled
+ public ProgramSource(string location, bool enabled = true)
+ {
+ loc = location;
+ Enabled = enabled;
+ UniqueIdentifier = location.ToLowerInvariant(); // For path comparison
+ }
+
+ public ProgramSource(IProgram source)
+ {
+ loc = source.Location;
+ Name = source.Name;
+ Enabled = source.Enabled;
+ UniqueIdentifier = source.UniqueIdentifier;
+ }
+
+ public override bool Equals(object obj)
+ {
+ return obj is ProgramSource other && other.UniqueIdentifier == this.UniqueIdentifier;
+ }
+
+ public bool Equals(IProgram program)
+ {
+ return program != null && program.UniqueIdentifier == this.UniqueIdentifier;
+ }
+
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(UniqueIdentifier);
+ }
+ }
}
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml b/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml
index f07879465..227c23ebc 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml
@@ -10,58 +10,88 @@
mc:Ignorable="d">
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml.cs b/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml.cs
index 1a31e8c28..b8c2bca22 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml.cs
@@ -1,7 +1,6 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
@@ -36,6 +35,7 @@ namespace Flow.Launcher.Plugin.Program.Views
_settings.EnableDescription = value;
}
}
+
public bool HideAppsPath
{
get => _settings.HideAppsPath;
@@ -66,6 +66,16 @@ namespace Flow.Launcher.Plugin.Program.Views
}
}
+ public bool EnablePATHSource
+ {
+ get => _settings.EnablePATHSource;
+ set
+ {
+ _settings.EnablePATHSource = value;
+ ReIndexing();
+ }
+ }
+
public string CustomizedExplorerPath
{
get => _settings.CustomizedExplorer;
@@ -88,7 +98,7 @@ namespace Flow.Launcher.Plugin.Program.Views
private void Setting_Loaded(object sender, RoutedEventArgs e)
{
- ProgramSettingDisplayList = _settings.ProgramSources.LoadProgramSources();
+ ProgramSettingDisplayList = ProgramSettingDisplay.LoadProgramSources();
programSourceView.ItemsSource = ProgramSettingDisplayList;
ViewRefresh();
@@ -147,20 +157,35 @@ namespace Flow.Launcher.Plugin.Program.Views
private void btnEditProgramSource_OnClick(object sender, RoutedEventArgs e)
{
- var selectedProgramSource = programSourceView.SelectedItem as Settings.ProgramSource;
- if (selectedProgramSource != null)
- {
- var add = new AddProgramSource(selectedProgramSource, _settings);
- if (add.ShowDialog() ?? false)
- {
- ReIndexing();
- }
- }
- else
+ var selectedProgramSource = programSourceView.SelectedItem as ProgramSource;
+ EditProgramSource(selectedProgramSource);
+ }
+
+ private void EditProgramSource(ProgramSource selectedProgramSource)
+ {
+ if (selectedProgramSource == null)
{
string msg = context.API.GetTranslation("flowlauncher_plugin_program_pls_select_program_source");
MessageBox.Show(msg);
}
+ else
+ {
+ var add = new AddProgramSource(context, _settings, selectedProgramSource);
+ if (add.ShowDialog() ?? false)
+ {
+ if (selectedProgramSource.Enabled)
+ {
+ ProgramSettingDisplay.SetProgramSourcesStatus(new List { selectedProgramSource }, true); // sync status in win32, uwp and disabled
+ ProgramSettingDisplay.RemoveDisabledFromSettings();
+ }
+ else
+ {
+ ProgramSettingDisplay.SetProgramSourcesStatus(new List { selectedProgramSource }, false);
+ ProgramSettingDisplay.StoreDisabledInSettings();
+ }
+ ReIndexing();
+ }
+ }
}
private void btnReindex_Click(object sender, RoutedEventArgs e)
@@ -199,19 +224,16 @@ namespace Flow.Launcher.Plugin.Program.Views
{
foreach (string directory in directories)
{
- if (Directory.Exists(directory) && !ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == directory))
+ if (Directory.Exists(directory)
+ && !ProgramSettingDisplayList.Any(x => x.UniqueIdentifier.Equals(directory, System.StringComparison.OrdinalIgnoreCase)))
{
- var source = new ProgramSource
- {
- Location = directory,
- UniqueIdentifier = directory
- };
+ var source = new ProgramSource(directory);
directoriesToAdd.Add(source);
}
}
- if (directoriesToAdd.Count() > 0)
+ if (directoriesToAdd.Count > 0)
{
directoriesToAdd.ForEach(x => _settings.ProgramSources.Add(x));
directoriesToAdd.ForEach(x => ProgramSettingDisplayList.Add(x));
@@ -224,7 +246,7 @@ namespace Flow.Launcher.Plugin.Program.Views
private void btnLoadAllProgramSource_OnClick(object sender, RoutedEventArgs e)
{
- ProgramSettingDisplayList.LoadAllApplications();
+ ProgramSettingDisplay.DisplayAllPrograms();
ViewRefresh();
}
@@ -235,18 +257,14 @@ namespace Flow.Launcher.Plugin.Program.Views
.SelectedItems.Cast()
.ToList();
- if (selectedItems.Count() == 0)
+ if (selectedItems.Count == 0)
{
string msg = context.API.GetTranslation("flowlauncher_plugin_program_pls_select_program_source");
MessageBox.Show(msg);
return;
}
- if (selectedItems
- .Where(t1 => !_settings
- .ProgramSources
- .Any(x => t1.UniqueIdentifier == x.UniqueIdentifier))
- .Count() == 0)
+ if (IsAllItemsUserAdded(selectedItems))
{
var msg = string.Format(context.API.GetTranslation("flowlauncher_plugin_program_delete_program_source"));
@@ -257,17 +275,17 @@ namespace Flow.Launcher.Plugin.Program.Views
DeleteProgramSources(selectedItems);
}
- else if (IsSelectedRowStatusEnabledMoreOrEqualThanDisabled(selectedItems))
+ else if (HasMoreOrEqualEnabledItems(selectedItems))
{
- ProgramSettingDisplayList.SetProgramSourcesStatus(selectedItems, false);
+ ProgramSettingDisplay.SetProgramSourcesStatus(selectedItems, false);
- ProgramSettingDisplayList.StoreDisabledInSettings();
+ ProgramSettingDisplay.StoreDisabledInSettings();
}
else
{
- ProgramSettingDisplayList.SetProgramSourcesStatus(selectedItems, true);
+ ProgramSettingDisplay.SetProgramSourcesStatus(selectedItems, true);
- ProgramSettingDisplayList.RemoveDisabledFromSettings();
+ ProgramSettingDisplay.RemoveDisabledFromSettings();
}
if (selectedItems.IsReindexRequired())
@@ -329,35 +347,41 @@ namespace Flow.Launcher.Plugin.Program.Views
dataView.Refresh();
}
- private bool IsSelectedRowStatusEnabledMoreOrEqualThanDisabled(List selectedItems)
+ private static bool HasMoreOrEqualEnabledItems(List items)
{
- return selectedItems.Where(x => x.Enabled).Count() >= selectedItems.Where(x => !x.Enabled).Count();
+ var enableCount = items.Where(x => x.Enabled).Count();
+ return enableCount >= items.Count - enableCount;
}
- private void Row_OnClick(object sender, RoutedEventArgs e)
+ private void programSourceView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var selectedItems = programSourceView
- .SelectedItems.Cast()
- .ToList();
+ .SelectedItems.Cast()
+ .ToList();
- if (selectedItems
- .Where(t1 => !_settings
- .ProgramSources
- .Any(x => t1.UniqueIdentifier == x.UniqueIdentifier))
- .Count() == 0)
+ if (IsAllItemsUserAdded(selectedItems))
{
btnProgramSourceStatus.Content = context.API.GetTranslation("flowlauncher_plugin_program_delete");
- return;
}
-
- if (IsSelectedRowStatusEnabledMoreOrEqualThanDisabled(selectedItems))
+ else if (HasMoreOrEqualEnabledItems(selectedItems))
{
- btnProgramSourceStatus.Content = "Disable";
+ btnProgramSourceStatus.Content = context.API.GetTranslation("flowlauncher_plugin_program_disable");
}
else
{
- btnProgramSourceStatus.Content = "Enable";
+ btnProgramSourceStatus.Content = context.API.GetTranslation("flowlauncher_plugin_program_enable");
}
}
+
+ private void programSourceView_MouseDoubleClick(object sender, MouseButtonEventArgs e)
+ {
+ var selectedProgramSource = programSourceView.SelectedItem as ProgramSource;
+ EditProgramSource(selectedProgramSource);
+ }
+
+ private bool IsAllItemsUserAdded(List items)
+ {
+ return items.All(x => _settings.ProgramSources.Any(y => y.UniqueIdentifier == x.UniqueIdentifier));
+ }
}
-}
\ No newline at end of file
+}