mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge Dev
This commit is contained in:
commit
c7eb4e213c
22 changed files with 586 additions and 552 deletions
|
|
@ -58,7 +58,7 @@
|
|||
<PackageReference Include="NLog.Schema" Version="4.7.10" />
|
||||
<PackageReference Include="NLog.Web.AspNetCore" Version="4.13.0" />
|
||||
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="5.0.2" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="5.0.3" />
|
||||
<!--ToolGood.Words.Pinyin v3.0.2.6 results in high memory usage when search with pinyin is enabled-->
|
||||
<!--Bumping to it or higher needs to test and ensure this is no longer a problem-->
|
||||
<PackageReference Include="ToolGood.Words.Pinyin" Version="3.0.1.4" />
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<UseWPF>true</UseWPF>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<UseWPF>true</UseWPF>
|
||||
<ProjectGuid>{9B130CC5-14FB-41FF-B310-0A95B6894C37}</ProjectGuid>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Flow.Launcher.Plugin.BrowserBookmark</RootNamespace>
|
||||
|
|
@ -23,7 +24,7 @@
|
|||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
|
|
@ -39,15 +40,6 @@
|
|||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="x64\SQLite.Interop.dll">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="x86\SQLite.Interop.dll">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" />
|
||||
|
|
@ -64,8 +56,8 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Data.SQLite" Version="1.0.114.4" />
|
||||
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.114.3" />
|
||||
<PackageReference Include="System.Data.SQLite" Version="1.0.116" />
|
||||
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.116" />
|
||||
<PackageReference Include="UnidecodeSharp" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -10,8 +10,7 @@
|
|||
Foreground="{DynamicResource PopupTextColor}"
|
||||
ResizeMode="NoResize"
|
||||
SizeToContent="WidthAndHeight"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
mc:Ignorable="d">
|
||||
WindowStartupLocation="CenterScreen">
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome CaptionHeight="32" ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
|
||||
</WindowChrome.WindowChrome>
|
||||
|
|
@ -20,7 +19,6 @@
|
|||
<RowDefinition />
|
||||
<RowDefinition Height="80" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel Grid.Row="0">
|
||||
<StackPanel>
|
||||
<Grid>
|
||||
|
|
@ -54,42 +52,78 @@
|
|||
</Button>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="26,12,26,0">
|
||||
<StackPanel Margin="0,0,0,12">
|
||||
<StackPanel Margin="26,0,26,0">
|
||||
<StackPanel Grid.Row="0" Margin="0,0,0,12">
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Margin="0,0,0,0"
|
||||
FontSize="20"
|
||||
FontWeight="SemiBold"
|
||||
Text="{DynamicResource flowlauncher_plugin_program_directory}"
|
||||
Text="{DynamicResource flowlauncher_plugin_program_edit_program_source_title}"
|
||||
TextAlignment="Left" />
|
||||
</StackPanel>
|
||||
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
|
||||
<Grid>
|
||||
<StackPanel>
|
||||
<TextBlock
|
||||
FontSize="14"
|
||||
Text="{DynamicResource flowlauncher_plugin_program_edit_program_source_tips}"
|
||||
TextAlignment="Left"
|
||||
TextWrapping="WrapWithOverflow" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Margin="0,10,0,0" Orientation="Horizontal">
|
||||
<Grid Width="470">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox
|
||||
Name="Directory"
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
MinWidth="248"
|
||||
Margin="0,7"
|
||||
VerticalAlignment="Center" />
|
||||
<Button
|
||||
Margin="10"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Text="{DynamicResource flowlauncher_plugin_program_directory}" />
|
||||
<DockPanel
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
LastChildFill="True">
|
||||
<Button
|
||||
Width="70"
|
||||
HorizontalAlignment="Right"
|
||||
Click="BrowseButton_Click"
|
||||
DockPanel.Dock="Right"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_browse}" />
|
||||
<TextBox
|
||||
Name="Directory"
|
||||
Margin="10"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Stretch" />
|
||||
</DockPanel>
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="10"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Text="{DynamicResource flowlauncher_plugin_program_enabled}" />
|
||||
<CheckBox x:Name="Chkbox"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
MinWidth="80"
|
||||
Margin="10,10,0,10"
|
||||
Padding="14,6,14,6"
|
||||
HorizontalAlignment="Right"
|
||||
Click="BrowseButton_Click"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_browse}" />
|
||||
Margin="10,0"
|
||||
VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<Border
|
||||
Grid.Row="1"
|
||||
Margin="0,14,0,0"
|
||||
Background="{DynamicResource PopupButtonAreaBGColor}"
|
||||
BorderBrush="{DynamicResource PopupButtonAreaBorderColor}"
|
||||
BorderThickness="0,1,0,0">
|
||||
|
|
@ -97,19 +131,17 @@
|
|||
<Button
|
||||
x:Name="btnCancel"
|
||||
MinWidth="140"
|
||||
Margin="0,0,5,0"
|
||||
Margin="10,0,5,0"
|
||||
Click="BtnCancel_OnClick"
|
||||
Content="{DynamicResource cancel}" />
|
||||
|
||||
<Button
|
||||
x:Name="btnAdd"
|
||||
MinWidth="140"
|
||||
Margin="5,0,0,0"
|
||||
HorizontalAlignment="Right"
|
||||
Click="ButtonAdd_OnClick"
|
||||
Margin="5,0,10,0"
|
||||
Click="BtnAdd_OnClick"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_update}"
|
||||
Style="{DynamicResource AccentButtonStyle}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
</Window>
|
||||
</Window>
|
||||
|
|
@ -9,11 +9,12 @@ namespace Flow.Launcher.Plugin.Program
|
|||
/// <summary>
|
||||
/// Interaction logic for AddProgramSource.xaml
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<string> WatchedPath = new List<string>();
|
||||
// // todo remove previous watcher events
|
||||
// public static void AddAll(List<UnregisteredPrograms> 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();
|
||||
// });
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
|
@ -10,16 +10,21 @@
|
|||
<system:String x:Key="flowlauncher_plugin_program_add">Add</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_name">Name</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_enable">Enable</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_enabled">Enabled</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_disable">Disable</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_location">Location</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_all_programs">All Programs</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_suffixes">File Type</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_reindex">Reindex</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_indexing">Indexing</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_index_start">Index Start Menu</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_index_source">Index Sources</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_index_option">Options</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_index_start">Start Menu</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_index_start_tooltip">When enabled, Flow will load programs from the start menu</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_index_registry">Index Registry</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_index_registry">Registry</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_index_registry_tooltip">When enabled, Flow will load programs from the registry</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_index_PATH">PATH</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_index_PATH_tooltip">When enabled, Flow will load programs from the PATH environment variable</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_enable_hidelnkpath">Hide app path</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_enable_hidelnkpath_tooltip">For executable files such as UWP or lnk, hide the file path from being visible</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_enable_description">Search in Program Description</system:String>
|
||||
|
|
@ -34,8 +39,12 @@
|
|||
|
||||
<system:String x:Key="flowlauncher_plugin_program_pls_select_program_source">Please select a program source</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_delete_program_source">Are you sure you want to delete the selected program sources?</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_duplicate_program_source">Another program source with the same location alreaday exists.</system:String>
|
||||
|
||||
<system:String x:Key="flowlauncher_plugin_program_update">OK</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_edit_program_source_title">Program Source</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_edit_program_source_tips">Edit directory and status of this program source.</system:String>
|
||||
|
||||
<system:String x:Key="flowlauncher_plugin_program_update">Update</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_only_index_tip">Program Plugin will only index files with selected suffixes and .url files with selected protocols.</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_update_file_suffixes">Successfully updated file suffixes</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_suffixes_cannot_empty">File suffixes can't be empty</system:String>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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[]>("Win32");
|
||||
_win32s = _win32Storage.TryLoad(new Win32[]
|
||||
{
|
||||
});
|
||||
_win32s = _win32Storage.TryLoad(Array.Empty<Win32>());
|
||||
_uwpStorage = new BinaryStorage<UWP.Application[]>("UWP");
|
||||
_uwps = _uwpStorage.TryLoad(new UWP.Application[]
|
||||
{
|
||||
});
|
||||
_uwps = _uwpStorage.TryLoad(Array.Empty<UWP.Application>());
|
||||
});
|
||||
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<UWP.Application>();
|
||||
_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<ProcessStartInfo, Process> runProcess, ProcessStartInfo info)
|
||||
|
|
@ -233,6 +235,7 @@ namespace Flow.Launcher.Plugin.Program
|
|||
{
|
||||
await IndexProgramsAsync();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Win32.Dispose();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<IAppxManifestApplication> getAppsFromManifest(IStream stream)
|
||||
public static List<IAppxManifestApplication> GetAppsFromManifest(IStream stream)
|
||||
{
|
||||
IAppxFactory appxFactory = (IAppxFactory)new AppxFactory();
|
||||
List<IAppxManifestApplication> apps = new List<IAppxManifestApplication>();
|
||||
var appxFactory = new AppxFactory();
|
||||
var reader = ((IAppxFactory)appxFactory).CreateManifestReader(stream);
|
||||
var reader = appxFactory.CreateManifestReader(stream);
|
||||
var manifestApps = reader.GetApplications();
|
||||
while (manifestApps.GetHasCurrent())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<Application>();
|
||||
List<AppxPackageHelper.IAppxManifestApplication> _apps = AppxPackageHelper.GetAppsFromManifest(stream);
|
||||
|
||||
List<AppxPackageHelper.IAppxManifestApplication> _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<Application>().ToArray();
|
||||
Apps = Array.Empty<Application>();
|
||||
}
|
||||
|
||||
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<string>();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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<Application>();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -233,9 +215,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
}
|
||||
else
|
||||
{
|
||||
return new Package[]
|
||||
{
|
||||
};
|
||||
return Array.Empty<Package>();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<Win32>
|
||||
{
|
||||
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<string> ProgramPaths(string directory, string[] suffixes)
|
||||
private static IEnumerable<string> ProgramPaths(string directory, string[] suffixes, bool recursive = true)
|
||||
{
|
||||
if (!Directory.Exists(directory))
|
||||
return Enumerable.Empty<string>();
|
||||
|
||||
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<Win32> UnregisteredPrograms(List<Settings.ProgramSource> sources, string[] suffixes)
|
||||
private static IEnumerable<Win32> UnregisteredPrograms(List<ProgramSource> 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<Win32> StartMenuPrograms(string[] suffixes)
|
||||
private static IEnumerable<Win32> 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<Win32> PATHPrograms(string[] suffixes, string[] protocols)
|
||||
{
|
||||
var pathEnv = Environment.GetEnvironmentVariable("Path");
|
||||
if (String.IsNullOrEmpty(pathEnv))
|
||||
{
|
||||
return Array.Empty<Win32>();
|
||||
}
|
||||
|
||||
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<Win32> AppPathsPrograms(string[] suffixes)
|
||||
private static IEnumerable<Win32> 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<string> 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<string> ExceptDisabledSource(IEnumerable<string> sources)
|
||||
public static IEnumerable<string> ExceptDisabledSource(IEnumerable<string> paths)
|
||||
{
|
||||
return ExceptDisabledSource(sources, x => x);
|
||||
return ExceptDisabledSource(paths, x => x.ToLowerInvariant());
|
||||
}
|
||||
|
||||
public static IEnumerable<TSource> ExceptDisabledSource<TSource>(IEnumerable<TSource> sources,
|
||||
|
|
@ -531,7 +545,8 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
|
||||
private static IEnumerable<Win32> ProgramsHasher(IEnumerable<Win32> 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<Win32>();
|
||||
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<string> 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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<ProgramSource> ProgramSources { get; set; } = new List<ProgramSource>();
|
||||
public List<DisabledProgramSource> DisabledProgramSources { get; set; } = new List<DisabledProgramSource>();
|
||||
|
||||
[Obsolete, JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
/// <summary>
|
||||
/// User-added program sources' directories
|
||||
/// </summary>
|
||||
public List<ProgramSource> ProgramSources { get; set; } = new List<ProgramSource>();
|
||||
|
||||
/// <summary>
|
||||
/// Disabled single programs, not including User-added directories
|
||||
/// </summary>
|
||||
public List<ProgramSource> DisabledProgramSources { get; set; } = new List<ProgramSource>();
|
||||
|
||||
[Obsolete("Should use GetSuffixes() instead."), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string[] ProgramSuffixes { get; set; } = null;
|
||||
public string[] CustomSuffixes { get; set; } = Array.Empty<string>(); // Custom suffixes only
|
||||
public string[] CustomProtocols { get; set; } = Array.Empty<string>();
|
||||
|
|
@ -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";
|
||||
|
||||
/// <summary>
|
||||
/// Contains user added folder location contents as well as all user disabled applications
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Win32 class applications set UniqueIdentifier using their full file path</para>
|
||||
/// <para>UWP class applications set UniqueIdentifier using their Application User Model ID</para>
|
||||
/// <para>Custom user added program sources set UniqueIdentifier using their location</para>
|
||||
/// </remarks>
|
||||
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 { }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<ProgramSource> LoadProgramSources(this List<Settings.ProgramSource> programSources)
|
||||
internal static List<ProgramSource> LoadProgramSources()
|
||||
{
|
||||
var list = new List<ProgramSource>();
|
||||
|
||||
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<ProgramSource> 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<ProgramSource> list, List<ProgramSource> selectedProgramSourcesToDisable, bool status)
|
||||
internal static void SetProgramSourcesStatus(List<ProgramSource> 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<ProgramSource> 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<ProgramSource> 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<ProgramSource> 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;
|
||||
|
|
|
|||
|
|
@ -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 { }
|
||||
/// <summary>
|
||||
/// Contains user added folder location contents as well as all user disabled applications
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Win32 class applications set UniqueIdentifier using their full file path</para>
|
||||
/// <para>UWP class applications set UniqueIdentifier using their Application User Model ID</para>
|
||||
/// <para>Custom user added program sources set UniqueIdentifier using their location</para>
|
||||
/// </remarks>
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add source by location
|
||||
/// </summary>
|
||||
/// <param name="location">location of program source</param>
|
||||
/// <param name="enabled">enabled</param>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,58 +10,88 @@
|
|||
mc:Ignorable="d">
|
||||
<Grid Margin="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="170" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="60" />
|
||||
</Grid.RowDefinitions>
|
||||
<DockPanel
|
||||
Margin="70,10,0,8"
|
||||
HorizontalAlignment="Stretch"
|
||||
LastChildFill="True">
|
||||
<TextBlock
|
||||
MinWidth="120"
|
||||
Margin="0,5,10,0"
|
||||
Text="{DynamicResource flowlauncher_plugin_program_index_source}" />
|
||||
<WrapPanel
|
||||
Width="Auto"
|
||||
Margin="0,0,14,0"
|
||||
HorizontalAlignment="Right"
|
||||
DockPanel.Dock="Right">
|
||||
<CheckBox
|
||||
Name="StartMenuEnabled"
|
||||
Margin="12,0,12,0"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_index_start}"
|
||||
IsChecked="{Binding EnableStartMenuSource}"
|
||||
ToolTip="{DynamicResource flowlauncher_plugin_program_index_start_tooltip}" />
|
||||
<CheckBox
|
||||
Name="RegistryEnabled"
|
||||
Margin="12,0,12,0"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_index_registry}"
|
||||
IsChecked="{Binding EnableRegistrySource}"
|
||||
ToolTip="{DynamicResource flowlauncher_plugin_program_index_registry_tooltip}" />
|
||||
|
||||
<CheckBox
|
||||
Name="PATHEnabled"
|
||||
Margin="12,0,12,0"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_index_PATH}"
|
||||
IsChecked="{Binding EnablePATHSource}"
|
||||
ToolTip="{DynamicResource flowlauncher_plugin_program_index_PATH_tooltip}" />
|
||||
</WrapPanel>
|
||||
</DockPanel>
|
||||
|
||||
<StackPanel
|
||||
Grid.Row="0"
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical">
|
||||
<StackPanel Width="Auto" Orientation="Vertical">
|
||||
<StackPanel Width="Auto" Orientation="Horizontal">
|
||||
<CheckBox
|
||||
Name="StartMenuEnabled"
|
||||
Width="220"
|
||||
Margin="70,8,10,8"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_index_start}"
|
||||
IsChecked="{Binding EnableStartMenuSource}"
|
||||
ToolTip="{DynamicResource flowlauncher_plugin_program_index_start_tooltip}" />
|
||||
<CheckBox
|
||||
Name="RegistryEnabled"
|
||||
Margin="70,8,10,8"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_index_registry}"
|
||||
IsChecked="{Binding EnableRegistrySource}"
|
||||
ToolTip="{DynamicResource flowlauncher_plugin_program_index_registry_tooltip}" />
|
||||
</StackPanel>
|
||||
|
||||
<Separator
|
||||
Height="1"
|
||||
BorderBrush="{DynamicResource Color03B}"
|
||||
BorderThickness="1" />
|
||||
<StackPanel Width="Auto" Orientation="Horizontal">
|
||||
<Separator
|
||||
Height="1"
|
||||
BorderBrush="{DynamicResource Color03B}"
|
||||
BorderThickness="1" />
|
||||
<DockPanel
|
||||
Margin="70,10,0,8"
|
||||
HorizontalAlignment="Stretch"
|
||||
LastChildFill="True">
|
||||
<TextBlock
|
||||
MinWidth="120"
|
||||
Margin="0,5,10,0"
|
||||
Text="{DynamicResource flowlauncher_plugin_program_index_option}" />
|
||||
<WrapPanel
|
||||
Width="Auto"
|
||||
Margin="0,0,14,0"
|
||||
HorizontalAlignment="Right"
|
||||
DockPanel.Dock="Right">
|
||||
<CheckBox
|
||||
Name="HideLnkEnabled"
|
||||
Width="220"
|
||||
Margin="70,8,10,8"
|
||||
Margin="12,0,12,0"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_enable_hidelnkpath}"
|
||||
IsChecked="{Binding HideAppsPath}"
|
||||
ToolTip="{DynamicResource flowlauncher_plugin_program_enable_hidelnkpath_tooltip}" />
|
||||
<CheckBox
|
||||
Name="DescriptionEnabled"
|
||||
Margin="70,8,10,8"
|
||||
Margin="12,0,12,0"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_enable_description}"
|
||||
IsChecked="{Binding EnableDescription}"
|
||||
ToolTip="{DynamicResource flowlauncher_plugin_program_enable_description_tooltip}" />
|
||||
</StackPanel>
|
||||
<Separator
|
||||
Height="1"
|
||||
BorderBrush="{DynamicResource Color03B}"
|
||||
BorderThickness="1" />
|
||||
</StackPanel>
|
||||
</WrapPanel>
|
||||
</DockPanel>
|
||||
<Separator
|
||||
Height="1"
|
||||
BorderBrush="{DynamicResource Color03B}"
|
||||
BorderThickness="1" />
|
||||
<StackPanel
|
||||
Width="Auto"
|
||||
Margin="10,6,0,0"
|
||||
Margin="60,6,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
|
|
@ -107,8 +137,8 @@
|
|||
</StackPanel>
|
||||
<ListView
|
||||
x:Name="programSourceView"
|
||||
Grid.Row="1"
|
||||
Margin="20,0,20,0"
|
||||
Grid.Row="2"
|
||||
Margin="70,0,20,0"
|
||||
AllowDrop="True"
|
||||
BorderBrush="DarkGray"
|
||||
BorderThickness="1"
|
||||
|
|
@ -116,6 +146,8 @@
|
|||
Drop="programSourceView_Drop"
|
||||
GridViewColumnHeader.Click="GridViewColumnHeaderClickedHandler"
|
||||
PreviewMouseRightButtonUp="ProgramSourceView_PreviewMouseRightButtonUp"
|
||||
MouseDoubleClick="programSourceView_MouseDoubleClick"
|
||||
SelectionChanged="programSourceView_SelectionChanged"
|
||||
SelectionMode="Extended">
|
||||
<ListView.View>
|
||||
<GridView>
|
||||
|
|
@ -126,7 +158,7 @@
|
|||
</DataTemplate>
|
||||
</GridViewColumn.CellTemplate>
|
||||
</GridViewColumn>
|
||||
<GridViewColumn Header="{DynamicResource flowlauncher_plugin_program_enable}">
|
||||
<GridViewColumn Header="{DynamicResource flowlauncher_plugin_program_enabled}">
|
||||
<GridViewColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock
|
||||
|
|
@ -147,7 +179,7 @@
|
|||
</ListView.View>
|
||||
</ListView>
|
||||
<DockPanel
|
||||
Grid.Row="2"
|
||||
Grid.Row="3"
|
||||
Grid.RowSpan="1"
|
||||
Margin="0,0,20,10">
|
||||
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
|
||||
|
|
@ -173,66 +205,3 @@
|
|||
</DockPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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<ProgramSource> { selectedProgramSource }, true); // sync status in win32, uwp and disabled
|
||||
ProgramSettingDisplay.RemoveDisabledFromSettings();
|
||||
}
|
||||
else
|
||||
{
|
||||
ProgramSettingDisplay.SetProgramSourcesStatus(new List<ProgramSource> { 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<ProgramSource>()
|
||||
.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<ProgramSource> selectedItems)
|
||||
private static bool HasMoreOrEqualEnabledItems(List<ProgramSource> 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<ProgramSource>()
|
||||
.ToList();
|
||||
.SelectedItems.Cast<ProgramSource>()
|
||||
.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<ProgramSource> items)
|
||||
{
|
||||
return items.All(x => _settings.ProgramSources.Any(y => y.UniqueIdentifier == x.UniqueIdentifier));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue