mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-05 18:57:19 +02:00
Add 'src/modules/launcher/' from commit '28acd466b352a807910cebbc74477d3173b31511'
git-subtree-dir: src/modules/launcher git-subtree-mainline:852689b3dfgit-subtree-split:28acd466b3
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
<Window x:Class="Wox.Plugin.Program.AddProgramSource"
|
||||
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"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
mc:Ignorable="d"
|
||||
Width="400"
|
||||
Height="120"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Label Content="{DynamicResource wox_plugin_program_directory}"/>
|
||||
<TextBox Name="Directory" VerticalAlignment="Center" Width="300" Margin="0,7" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Button Click="BrowseButton_Click" Content="{DynamicResource wox_plugin_program_browse}"
|
||||
HorizontalAlignment="Right" Margin="10" Height="20" Width="70" />
|
||||
<Button Click="ButtonAdd_OnClick" Content="{DynamicResource wox_plugin_program_update}"
|
||||
HorizontalAlignment="Right" Margin="10" Height="20" Width="70" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Window>
|
||||
@@ -0,0 +1,76 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Forms;
|
||||
using Wox.Plugin.Program.Views.Models;
|
||||
using Wox.Plugin.Program.Views;
|
||||
using System.Linq;
|
||||
|
||||
namespace Wox.Plugin.Program
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for AddProgramSource.xaml
|
||||
/// </summary>
|
||||
public partial class AddProgramSource
|
||||
{
|
||||
private PluginInitContext _context;
|
||||
private Settings.ProgramSource _editing;
|
||||
private Settings _settings;
|
||||
|
||||
public AddProgramSource(PluginInitContext context, Settings settings)
|
||||
{
|
||||
InitializeComponent();
|
||||
_context = context;
|
||||
_settings = settings;
|
||||
Directory.Focus();
|
||||
}
|
||||
|
||||
public AddProgramSource(Settings.ProgramSource edit, Settings settings)
|
||||
{
|
||||
_editing = edit;
|
||||
_settings = settings;
|
||||
|
||||
InitializeComponent();
|
||||
Directory.Text = _editing.Location;
|
||||
}
|
||||
|
||||
private void BrowseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var dialog = new FolderBrowserDialog();
|
||||
DialogResult result = dialog.ShowDialog();
|
||||
if (result == System.Windows.Forms.DialogResult.OK)
|
||||
{
|
||||
Directory.Text = dialog.SelectedPath;
|
||||
}
|
||||
}
|
||||
|
||||
private void ButtonAdd_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
string s = Directory.Text;
|
||||
if (!System.IO.Directory.Exists(s))
|
||||
{
|
||||
System.Windows.MessageBox.Show(_context.API.GetTranslation("wox_plugin_program_invalid_path"));
|
||||
return;
|
||||
}
|
||||
if (_editing == null)
|
||||
{
|
||||
if (!ProgramSetting.ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == Directory.Text))
|
||||
{
|
||||
var source = new ProgramSource
|
||||
{
|
||||
Location = Directory.Text,
|
||||
UniqueIdentifier = Directory.Text
|
||||
};
|
||||
|
||||
_settings.ProgramSources.Insert(0, source);
|
||||
ProgramSetting.ProgramSettingDisplayList.Add(source);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_editing.Location = Directory.Text;
|
||||
}
|
||||
|
||||
DialogResult = true;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,58 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Wox.Infrastructure.Logger;
|
||||
using Wox.Plugin.Program.Programs;
|
||||
|
||||
namespace Wox.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();
|
||||
// });
|
||||
// }
|
||||
//}
|
||||
}
|
||||
BIN
src/modules/launcher/Plugins/Wox.Plugin.Program/Images/cmd.png
Normal file
BIN
src/modules/launcher/Plugins/Wox.Plugin.Program/Images/cmd.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 752 B |
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 468 B |
Binary file not shown.
|
After Width: | Height: | Size: 774 B |
BIN
src/modules/launcher/Plugins/Wox.Plugin.Program/Images/user.png
Normal file
BIN
src/modules/launcher/Plugins/Wox.Plugin.Program/Images/user.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 64 KiB |
@@ -0,0 +1,37 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||
|
||||
<!--Program setting-->
|
||||
<system:String x:Key="wox_plugin_program_delete">Löschen</system:String>
|
||||
<system:String x:Key="wox_plugin_program_edit">Bearbeiten</system:String>
|
||||
<system:String x:Key="wox_plugin_program_add">Hinzufügen</system:String>
|
||||
<system:String x:Key="wox_plugin_program_location">Speicherort</system:String>
|
||||
<system:String x:Key="wox_plugin_program_suffixes">Indexiere Dateiendungen</system:String>
|
||||
<system:String x:Key="wox_plugin_program_reindex">erneut Indexieren</system:String>
|
||||
<system:String x:Key="wox_plugin_program_indexing">Indexieren</system:String>
|
||||
<system:String x:Key="wox_plugin_program_index_start">Indexierungs Startmenü</system:String>
|
||||
<system:String x:Key="wox_plugin_program_index_registry">Indexierungsspeicher</system:String>
|
||||
<system:String x:Key="wox_plugin_program_suffixes_header">Endungen</system:String>
|
||||
<system:String x:Key="wox_plugin_program_max_depth_header">Maximale Tiefe</system:String>
|
||||
|
||||
<system:String x:Key="wox_plugin_program_directory">Verzeichnis:</system:String>
|
||||
<system:String x:Key="wox_plugin_program_browse">Durchsuchen</system:String>
|
||||
<system:String x:Key="wox_plugin_program_file_suffixes">Dateiendungen:</system:String>
|
||||
<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_update">Aktualisieren</system:String>
|
||||
<system:String x:Key="wox_plugin_program_only_index_tip">Wox indexiert nur Datien mit folgenden Endungen:</system:String>
|
||||
<system:String x:Key="wox_plugin_program_split_by_tip">(Jede Endung muss durch ein ; getrennt werden)</system:String>
|
||||
<system:String x:Key="wox_plugin_program_update_file_suffixes">Dateiendungen wurden erfolgreich aktualisiert</system:String>
|
||||
<system:String x:Key="wox_plugin_program_suffixes_cannot_empty">Dateiendungen dürfen nicht leer sein</system:String>
|
||||
|
||||
<system:String x:Key="wox_plugin_program_run_as_administrator">Als Administrator ausführen</system:String>
|
||||
<system:String x:Key="wox_plugin_program_open_containing_folder">Enthaltenden Ordner öffnen</system:String>
|
||||
|
||||
<system:String x:Key="wox_plugin_program_plugin_name">Programm</system:String>
|
||||
<system:String x:Key="wox_plugin_program_plugin_description">Suche Programme mit Wox</system:String>
|
||||
|
||||
</ResourceDictionary>
|
||||
@@ -0,0 +1,48 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||
|
||||
<!--Program setting-->
|
||||
<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_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>
|
||||
<system:String x:Key="wox_plugin_program_index_registry">Index Registry</system:String>
|
||||
<system:String x:Key="wox_plugin_program_suffixes_header">Suffixes</system:String>
|
||||
<system:String x:Key="wox_plugin_program_max_depth_header">Max Depth</system:String>
|
||||
|
||||
<system:String x:Key="wox_plugin_program_directory">Directory:</system:String>
|
||||
<system:String x:Key="wox_plugin_program_browse">Browse</system:String>
|
||||
<system:String x:Key="wox_plugin_program_file_suffixes">File Suffixes:</system:String>
|
||||
<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 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_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_different_user">Run As Different User</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>
|
||||
@@ -0,0 +1,38 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||
|
||||
<!--Program setting-->
|
||||
<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_location">Location</system:String>
|
||||
<system:String x:Key="wox_plugin_program_suffixes">Index 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>
|
||||
<system:String x:Key="wox_plugin_program_index_registry">Index Registry</system:String>
|
||||
<system:String x:Key="wox_plugin_program_suffixes_header">Suffixes</system:String>
|
||||
<system:String x:Key="wox_plugin_program_max_depth_header">Max Depth</system:String>
|
||||
|
||||
<system:String x:Key="wox_plugin_program_directory">Directory:</system:String>
|
||||
<system:String x:Key="wox_plugin_program_browse">Browse</system:String>
|
||||
<system:String x:Key="wox_plugin_program_file_suffixes">File Suffixes:</system:String>
|
||||
<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 your sure to delete {0}?</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 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_update_file_suffixes">Sucessfully update 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_plugin_name">Program</system:String>
|
||||
<system:String x:Key="wox_plugin_program_plugin_description">Search programs in Wox</system:String>
|
||||
|
||||
</ResourceDictionary>
|
||||
@@ -0,0 +1,37 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||
|
||||
<!--Program setting-->
|
||||
<system:String x:Key="wox_plugin_program_delete">Usuń</system:String>
|
||||
<system:String x:Key="wox_plugin_program_edit">Edytuj</system:String>
|
||||
<system:String x:Key="wox_plugin_program_add">Dodaj</system:String>
|
||||
<system:String x:Key="wox_plugin_program_location">Lokalizacja</system:String>
|
||||
<system:String x:Key="wox_plugin_program_suffixes">Rozszerzenia indeksowanych plików</system:String>
|
||||
<system:String x:Key="wox_plugin_program_reindex">Re-indeksuj</system:String>
|
||||
<system:String x:Key="wox_plugin_program_indexing">Indeksowanie</system:String>
|
||||
<system:String x:Key="wox_plugin_program_index_start">Indeksuj Menu Start</system:String>
|
||||
<system:String x:Key="wox_plugin_program_index_registry">Indeksuj rejestr</system:String>
|
||||
<system:String x:Key="wox_plugin_program_suffixes_header">Rozszerzenia</system:String>
|
||||
<system:String x:Key="wox_plugin_program_max_depth_header">Maksymalna głębokość</system:String>
|
||||
|
||||
<system:String x:Key="wox_plugin_program_directory">Katalog:</system:String>
|
||||
<system:String x:Key="wox_plugin_program_browse">Przeglądaj</system:String>
|
||||
<system:String x:Key="wox_plugin_program_file_suffixes">Rozszerzenia plików:</system:String>
|
||||
<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_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>
|
||||
<system:String x:Key="wox_plugin_program_split_by_tip">(Każde rozszerzenie musi być oddzielone ;)</system:String>
|
||||
<system:String x:Key="wox_plugin_program_update_file_suffixes">Pomyślnie zaktualizowano rozszerzenia plików</system:String>
|
||||
<system:String x:Key="wox_plugin_program_suffixes_cannot_empty">Musisz podać rozszerzenia plików</system:String>
|
||||
|
||||
<system:String x:Key="wox_plugin_program_run_as_administrator">Uruchom jako administrator</system:String>
|
||||
<system:String x:Key="wox_plugin_program_open_containing_folder">Otwórz folder zawierający</system:String>
|
||||
|
||||
<system:String x:Key="wox_plugin_program_plugin_name">Programy</system:String>
|
||||
<system:String x:Key="wox_plugin_program_plugin_description">Szukaj i uruchamiaj programy z poziomu Woxa</system:String>
|
||||
|
||||
</ResourceDictionary>
|
||||
@@ -0,0 +1,39 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||
|
||||
<!--Program setting-->
|
||||
<system:String x:Key="wox_plugin_program_delete">Sil</system:String>
|
||||
<system:String x:Key="wox_plugin_program_edit">Düzenle</system:String>
|
||||
<system:String x:Key="wox_plugin_program_add">Ekle</system:String>
|
||||
<system:String x:Key="wox_plugin_program_location">Konum</system:String>
|
||||
<system:String x:Key="wox_plugin_program_suffixes">İndekslenecek Uzantılar</system:String>
|
||||
<system:String x:Key="wox_plugin_program_reindex">Yeniden İndeksle</system:String>
|
||||
<system:String x:Key="wox_plugin_program_indexing">İndeksleniyor</system:String>
|
||||
<system:String x:Key="wox_plugin_program_index_start">Başlat Menüsünü İndeksle</system:String>
|
||||
<system:String x:Key="wox_plugin_program_index_registry">Registry'i İndeksle</system:String>
|
||||
<system:String x:Key="wox_plugin_program_suffixes_header">Uzantılar</system:String>
|
||||
<system:String x:Key="wox_plugin_program_max_depth_header">Derinlik</system:String>
|
||||
|
||||
<system:String x:Key="wox_plugin_program_directory">Dizin:</system:String>
|
||||
<system:String x:Key="wox_plugin_program_browse">Gözat</system:String>
|
||||
<system:String x:Key="wox_plugin_program_file_suffixes">Dosya Uzantıları:</system:String>
|
||||
<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_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>
|
||||
<system:String x:Key="wox_plugin_program_split_by_tip">(Her uzantıyı ; işareti ile ayırın)</system:String>
|
||||
<system:String x:Key="wox_plugin_program_update_file_suffixes">Dosya uzantıları başarıyla güncellendi</system:String>
|
||||
<system:String x:Key="wox_plugin_program_suffixes_cannot_empty">Dosya uzantıları boş olamaz</system:String>
|
||||
|
||||
<system:String x:Key="wox_plugin_program_run_as_administrator">Yönetici Olarak Çalıştır</system:String>
|
||||
<system:String x:Key="wox_plugin_program_open_containing_folder">İçeren Klasörü Aç</system:String>
|
||||
|
||||
<system:String x:Key="wox_plugin_program_plugin_name">Program</system:String>
|
||||
<system:String x:Key="wox_plugin_program_plugin_description">Programları Wox'tan arayın</system:String>
|
||||
|
||||
<system:String x:Key="wox_plugin_program_invalid_path">Geçersiz Konum</system:String>
|
||||
|
||||
</ResourceDictionary>
|
||||
@@ -0,0 +1,39 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||
|
||||
<!--Program setting-->
|
||||
<system:String x:Key="wox_plugin_program_delete">删除</system:String>
|
||||
<system:String x:Key="wox_plugin_program_edit">编辑</system:String>
|
||||
<system:String x:Key="wox_plugin_program_add">增加</system:String>
|
||||
<system:String x:Key="wox_plugin_program_location">位置</system:String>
|
||||
<system:String x:Key="wox_plugin_program_suffixes">索引文件后缀</system:String>
|
||||
<system:String x:Key="wox_plugin_program_reindex">重新索引</system:String>
|
||||
<system:String x:Key="wox_plugin_program_indexing">索引中</system:String>
|
||||
<system:String x:Key="wox_plugin_program_index_start">索引开始菜单</system:String>
|
||||
<system:String x:Key="wox_plugin_program_index_registry">索引注册表</system:String>
|
||||
<system:String x:Key="wox_plugin_program_suffixes_header">后缀</system:String>
|
||||
<system:String x:Key="wox_plugin_program_max_depth_header">最大深度</system:String>
|
||||
|
||||
<system:String x:Key="wox_plugin_program_directory">目录</system:String>
|
||||
<system:String x:Key="wox_plugin_program_browse">浏览</system:String>
|
||||
<system:String x:Key="wox_plugin_program_file_suffixes">文件后缀</system:String>
|
||||
<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_update">更新</system:String>
|
||||
<system:String x:Key="wox_plugin_program_only_index_tip">Wox仅索引下列后缀的文件:</system:String>
|
||||
<system:String x:Key="wox_plugin_program_split_by_tip">(每个后缀以英文状态下的分号分隔)</system:String>
|
||||
<system:String x:Key="wox_plugin_program_update_file_suffixes">成功更新索引文件后缀</system:String>
|
||||
<system:String x:Key="wox_plugin_program_suffixes_cannot_empty">文件后缀不能为空</system:String>
|
||||
|
||||
<system:String x:Key="wox_plugin_program_run_as_administrator">以管理员身份运行</system:String>
|
||||
<system:String x:Key="wox_plugin_program_open_containing_folder">打开所属文件夹</system:String>
|
||||
|
||||
<system:String x:Key="wox_plugin_program_plugin_name">程序</system:String>
|
||||
<system:String x:Key="wox_plugin_program_plugin_description">在Wox中搜索程序</system:String>
|
||||
|
||||
<system:String x:Key="wox_plugin_program_invalid_path">无效路径</system:String>
|
||||
|
||||
</ResourceDictionary>
|
||||
@@ -0,0 +1,36 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||
|
||||
<!--Program setting-->
|
||||
<system:String x:Key="wox_plugin_program_delete">刪除</system:String>
|
||||
<system:String x:Key="wox_plugin_program_edit">編輯</system:String>
|
||||
<system:String x:Key="wox_plugin_program_add">新增</system:String>
|
||||
<system:String x:Key="wox_plugin_program_location">路徑</system:String>
|
||||
<system:String x:Key="wox_plugin_program_suffixes">索引副檔名清單</system:String>
|
||||
<system:String x:Key="wox_plugin_program_reindex">重新建立索引</system:String>
|
||||
<system:String x:Key="wox_plugin_program_indexing">正在建立索引</system:String>
|
||||
<system:String x:Key="wox_plugin_program_index_start">替開始選單建立索引</system:String>
|
||||
<system:String x:Key="wox_plugin_program_index_registry">替登錄檔建立索引</system:String>
|
||||
<system:String x:Key="wox_plugin_program_suffixes_header">副檔名</system:String>
|
||||
<system:String x:Key="wox_plugin_program_max_depth_header">最大深度</system:String>
|
||||
|
||||
<system:String x:Key="wox_plugin_program_directory">目錄</system:String>
|
||||
<system:String x:Key="wox_plugin_program_browse">瀏覽</system:String>
|
||||
<system:String x:Key="wox_plugin_program_file_suffixes">副檔名</system:String>
|
||||
<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_update">更新</system:String>
|
||||
<system:String x:Key="wox_plugin_program_only_index_tip">Wox 僅索引下列副檔名的檔案:</system:String>
|
||||
<system:String x:Key="wox_plugin_program_split_by_tip">(每個副檔名以英文的分號分隔)</system:String>
|
||||
<system:String x:Key="wox_plugin_program_update_file_suffixes">成功更新索引副檔名清單</system:String>
|
||||
<system:String x:Key="wox_plugin_program_suffixes_cannot_empty">副檔名不能為空</system:String>
|
||||
|
||||
<system:String x:Key="wox_plugin_program_run_as_administrator">以系統管理員身分執行</system:String>
|
||||
<system:String x:Key="wox_plugin_program_open_containing_folder">開啟檔案位置</system:String>
|
||||
|
||||
<system:String x:Key="wox_plugin_program_plugin_name">程式</system:String>
|
||||
<system:String x:Key="wox_plugin_program_plugin_description">在 Wox 中搜尋程式</system:String>
|
||||
</ResourceDictionary>
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Markup;
|
||||
|
||||
namespace Wox.Plugin.Program
|
||||
{
|
||||
public class LocationConverter : MarkupExtension, IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
var text = value as string;
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override object ProvideValue(IServiceProvider serviceProvider)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
using NLog;
|
||||
using NLog.Config;
|
||||
using NLog.Targets;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security;
|
||||
using Wox.Infrastructure;
|
||||
|
||||
namespace Wox.Plugin.Program.Logger
|
||||
{
|
||||
/// <summary>
|
||||
/// The Program plugin has seen many issues recorded in the Wox repo related to various loading of Windows programs.
|
||||
/// This is a dedicated logger for this Program plugin with the aim to output a more friendlier message and clearer
|
||||
/// log that will allow debugging to be quicker and easier.
|
||||
/// </summary>
|
||||
internal static class ProgramLogger
|
||||
{
|
||||
public const string DirectoryName = "Logs";
|
||||
|
||||
static ProgramLogger()
|
||||
{
|
||||
var path = Path.Combine(Constant.DataDirectory, DirectoryName, Constant.Version);
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
}
|
||||
|
||||
var configuration = new LoggingConfiguration();
|
||||
var target = new FileTarget();
|
||||
configuration.AddTarget("file", target);
|
||||
target.FileName = path.Replace(@"\", "/") + "/${shortdate}.txt";
|
||||
#if DEBUG
|
||||
var rule = new LoggingRule("*", LogLevel.Debug, target);
|
||||
#else
|
||||
var rule = new LoggingRule("*", LogLevel.Error, target);
|
||||
#endif
|
||||
configuration.LoggingRules.Add(rule);
|
||||
LogManager.Configuration = configuration;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs an exception
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.Synchronized)]
|
||||
internal static void LogException(string classname, string callingMethodName, string loadingProgramPath,
|
||||
string interpretationMessage, Exception e)
|
||||
{
|
||||
Debug.WriteLine($"ERROR{classname}|{callingMethodName}|{loadingProgramPath}|{interpretationMessage}");
|
||||
|
||||
var logger = LogManager.GetLogger("");
|
||||
|
||||
var innerExceptionNumber = 1;
|
||||
|
||||
var possibleResolution = "Not yet known";
|
||||
var errorStatus = "UNKNOWN";
|
||||
|
||||
logger.Error("------------- BEGIN Wox.Plugin.Program exception -------------");
|
||||
|
||||
do
|
||||
{
|
||||
if (IsKnownWinProgramError(e, callingMethodName) || IsKnownUWPProgramError(e, callingMethodName))
|
||||
{
|
||||
possibleResolution = "Can be ignored and Wox should still continue, however the program may not be loaded";
|
||||
errorStatus = "KNOWN";
|
||||
}
|
||||
|
||||
var calledMethod = e.TargetSite != null ? e.TargetSite.ToString() : e.StackTrace;
|
||||
|
||||
calledMethod = string.IsNullOrEmpty(calledMethod) ? "Not available" : calledMethod;
|
||||
|
||||
logger.Error($"\nException full name: {e.GetType().FullName}"
|
||||
+ $"\nError status: {errorStatus}"
|
||||
+ $"\nClass name: {classname}"
|
||||
+ $"\nCalling method: {callingMethodName}"
|
||||
+ $"\nProgram path: {loadingProgramPath}"
|
||||
+ $"\nInnerException number: {innerExceptionNumber}"
|
||||
+ $"\nException message: {e.Message}"
|
||||
+ $"\nException error type: HResult {e.HResult}"
|
||||
+ $"\nException thrown in called method: {calledMethod}"
|
||||
+ $"\nPossible interpretation of the error: {interpretationMessage}"
|
||||
+ $"\nPossible resolution: {possibleResolution}");
|
||||
|
||||
innerExceptionNumber++;
|
||||
e = e.InnerException;
|
||||
} while (e != null);
|
||||
|
||||
logger.Error("------------- END Wox.Plugin.Program exception -------------");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Please follow exception format: |class name|calling method name|loading program path|user friendly message that explains the error
|
||||
/// => Example: |Win32|LnkProgram|c:\..\chrome.exe|Permission denied on directory, but Wox should continue
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.Synchronized)]
|
||||
internal static void LogException(string message, Exception e)
|
||||
{
|
||||
//Index 0 is always empty.
|
||||
var parts = message.Split('|');
|
||||
if (parts.Length < 4)
|
||||
{
|
||||
var logger = LogManager.GetLogger("");
|
||||
logger.Error(e, $"fail to log exception in program logger, parts length is too small: {parts.Length}, message: {message}");
|
||||
}
|
||||
|
||||
var classname = parts[1];
|
||||
var callingMethodName = parts[2];
|
||||
var loadingProgramPath = parts[3];
|
||||
var interpretationMessage = parts[4];
|
||||
|
||||
LogException(classname, callingMethodName, loadingProgramPath, interpretationMessage, e);
|
||||
}
|
||||
|
||||
private static bool IsKnownWinProgramError(Exception e, string callingMethodName)
|
||||
{
|
||||
if (e.TargetSite?.Name == "GetDescription" && callingMethodName == "LnkProgram")
|
||||
return true;
|
||||
|
||||
if (e is SecurityException || e is UnauthorizedAccessException || e is DirectoryNotFoundException)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsKnownUWPProgramError(Exception e, string callingMethodName)
|
||||
{
|
||||
if (((e.HResult == -2147024774 || e.HResult == -2147009769) && callingMethodName == "ResourceFromPri")
|
||||
|| (e.HResult == -2147024894 && (callingMethodName == "LogoPathFromUri" || callingMethodName == "ImageFromPath"))
|
||||
|| (e.HResult == -2147024864 && callingMethodName == "InitializeAppInfo"))
|
||||
return true;
|
||||
|
||||
if (callingMethodName == "XmlNamespaces")
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
214
src/modules/launcher/Plugins/Wox.Plugin.Program/Main.cs
Normal file
214
src/modules/launcher/Plugins/Wox.Plugin.Program/Main.cs
Normal file
@@ -0,0 +1,214 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
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
|
||||
{
|
||||
public class Main : ISettingProvider, IPlugin, IPluginI18n, IContextMenu, ISavable, IReloadable
|
||||
{
|
||||
private static readonly object IndexLock = new object();
|
||||
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 readonly PluginJsonStorage<Settings> _settingsStorage;
|
||||
|
||||
public Main()
|
||||
{
|
||||
_settingsStorage = new PluginJsonStorage<Settings>();
|
||||
_settings = _settingsStorage.Load();
|
||||
|
||||
Stopwatch.Normal("|Wox.Plugin.Program.Main|Preload programs cost", () =>
|
||||
{
|
||||
_win32Storage = new BinaryStorage<Win32[]>("Win32");
|
||||
_win32s = _win32Storage.TryLoad(new Win32[] { });
|
||||
_uwpStorage = new BinaryStorage<UWP.Application[]>("UWP");
|
||||
_uwps = _uwpStorage.TryLoad(new UWP.Application[] { });
|
||||
});
|
||||
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}>");
|
||||
|
||||
var a = Task.Run(() =>
|
||||
{
|
||||
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()
|
||||
{
|
||||
_settingsStorage.Save();
|
||||
_win32Storage.Save(_win32s);
|
||||
_uwpStorage.Save(_uwps);
|
||||
}
|
||||
|
||||
public List<Result> Query(Query query)
|
||||
{
|
||||
Win32[] win32;
|
||||
UWP.Application[] uwps;
|
||||
|
||||
lock (IndexLock)
|
||||
{ // just take the reference inside the lock to eliminate query time issues.
|
||||
win32 = _win32s;
|
||||
uwps = _uwps;
|
||||
}
|
||||
|
||||
var results1 = win32.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 != null && r.Score > 0).ToList();
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Init(PluginInitContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public static void IndexWin32Programs()
|
||||
{
|
||||
var win32S = Win32.All(_settings);
|
||||
lock (IndexLock)
|
||||
{
|
||||
_win32s = win32S;
|
||||
}
|
||||
}
|
||||
|
||||
public static void IndexUWPPrograms()
|
||||
{
|
||||
var windows10 = new Version(10, 0);
|
||||
var support = Environment.OSVersion.Version.Major >= windows10.Major;
|
||||
|
||||
var applications = support ? UWP.All() : new UWP.Application[] { };
|
||||
lock (IndexLock)
|
||||
{
|
||||
_uwps = applications;
|
||||
}
|
||||
}
|
||||
|
||||
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, _win32s, _uwps);
|
||||
}
|
||||
|
||||
public string GetTranslatedPluginTitle()
|
||||
{
|
||||
return _context.API.GetTranslation("wox_plugin_program_plugin_name");
|
||||
}
|
||||
|
||||
public string GetTranslatedPluginDescription()
|
||||
{
|
||||
return _context.API.GetTranslation("wox_plugin_program_plugin_description");
|
||||
}
|
||||
|
||||
public List<Result> LoadContextMenus(Result selectedResult)
|
||||
{
|
||||
var menuOptions = new List<Result>();
|
||||
var program = selectedResult.ContextData as IProgram;
|
||||
if (program != null)
|
||||
{
|
||||
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 void StartProcess(Func<ProcessStartInfo, Process> runProcess, ProcessStartInfo info)
|
||||
{
|
||||
bool hide;
|
||||
try
|
||||
{
|
||||
runProcess(info);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
var name = "Plugin: Program";
|
||||
var message = $"Unable to start: {info.FileName}";
|
||||
_context.API.ShowMsg(name, message, string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public void ReloadData()
|
||||
{
|
||||
IndexPrograms();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<Window x:Class="Wox.Plugin.Program.ProgramSuffixes"
|
||||
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"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
mc:Ignorable="d"
|
||||
Width="400"
|
||||
Height="180"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
d:DesignHeight="400" d:DesignWidth="300">
|
||||
<StackPanel>
|
||||
<TextBlock Margin="10 10 0 0" Foreground="Gray" Text="{DynamicResource wox_plugin_program_only_index_tip}" />
|
||||
<TextBlock Margin="10 10 0 0" Foreground="Gray" Text="{DynamicResource wox_plugin_program_split_by_tip}" />
|
||||
<TextBox x:Name="tbSuffixes" Margin="10"/>
|
||||
<Button HorizontalAlignment="Right" Height="30" Width="80" Click="ButtonBase_OnClick" Margin="10" Content="{DynamicResource wox_plugin_program_update}" />
|
||||
</StackPanel>
|
||||
</Window>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
using System.Windows;
|
||||
|
||||
namespace Wox.Plugin.Program
|
||||
{
|
||||
/// <summary>
|
||||
/// ProgramSuffixes.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class ProgramSuffixes
|
||||
{
|
||||
private PluginInitContext context;
|
||||
private Settings _settings;
|
||||
|
||||
public ProgramSuffixes(PluginInitContext context, Settings settings)
|
||||
{
|
||||
this.context = context;
|
||||
InitializeComponent();
|
||||
_settings = settings;
|
||||
tbSuffixes.Text = string.Join(Settings.SuffixSeperator.ToString(), _settings.ProgramSuffixes);
|
||||
}
|
||||
|
||||
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (string.IsNullOrEmpty(tbSuffixes.Text))
|
||||
{
|
||||
string warning = context.API.GetTranslation("wox_plugin_program_suffixes_cannot_empty");
|
||||
MessageBox.Show(warning);
|
||||
return;
|
||||
}
|
||||
|
||||
_settings.ProgramSuffixes = tbSuffixes.Text.Split(Settings.SuffixSeperator);
|
||||
string msg = context.API.GetTranslation("wox_plugin_program_update_file_suffixes");
|
||||
MessageBox.Show(msg);
|
||||
|
||||
DialogResult = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Wox.Infrastructure.Storage.UserSettings;
|
||||
|
||||
namespace Wox.Plugin.SystemPlugins.Program.ProgramSources {
|
||||
//TODO: Consider Removing
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class FileSystemFolderSourceShallow : FileSystemProgramSource {
|
||||
//private static Dictionary<string, DirectoryInfo[]> parentDirectories = new Dictionary<string, DirectoryInfo[]>();
|
||||
|
||||
|
||||
public FileSystemFolderSourceShallow(string baseDirectory)
|
||||
: base(baseDirectory) { }
|
||||
|
||||
public FileSystemFolderSourceShallow(ProgramSource source)
|
||||
: base(source) { }
|
||||
|
||||
public override List<Program> LoadPrograms() {
|
||||
List<Program> list = new List<Program>();
|
||||
|
||||
foreach (var Folder in Directory.GetDirectories(BaseDirectory)) {
|
||||
list.Add(CreateEntry(Folder));
|
||||
}
|
||||
|
||||
|
||||
foreach (string file in Directory.GetFiles(base.BaseDirectory)) {
|
||||
if (Suffixes.Any(o => file.EndsWith("." + o))) {
|
||||
list.Add(CreateEntry(file));
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
public override string ToString() {
|
||||
return typeof(UserStartMenuProgramSource).Name;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
public class FolderSource : IProgramSource {
|
||||
private PluginInitContext context;
|
||||
public string Location { get; set; }
|
||||
public int BonusPoints { get; set; }
|
||||
|
||||
public FolderSource(string Location) {
|
||||
this.Location = Location;
|
||||
}
|
||||
|
||||
public List<Program> LoadPrograms() {
|
||||
List<Result> results = new List<Result>();
|
||||
|
||||
if (Directory.Exists(Location)) {
|
||||
// show all child directory
|
||||
if (Location.EndsWith("\\") || Location.EndsWith("/")) {
|
||||
var dirInfo = new DirectoryInfo(Location);
|
||||
var dirs = dirInfo.GetDirectories();
|
||||
|
||||
var parentDirKey = Location.TrimEnd('\\', '/');
|
||||
if (!parentDirectories.ContainsKey(parentDirKey))
|
||||
parentDirectories.Add(parentDirKey, dirs);
|
||||
|
||||
foreach (var dir in dirs) {
|
||||
if ((dir.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
|
||||
continue;
|
||||
|
||||
var dirPath = dir.FullName;
|
||||
Result result = new Result {
|
||||
Title = dir.Name,
|
||||
IcoPath = "Images/folder.png",
|
||||
Action = (c) => {
|
||||
context.ChangeQuery(dirPath);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
results.Add(result);
|
||||
}
|
||||
|
||||
if (results.Count == 0) {
|
||||
Result result = new Result {
|
||||
Title = "Open this directory",
|
||||
SubTitle = "No files in this directory",
|
||||
IcoPath = "Images/folder.png",
|
||||
Action = (c) => {
|
||||
Process.Start(Location);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
results.Add(result);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Result result = new Result {
|
||||
Title = "Open this directory",
|
||||
SubTitle = string.Format("path: {0}", Location),
|
||||
Score = 50,
|
||||
IcoPath = "Images/folder.png",
|
||||
Action = (c) => {
|
||||
Process.Start(Location);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
results.Add(result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// change to search in current directory
|
||||
var parentDir = Path.GetDirectoryName(Location);
|
||||
if (!string.IsNullOrEmpty(parentDir) && results.Count == 0) {
|
||||
parentDir = parentDir.TrimEnd('\\', '/');
|
||||
if (parentDirectories.ContainsKey(parentDir)) {
|
||||
|
||||
var dirs = parentDirectories[parentDir];
|
||||
var queryFileName = Path.GetFileName(Location).ToLower();
|
||||
var fuzzy = FuzzyMatcher.Create(queryFileName);
|
||||
foreach (var dir in dirs) {
|
||||
if ((dir.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
|
||||
continue;
|
||||
|
||||
var matchResult = fuzzy.Evaluate(dir.Name);
|
||||
if (!matchResult.Success)
|
||||
continue;
|
||||
|
||||
var dirPath = dir.FullName;
|
||||
Result result = new Result {
|
||||
Title = dir.Name,
|
||||
IcoPath = "Images/folder.png",
|
||||
Score = matchResult.Score,
|
||||
Action = (c) => {
|
||||
context.ChangeQuery(dirPath);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
results.Add(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
throw new Exception("Debug this!");
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Wox.Plugin.Program.Programs
|
||||
{
|
||||
public interface IProgram
|
||||
{
|
||||
List<Result> ContextMenus(IPublicAPI api);
|
||||
Result Result(string query, IPublicAPI api);
|
||||
string UniqueIdentifier { get; set; }
|
||||
string Name { get; }
|
||||
string Location { get; }
|
||||
}
|
||||
}
|
||||
634
src/modules/launcher/Plugins/Wox.Plugin.Program/Programs/UWP.cs
Normal file
634
src/modules/launcher/Plugins/Wox.Plugin.Program/Programs/UWP.cs
Normal file
@@ -0,0 +1,634 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Xml.Linq;
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.Management.Deployment;
|
||||
using AppxPackaing;
|
||||
using Shell;
|
||||
using Wox.Infrastructure;
|
||||
using Wox.Plugin.Program.Logger;
|
||||
using IStream = AppxPackaing.IStream;
|
||||
using Rect = System.Windows.Rect;
|
||||
|
||||
namespace Wox.Plugin.Program.Programs
|
||||
{
|
||||
[Serializable]
|
||||
public class UWP
|
||||
{
|
||||
public string Name { get; }
|
||||
public string FullName { get; }
|
||||
public string FamilyName { get; }
|
||||
public string Location { get; set; }
|
||||
|
||||
public Application[] Apps { get; set; }
|
||||
|
||||
public PackageVersion Version { get; set; }
|
||||
|
||||
public UWP(Package package)
|
||||
{
|
||||
Location = package.InstalledLocation.Path;
|
||||
Name = package.Id.Name;
|
||||
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()
|
||||
{
|
||||
var path = Path.Combine(Location, "AppxManifest.xml");
|
||||
|
||||
var namespaces = XmlNamespaces(path);
|
||||
InitPackageVersion(namespaces);
|
||||
|
||||
var appxFactory = new AppxFactory();
|
||||
IStream stream;
|
||||
const uint noAttribute = 0x80;
|
||||
const Stgm exclusiveRead = Stgm.Read | Stgm.ShareExclusive;
|
||||
var hResult = SHCreateStreamOnFileEx(path, exclusiveRead, noAttribute, false, null, out stream);
|
||||
|
||||
if (hResult == Hresult.Ok)
|
||||
{
|
||||
var reader = appxFactory.CreateManifestReader(stream);
|
||||
var manifestApps = reader.GetApplications();
|
||||
var apps = new List<Application>();
|
||||
while (manifestApps.GetHasCurrent() != 0)
|
||||
{
|
||||
var manifestApp = manifestApps.GetCurrent();
|
||||
var appListEntry = manifestApp.GetStringValue("AppListEntry");
|
||||
if (appListEntry != "none")
|
||||
{
|
||||
var app = new Application(manifestApp, this);
|
||||
apps.Add(app);
|
||||
}
|
||||
manifestApps.MoveNext();
|
||||
}
|
||||
Apps = apps.Where(a => a.AppListEntry != "none").ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
var e = Marshal.GetExceptionForHR((int)hResult);
|
||||
ProgramLogger.LogException($"|UWP|InitializeAppInfo|{path}" +
|
||||
"|Error caused while trying to get the details of the UWP program", e);
|
||||
|
||||
Apps = new List<Application>().ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// http://www.hanselman.com/blog/GetNamespacesFromAnXMLDocumentWithXPathDocumentAndLINQToXML.aspx
|
||||
private string[] XmlNamespaces(string path)
|
||||
{
|
||||
XDocument z = XDocument.Load(path);
|
||||
if (z.Root != null)
|
||||
{
|
||||
var namespaces = z.Root.Attributes().
|
||||
Where(a => a.IsNamespaceDeclaration).
|
||||
GroupBy(
|
||||
a => a.Name.Namespace == XNamespace.None ? string.Empty : a.Name.LocalName,
|
||||
a => XNamespace.Get(a.Value)
|
||||
).Select(
|
||||
g => g.First().ToString()
|
||||
).ToArray();
|
||||
return namespaces;
|
||||
}
|
||||
else
|
||||
{
|
||||
ProgramLogger.LogException($"|UWP|XmlNamespaces|{path}" +
|
||||
$"|Error occured while trying to get the XML from {path}", new ArgumentNullException());
|
||||
|
||||
return new string[] { };
|
||||
}
|
||||
}
|
||||
|
||||
private void InitPackageVersion(string[] namespaces)
|
||||
{
|
||||
var versionFromNamespace = new Dictionary<string, PackageVersion>
|
||||
{
|
||||
{"http://schemas.microsoft.com/appx/manifest/foundation/windows10", PackageVersion.Windows10},
|
||||
{"http://schemas.microsoft.com/appx/2013/manifest", PackageVersion.Windows81},
|
||||
{"http://schemas.microsoft.com/appx/2010/manifest", PackageVersion.Windows8},
|
||||
};
|
||||
|
||||
foreach (var n in versionFromNamespace.Keys)
|
||||
{
|
||||
if (namespaces.Contains(n))
|
||||
{
|
||||
Version = versionFromNamespace[n];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ProgramLogger.LogException($"|UWP|XmlNamespaces|{Location}" +
|
||||
"|Trying to get the package version of the UWP program, but a unknown UWP appmanifest version "
|
||||
+ $"{FullName} from location {Location} is returned.", new FormatException());
|
||||
|
||||
Version = PackageVersion.Unknown;
|
||||
}
|
||||
|
||||
public static Application[] All()
|
||||
{
|
||||
var windows10 = new Version(10, 0);
|
||||
var support = Environment.OSVersion.Version.Major >= windows10.Major;
|
||||
if (support)
|
||||
{
|
||||
var applications = CurrentUserPackages().AsParallel().SelectMany(p =>
|
||||
{
|
||||
UWP u;
|
||||
try
|
||||
{
|
||||
u = new UWP(p);
|
||||
}
|
||||
#if !DEBUG
|
||||
catch (Exception e)
|
||||
{
|
||||
ProgramLogger.LogException($"|UWP|All|{p.InstalledLocation}|An unexpected error occured and "
|
||||
+ $"unable to convert Package to UWP for {p.Id.FullName}", e);
|
||||
return new Application[] { };
|
||||
}
|
||||
#endif
|
||||
#if DEBUG //make developer aware and implement handling
|
||||
catch
|
||||
{
|
||||
throw;
|
||||
}
|
||||
#endif
|
||||
return u.Apps;
|
||||
}).ToArray();
|
||||
|
||||
var updatedListWithoutDisabledApps = applications
|
||||
.Where(t1 => !Main._settings.DisabledProgramSources
|
||||
.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier))
|
||||
.Select(x => x);
|
||||
|
||||
return updatedListWithoutDisabledApps.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Application[] { };
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<Package> CurrentUserPackages()
|
||||
{
|
||||
var u = WindowsIdentity.GetCurrent().User;
|
||||
|
||||
if (u != null)
|
||||
{
|
||||
var id = u.Value;
|
||||
var m = new PackageManager();
|
||||
var ps = m.FindPackagesForUser(id);
|
||||
ps = ps.Where(p =>
|
||||
{
|
||||
bool valid;
|
||||
try
|
||||
{
|
||||
var f = p.IsFramework;
|
||||
var d = p.IsDevelopmentMode;
|
||||
var path = p.InstalledLocation.Path;
|
||||
valid = !f && !d && !string.IsNullOrEmpty(path);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ProgramLogger.LogException("UWP" ,"CurrentUserPackages", $"id","An unexpected error occured and "
|
||||
+ $"unable to verify if package is valid", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return valid;
|
||||
});
|
||||
return ps;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Package[] { };
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return FamilyName;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is UWP uwp)
|
||||
{
|
||||
return FamilyName.Equals(uwp.FamilyName);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return FamilyName.GetHashCode();
|
||||
}
|
||||
|
||||
[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; }
|
||||
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; }
|
||||
|
||||
private int Score(string query)
|
||||
{
|
||||
var displayNameMatch = StringMatcher.FuzzySearch(query, DisplayName);
|
||||
var descriptionMatch = StringMatcher.FuzzySearch(query, Description);
|
||||
var score = new[] { displayNameMatch.Score, descriptionMatch.Score }.Max();
|
||||
return score;
|
||||
}
|
||||
|
||||
public Result Result(string query, IPublicAPI api)
|
||||
{
|
||||
var score = Score(query);
|
||||
if (score <= 0)
|
||||
{ // no need to create result if score is 0
|
||||
return null;
|
||||
}
|
||||
|
||||
var result = new Result
|
||||
{
|
||||
SubTitle = Package.Location,
|
||||
Icon = Logo,
|
||||
Score = score,
|
||||
ContextData = this,
|
||||
Action = e =>
|
||||
{
|
||||
Launch(api);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
if (Description.Length >= DisplayName.Length &&
|
||||
Description.Substring(0, DisplayName.Length) == DisplayName)
|
||||
{
|
||||
result.Title = Description;
|
||||
result.TitleHighlightData = StringMatcher.FuzzySearch(query, Description).MatchData;
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(Description))
|
||||
{
|
||||
var title = $"{DisplayName}: {Description}";
|
||||
result.Title = title;
|
||||
result.TitleHighlightData = StringMatcher.FuzzySearch(query, title).MatchData;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Title = DisplayName;
|
||||
result.TitleHighlightData = StringMatcher.FuzzySearch(query, DisplayName).MatchData;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<Result> ContextMenus(IPublicAPI api)
|
||||
{
|
||||
var contextMenus = new List<Result>
|
||||
{
|
||||
new Result
|
||||
{
|
||||
Title = api.GetTranslation("wox_plugin_program_open_containing_folder"),
|
||||
|
||||
Action = _ =>
|
||||
{
|
||||
Main.StartProcess(Process.Start, new ProcessStartInfo(Package.Location));
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
IcoPath = "Images/folder.png"
|
||||
}
|
||||
};
|
||||
return contextMenus;
|
||||
}
|
||||
|
||||
private async void Launch(IPublicAPI api)
|
||||
{
|
||||
var appManager = new ApplicationActivationManager();
|
||||
uint unusedPid;
|
||||
const string noArgs = "";
|
||||
const ACTIVATEOPTIONS noFlags = ACTIVATEOPTIONS.AO_NONE;
|
||||
await Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
appManager.ActivateApplication(UserModelId, noArgs, noFlags, out unusedPid);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
var name = "Plugin: Program";
|
||||
var message = $"Can't start UWP: {DisplayName}";
|
||||
api.ShowMsg(name, message, string.Empty);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Application(IAppxManifestApplication manifestApp, UWP package)
|
||||
{
|
||||
UserModelId = manifestApp.GetAppUserModelId();
|
||||
UniqueIdentifier = manifestApp.GetAppUserModelId();
|
||||
DisplayName = manifestApp.GetStringValue("DisplayName");
|
||||
Description = manifestApp.GetStringValue("Description");
|
||||
BackgroundColor = manifestApp.GetStringValue("BackgroundColor");
|
||||
Package = package;
|
||||
|
||||
DisplayName = ResourceFromPri(package.FullName, DisplayName);
|
||||
Description = ResourceFromPri(package.FullName, Description);
|
||||
LogoUri = LogoUriFromManifest(manifestApp);
|
||||
LogoPath = LogoPathFromUri(LogoUri);
|
||||
|
||||
Enabled = true;
|
||||
}
|
||||
|
||||
internal string ResourceFromPri(string packageFullName, string resourceReference)
|
||||
{
|
||||
const string prefix = "ms-resource:";
|
||||
if (!string.IsNullOrWhiteSpace(resourceReference) && resourceReference.StartsWith(prefix))
|
||||
{
|
||||
// magic comes from @talynone
|
||||
// https://github.com/talynone/Wox.Plugin.WindowsUniversalAppLauncher/blob/master/StoreAppLauncher/Helpers/NativeApiHelper.cs#L139-L153
|
||||
string key = resourceReference.Substring(prefix.Length);
|
||||
string parsed;
|
||||
if (key.StartsWith("//"))
|
||||
{
|
||||
parsed = prefix + key;
|
||||
}
|
||||
else if (key.StartsWith("/"))
|
||||
{
|
||||
parsed = prefix + "//" + key;
|
||||
}
|
||||
else
|
||||
{
|
||||
parsed = prefix + "///resources/" + key;
|
||||
}
|
||||
|
||||
var outBuffer = new StringBuilder(128);
|
||||
string source = $"@{{{packageFullName}? {parsed}}}";
|
||||
var capacity = (uint)outBuffer.Capacity;
|
||||
var hResult = SHLoadIndirectString(source, outBuffer, capacity, IntPtr.Zero);
|
||||
if (hResult == Hresult.Ok)
|
||||
{
|
||||
var loaded = outBuffer.ToString();
|
||||
if (!string.IsNullOrEmpty(loaded))
|
||||
{
|
||||
return loaded;
|
||||
}
|
||||
else
|
||||
{
|
||||
ProgramLogger.LogException($"|UWP|ResourceFromPri|{Package.Location}|Can't load null or empty result "
|
||||
+ $"pri {source} in uwp location {Package.Location}", new NullReferenceException());
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// https://github.com/Wox-launcher/Wox/issues/964
|
||||
// known hresult 2147942522:
|
||||
// 'Microsoft Corporation' violates pattern constraint of '\bms-resource:.{1,256}'.
|
||||
// for
|
||||
// Microsoft.MicrosoftOfficeHub_17.7608.23501.0_x64__8wekyb3d8bbwe: ms-resource://Microsoft.MicrosoftOfficeHub/officehubintl/AppManifest_GetOffice_Description
|
||||
// Microsoft.BingFoodAndDrink_3.0.4.336_x64__8wekyb3d8bbwe: ms-resource:AppDescription
|
||||
var e = Marshal.GetExceptionForHR((int)hResult);
|
||||
ProgramLogger.LogException($"|UWP|ResourceFromPri|{Package.Location}|Load pri failed {source} with HResult {hResult} and location {Package.Location}", e);
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return resourceReference;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal string LogoUriFromManifest(IAppxManifestApplication app)
|
||||
{
|
||||
var logoKeyFromVersion = new Dictionary<PackageVersion, string>
|
||||
{
|
||||
{ PackageVersion.Windows10, "Square44x44Logo" },
|
||||
{ PackageVersion.Windows81, "Square30x30Logo" },
|
||||
{ PackageVersion.Windows8, "SmallLogo" },
|
||||
};
|
||||
if (logoKeyFromVersion.ContainsKey(Package.Version))
|
||||
{
|
||||
var key = logoKeyFromVersion[Package.Version];
|
||||
var logoUri = app.GetStringValue(key);
|
||||
return logoUri;
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
internal string LogoPathFromUri(string uri)
|
||||
{
|
||||
// all https://msdn.microsoft.com/windows/uwp/controls-and-patterns/tiles-and-notifications-app-assets
|
||||
// windows 10 https://msdn.microsoft.com/en-us/library/windows/apps/dn934817.aspx
|
||||
// windows 8.1 https://msdn.microsoft.com/en-us/library/windows/apps/hh965372.aspx#target_size
|
||||
// windows 8 https://msdn.microsoft.com/en-us/library/windows/apps/br211475.aspx
|
||||
|
||||
string path;
|
||||
if (uri.Contains("\\"))
|
||||
{
|
||||
path = Path.Combine(Package.Location, uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
// for C:\Windows\MiracastView etc
|
||||
path = Path.Combine(Package.Location, "Assets", uri);
|
||||
}
|
||||
|
||||
var extension = Path.GetExtension(path);
|
||||
if (extension != null)
|
||||
{
|
||||
var end = path.Length - extension.Length;
|
||||
var prefix = path.Substring(0, end);
|
||||
var paths = new List<string> { path };
|
||||
|
||||
var scaleFactors = new Dictionary<PackageVersion, List<int>>
|
||||
{
|
||||
// scale factors on win10: https://docs.microsoft.com/en-us/windows/uwp/controls-and-patterns/tiles-and-notifications-app-assets#asset-size-tables,
|
||||
{ PackageVersion.Windows10, new List<int> { 100, 125, 150, 200, 400 } },
|
||||
{ PackageVersion.Windows81, new List<int> { 100, 120, 140, 160, 180 } },
|
||||
{ PackageVersion.Windows8, new List<int> { 100 } }
|
||||
};
|
||||
|
||||
if (scaleFactors.ContainsKey(Package.Version))
|
||||
{
|
||||
foreach (var factor in scaleFactors[Package.Version])
|
||||
{
|
||||
paths.Add($"{prefix}.scale-{factor}{extension}");
|
||||
}
|
||||
}
|
||||
|
||||
var selected = paths.FirstOrDefault(File.Exists);
|
||||
if (!string.IsNullOrEmpty(selected))
|
||||
{
|
||||
return selected;
|
||||
}
|
||||
else
|
||||
{
|
||||
ProgramLogger.LogException($"|UWP|LogoPathFromUri|{Package.Location}" +
|
||||
$"|{UserModelId} can't find logo uri for {uri} in package location: {Package.Location}", new FileNotFoundException());
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ProgramLogger.LogException($"|UWP|LogoPathFromUri|{Package.Location}" +
|
||||
$"|Unable to find extension from {uri} for {UserModelId} " +
|
||||
$"in package location {Package.Location}", new FileNotFoundException());
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public ImageSource Logo()
|
||||
{
|
||||
var logo = ImageFromPath(LogoPath);
|
||||
var plated = PlatedImage(logo);
|
||||
|
||||
// todo magic! temp fix for cross thread object
|
||||
plated.Freeze();
|
||||
return plated;
|
||||
}
|
||||
|
||||
|
||||
private BitmapImage ImageFromPath(string path)
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
var image = new BitmapImage(new Uri(path));
|
||||
return image;
|
||||
}
|
||||
else
|
||||
{
|
||||
ProgramLogger.LogException($"|UWP|ImageFromPath|{path}" +
|
||||
$"|Unable to get logo for {UserModelId} from {path} and" +
|
||||
$" located in {Package.Location}", new FileNotFoundException());
|
||||
return new BitmapImage(new Uri(Constant.ErrorIcon));
|
||||
}
|
||||
}
|
||||
|
||||
private ImageSource PlatedImage(BitmapImage image)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(BackgroundColor) && BackgroundColor != "transparent")
|
||||
{
|
||||
var width = image.Width;
|
||||
var height = image.Height;
|
||||
var x = 0;
|
||||
var y = 0;
|
||||
|
||||
var group = new DrawingGroup();
|
||||
|
||||
var converted = ColorConverter.ConvertFromString(BackgroundColor);
|
||||
if (converted != null)
|
||||
{
|
||||
var color = (Color)converted;
|
||||
var brush = new SolidColorBrush(color);
|
||||
var pen = new Pen(brush, 1);
|
||||
var backgroundArea = new Rect(0, 0, width, width);
|
||||
var rectabgle = new RectangleGeometry(backgroundArea);
|
||||
var rectDrawing = new GeometryDrawing(brush, pen, rectabgle);
|
||||
group.Children.Add(rectDrawing);
|
||||
|
||||
var imageArea = new Rect(x, y, image.Width, image.Height);
|
||||
var imageDrawing = new ImageDrawing(image, imageArea);
|
||||
group.Children.Add(imageDrawing);
|
||||
|
||||
// http://stackoverflow.com/questions/6676072/get-system-drawing-bitmap-of-a-wpf-area-using-visualbrush
|
||||
var visual = new DrawingVisual();
|
||||
var context = visual.RenderOpen();
|
||||
context.DrawDrawing(group);
|
||||
context.Close();
|
||||
const int dpiScale100 = 96;
|
||||
var bitmap = new RenderTargetBitmap(
|
||||
Convert.ToInt32(width), Convert.ToInt32(height),
|
||||
dpiScale100, dpiScale100,
|
||||
PixelFormats.Pbgra32
|
||||
);
|
||||
bitmap.Render(visual);
|
||||
return bitmap;
|
||||
}
|
||||
else
|
||||
{
|
||||
ProgramLogger.LogException($"|UWP|PlatedImage|{Package.Location}" +
|
||||
$"|Unable to convert background string {BackgroundColor} " +
|
||||
$"to color for {Package.Location}", new InvalidOperationException());
|
||||
|
||||
return new BitmapImage(new Uri(Constant.ErrorIcon));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// todo use windows theme as background
|
||||
return image;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{DisplayName}: {Description}";
|
||||
}
|
||||
}
|
||||
|
||||
public enum PackageVersion
|
||||
{
|
||||
Windows10,
|
||||
Windows81,
|
||||
Windows8,
|
||||
Unknown
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum Stgm : uint
|
||||
{
|
||||
Read = 0x0,
|
||||
ShareExclusive = 0x10,
|
||||
}
|
||||
|
||||
private enum Hresult : uint
|
||||
{
|
||||
Ok = 0x0000,
|
||||
}
|
||||
|
||||
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
|
||||
private static extern Hresult SHCreateStreamOnFileEx(string fileName, Stgm grfMode, uint attributes, bool create,
|
||||
IStream reserved, out IStream stream);
|
||||
|
||||
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
|
||||
private static extern Hresult SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, uint cchOutBuf,
|
||||
IntPtr ppvReserved);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,487 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Win32;
|
||||
using Shell;
|
||||
using Wox.Infrastructure;
|
||||
using Wox.Plugin.Program.Logger;
|
||||
using Wox.Plugin.SharedCommands;
|
||||
|
||||
namespace Wox.Plugin.Program.Programs
|
||||
{
|
||||
[Serializable]
|
||||
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";
|
||||
private const string ExeExtension = "exe";
|
||||
|
||||
private int Score(string query)
|
||||
{
|
||||
var nameMatch = StringMatcher.FuzzySearch(query, Name);
|
||||
var descriptionMatch = StringMatcher.FuzzySearch(query, Description);
|
||||
var executableNameMatch = StringMatcher.FuzzySearch(query, ExecutableName);
|
||||
var score = new[] { nameMatch.Score, descriptionMatch.Score, executableNameMatch.Score }.Max();
|
||||
return score;
|
||||
}
|
||||
|
||||
|
||||
public Result Result(string query, IPublicAPI api)
|
||||
{
|
||||
var score = Score(query);
|
||||
if (score <= 0)
|
||||
{ // no need to create result if this is zero
|
||||
return null;
|
||||
}
|
||||
|
||||
var result = new Result
|
||||
{
|
||||
SubTitle = FullPath,
|
||||
IcoPath = IcoPath,
|
||||
Score = score,
|
||||
ContextData = this,
|
||||
Action = e =>
|
||||
{
|
||||
var info = new ProcessStartInfo
|
||||
{
|
||||
FileName = FullPath,
|
||||
WorkingDirectory = ParentDirectory
|
||||
};
|
||||
|
||||
Main.StartProcess(Process.Start, info);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
if (Description.Length >= Name.Length &&
|
||||
Description.Substring(0, Name.Length) == Name)
|
||||
{
|
||||
result.Title = Description;
|
||||
result.TitleHighlightData = StringMatcher.FuzzySearch(query, Description).MatchData;
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(Description))
|
||||
{
|
||||
var title = $"{Name}: {Description}";
|
||||
result.Title = title;
|
||||
result.TitleHighlightData = StringMatcher.FuzzySearch(query, title).MatchData;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Title = Name;
|
||||
result.TitleHighlightData = StringMatcher.FuzzySearch(query, Name).MatchData;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public List<Result> ContextMenus(IPublicAPI api)
|
||||
{
|
||||
var contextMenus = new List<Result>
|
||||
{
|
||||
new Result
|
||||
{
|
||||
Title = api.GetTranslation("wox_plugin_program_run_as_different_user"),
|
||||
Action = _ =>
|
||||
{
|
||||
var info = FullPath.SetProcessStartInfo(ParentDirectory);
|
||||
|
||||
Task.Run(() => Main.StartProcess(ShellCommand.RunAsDifferentUser, info));
|
||||
|
||||
return true;
|
||||
},
|
||||
IcoPath = "Images/user.png"
|
||||
},
|
||||
new Result
|
||||
{
|
||||
Title = api.GetTranslation("wox_plugin_program_run_as_administrator"),
|
||||
Action = _ =>
|
||||
{
|
||||
var info = new ProcessStartInfo
|
||||
{
|
||||
FileName = FullPath,
|
||||
WorkingDirectory = ParentDirectory,
|
||||
Verb = "runas"
|
||||
};
|
||||
|
||||
Task.Run(() => Main.StartProcess(Process.Start, info));
|
||||
|
||||
return true;
|
||||
},
|
||||
IcoPath = "Images/cmd.png"
|
||||
},
|
||||
new Result
|
||||
{
|
||||
Title = api.GetTranslation("wox_plugin_program_open_containing_folder"),
|
||||
Action = _ =>
|
||||
{
|
||||
Main.StartProcess(Process.Start, new ProcessStartInfo(ParentDirectory));
|
||||
|
||||
return true;
|
||||
},
|
||||
IcoPath = "Images/folder.png"
|
||||
}
|
||||
};
|
||||
return contextMenus;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return ExecutableName;
|
||||
}
|
||||
|
||||
private static Win32 Win32Program(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
var p = new Win32
|
||||
{
|
||||
Name = Path.GetFileNameWithoutExtension(path),
|
||||
IcoPath = path,
|
||||
FullPath = path,
|
||||
UniqueIdentifier = path,
|
||||
ParentDirectory = Directory.GetParent(path).FullName,
|
||||
Description = string.Empty,
|
||||
Valid = true,
|
||||
Enabled = true
|
||||
};
|
||||
return p;
|
||||
}
|
||||
catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException)
|
||||
{
|
||||
ProgramLogger.LogException($"|Win32|Win32Program|{path}" +
|
||||
$"|Permission denied when trying to load the program from {path}", e);
|
||||
|
||||
return new Win32() { Valid = false, Enabled = false };
|
||||
}
|
||||
}
|
||||
|
||||
private static Win32 LnkProgram(string path)
|
||||
{
|
||||
var program = Win32Program(path);
|
||||
try
|
||||
{
|
||||
var link = new ShellLink();
|
||||
const uint STGM_READ = 0;
|
||||
((IPersistFile)link).Load(path, STGM_READ);
|
||||
var hwnd = new _RemotableHandle();
|
||||
link.Resolve(ref hwnd, 0);
|
||||
|
||||
const int MAX_PATH = 260;
|
||||
StringBuilder buffer = new StringBuilder(MAX_PATH);
|
||||
|
||||
var data = new _WIN32_FIND_DATAW();
|
||||
const uint SLGP_SHORTPATH = 1;
|
||||
link.GetPath(buffer, buffer.Capacity, ref data, SLGP_SHORTPATH);
|
||||
var target = buffer.ToString();
|
||||
if (!string.IsNullOrEmpty(target))
|
||||
{
|
||||
var extension = Extension(target);
|
||||
if (extension == ExeExtension && File.Exists(target))
|
||||
{
|
||||
buffer = new StringBuilder(MAX_PATH);
|
||||
link.GetDescription(buffer, MAX_PATH);
|
||||
var description = buffer.ToString();
|
||||
if (!string.IsNullOrEmpty(description))
|
||||
{
|
||||
program.Description = description;
|
||||
}
|
||||
else
|
||||
{
|
||||
var info = FileVersionInfo.GetVersionInfo(target);
|
||||
if (!string.IsNullOrEmpty(info.FileDescription))
|
||||
{
|
||||
program.Description = info.FileDescription;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return program;
|
||||
}
|
||||
catch (COMException e)
|
||||
{
|
||||
// C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\MiracastView.lnk always cause exception
|
||||
ProgramLogger.LogException($"|Win32|LnkProgram|{path}" +
|
||||
"|Error caused likely due to trying to get the description of the program", 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)
|
||||
{
|
||||
ProgramLogger.LogException($"|Win32|LnkProgram|{path}" +
|
||||
"|An unexpected error occurred in the calling method LnkProgram", e);
|
||||
|
||||
program.Valid = false;
|
||||
return program;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private static Win32 ExeProgram(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
var program = Win32Program(path);
|
||||
var info = FileVersionInfo.GetVersionInfo(path);
|
||||
if (!string.IsNullOrEmpty(info.FileDescription))
|
||||
{
|
||||
program.Description = info.FileDescription;
|
||||
}
|
||||
return program;
|
||||
}
|
||||
catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException)
|
||||
{
|
||||
ProgramLogger.LogException($"|Win32|ExeProgram|{path}" +
|
||||
$"|Permission denied when trying to load the program from {path}", e);
|
||||
|
||||
return new Win32() { Valid = false, Enabled = false };
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<string> ProgramPaths(string directory, string[] suffixes)
|
||||
{
|
||||
if (!Directory.Exists(directory))
|
||||
return new string[] { };
|
||||
var files = new List<string>();
|
||||
var folderQueue = new Queue<string>();
|
||||
folderQueue.Enqueue(directory);
|
||||
do
|
||||
{
|
||||
var currentDirectory = folderQueue.Dequeue();
|
||||
try
|
||||
{
|
||||
foreach (var suffix in suffixes)
|
||||
{
|
||||
try
|
||||
{
|
||||
files.AddRange(Directory.EnumerateFiles(currentDirectory, $"*.{suffix}", SearchOption.TopDirectoryOnly));
|
||||
}
|
||||
catch (DirectoryNotFoundException e)
|
||||
{
|
||||
ProgramLogger.LogException($"|Win32|ProgramPaths|{currentDirectory}" +
|
||||
"|The directory trying to load the program from does not exist", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException)
|
||||
{
|
||||
ProgramLogger.LogException($"|Win32|ProgramPaths|{currentDirectory}" +
|
||||
$"|Permission denied when trying to load programs from {currentDirectory}", e);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var childDirectory in Directory.EnumerateDirectories(currentDirectory, "*", SearchOption.TopDirectoryOnly))
|
||||
{
|
||||
folderQueue.Enqueue(childDirectory);
|
||||
}
|
||||
}
|
||||
catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException)
|
||||
{
|
||||
ProgramLogger.LogException($"|Win32|ProgramPaths|{currentDirectory}" +
|
||||
$"|Permission denied when trying to load programs from {currentDirectory}", e);
|
||||
}
|
||||
} while (folderQueue.Any());
|
||||
return files;
|
||||
}
|
||||
|
||||
private static string Extension(string path)
|
||||
{
|
||||
var extension = Path.GetExtension(path)?.ToLower();
|
||||
if (!string.IsNullOrEmpty(extension))
|
||||
{
|
||||
return extension.Substring(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private static ParallelQuery<Win32> UnregisteredPrograms(List<Settings.ProgramSource> sources, string[] suffixes)
|
||||
{
|
||||
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()
|
||||
let e = Extension(p)
|
||||
where e != ShortcutExtension && e != ExeExtension
|
||||
select Win32Program(p);
|
||||
return programs1.Concat(programs2).Concat(programs3);
|
||||
}
|
||||
|
||||
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 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
|
||||
const string appPaths = @"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths";
|
||||
var programs = new List<Win32>();
|
||||
using (var root = Registry.LocalMachine.OpenSubKey(appPaths))
|
||||
{
|
||||
if (root != null)
|
||||
{
|
||||
programs.AddRange(GetProgramsFromRegistry(root));
|
||||
}
|
||||
}
|
||||
using (var root = Registry.CurrentUser.OpenSubKey(appPaths))
|
||||
{
|
||||
if (root != null)
|
||||
{
|
||||
programs.AddRange(GetProgramsFromRegistry(root));
|
||||
}
|
||||
}
|
||||
|
||||
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> GetProgramsFromRegistry(RegistryKey root)
|
||||
{
|
||||
return root
|
||||
.GetSubKeyNames()
|
||||
.Select(x => GetProgramPathFromRegistrySubKeys(root, x))
|
||||
.Distinct()
|
||||
.Select(x => GetProgramFromPath(x));
|
||||
}
|
||||
|
||||
private static string GetProgramPathFromRegistrySubKeys(RegistryKey root, string subkey)
|
||||
{
|
||||
var path = string.Empty;
|
||||
try
|
||||
{
|
||||
using (var key = root.OpenSubKey(subkey))
|
||||
{
|
||||
if (key == null)
|
||||
return string.Empty;
|
||||
|
||||
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('"', ' ');
|
||||
}
|
||||
catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException)
|
||||
{
|
||||
ProgramLogger.LogException($"|Win32|GetProgramPathFromRegistrySubKeys|{path}" +
|
||||
$"|Permission denied when trying to load the program from {path}", e);
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private static Win32 GetProgramFromPath(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return new Win32();
|
||||
|
||||
path = Environment.ExpandEnvironmentVariables(path);
|
||||
|
||||
if (!File.Exists(path))
|
||||
return new Win32();
|
||||
|
||||
var entry = Win32Program(path);
|
||||
entry.ExecutableName = Path.GetFileName(path);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
public static Win32[] All(Settings settings)
|
||||
{
|
||||
try
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
return programs.ToArray();
|
||||
}
|
||||
#if DEBUG //This is to make developer aware of any unhandled exception and add in handling.
|
||||
catch (Exception e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !DEBUG //Only do a catch all in production.
|
||||
catch (Exception e)
|
||||
{
|
||||
ProgramLogger.LogException("|Win32|All|Not available|An unexpected error occurred", e);
|
||||
|
||||
return new Win32[0];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("Wox.Plugin.Program")]
|
||||
[assembly: Guid("82f60d9a-9280-4b6a-8b21-f3c694cb7e1d")]
|
||||
40
src/modules/launcher/Plugins/Wox.Plugin.Program/Settings.cs
Normal file
40
src/modules/launcher/Plugins/Wox.Plugin.Program/Settings.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
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;
|
||||
|
||||
public bool EnableRegistrySource { get; set; } = true;
|
||||
|
||||
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 { }
|
||||
}
|
||||
}
|
||||
BIN
src/modules/launcher/Plugins/Wox.Plugin.Program/ShObjIdlTlb.dll
Normal file
BIN
src/modules/launcher/Plugins/Wox.Plugin.Program/ShObjIdlTlb.dll
Normal file
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Markup;
|
||||
|
||||
namespace Wox.Plugin.Program
|
||||
{
|
||||
public class SuffixesConvert : MarkupExtension, IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
var text = value as string[];
|
||||
if (text != null)
|
||||
{
|
||||
return string.Join(";", text);
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override object ProvideValue(IServiceProvider serviceProvider)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
namespace Wox.Plugin.Program.Views.Models
|
||||
{
|
||||
public class ProgramSource : Settings.ProgramSource { }
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
<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"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:program="clr-namespace:Wox.Plugin.Program"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="300" d:DesignWidth="600">
|
||||
<Grid Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="50"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="50"/>
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Stretch">
|
||||
<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>
|
||||
<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" 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>
|
||||
<TextBlock Text="{Binding Location, ConverterParameter=(null), Converter={program:LocationConverter}}"/>
|
||||
</DataTemplate>
|
||||
</GridViewColumn.CellTemplate>
|
||||
</GridViewColumn>
|
||||
</GridView>
|
||||
</ListView.View>
|
||||
</ListView>
|
||||
<DockPanel Grid.Row="2">
|
||||
<StackPanel x:Name="indexingPanel" Visibility="Hidden" HorizontalAlignment="Left" Orientation="Horizontal">
|
||||
<ProgressBar x:Name="progressBarIndexing" Height="20" Width="80" Minimum="0" Maximum="100" IsIndeterminate="True" />
|
||||
<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="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>
|
||||
</UserControl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{FDB3555B-58EF-4AE6-B5F1-904719637AB4}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Wox.Plugin.Program</RootNamespace>
|
||||
<AssemblyName>Wox.Plugin.Program</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
|
||||
<TargetFrameworkProfile />
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\..\Output\Debug\Plugins\Wox.Plugin.Program\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\..\Output\Release\Plugins\Wox.Plugin.Program\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="AppxPackagingTlb">
|
||||
<HintPath>.\AppxPackagingTlb.dll</HintPath>
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
<Reference Include="ShObjIdlTlb">
|
||||
<HintPath>.\ShObjIdlTlb.dll</HintPath>
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xaml" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="WindowsBase" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Include="AddProgramSource.xaml" />
|
||||
<Compile Include="..\..\SolutionAssemblyInfo.cs">
|
||||
<Link>Properties\SolutionAssemblyInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="AddProgramSource.xaml.cs">
|
||||
<DependentUpon>AddProgramSource.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Logger\ProgramLogger.cs" />
|
||||
<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="Views\ProgramSetting.xaml.cs">
|
||||
<DependentUpon>ProgramSetting.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Settings.cs" />
|
||||
<Compile Include="ProgramSuffixes.xaml.cs">
|
||||
<DependentUpon>ProgramSuffixes.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="LocationConverter.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="plugin.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Images\program.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Images\cmd.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Images\folder.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Images\disable.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Images\user.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<Content Include="Languages\en.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\zh-cn.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Languages\zh-tw.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Languages\de.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Languages\pl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Languages\tr.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Page Include="Views\ProgramSetting.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="ProgramSuffixes.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Wox.Infrastructure\Wox.Infrastructure.csproj">
|
||||
<Project>{4fd29318-a8ab-4d8f-aa47-60bc241b8da3}</Project>
|
||||
<Name>Wox.Infrastructure</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\Wox.Plugin\Wox.Plugin.csproj">
|
||||
<Project>{8451ecdd-2ea4-4966-bb0a-7bbc40138e80}</Project>
|
||||
<Name>Wox.Plugin</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Annotations">
|
||||
<Version>10.3.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Newtonsoft.Json">
|
||||
<Version>9.0.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="NLog">
|
||||
<Version>4.2.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Runtime">
|
||||
<Version>4.0.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="UwpDesktop">
|
||||
<Version>10.0.14393.3</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
12
src/modules/launcher/Plugins/Wox.Plugin.Program/plugin.json
Normal file
12
src/modules/launcher/Plugins/Wox.Plugin.Program/plugin.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ID":"791FC278BA414111B8D1886DFE447410",
|
||||
"ActionKeyword":"*",
|
||||
"Name":"Program",
|
||||
"Description":"Search programs in Wox",
|
||||
"Author":"qianlifeng",
|
||||
"Version":"1.0.0",
|
||||
"Language":"csharp",
|
||||
"Website":"http://www.wox.one/plugin",
|
||||
"ExecuteFileName":"Wox.Plugin.Program.dll",
|
||||
"IcoPath":"Images\\program.png"
|
||||
}
|
||||
Reference in New Issue
Block a user