diff --git a/Flow.Launcher/ViewModel/ResultViewModel.cs b/Flow.Launcher/ViewModel/ResultViewModel.cs
index a188cd357e1..458aa498f4b 100644
--- a/Flow.Launcher/ViewModel/ResultViewModel.cs
+++ b/Flow.Launcher/ViewModel/ResultViewModel.cs
@@ -163,15 +163,17 @@ private async ValueTask LoadImageAsync()
}
}
+ var loadFullImage = (Path.GetExtension(imagePath) ?? "").Equals(".url", StringComparison.OrdinalIgnoreCase);
+
if (ImageLoader.CacheContainImage(imagePath))
{
// will get here either when icoPath has value\icon delegate is null\when had exception in delegate
- image = ImageLoader.Load(imagePath);
+ image = ImageLoader.Load(imagePath, loadFullImage);
return;
}
// We need to modify the property not field here to trigger the OnPropertyChanged event
- Image = await Task.Run(() => ImageLoader.Load(imagePath)).ConfigureAwait(false);
+ Image = await Task.Run(() => ImageLoader.Load(imagePath, loadFullImage)).ConfigureAwait(false);
}
public Result Result { get; }
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj
index 2809e0b5cfa..83f9464c436 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj
+++ b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj
@@ -58,6 +58,7 @@
+
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml
index 8d8cae02c9f..91e0fda1142 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml
@@ -4,6 +4,7 @@
xmlns:system="clr-namespace:System;assembly=mscorlib">
+ Reset Default
Delete
Edit
Add
@@ -12,7 +13,7 @@
Disable
Location
All Programs
- File Suffixes
+ File Type
Reindex
Indexing
Index Start Menu
@@ -35,9 +36,21 @@
Are you sure you want to delete the selected program sources?
OK
- Flow Launcher will only index files that end with the following suffixes. (Each suffix should split by ';' )
+ 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
+ Protocols can't be empty
+
+ File Suffixes
+ URL Protocols
+ Custom URL Protocols
+ Custom File Suffixes
+
+ Insert file suffixes you want to index. Suffixes should be separated by ';'. (ex>bat;py)
+
+
+ Insert protocols of .url files you want to index. Protocols should be separated by ';'. (ex>ftp;netflix)
+
Run As Different User
Run As Administrator
diff --git a/Plugins/Flow.Launcher.Plugin.Program/ProgramSuffixes.xaml b/Plugins/Flow.Launcher.Plugin.Program/ProgramSuffixes.xaml
index e5f4041410f..1d0267e3da7 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/ProgramSuffixes.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/ProgramSuffixes.xaml
@@ -4,10 +4,12 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:ui="http://schemas.modernwpf.com/2019"
Title="{DynamicResource flowlauncher_plugin_program_suffixes}"
- Width="400"
+ Width="600"
Background="{DynamicResource PopuBGColor}"
Foreground="{DynamicResource PopupTextColor}"
+ DataContext="{Binding RelativeSource={RelativeSource Self}}"
ResizeMode="NoResize"
SizeToContent="Height"
WindowStartupLocation="CenterScreen"
@@ -15,9 +17,73 @@
-
+
+
+
+
+
+
+
+
+
+
+
-
+
@@ -55,7 +121,9 @@
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ appref-ms
+ exe
+ lnk
+
+
+
+
+
+
+
+ Steam Games
+ Epic Games
+ Http/Https
+
+
+
+
+
+
@@ -90,7 +232,7 @@
MinWidth="140"
Margin="5,0,0,0"
HorizontalAlignment="Right"
- Click="ButtonBase_OnClick"
+ Click="BtnAdd_OnClick"
Content="{DynamicResource flowlauncher_plugin_program_update}"
Style="{DynamicResource AccentButtonStyle}" />
diff --git a/Plugins/Flow.Launcher.Plugin.Program/ProgramSuffixes.xaml.cs b/Plugins/Flow.Launcher.Plugin.Program/ProgramSuffixes.xaml.cs
index 2a10928e66c..31565c8b086 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/ProgramSuffixes.xaml.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/ProgramSuffixes.xaml.cs
@@ -1,44 +1,76 @@
using System;
+using System.Collections.Generic;
using System.Windows;
namespace Flow.Launcher.Plugin.Program
{
- ///
- /// ProgramSuffixes.xaml 的交互逻辑
- ///
public partial class ProgramSuffixes
{
private PluginInitContext context;
private Settings _settings;
+ public Dictionary SuffixesStatus { get; set; }
+ public Dictionary ProtocolsStatus { get; set; }
+ public bool UseCustomSuffixes { get; set; }
+ public bool UseCustomProtocols { get; set; }
public ProgramSuffixes(PluginInitContext context, Settings settings)
{
this.context = context;
- InitializeComponent();
_settings = settings;
- tbSuffixes.Text = string.Join(Settings.SuffixSeperator.ToString(), _settings.ProgramSuffixes);
+ SuffixesStatus = new Dictionary(_settings.BuiltinSuffixesStatus);
+ ProtocolsStatus = new Dictionary(_settings.BuiltinProtocolsStatus);
+ UseCustomSuffixes = _settings.UseCustomSuffixes;
+ UseCustomProtocols = _settings.UseCustomProtocols;
+ InitializeComponent();
+ tbSuffixes.Text = string.Join(Settings.SuffixSeparator, _settings.CustomSuffixes);
+ tbProtocols.Text = string.Join(Settings.SuffixSeparator, _settings.CustomProtocols);
}
+
private void BtnCancel_OnClick(object sender, RoutedEventArgs e)
{
Close();
}
- private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
+
+ private void BtnAdd_OnClick(object sender, RoutedEventArgs e)
{
- var suffixes = tbSuffixes.Text.Split(Settings.SuffixSeperator, StringSplitOptions.RemoveEmptyEntries);
+ var suffixes = tbSuffixes.Text.Split(Settings.SuffixSeparator, StringSplitOptions.RemoveEmptyEntries);
+ var protocols = tbProtocols.Text.Split(Settings.SuffixSeparator, StringSplitOptions.RemoveEmptyEntries);
- if (suffixes.Length == 0)
+ if (suffixes.Length == 0 && UseCustomSuffixes)
{
string warning = context.API.GetTranslation("flowlauncher_plugin_program_suffixes_cannot_empty");
MessageBox.Show(warning);
return;
}
- _settings.ProgramSuffixes = suffixes;
+ if (protocols.Length == 0 && UseCustomProtocols)
+ {
+ string warning = context.API.GetTranslation("flowlauncher_plugin_protocols_cannot_empty");
+ MessageBox.Show(warning);
+ return;
+ }
- string msg = context.API.GetTranslation("flowlauncher_plugin_program_update_file_suffixes");
- MessageBox.Show(msg);
+ _settings.CustomSuffixes = suffixes;
+ _settings.CustomProtocols = protocols;
+ _settings.BuiltinSuffixesStatus = new Dictionary(SuffixesStatus);
+ _settings.BuiltinProtocolsStatus = new Dictionary(ProtocolsStatus);
+ _settings.UseCustomSuffixes = UseCustomSuffixes;
+ _settings.UseCustomProtocols = UseCustomProtocols;
DialogResult = true;
}
+
+ private void BtnReset_OnClick(object sender, RoutedEventArgs e)
+ {
+ apprefMS.IsChecked = true;
+ exe.IsChecked = true;
+ lnk.IsChecked = true;
+ CustomFiles.IsChecked = false;
+
+ steam.IsChecked = true;
+ epic.IsChecked = true;
+ http.IsChecked = false;
+ CustomProtocol.IsChecked = false;
+ }
}
-}
\ No newline at end of file
+}
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs
index 6a8b232e94e..cc16a1ac770 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs
@@ -16,7 +16,10 @@
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 IniParser;
namespace Flow.Launcher.Plugin.Program.Programs
{
@@ -36,6 +39,7 @@ public class Win32 : IProgram, IEquatable
public string Location => ParentDirectory;
private const string ShortcutExtension = "lnk";
+ private const string UrlExtension = "url";
private const string ExeExtension = "exe";
private static readonly Win32 Default = new Win32()
@@ -287,6 +291,45 @@ private static Win32 LnkProgram(string path)
#endif
}
+ private static Win32 UrlProgram(string path)
+ {
+ var program = Win32Program(path);
+ program.Valid = false;
+
+ try
+ {
+ var parser = new FileIniDataParser();
+ var data = parser.ReadFile(path);
+ var urlSection = data["InternetShortcut"];
+ var url = urlSection?["URL"];
+ if (String.IsNullOrEmpty(url))
+ {
+ return program;
+ }
+ foreach(var protocol in Main._settings.GetProtocols())
+ {
+ if(url.StartsWith(protocol))
+ {
+ program.LnkResolvedPath = url;
+ program.Valid = true;
+ break;
+ }
+ }
+
+ var iconPath = urlSection?["IconFile"];
+ if (!String.IsNullOrEmpty(iconPath))
+ {
+ program.IcoPath = iconPath;
+ }
+ }
+ catch (Exception e)
+ {
+ // Many files do not have the required fields, so no logging is done.
+ }
+
+ return program;
+ }
+
private static Win32 ExeProgram(string path)
{
try
@@ -343,6 +386,7 @@ private static IEnumerable UnregisteredPrograms(List ExeProgram(x),
ShortcutExtension => LnkProgram(x),
+ UrlExtension => UrlProgram(x),
_ => Win32Program(x)
});
@@ -365,6 +409,7 @@ private static IEnumerable StartMenuPrograms(string[] suffixes)
.Select(x => Extension(x) switch
{
ShortcutExtension => LnkProgram(x),
+ UrlExtension => UrlProgram(x),
_ => Win32Program(x)
}).Where(x => x.Valid);
return programs;
@@ -504,7 +549,7 @@ public static Win32[] All(Settings settings)
{
var programs = Enumerable.Empty();
- var unregistered = UnregisteredPrograms(settings.ProgramSources, settings.ProgramSuffixes);
+ var unregistered = UnregisteredPrograms(settings.ProgramSources, settings.GetSuffixes());
programs = programs.Concat(unregistered);
@@ -512,13 +557,13 @@ public static Win32[] All(Settings settings)
if (settings.EnableRegistrySource)
{
- var appPaths = AppPathsPrograms(settings.ProgramSuffixes);
+ var appPaths = AppPathsPrograms(settings.GetSuffixes());
autoIndexPrograms = autoIndexPrograms.Concat(appPaths);
}
if (settings.EnableStartMenuSource)
{
- var startMenu = StartMenuPrograms(settings.ProgramSuffixes);
+ var startMenu = StartMenuPrograms(settings.GetSuffixes());
autoIndexPrograms = autoIndexPrograms.Concat(startMenu);
}
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Settings.cs b/Plugins/Flow.Launcher.Plugin.Program/Settings.cs
index d97ddd9932a..8044e8065a1 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Settings.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/Settings.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
+using System.Text.Json.Serialization;
namespace Flow.Launcher.Plugin.Program
{
@@ -9,17 +11,87 @@ public class Settings
public DateTime LastIndexTime { get; set; }
public List ProgramSources { get; set; } = new List();
public List DisabledProgramSources { get; set; } = new List();
- public string[] ProgramSuffixes { get; set; } = {"appref-ms", "exe", "lnk"};
+ public string[] CustomSuffixes { get; set; } = Array.Empty();
+ public string[] CustomProtocols { get; set; } = Array.Empty();
- public bool EnableStartMenuSource { get; set; } = true;
+ public Dictionary BuiltinSuffixesStatus { get; set; } = new Dictionary{
+ { "exe", true }, { "appref-ms", true }, { "lnk", true }
+ };
+
+ public Dictionary BuiltinProtocolsStatus { get; set; } = new Dictionary{
+ { "steam", true }, { "epic", true }, { "http", false }
+ };
+
+ [JsonIgnore]
+ public Dictionary BuiltinProtocols { get; set; } = new Dictionary{
+ { "steam", $"steam://run/{SuffixSeparator}steam://rungameid/" }, { "epic", "com.epicgames.launcher://apps/" }, { "http", $"http://{SuffixSeparator}https://"}
+ };
+
+ public bool UseCustomSuffixes { get; set; } = false;
+ public bool UseCustomProtocols { get; set; } = false;
+
+ public string[] GetSuffixes()
+ {
+ List extensions = new List();
+ foreach (var item in BuiltinSuffixesStatus)
+ {
+ if (item.Value)
+ {
+ extensions.Add(item.Key);
+ }
+ }
+
+ if (BuiltinProtocolsStatus.Values.Any(x => x == true) || UseCustomProtocols)
+ {
+ extensions.Add("url");
+ }
+ if (UseCustomSuffixes)
+ {
+ return extensions.Concat(CustomSuffixes).DistinctBy(x => x.ToLower()).ToArray();
+ }
+ else
+ {
+ return extensions.DistinctBy(x => x.ToLower()).ToArray();
+ }
+ }
+
+ public string[] GetProtocols()
+ {
+ List protocols = new List();
+ foreach (var item in BuiltinProtocolsStatus)
+ {
+ if (item.Value)
+ {
+ if (BuiltinProtocols.TryGetValue(item.Key, out string ps))
+ {
+ var tmp = ps.Split(SuffixSeparator, StringSplitOptions.RemoveEmptyEntries);
+ foreach (var protocol in tmp)
+ {
+ protocols.Add(protocol);
+ }
+ }
+ }
+ }
+
+ if (UseCustomProtocols)
+ {
+ return protocols.Concat(CustomProtocols).DistinctBy(x => x.ToLower()).ToArray();
+ }
+ else
+ {
+ return protocols.DistinctBy(x => x.ToLower()).ToArray();
+ }
+ }
+
+ public bool EnableStartMenuSource { get; set; } = true;
public bool EnableDescription { get; set; } = false;
public bool HideAppsPath { get; set; } = true;
public bool EnableRegistrySource { get; set; } = true;
public string CustomizedExplorer { get; set; } = Explorer;
public string CustomizedArgs { get; set; } = ExplorerArgs;
- internal const char SuffixSeperator = ';';
+ internal const char SuffixSeparator = ';';
internal const string Explorer = "explorer";
diff --git a/Plugins/Flow.Launcher.Plugin.Program/SuffixesConverter.cs b/Plugins/Flow.Launcher.Plugin.Program/SuffixesConverter.cs
index a5e9f75dcf0..ef93913a58b 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/SuffixesConverter.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/SuffixesConverter.cs
@@ -12,7 +12,7 @@ public object Convert(object value, Type targetType, object parameter, CultureIn
var text = value as string[];
if (text != null)
{
- return string.Join(";", text);
+ return string.Join(Settings.SuffixSeparator, text);
}
else
{