Merge pull request #31 from jjw24/enable_userselected_programloading

Enable users to configure what programs to load
This commit is contained in:
Jeremy Wu 2019-10-27 20:43:50 +11:00 committed by GitHub
commit 15eca63ff0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 731 additions and 292 deletions

View File

@ -1,6 +1,8 @@
using System.Windows;
using System.Windows.Forms;
using Wox.Plugin.Program.Programs;
using Wox.Plugin.Program.Views.Models;
using Wox.Plugin.Program.Views;
using System.Linq;
namespace Wox.Plugin.Program
{
@ -50,11 +52,17 @@ namespace Wox.Plugin.Program
}
if (_editing == null)
{
var source = new Settings.ProgramSource
if (!ProgramSetting.ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == Directory.Text))
{
Location = Directory.Text,
};
_settings.ProgramSources.Add(source);
var source = new ProgramSource
{
Location = Directory.Text,
UniqueIdentifier = Directory.Text
};
_settings.ProgramSources.Insert(0, source);
ProgramSetting.ProgramSettingDisplayList.Add(source);
}
}
else
{

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -21,7 +21,6 @@
<system:String x:Key="wox_plugin_program_max_search_depth">Maximale Suchtiefe (-1 ist unlimitiert):</system:String>
<system:String x:Key="wox_plugin_program_pls_select_program_source">Bitte wähle eine Programmquelle</system:String>
<system:String x:Key="wox_plugin_program_delete_program_source">Willst du wirklich {0} löschen?</system:String>
<system:String x:Key="wox_plugin_program_update">Aktualisieren</system:String>
<system:String x:Key="wox_plugin_program_only_index_tip">Wox indexiert nur Datien mit folgenden Endungen:</system:String>

View File

@ -6,8 +6,10 @@
<system:String x:Key="wox_plugin_program_delete">Delete</system:String>
<system:String x:Key="wox_plugin_program_edit">Edit</system:String>
<system:String x:Key="wox_plugin_program_add">Add</system:String>
<system:String x:Key="wox_plugin_program_disable">Disable</system:String>
<system:String x:Key="wox_plugin_program_location">Location</system:String>
<system:String x:Key="wox_plugin_program_suffixes">Index file suffixes</system:String>
<system:String x:Key="wox_plugin_program_all_programs">All Programs</system:String>
<system:String x:Key="wox_plugin_program_suffixes">File Suffixes</system:String>
<system:String x:Key="wox_plugin_program_reindex">Reindex</system:String>
<system:String x:Key="wox_plugin_program_indexing">Indexing</system:String>
<system:String x:Key="wox_plugin_program_index_start">Index Start Menu</system:String>
@ -21,20 +23,25 @@
<system:String x:Key="wox_plugin_program_max_search_depth">Maximum Search Depth (-1 is unlimited):</system:String>
<system:String x:Key="wox_plugin_program_pls_select_program_source">Please select a program source</system:String>
<system:String x:Key="wox_plugin_program_delete_program_source">Are you sure you want to delete {0}?</system:String>
<system:String x:Key="wox_plugin_program_delete_program_source">Are you sure you want to delete the selected program sources?</system:String>
<system:String x:Key="wox_plugin_program_update">Update</system:String>
<system:String x:Key="wox_plugin_program_only_index_tip">Wox will only index files that end with the following suffixes:</system:String>
<system:String x:Key="wox_plugin_program_split_by_tip">(Each suffix should split by ;)</system:String>
<system:String x:Key="wox_plugin_program_split_by_tip">(Each suffix should split by ';' )</system:String>
<system:String x:Key="wox_plugin_program_update_file_suffixes">Successfully updated file suffixes</system:String>
<system:String x:Key="wox_plugin_program_suffixes_cannot_empty">File suffixes can't be empty</system:String>
<system:String x:Key="wox_plugin_program_run_as_administrator">Run As Administrator</system:String>
<system:String x:Key="wox_plugin_program_open_containing_folder">Open containing folder</system:String>
<system:String x:Key="wox_plugin_program_disable_program">Disable this program from displaying</system:String>
<system:String x:Key="wox_plugin_program_plugin_name">Program</system:String>
<system:String x:Key="wox_plugin_program_plugin_description">Search programs in Wox</system:String>
<system:String x:Key="wox_plugin_program_invalid_path">Invalid Path</system:String>
<!--Dialogs-->
<system:String x:Key="wox_plugin_program_disable_dlgtitle_success">Success</system:String>
<system:String x:Key="wox_plugin_program_disable_dlgtitle_success_message">Successfully disabled this program from displaying in your query</system:String>
</ResourceDictionary>

View File

@ -21,7 +21,6 @@
<system:String x:Key="wox_plugin_program_max_search_depth">Maksymalna głębokość wyszukiwania (-1 to nieograniczona):</system:String>
<system:String x:Key="wox_plugin_program_pls_select_program_source">Musisz wybrać katalog programu</system:String>
<system:String x:Key="wox_plugin_program_delete_program_source">Czy jesteś pewien że chcesz usunąć {0}?</system:String>
<system:String x:Key="wox_plugin_program_update">Aktualizuj</system:String>
<system:String x:Key="wox_plugin_program_only_index_tip">Wox będzie indeksował tylko te pliki z podanymi rozszerzeniami:</system:String>

View File

@ -21,7 +21,6 @@
<system:String x:Key="wox_plugin_program_max_search_depth">Maksimum Arama Derinliği (Limitsiz için -1):</system:String>
<system:String x:Key="wox_plugin_program_pls_select_program_source">İşlem yapmak istediğiniz klasörü seçin.</system:String>
<system:String x:Key="wox_plugin_program_delete_program_source">{0} klasörünü silmek istediğinize emin misiniz?</system:String>
<system:String x:Key="wox_plugin_program_update">Güncelle</system:String>
<system:String x:Key="wox_plugin_program_only_index_tip">Wox yalnızca aşağıdaki uzantılara sahip dosyaları indeksleyecektir:</system:String>

View File

@ -21,7 +21,6 @@
<system:String x:Key="wox_plugin_program_max_search_depth">最大搜索深度(-1是无限的</system:String>
<system:String x:Key="wox_plugin_program_pls_select_program_source">请先选择一项</system:String>
<system:String x:Key="wox_plugin_program_delete_program_source">你确定要删除{0}吗?</system:String>
<system:String x:Key="wox_plugin_program_update">更新</system:String>
<system:String x:Key="wox_plugin_program_only_index_tip">Wox仅索引下列后缀的文件</system:String>

View File

@ -21,7 +21,6 @@
<system:String x:Key="wox_plugin_program_max_search_depth">最大搜尋深度(-1是無限的</system:String>
<system:String x:Key="wox_plugin_program_pls_select_program_source">請先選擇一項</system:String>
<system:String x:Key="wox_plugin_program_delete_program_source">你確定要刪除{0}嗎?</system:String>
<system:String x:Key="wox_plugin_program_update">更新</system:String>
<system:String x:Key="wox_plugin_program_only_index_tip">Wox 僅索引下列副檔名的檔案:</system:String>

View File

@ -1,14 +1,13 @@
using System;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Controls;
using Wox.Infrastructure;
using Wox.Infrastructure.Logger;
using Wox.Infrastructure.Storage;
using Wox.Plugin.Program.Programs;
using Wox.Plugin.Program.Views;
using Stopwatch = Wox.Infrastructure.Stopwatch;
namespace Wox.Plugin.Program
@ -16,14 +15,16 @@ namespace Wox.Plugin.Program
public class Main : ISettingProvider, IPlugin, IPluginI18n, IContextMenu, ISavable, IReloadable
{
private static readonly object IndexLock = new object();
private static Win32[] _win32s;
private static UWP.Application[] _uwps;
internal static Win32[] _win32s { get; set; }
internal static UWP.Application[] _uwps { get; set; }
internal static Settings _settings { get; set; }
private static bool IsStartupIndexProgramsRequired => _settings.LastIndexTime.AddDays(3) < DateTime.Today;
private static PluginInitContext _context;
private static BinaryStorage<Win32[]> _win32Storage;
private static BinaryStorage<UWP.Application[]> _uwpStorage;
private static Settings _settings;
private static BinaryStorage<UWP.Application[]> _uwpStorage;
private readonly PluginJsonStorage<Settings> _settingsStorage;
public Main()
@ -40,10 +41,22 @@ namespace Wox.Plugin.Program
});
Log.Info($"|Wox.Plugin.Program.Main|Number of preload win32 programs <{_win32s.Length}>");
Log.Info($"|Wox.Plugin.Program.Main|Number of preload uwps <{_uwps.Length}>");
Task.Run(() =>
var a = Task.Run(() =>
{
Stopwatch.Normal("|Wox.Plugin.Program.Main|Program index cost", IndexPrograms);
if (IsStartupIndexProgramsRequired || !_win32s.Any())
Stopwatch.Normal("|Wox.Plugin.Program.Main|Win32Program index cost", IndexWin32Programs);
});
var b = Task.Run(() =>
{
if (IsStartupIndexProgramsRequired || !_uwps.Any())
Stopwatch.Normal("|Wox.Plugin.Program.Main|Win32Program index cost", IndexUWPPrograms);
});
Task.WaitAll(a, b);
_settings.LastIndexTime = DateTime.Today;
}
public void Save()
@ -57,8 +70,14 @@ namespace Wox.Plugin.Program
{
lock (IndexLock)
{
var results1 = _win32s.AsParallel().Select(p => p.Result(query.Search, _context.API));
var results2 = _uwps.AsParallel().Select(p => p.Result(query.Search, _context.API));
var results1 = _win32s.AsParallel()
.Where(p => p.Enabled)
.Select(p => p.Result(query.Search, _context.API));
var results2 = _uwps.AsParallel()
.Where(p => p.Enabled)
.Select(p => p.Result(query.Search, _context.API));
var result = results1.Concat(results2).Where(r => r.Score > 0).ToList();
return result;
}
@ -69,39 +88,39 @@ namespace Wox.Plugin.Program
_context = context;
}
public static void IndexPrograms()
public static void IndexWin32Programs()
{
Win32[] w = { };
UWP.Application[] u = { };
var t1 = Task.Run(() =>
{
w = Win32.All(_settings);
});
var t2 = Task.Run(() =>
{
var windows10 = new Version(10, 0);
var support = Environment.OSVersion.Version.Major >= windows10.Major;
if (support)
{
u = UWP.All();
}
else
{
u = new UWP.Application[] { };
}
});
Task.WaitAll(t1, t2);
lock (IndexLock)
{
_win32s = w;
_uwps = u;
_win32s = Win32.All(_settings);
}
}
public static void IndexUWPPrograms()
{
var windows10 = new Version(10, 0);
var support = Environment.OSVersion.Version.Major >= windows10.Major;
lock (IndexLock)
{
_uwps = support ? UWP.All() : new UWP.Application[] { };
}
}
public static void IndexPrograms()
{
var t1 = Task.Run(() => { IndexWin32Programs(); });
var t2 = Task.Run(() => { IndexUWPPrograms(); });
Task.WaitAll(t1, t2);
_settings.LastIndexTime = DateTime.Today;
}
public Control CreateSettingPanel()
{
return new ProgramSetting(_context, _settings);
return new ProgramSetting(_context, _settings, _win32s, _uwps);
}
public string GetTranslatedPluginTitle()
@ -116,16 +135,52 @@ namespace Wox.Plugin.Program
public List<Result> LoadContextMenus(Result selectedResult)
{
var menuOptions = new List<Result>();
var program = selectedResult.ContextData as IProgram;
if (program != null)
{
var menus = program.ContextMenus(_context.API);
return menus;
}
else
{
return new List<Result>();
menuOptions = program.ContextMenus(_context.API);
}
menuOptions.Add(
new Result
{
Title = _context.API.GetTranslation("wox_plugin_program_disable_program"),
Action = c =>
{
DisableProgram(program);
_context.API.ShowMsg(_context.API.GetTranslation("wox_plugin_program_disable_dlgtitle_success"),
_context.API.GetTranslation("wox_plugin_program_disable_dlgtitle_success_message"));
return false;
},
IcoPath = "Images/disable.png"
}
);
return menuOptions;
}
private void DisableProgram(IProgram programToDelete)
{
if (_settings.DisabledProgramSources.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier))
return;
if (_uwps.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier))
_uwps.Where(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier).FirstOrDefault().Enabled = false;
if (_win32s.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier))
_win32s.Where(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier).FirstOrDefault().Enabled = false;
_settings.DisabledProgramSources
.Add(
new Settings.DisabledProgramSource
{
Name = programToDelete.Name,
Location = programToDelete.Location,
UniqueIdentifier = programToDelete.UniqueIdentifier,
Enabled = false
}
);
}
public static bool StartProcess(ProcessStartInfo info)

View File

@ -1,149 +0,0 @@
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using Wox.Plugin.Program.Programs;
namespace Wox.Plugin.Program
{
/// <summary>
/// Interaction logic for ProgramSetting.xaml
/// </summary>
public partial class ProgramSetting : UserControl
{
private PluginInitContext context;
private Settings _settings;
public ProgramSetting(PluginInitContext context, Settings settings)
{
this.context = context;
InitializeComponent();
Loaded += Setting_Loaded;
_settings = settings;
}
private void Setting_Loaded(object sender, RoutedEventArgs e)
{
programSourceView.ItemsSource = _settings.ProgramSources;
StartMenuEnabled.IsChecked = _settings.EnableStartMenuSource;
RegistryEnabled.IsChecked = _settings.EnableRegistrySource;
}
private void ReIndexing()
{
programSourceView.Items.Refresh();
Task.Run(() =>
{
Dispatcher.Invoke(() => { indexingPanel.Visibility = Visibility.Visible; });
Main.IndexPrograms();
Dispatcher.Invoke(() => { indexingPanel.Visibility = Visibility.Hidden; });
});
}
private void btnAddProgramSource_OnClick(object sender, RoutedEventArgs e)
{
var add = new AddProgramSource(context, _settings);
if(add.ShowDialog() ?? false)
{
ReIndexing();
}
}
private void btnDeleteProgramSource_OnClick(object sender, RoutedEventArgs e)
{
var selectedProgramSource = programSourceView.SelectedItem as Settings.ProgramSource;
if (selectedProgramSource != null)
{
string msg = string.Format(context.API.GetTranslation("wox_plugin_program_delete_program_source"), selectedProgramSource.Location);
if (MessageBox.Show(msg, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
_settings.ProgramSources.Remove(selectedProgramSource);
ReIndexing();
}
}
else
{
string msg = context.API.GetTranslation("wox_plugin_program_pls_select_program_source");
MessageBox.Show(msg);
}
}
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
{
string msg = context.API.GetTranslation("wox_plugin_program_pls_select_program_source");
MessageBox.Show(msg);
}
}
private void btnReindex_Click(object sender, RoutedEventArgs e)
{
ReIndexing();
}
private void BtnProgramSuffixes_OnClick(object sender, RoutedEventArgs e)
{
var p = new ProgramSuffixes(context, _settings);
if (p.ShowDialog() ?? false)
{
ReIndexing();
}
}
private void programSourceView_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
e.Effects = DragDropEffects.Link;
}
else
{
e.Effects = DragDropEffects.None;
}
}
private void programSourceView_Drop(object sender, DragEventArgs e)
{
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
if (files != null && files.Length > 0)
{
foreach (string s in files)
{
if (Directory.Exists(s))
{
_settings.ProgramSources.Add(new Settings.ProgramSource
{
Location = s
});
ReIndexing();
}
}
}
}
private void StartMenuEnabled_Click(object sender, RoutedEventArgs e)
{
_settings.EnableStartMenuSource = StartMenuEnabled.IsChecked ?? false;
ReIndexing();
}
private void RegistryEnabled_Click(object sender, RoutedEventArgs e)
{
_settings.EnableRegistrySource = RegistryEnabled.IsChecked ?? false;
ReIndexing();
}
}
}

View File

@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace Wox.Plugin.Program.Programs
{
@ -10,5 +6,8 @@ namespace Wox.Plugin.Program.Programs
{
List<Result> ContextMenus(IPublicAPI api);
Result Result(string query, IPublicAPI api);
string UniqueIdentifier { get; set; }
string Name { get; }
string Location { get; }
}
}

View File

@ -160,7 +160,13 @@ namespace Wox.Plugin.Program.Programs
}
return u.Apps;
}).ToArray();
return applications;
var updatedListWithoutDisabledApps = applications
.Where(t1 => !Main._settings.DisabledProgramSources
.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier))
.Select(x => x);
return updatedListWithoutDisabledApps.ToArray();
}
else
{
@ -229,11 +235,17 @@ namespace Wox.Plugin.Program.Programs
public class Application : IProgram
{
public string AppListEntry { get; set; }
public string UniqueIdentifier { get; set; }
public string DisplayName { get; set; }
public string Description { get; set; }
public string UserModelId { get; set; }
public string BackgroundColor { get; set; }
public string Name => DisplayName;
public string Location => Package.Location;
public bool Enabled { get; set; }
public string LogoUri { get; set; }
public string LogoPath { get; set; }
public UWP Package { get; set; }
@ -321,6 +333,7 @@ namespace Wox.Plugin.Program.Programs
public Application(IAppxManifestApplication manifestApp, UWP package)
{
UserModelId = manifestApp.GetAppUserModelId();
UniqueIdentifier = manifestApp.GetAppUserModelId();
DisplayName = manifestApp.GetStringValue("DisplayName");
Description = manifestApp.GetStringValue("Description");
BackgroundColor = manifestApp.GetStringValue("BackgroundColor");
@ -330,6 +343,8 @@ namespace Wox.Plugin.Program.Programs
Description = ResourceFromPri(package.FullName, Description);
LogoUri = LogoUriFromManifest(manifestApp);
LogoPath = LogoPathFromUri(LogoUri);
Enabled = true;
}
internal string ResourceFromPri(string packageFullName, string resourceReference)

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
@ -18,12 +18,15 @@ namespace Wox.Plugin.Program.Programs
public class Win32 : IProgram
{
public string Name { get; set; }
public string UniqueIdentifier { get; set; }
public string IcoPath { get; set; }
public string FullPath { get; set; }
public string ParentDirectory { get; set; }
public string ExecutableName { get; set; }
public string Description { get; set; }
public bool Valid { get; set; }
public bool Enabled { get; set; }
public string Location => ParentDirectory;
private const string ShortcutExtension = "lnk";
private const string ApplicationReferenceExtension = "appref-ms";
@ -127,9 +130,11 @@ namespace Wox.Plugin.Program.Programs
Name = Path.GetFileNameWithoutExtension(path),
IcoPath = path,
FullPath = path,
UniqueIdentifier = path,
ParentDirectory = Directory.GetParent(path).FullName,
Description = string.Empty,
Valid = true
Valid = true,
Enabled = true
};
return p;
}
@ -262,9 +267,16 @@ namespace Wox.Plugin.Program.Programs
private static ParallelQuery<Win32> UnregisteredPrograms(List<Settings.ProgramSource> sources, string[] suffixes)
{
var paths = sources.Where(s => Directory.Exists(s.Location))
.SelectMany(s => ProgramPaths(s.Location, suffixes))
.ToArray();
var listToAdd = new List<string>();
sources.Where(s => Directory.Exists(s.Location) && s.Enabled)
.SelectMany(s => ProgramPaths(s.Location, suffixes))
.ToList()
.Where(t1 => !Main._settings.DisabledProgramSources.Any(x => t1 == x.UniqueIdentifier))
.ToList()
.ForEach(x => listToAdd.Add(x));
var paths = listToAdd.Distinct().ToArray();
var programs1 = paths.AsParallel().Where(p => Extension(p) == ExeExtension).Select(ExeProgram);
var programs2 = paths.AsParallel().Where(p => Extension(p) == ShortcutExtension).Select(ExeProgram);
var programs3 = from p in paths.AsParallel()
@ -276,18 +288,26 @@ namespace Wox.Plugin.Program.Programs
private static ParallelQuery<Win32> StartMenuPrograms(string[] suffixes)
{
var disabledProgramsList = Main._settings.DisabledProgramSources;
var directory1 = Environment.GetFolderPath(Environment.SpecialFolder.Programs);
var directory2 = Environment.GetFolderPath(Environment.SpecialFolder.CommonPrograms);
var paths1 = ProgramPaths(directory1, suffixes);
var paths2 = ProgramPaths(directory2, suffixes);
var paths = paths1.Concat(paths2).ToArray();
var toFilter = paths1.Concat(paths2);
var paths = toFilter
.Where(t1 => !disabledProgramsList.Any(x => x.UniqueIdentifier == t1))
.Select(t1 => t1)
.Distinct()
.ToArray();
var programs1 = paths.AsParallel().Where(p => Extension(p) == ShortcutExtension).Select(LnkProgram);
var programs2 = paths.AsParallel().Where(p => Extension(p) == ApplicationReferenceExtension).Select(Win32Program);
var programs = programs1.Concat(programs2).Where(p => p.Valid);
return programs;
}
private static ParallelQuery<Win32> AppPathsPrograms(string[] suffixes)
{
// https://msdn.microsoft.com/en-us/library/windows/desktop/ee872121
@ -297,106 +317,88 @@ namespace Wox.Plugin.Program.Programs
{
if (root != null)
{
programs.AddRange(ProgramsFromRegistryKey(root));
programs.AddRange(GetProgramsFromRegistry(root));
}
}
using (var root = Registry.CurrentUser.OpenSubKey(appPaths))
{
if (root != null)
{
programs.AddRange(ProgramsFromRegistryKey(root));
programs.AddRange(GetProgramsFromRegistry(root));
}
}
var filtered = programs.AsParallel().Where(p => suffixes.Contains(Extension(p.ExecutableName)));
var disabledProgramsList = Main._settings.DisabledProgramSources;
var toFilter = programs.AsParallel().Where(p => suffixes.Contains(Extension(p.ExecutableName)));
var filtered = toFilter.Where(t1 => !disabledProgramsList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier)).Select(t1 => t1);
return filtered;
}
private static IEnumerable<Win32> ProgramsFromRegistryKey(RegistryKey root)
private static IEnumerable<Win32> GetProgramsFromRegistry(RegistryKey root)
{
var programs = root.GetSubKeyNames()
.Select(subkey => ProgramFromRegistrySubkey(root, subkey))
.Where(p => !string.IsNullOrEmpty(p.Name));
return programs;
return root
.GetSubKeyNames()
.Select(x => GetProgramPathFromRegistrySubKeys(root, x))
.Distinct()
.Select(x => GetProgramFromPath(x));
}
private static Win32 ProgramFromRegistrySubkey(RegistryKey root, string subkey)
private static string GetProgramPathFromRegistrySubKeys(RegistryKey root, string subkey)
{
var path = string.Empty;
using (var key = root.OpenSubKey(subkey))
{
if (key != null)
{
var defaultValue = string.Empty;
var path = key.GetValue(defaultValue) as string;
if (!string.IsNullOrEmpty(path))
{
// fix path like this: ""\"C:\\folder\\executable.exe\""
path = path.Trim('"', ' ');
path = Environment.ExpandEnvironmentVariables(path);
if (key == null)
return string.Empty;
if (File.Exists(path))
{
var entry = Win32Program(path);
entry.ExecutableName = subkey;
return entry;
}
else
{
return new Win32();
}
}
else
{
return new Win32();
}
}
else
{
return new Win32();
}
var defaultValue = string.Empty;
path = key.GetValue(defaultValue) as string;
}
if (string.IsNullOrEmpty(path))
return string.Empty;
// fix path like this: ""\"C:\\folder\\executable.exe\""
return path = path.Trim('"', ' ');
}
//private static Win32 ScoreFilter(Win32 p)
//{
// var start = new[] { "启动", "start" };
// var doc = new[] { "帮助", "help", "文档", "documentation" };
// var uninstall = new[] { "卸载", "uninstall" };
private static Win32 GetProgramFromPath(string path)
{
if (string.IsNullOrEmpty(path))
return new Win32();
// var contained = start.Any(s => p.Name.ToLower().Contains(s));
// if (contained)
// {
// p.Score += 10;
// }
// contained = doc.Any(d => p.Name.ToLower().Contains(d));
// if (contained)
// {
// p.Score -= 10;
// }
// contained = uninstall.Any(u => p.Name.ToLower().Contains(u));
// if (contained)
// {
// p.Score -= 20;
// }
path = Environment.ExpandEnvironmentVariables(path);
// return p;
//}
if (!File.Exists(path))
return new Win32();
var entry = Win32Program(path);
entry.ExecutableName = Path.GetFileName(path);
return entry;
}
public static Win32[] All(Settings settings)
{
ParallelQuery<Win32> programs = new List<Win32>().AsParallel();
var programs = new List<Win32>().AsParallel();
var unregistered = UnregisteredPrograms(settings.ProgramSources, settings.ProgramSuffixes);
programs = programs.Concat(unregistered);
if (settings.EnableRegistrySource)
{
var appPaths = AppPathsPrograms(settings.ProgramSuffixes);
programs = programs.Concat(appPaths);
}
if (settings.EnableStartMenuSource)
{
var startMenu = StartMenuPrograms(settings.ProgramSuffixes);
programs = programs.Concat(startMenu);
}
var unregistered = UnregisteredPrograms(settings.ProgramSources, settings.ProgramSuffixes);
programs = programs.Concat(unregistered);
//.Select(ScoreFilter);
return programs.ToArray();
}
}

View File

@ -1,11 +1,14 @@
using System.Collections.Generic;
using Wox.Plugin.Program.Programs;
using System;
using System.Collections.Generic;
using System.IO;
namespace Wox.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>();
public string[] ProgramSuffixes { get; set; } = {"bat", "appref-ms", "exe", "lnk"};
public bool EnableStartMenuSource { get; set; } = true;
@ -14,9 +17,24 @@ namespace Wox.Plugin.Program
internal const char SuffixSeperator = ';';
/// <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 { }
}
}

View File

@ -0,0 +1,149 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Wox.Plugin.Program.Views.Models;
namespace Wox.Plugin.Program.Views.Commands
{
internal static class ProgramSettingDisplay
{
internal static List<ProgramSource> LoadProgramSources(this List<Settings.ProgramSource> programSources)
{
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;
}
internal static void LoadAllApplications(this List<ProgramSource> list)
{
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
}
));
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
}
));
}
internal static void SetProgramSourcesStatus(this List<ProgramSource> list, 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);
Main._win32s
.Where(t1 => selectedProgramSourcesToDisable.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier && t1.Enabled != status))
.ToList()
.ForEach(t1 => t1.Enabled = status);
Main._uwps
.Where(t1 => selectedProgramSourcesToDisable.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier && t1.Enabled != status))
.ToList()
.ForEach(t1 => t1.Enabled = status);
}
internal static void StoreDisabledInSettings(this List<ProgramSource> list)
{
Main._settings.ProgramSources
.Where(t1 => ProgramSetting.ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier && !x.Enabled))
.ToList()
.ForEach(t1 => t1.Enabled = false);
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
}
));
}
internal static void RemoveDisabledFromSettings(this List<ProgramSource> list)
{
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));
}
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)
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)
return true;
return false;
}
}
}

View File

@ -0,0 +1,5 @@

namespace Wox.Plugin.Program.Views.Models
{
public class ProgramSource : Settings.ProgramSource { }
}

View File

@ -1,4 +1,4 @@
<UserControl x:Class="Wox.Plugin.Program.ProgramSetting"
<UserControl x:Class="Wox.Plugin.Program.Views.ProgramSetting"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@ -13,18 +13,41 @@
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Stretch">
<StackPanel Orientation="Vertical" Width="310">
<StackPanel Orientation="Vertical" Width="205">
<CheckBox Name="StartMenuEnabled" Click="StartMenuEnabled_Click" Margin="5" Content="{DynamicResource wox_plugin_program_index_start}" />
<CheckBox Name="RegistryEnabled" Click="RegistryEnabled_Click" Margin="5" Content="{DynamicResource wox_plugin_program_index_registry}" />
</StackPanel>
<Button Height="30" HorizontalAlignment="Right" x:Name="btnProgramSuffixes" Width="130" Click="BtnProgramSuffixes_OnClick" Content="{DynamicResource wox_plugin_program_suffixes}" />
<Button Height="30" HorizontalAlignment="Right" Margin="10 0 0 0" x:Name="btnReindex" Width="100" Click="btnReindex_Click" Content="{DynamicResource wox_plugin_program_reindex}" />
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<Button Height="30" HorizontalAlignment="Right" Margin="10" Width="100" x:Name="btnLoadAllProgramSource" Click="btnLoadAllProgramSource_OnClick" Content="{DynamicResource wox_plugin_program_all_programs}" />
<Button Height="30" HorizontalAlignment="Right" Margin="10" Width="100" x:Name="btnProgramSuffixes" Click="BtnProgramSuffixes_OnClick" Content="{DynamicResource wox_plugin_program_suffixes}" />
<Button Height="30" HorizontalAlignment="Right" Margin="10" Width="100" x:Name="btnReindex" Click="btnReindex_Click" Content="{DynamicResource wox_plugin_program_reindex}" />
</StackPanel>
</StackPanel>
<ListView x:Name="programSourceView" Grid.Row="1" AllowDrop="True"
<ListView x:Name="programSourceView" Grid.Row="1" AllowDrop="True" SelectionMode="Extended" GridViewColumnHeader.Click="GridViewColumnHeaderClickedHandler"
PreviewMouseRightButtonUp="ProgramSourceView_PreviewMouseRightButtonUp"
DragEnter="programSourceView_DragEnter"
Drop="programSourceView_Drop" >
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<EventSetter Event="PreviewMouseUp" Handler="Row_OnClick"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Header="Name" Width="150">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Enabled" Width="50">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Enabled}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="{DynamicResource wox_plugin_program_location}" Width="550">
<GridViewColumn.CellTemplate>
<DataTemplate>
@ -41,9 +64,9 @@
<TextBlock Margin="10 0 0 0" Height="20" HorizontalAlignment="Center" Text="{DynamicResource wox_plugin_program_indexing}" />
</StackPanel>
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<Button x:Name="btnDeleteProgramSource" Click="btnDeleteProgramSource_OnClick" Width="100" Margin="10" Content="{DynamicResource wox_plugin_program_delete}"/>
<Button x:Name="btnEditProgramSource" Click="btnEditProgramSource_OnClick" Width="100" Margin="10" Content="{DynamicResource wox_plugin_program_edit}"/>
<Button x:Name="btnProgramSourceStatus" Click="btnProgramSourceStatus_OnClick" Width="100" Margin="10" Content="{DynamicResource wox_plugin_program_disable}" />
<Button x:Name="btnAddProgramSource" Click="btnAddProgramSource_OnClick" Width="100" Margin="10" Content="{DynamicResource wox_plugin_program_add}"/>
<Button x:Name="btnEditProgramSource" Click="btnEditProgramSource_OnClick" Width="100" Margin="10" Content="{DynamicResource wox_plugin_program_edit}"/>
</StackPanel>
</DockPanel>
</Grid>

View File

@ -0,0 +1,307 @@
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;
using Wox.Plugin.Program.Views.Models;
using Wox.Plugin.Program.Views.Commands;
using Wox.Plugin.Program.Programs;
using System.ComponentModel;
using System.Windows.Data;
namespace Wox.Plugin.Program.Views
{
/// <summary>
/// Interaction logic for ProgramSetting.xaml
/// </summary>
public partial class ProgramSetting : UserControl
{
private PluginInitContext context;
private Settings _settings;
private GridViewColumnHeader _lastHeaderClicked;
private ListSortDirection _lastDirection;
// We do not save all program sources to settings, so using
// this as temporary holder for displaying all loaded programs sources.
internal static List<ProgramSource> ProgramSettingDisplayList { get; set; }
public ProgramSetting(PluginInitContext context, Settings settings, Win32[] win32s, UWP.Application[] uwps)
{
this.context = context;
InitializeComponent();
Loaded += Setting_Loaded;
_settings = settings;
}
private void Setting_Loaded(object sender, RoutedEventArgs e)
{
ProgramSettingDisplayList = _settings.ProgramSources.LoadProgramSources();
programSourceView.ItemsSource = ProgramSettingDisplayList;
StartMenuEnabled.IsChecked = _settings.EnableStartMenuSource;
RegistryEnabled.IsChecked = _settings.EnableRegistrySource;
}
private void ReIndexing()
{
programSourceView.Items.Refresh();
Task.Run(() =>
{
Dispatcher.Invoke(() => { indexingPanel.Visibility = Visibility.Visible; });
Main.IndexPrograms();
Dispatcher.Invoke(() => { indexingPanel.Visibility = Visibility.Hidden; });
});
}
private void btnAddProgramSource_OnClick(object sender, RoutedEventArgs e)
{
var add = new AddProgramSource(context, _settings);
if(add.ShowDialog() ?? false)
{
ReIndexing();
}
programSourceView.Items.Refresh();
}
private void DeleteProgramSources(List<ProgramSource> itemsToDelete)
{
itemsToDelete.ForEach(t1 => _settings.ProgramSources
.Remove(_settings.ProgramSources
.Where(x => x.UniqueIdentifier == t1.UniqueIdentifier)
.FirstOrDefault()));
itemsToDelete.ForEach(x => ProgramSettingDisplayList.Remove(x));
ReIndexing();
}
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
{
string msg = context.API.GetTranslation("wox_plugin_program_pls_select_program_source");
MessageBox.Show(msg);
}
}
private void btnReindex_Click(object sender, RoutedEventArgs e)
{
ReIndexing();
}
private void BtnProgramSuffixes_OnClick(object sender, RoutedEventArgs e)
{
var p = new ProgramSuffixes(context, _settings);
if (p.ShowDialog() ?? false)
{
ReIndexing();
}
}
private void programSourceView_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
e.Effects = DragDropEffects.Link;
}
else
{
e.Effects = DragDropEffects.None;
}
}
private void programSourceView_Drop(object sender, DragEventArgs e)
{
var directories = (string[])e.Data.GetData(DataFormats.FileDrop);
var directoriesToAdd = new List<ProgramSource>();
if (directories != null && directories.Length > 0)
{
foreach (string directory in directories)
{
if (Directory.Exists(directory) && !ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == directory))
{
var source = new ProgramSource
{
Location = directory,
UniqueIdentifier = directory
};
directoriesToAdd.Add(source);
}
}
if (directoriesToAdd.Count() > 0)
{
directoriesToAdd.ForEach(x => _settings.ProgramSources.Add(x));
directoriesToAdd.ForEach(x => ProgramSettingDisplayList.Add(x));
programSourceView.Items.Refresh();
ReIndexing();
}
}
}
private void StartMenuEnabled_Click(object sender, RoutedEventArgs e)
{
_settings.EnableStartMenuSource = StartMenuEnabled.IsChecked ?? false;
ReIndexing();
}
private void RegistryEnabled_Click(object sender, RoutedEventArgs e)
{
_settings.EnableRegistrySource = RegistryEnabled.IsChecked ?? false;
ReIndexing();
}
private void btnLoadAllProgramSource_OnClick(object sender, RoutedEventArgs e)
{
ProgramSettingDisplayList.LoadAllApplications();
programSourceView.Items.Refresh();
}
private void btnProgramSourceStatus_OnClick(object sender, RoutedEventArgs e)
{
var selectedItems = programSourceView
.SelectedItems.Cast<ProgramSource>()
.ToList();
if (selectedItems.Count() == 0)
{
string msg = context.API.GetTranslation("wox_plugin_program_pls_select_program_source");
MessageBox.Show(msg);
return;
}
if (selectedItems
.Where(t1 => !_settings
.ProgramSources
.Any(x => t1.UniqueIdentifier == x.UniqueIdentifier))
.Count() == 0)
{
var msg = string.Format(context.API.GetTranslation("wox_plugin_program_delete_program_source"));
if (MessageBox.Show(msg, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No)
{
return;
}
DeleteProgramSources(selectedItems);
}
else if (IsSelectedRowStatusEnabledMoreOrEqualThanDisabled(selectedItems))
{
ProgramSettingDisplayList.SetProgramSourcesStatus(selectedItems, false);
ProgramSettingDisplayList.StoreDisabledInSettings();
}
else
{
ProgramSettingDisplayList.SetProgramSourcesStatus(selectedItems, true);
ProgramSettingDisplayList.RemoveDisabledFromSettings();
}
if (selectedItems.IsReindexRequired())
ReIndexing();
programSourceView.SelectedItems.Clear();
programSourceView.Items.Refresh();
}
private void ProgramSourceView_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
programSourceView.SelectedItems.Clear();
}
private void GridViewColumnHeaderClickedHandler(object sender, RoutedEventArgs e)
{
var headerClicked = e.OriginalSource as GridViewColumnHeader;
ListSortDirection direction;
if (headerClicked != null)
{
if (headerClicked.Role != GridViewColumnHeaderRole.Padding)
{
if (headerClicked != _lastHeaderClicked)
{
direction = ListSortDirection.Ascending;
}
else
{
if (_lastDirection == ListSortDirection.Ascending)
{
direction = ListSortDirection.Descending;
}
else
{
direction = ListSortDirection.Ascending;
}
}
var columnBinding = headerClicked.Column.DisplayMemberBinding as Binding;
var sortBy = columnBinding?.Path.Path ?? headerClicked.Column.Header as string;
Sort(sortBy, direction);
_lastHeaderClicked = headerClicked;
_lastDirection = direction;
}
}
}
private void Sort(string sortBy, ListSortDirection direction)
{
var dataView = CollectionViewSource.GetDefaultView(programSourceView.ItemsSource);
dataView.SortDescriptions.Clear();
SortDescription sd = new SortDescription(sortBy, direction);
dataView.SortDescriptions.Add(sd);
dataView.Refresh();
}
private bool IsSelectedRowStatusEnabledMoreOrEqualThanDisabled(List<ProgramSource> selectedItems)
{
return selectedItems.Where(x => x.Enabled).Count() >= selectedItems.Where(x => !x.Enabled).Count();
}
private void Row_OnClick(object sender, RoutedEventArgs e)
{
var selectedItems = programSourceView
.SelectedItems.Cast<ProgramSource>()
.ToList();
if (selectedItems
.Where(t1 => !_settings
.ProgramSources
.Any(x => t1.UniqueIdentifier == x.UniqueIdentifier))
.Count() == 0)
{
btnProgramSourceStatus.Content = context.API.GetTranslation("wox_plugin_program_delete");
return;
}
if (IsSelectedRowStatusEnabledMoreOrEqualThanDisabled(selectedItems))
{
btnProgramSourceStatus.Content = "Disable";
}
else
{
btnProgramSourceStatus.Content = "Enable";
}
}
}
}

View File

@ -71,13 +71,15 @@
<Compile Include="AddProgramSource.xaml.cs">
<DependentUpon>AddProgramSource.xaml</DependentUpon>
</Compile>
<Compile Include="Views\Commands\ProgramSettingDisplay.cs" />
<Compile Include="FileChangeWatcher.cs" />
<Compile Include="Views\Models\ProgramSource.cs" />
<Compile Include="Programs\IProgram.cs" />
<Compile Include="Programs\UWP.cs" />
<Compile Include="Programs\Win32.cs" />
<Compile Include="SuffixesConverter.cs" />
<Compile Include="Main.cs" />
<Compile Include="ProgramSetting.xaml.cs">
<Compile Include="Views\ProgramSetting.xaml.cs">
<DependentUpon>ProgramSetting.xaml</DependentUpon>
</Compile>
<Compile Include="Settings.cs" />
@ -103,6 +105,9 @@
<None Include="Images\folder.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Images\disable.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<Content Include="Languages\en.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
@ -135,7 +140,7 @@
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Page Include="ProgramSetting.xaml">
<Page Include="Views\ProgramSetting.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>