Merge branch 'json-rpc'

This commit is contained in:
qianlifeng
2014-07-14 22:28:54 +08:00
1123 changed files with 326363 additions and 15366 deletions

View File

@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Python.Runtime;
using Wox.Helper;
using Wox.Infrastructure.Storage.UserSettings;
using Wox.Plugin;
@@ -12,43 +11,25 @@ namespace Wox.Commands
{
public class PluginCommand : BaseCommand
{
private string currentPythonModulePath = string.Empty;
private IntPtr GIL;
public override void Dispatch(Query q)
public override void Dispatch(Query query)
{
PluginPair thirdPlugin = Plugins.AllPlugins.FirstOrDefault(o => o.Metadata.ActionKeyword == q.ActionName);
PluginPair thirdPlugin = Plugins.AllPlugins.FirstOrDefault(o => o.Metadata.ActionKeyword == query.ActionName);
if (thirdPlugin != null && !string.IsNullOrEmpty(thirdPlugin.Metadata.ActionKeyword))
{
var customizedPluginConfig = UserSettingStorage.Instance.CustomizedPluginConfigs.FirstOrDefault(o => o.ID == thirdPlugin.Metadata.ID);
if (customizedPluginConfig != null && customizedPluginConfig.Disabled)
{
//need to stop the loading animation
UpdateResultView(null);
return;
}
if (thirdPlugin.Metadata.Language == AllowedLanguage.Python)
{
SwitchPythonEnv(thirdPlugin);
}
ThreadPool.QueueUserWorkItem(t =>
{
try
{
thirdPlugin.InitContext.PushResults = (qu, r) =>
{
if (r != null)
{
r.ForEach(o =>
{
o.PluginDirectory = thirdPlugin.Metadata.PluginDirecotry;
o.OriginQuery = qu;
});
UpdateResultView(r);
}
};
List<Result> results = thirdPlugin.Plugin.Query(q) ?? new List<Result>();
thirdPlugin.InitContext.PushResults(q, results);
List<Result> results = thirdPlugin.Plugin.Query(query) ?? new List<Result>();
App.Window.PushResults(query,thirdPlugin.Metadata,results);
}
catch (Exception queryException)
{
@@ -63,24 +44,5 @@ namespace Wox.Commands
});
}
}
private void SwitchPythonEnv(PluginPair thirdPlugin)
{
if (currentPythonModulePath != thirdPlugin.Metadata.PluginDirecotry)
{
currentPythonModulePath = thirdPlugin.Metadata.PluginDirecotry;
if (GIL != IntPtr.Zero)
{
Runtime.PyEval_RestoreThread(GIL);
PythonEngine.Shutdown();
}
PythonEngine.Initialize();
IntPtr pyStrPtr = Runtime.PyString_FromString(thirdPlugin.Metadata.PluginDirecotry);
IntPtr sysDotPath = Runtime.PySys_GetObject("path");
Runtime.PyList_Append(sysDotPath, pyStrPtr);
GIL = PythonEngine.BeginAllowThreads();
}
}
}
}

View File

@@ -19,19 +19,10 @@ namespace Wox.Commands
ThreadPool.QueueUserWorkItem(state =>
{
pair1.InitContext.PushResults = (q, r) =>
{
foreach (Result result in r)
{
result.PluginDirectory = pair1.Metadata.PluginDirecotry;
result.OriginQuery = q;
result.AutoAjustScore = true;
}
UpdateResultView(r);
};
List<Result> results = pair1.Plugin.Query(query);
pair1.InitContext.PushResults(query, results);
results.ForEach(o=> { o.AutoAjustScore = true; });
App.Window.PushResults(query,pair1.Metadata,results);
});
}
}

View File

@@ -1,230 +1,32 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Wox.Helper;
namespace Wox.Converters
{
public class ImagePathConverter : IMultiValueConverter
{
private static readonly Dictionary<string, object> imageCache = new Dictionary<string, object>();
private static readonly List<string> imageExts = new List<string>
{
".png",
".jpg",
".jpeg",
".gif",
".bmp",
".tiff",
".ico"
};
private static readonly List<string> selfExts = new List<string>
{
".exe",
".lnk",
".ani",
".cur",
".sln",
".appref-ms"
};
private static ImageSource GetIcon(string fileName)
{
Icon icon = GetFileIcon(fileName);
if (icon == null) icon = Icon.ExtractAssociatedIcon(fileName);
if (icon != null)
{
return System.Windows.Interop.Imaging.CreateBitmapSourceFromHIcon(icon.Handle,
new Int32Rect(0, 0, icon.Width, icon.Height), BitmapSizeOptions.FromEmptyOptions());
}
return null;
}
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
object img;
if (values[0] == null) return null;
string path = values[0].ToString();
if (path.StartsWith("data:", StringComparison.OrdinalIgnoreCase))
{
return new BitmapImage(new Uri(path));
}
string relativePath = values[0].ToString();
string pluginDirectory = values[1].ToString();
string fullPath = Path.Combine(pluginDirectory, path);
string ext = Path.GetExtension(path).ToLower();
if (imageCache.ContainsKey(fullPath))
{
return imageCache[fullPath];
}
if (!selfExts.Contains(ext) && imageCache.ContainsKey(ext))
{
return imageCache[ext];
}
string fullPath = Path.Combine(pluginDirectory, relativePath);
string resolvedPath = string.Empty;
if (!string.IsNullOrEmpty(path) && path.Contains(":\\") && File.Exists(path))
if (relativePath.StartsWith("data:", StringComparison.OrdinalIgnoreCase))
{
resolvedPath = path;
return new BitmapImage(new Uri(relativePath));
}
else if (!string.IsNullOrEmpty(path) && File.Exists(fullPath))
{
resolvedPath = fullPath;
}
string extn = Path.GetExtension(resolvedPath).ToLower();
var cacheKey = fullPath;
if (selfExts.Contains(extn))
{
img = GetIcon(resolvedPath);
}
else if (!string.IsNullOrEmpty(resolvedPath) && imageExts.Contains(ext) && File.Exists(resolvedPath))
{
img = new BitmapImage(new Uri(resolvedPath));
}
else
{
img = GetIcon(resolvedPath);
if (!selfExts.Contains(ext)) cacheKey = ext;
}
if (img != null)
{
imageCache.Add(cacheKey, img);
}
return img;
return ImageLoader.Load(fullPath);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
// http://blogs.msdn.com/b/oldnewthing/archive/2011/01/27/10120844.aspx
public static Icon GetFileIcon(string name)
{
SHFILEINFO shfi = new SHFILEINFO();
uint flags = SHGFI_SYSICONINDEX;
IntPtr himl = SHGetFileInfo(name,
FILE_ATTRIBUTE_NORMAL,
ref shfi,
(uint) Marshal.SizeOf(shfi),
flags);
if (himl != IntPtr.Zero)
{
IntPtr hIcon = ImageList_GetIcon(himl, shfi.iIcon, ILD_NORMAL);
var icon = (Icon) Icon.FromHandle(hIcon).Clone();
DestroyIcon(hIcon);
return icon;
}
return null;
}
[DllImport("comctl32.dll", SetLastError = true)]
private static extern IntPtr ImageList_GetIcon(IntPtr himl, int i, uint flags);
private const int MAX_PATH = 256;
[StructLayout(LayoutKind.Sequential)]
private struct SHITEMID
{
public ushort cb;
[MarshalAs(UnmanagedType.LPArray)] public byte[] abID;
}
[StructLayout(LayoutKind.Sequential)]
private struct ITEMIDLIST
{
public SHITEMID mkid;
}
[StructLayout(LayoutKind.Sequential)]
private struct BROWSEINFO
{
public IntPtr hwndOwner;
public IntPtr pidlRoot;
public IntPtr pszDisplayName;
[MarshalAs(UnmanagedType.LPTStr)] public string lpszTitle;
public uint ulFlags;
public IntPtr lpfn;
public int lParam;
public IntPtr iImage;
}
// Browsing for directory.
private const uint BIF_RETURNONLYFSDIRS = 0x0001;
private const uint BIF_DONTGOBELOWDOMAIN = 0x0002;
private const uint BIF_STATUSTEXT = 0x0004;
private const uint BIF_RETURNFSANCESTORS = 0x0008;
private const uint BIF_EDITBOX = 0x0010;
private const uint BIF_VALIDATE = 0x0020;
private const uint BIF_NEWDIALOGSTYLE = 0x0040;
private const uint BIF_USENEWUI = (BIF_NEWDIALOGSTYLE | BIF_EDITBOX);
private const uint BIF_BROWSEINCLUDEURLS = 0x0080;
private const uint BIF_BROWSEFORCOMPUTER = 0x1000;
private const uint BIF_BROWSEFORPRINTER = 0x2000;
private const uint BIF_BROWSEINCLUDEFILES = 0x4000;
private const uint BIF_SHAREABLE = 0x8000;
[StructLayout(LayoutKind.Sequential)]
private struct SHFILEINFO
{
public const int NAMESIZE = 80;
public IntPtr hIcon;
public int iIcon;
public uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)] public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NAMESIZE)] public string szTypeName;
};
private const uint SHGFI_ICON = 0x000000100; // get icon
private const uint SHGFI_DISPLAYNAME = 0x000000200; // get display name
private const uint SHGFI_TYPENAME = 0x000000400; // get type name
private const uint SHGFI_ATTRIBUTES = 0x000000800; // get attributes
private const uint SHGFI_ICONLOCATION = 0x000001000; // get icon location
private const uint SHGFI_EXETYPE = 0x000002000; // return exe type
private const uint SHGFI_SYSICONINDEX = 0x000004000; // get system icon index
private const uint SHGFI_LINKOVERLAY = 0x000008000; // put a link overlay on icon
private const uint SHGFI_SELECTED = 0x000010000; // show icon in selected state
private const uint SHGFI_ATTR_SPECIFIED = 0x000020000; // get only specified attributes
private const uint SHGFI_LARGEICON = 0x000000000; // get large icon
private const uint SHGFI_SMALLICON = 0x000000001; // get small icon
private const uint SHGFI_OPENICON = 0x000000002; // get open icon
private const uint SHGFI_SHELLICONSIZE = 0x000000004; // get shell size icon
private const uint SHGFI_PIDL = 0x000000008; // pszPath is a pidl
private const uint SHGFI_USEFILEATTRIBUTES = 0x000000010; // use passed dwFileAttribute
private const uint SHGFI_ADDOVERLAYS = 0x000000020; // apply the appropriate overlays
private const uint SHGFI_OVERLAYINDEX = 0x000000040; // Get the index of the overlay
private const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
private const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;
private const uint ILD_NORMAL = 0x00000000;
[DllImport("Shell32.dll")]
private static extern IntPtr SHGetFileInfo(
string pszPath,
uint dwFileAttributes,
ref SHFILEINFO psfi,
uint cbFileInfo,
uint uFlags
);
[DllImport("User32.dll")]
private static extern int DestroyIcon(IntPtr hIcon);
}
}

217
Wox/Helper/ImageLoader.cs Normal file
View File

@@ -0,0 +1,217 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace Wox.Helper
{
public class ImageLoader
{
private static readonly Dictionary<string, ImageSource> imageCache = new Dictionary<string, ImageSource>();
private static object locker = new object();
private static readonly List<string> imageExts = new List<string>
{
".png",
".jpg",
".jpeg",
".gif",
".bmp",
".tiff",
".ico"
};
private static readonly List<string> selfExts = new List<string>
{
".exe",
".lnk",
".ani",
".cur",
".sln",
".appref-ms"
};
private static ImageSource GetIcon(string fileName)
{
Icon icon = GetFileIcon(fileName);
if (icon == null) icon = Icon.ExtractAssociatedIcon(fileName);
if (icon != null)
{
return System.Windows.Interop.Imaging.CreateBitmapSourceFromHIcon(icon.Handle,
new Int32Rect(0, 0, icon.Width, icon.Height), BitmapSizeOptions.FromEmptyOptions());
}
return null;
}
public static ImageSource Load(string path)
{
ImageSource img = null;
if (path == null) return null;
if (imageCache.ContainsKey(path))
{
return imageCache[path];
}
string ext = Path.GetExtension(path).ToLower();
string resolvedPath = string.Empty;
if (!string.IsNullOrEmpty(path) && path.Contains(":\\") && File.Exists(path))
{
resolvedPath = path;
}
if (selfExts.Contains(ext))
{
img = GetIcon(resolvedPath);
}
else if (!string.IsNullOrEmpty(resolvedPath) && imageExts.Contains(ext) && File.Exists(resolvedPath))
{
img = new BitmapImage(new Uri(resolvedPath));
}
if (img != null)
{
if (!imageCache.ContainsKey(path))
{
lock (locker)
{
if (!imageCache.ContainsKey(path))
{
imageCache.Add(path, img);
}
}
}
}
return img;
}
// http://blogs.msdn.com/b/oldnewthing/archive/2011/01/27/10120844.aspx
private static Icon GetFileIcon(string name)
{
SHFILEINFO shfi = new SHFILEINFO();
uint flags = SHGFI_SYSICONINDEX;
IntPtr himl = SHGetFileInfo(name,
FILE_ATTRIBUTE_NORMAL,
ref shfi,
(uint)Marshal.SizeOf(shfi),
flags);
if (himl != IntPtr.Zero)
{
IntPtr hIcon = ImageList_GetIcon(himl, shfi.iIcon, ILD_NORMAL);
var icon = (Icon)Icon.FromHandle(hIcon).Clone();
DestroyIcon(hIcon);
return icon;
}
return null;
}
[DllImport("comctl32.dll", SetLastError = true)]
private static extern IntPtr ImageList_GetIcon(IntPtr himl, int i, uint flags);
private const int MAX_PATH = 256;
[StructLayout(LayoutKind.Sequential)]
private struct SHITEMID
{
public ushort cb;
[MarshalAs(UnmanagedType.LPArray)]
public byte[] abID;
}
[StructLayout(LayoutKind.Sequential)]
private struct ITEMIDLIST
{
public SHITEMID mkid;
}
[StructLayout(LayoutKind.Sequential)]
private struct BROWSEINFO
{
public IntPtr hwndOwner;
public IntPtr pidlRoot;
public IntPtr pszDisplayName;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpszTitle;
public uint ulFlags;
public IntPtr lpfn;
public int lParam;
public IntPtr iImage;
}
// Browsing for directory.
private const uint BIF_RETURNONLYFSDIRS = 0x0001;
private const uint BIF_DONTGOBELOWDOMAIN = 0x0002;
private const uint BIF_STATUSTEXT = 0x0004;
private const uint BIF_RETURNFSANCESTORS = 0x0008;
private const uint BIF_EDITBOX = 0x0010;
private const uint BIF_VALIDATE = 0x0020;
private const uint BIF_NEWDIALOGSTYLE = 0x0040;
private const uint BIF_USENEWUI = (BIF_NEWDIALOGSTYLE | BIF_EDITBOX);
private const uint BIF_BROWSEINCLUDEURLS = 0x0080;
private const uint BIF_BROWSEFORCOMPUTER = 0x1000;
private const uint BIF_BROWSEFORPRINTER = 0x2000;
private const uint BIF_BROWSEINCLUDEFILES = 0x4000;
private const uint BIF_SHAREABLE = 0x8000;
[StructLayout(LayoutKind.Sequential)]
private struct SHFILEINFO
{
public const int NAMESIZE = 80;
public IntPtr hIcon;
public int iIcon;
public uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)]
public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NAMESIZE)]
public string szTypeName;
};
private const uint SHGFI_ICON = 0x000000100; // get icon
private const uint SHGFI_DISPLAYNAME = 0x000000200; // get display name
private const uint SHGFI_TYPENAME = 0x000000400; // get type name
private const uint SHGFI_ATTRIBUTES = 0x000000800; // get attributes
private const uint SHGFI_ICONLOCATION = 0x000001000; // get icon location
private const uint SHGFI_EXETYPE = 0x000002000; // return exe type
private const uint SHGFI_SYSICONINDEX = 0x000004000; // get system icon index
private const uint SHGFI_LINKOVERLAY = 0x000008000; // put a link overlay on icon
private const uint SHGFI_SELECTED = 0x000010000; // show icon in selected state
private const uint SHGFI_ATTR_SPECIFIED = 0x000020000; // get only specified attributes
private const uint SHGFI_LARGEICON = 0x000000000; // get large icon
private const uint SHGFI_SMALLICON = 0x000000001; // get small icon
private const uint SHGFI_OPENICON = 0x000000002; // get open icon
private const uint SHGFI_SHELLICONSIZE = 0x000000004; // get shell size icon
private const uint SHGFI_PIDL = 0x000000008; // pszPath is a pidl
private const uint SHGFI_USEFILEATTRIBUTES = 0x000000010; // use passed dwFileAttribute
private const uint SHGFI_ADDOVERLAYS = 0x000000020; // apply the appropriate overlays
private const uint SHGFI_OVERLAYINDEX = 0x000000040; // Get the index of the overlay
private const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
private const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;
private const uint ILD_NORMAL = 0x00000000;
[DllImport("Shell32.dll")]
private static extern IntPtr SHGetFileInfo(
string pszPath,
uint dwFileAttributes,
ref SHFILEINFO psfi,
uint cbFileInfo,
uint uFlags
);
[DllImport("User32.dll")]
private static extern int DestroyIcon(IntPtr hIcon);
}
}

View File

@@ -0,0 +1,77 @@
using System.Collections.Generic;
using System.Linq;
using Wox.Plugin;
namespace Wox.JsonRPC
{
public class JsonRPCErrorModel
{
public int Code { get; set; }
public string Message { get; set; }
public string Data { get; set; }
}
public class JsonRPCModelBase
{
public int Id { get; set; }
public string JsonRPC { get; set; }
}
public class JsonRPCResponseModel : JsonRPCModelBase
{
public string Result { get; set; }
public JsonRPCErrorModel Error { get; set; }
}
public class JsonRPCQueryResponseModel : JsonRPCResponseModel
{
public new List<JsonRPCResult> Result { get; set; }
}
public class JsonRPCRequestModel : JsonRPCModelBase
{
public string Method { get; set; }
public object[] Parameters { get; set; }
public bool DontHideAfterAction { get; set; }
public override string ToString()
{
if (Parameters != null && Parameters.Length > 0)
{
string parameters = Parameters.Aggregate("[", (current, o) => current + (GetParamterByType(o) + ","));
parameters = parameters.Substring(0, parameters.Length - 1) + "]";
return string.Format(@"{{\""method\"":\""{0}\"",\""parameters\"":{1}}}", Method, parameters);
}
return string.Format(@"{{\""method\"":\""{0}\"",\""parameters\"":[]}}",Method);
}
private string GetParamterByType(object paramter)
{
if (paramter is string)
{
return string.Format(@"\""{0}\""", paramter);
}
if (paramter is int || paramter is float || paramter is double)
{
return string.Format(@"{0}", paramter);
}
if (paramter is bool)
{
return string.Format(@"{0}", paramter.ToString().ToLower());
}
return paramter.ToString();
}
}
public class JsonRPCResult : Result
{
public JsonRPCRequestModel JsonRPCAction { get; set; }
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,132 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using Newtonsoft.Json;
using Wox.JsonRPC;
using Wox.Plugin;
namespace Wox.PluginLoader
{
public abstract class BasePlugin : IPlugin
{
protected PluginInitContext context;
public abstract string SupportedLanguage { get; }
protected abstract string ExecuteQuery(Query query);
protected abstract string ExecuteAction(JsonRPCRequestModel rpcRequest);
public List<Result> Query(Query query)
{
string output = ExecuteQuery(query);
if (!string.IsNullOrEmpty(output))
{
try
{
List<Result> results = new List<Result>();
JsonRPCQueryResponseModel queryResponseModel = JsonConvert.DeserializeObject<JsonRPCQueryResponseModel>(output);
foreach (JsonRPCResult result in queryResponseModel.Result)
{
JsonRPCResult result1 = result;
result.Action = (c) =>
{
if (!string.IsNullOrEmpty(result1.JsonRPCAction.Method))
{
if (result1.JsonRPCAction.Method.StartsWith("Wox."))
{
ExecuteWoxAPI(result1.JsonRPCAction.Method.Substring(4), result1.JsonRPCAction.Parameters);
}
else
{
ThreadPool.QueueUserWorkItem(state =>
{
string actionReponse = ExecuteAction(result1.JsonRPCAction);
JsonRPCRequestModel jsonRpcRequestModel = JsonConvert.DeserializeObject<JsonRPCRequestModel>(actionReponse);
if (jsonRpcRequestModel != null
&& string.IsNullOrEmpty(jsonRpcRequestModel.Method)
&& jsonRpcRequestModel.Method.StartsWith("Wox."))
{
ExecuteWoxAPI(jsonRpcRequestModel.Method.Substring(4), jsonRpcRequestModel.Parameters);
}
});
}
}
return !result1.JsonRPCAction.DontHideAfterAction;
};
results.Add(result);
}
return results;
}
catch
{
}
}
return null;
}
private void ExecuteWoxAPI(string method, object[] parameters)
{
MethodInfo methodInfo = App.Window.GetType().GetMethod(method);
if (methodInfo != null)
{
try
{
methodInfo.Invoke(App.Window, parameters);
}
catch (Exception)
{
#if (DEBUG)
{
throw;
}
#endif
}
}
}
/// <summary>
/// Execute external program and return the output
/// </summary>
/// <param name="fileName"></param>
/// <param name="arguments"></param>
/// <returns></returns>
protected string Execute(string fileName, string arguments)
{
try
{
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = fileName;
start.Arguments = arguments;
start.UseShellExecute = false;
start.CreateNoWindow = true;
start.RedirectStandardOutput = true;
using (Process process = Process.Start(start))
{
if (process != null)
{
using (StreamReader reader = process.StandardOutput)
{
return reader.ReadToEnd();
}
}
}
}
catch
{
return null;
}
return null;
}
public void Init(PluginInitContext ctx)
{
this.context = ctx;
}
}
}

View File

@@ -1,118 +1,23 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using Newtonsoft.Json;
using Wox.Helper;
using Wox.Infrastructure.Storage.UserSettings;
using System.Text;
using Wox.Plugin;
using Wox.Plugin.SystemPlugins;
namespace Wox.PluginLoader {
public abstract class BasePluginLoader {
private static string PluginPath = Path.Combine(Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath), "Plugins");
private static string PluginConfigName = "plugin.json";
protected static List<PluginMetadata> pluginMetadatas = new List<PluginMetadata>();
public abstract List<PluginPair> LoadPlugin();
namespace Wox.PluginLoader
{
public class BasePluginLoader<T> : IPluginLoader where T : BasePlugin, new()
{
public virtual List<PluginPair> LoadPlugin(List<PluginMetadata> pluginMetadatas)
{
string supportedLanguage = new T().SupportedLanguage;
List<PluginMetadata> metadatas = pluginMetadatas.Where(o => supportedLanguage.ToUpper() == o.Language.ToUpper()).ToList();
public static void ParsePluginsConfig() {
pluginMetadatas.Clear();
ParseSystemPlugins();
ParseThirdPartyPlugins();
if (Plugins.DebuggerMode != null) {
PluginMetadata metadata = GetMetadataFromJson(Plugins.DebuggerMode);
if (metadata != null) pluginMetadatas.Add(metadata);
}
}
private static void ParseSystemPlugins() {
pluginMetadatas.Add(new PluginMetadata() {
Name = "System Plugins",
Author = "System",
Description = "system plugins collection",
Website = "http://www.getwox.com",
Language = AllowedLanguage.CSharp,
Version = "1.0",
PluginType = PluginType.System,
ActionKeyword = "*",
ExecuteFileName = "Wox.Plugin.SystemPlugins.dll",
PluginDirecotry = Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath)
});
}
private static void ParseThirdPartyPlugins() {
if (!Directory.Exists(PluginPath))
Directory.CreateDirectory(PluginPath);
string[] directories = Directory.GetDirectories(PluginPath);
foreach (string directory in directories) {
if (File.Exists((Path.Combine(directory, "NeedDelete.txt")))) {
Directory.Delete(directory, true);
continue;
}
PluginMetadata metadata = GetMetadataFromJson(directory);
if (metadata != null) pluginMetadatas.Add(metadata);
}
}
private static PluginMetadata GetMetadataFromJson(string pluginDirectory) {
string configPath = Path.Combine(pluginDirectory, PluginConfigName);
PluginMetadata metadata;
if (!File.Exists(configPath)) {
Log.Warn(string.Format("parse plugin {0} failed: didn't find config file.", configPath));
return null;
}
try {
metadata = JsonConvert.DeserializeObject<PluginMetadata>(File.ReadAllText(configPath));
metadata.PluginType = PluginType.ThirdParty;
metadata.PluginDirecotry = pluginDirectory;
}
catch (Exception) {
string error = string.Format("Parse plugin config {0} failed: json format is not valid", configPath);
Log.Warn(error);
#if (DEBUG)
{
throw new WoxException(error);
}
#endif
return null;
}
if (!AllowedLanguage.IsAllowed(metadata.Language)) {
string error = string.Format("Parse plugin config {0} failed: invalid language {1}", configPath, metadata.Language);
Log.Warn(error);
#if (DEBUG)
{
throw new WoxException(error);
}
#endif
return null;
}
if (!File.Exists(metadata.ExecuteFilePath)) {
string error = string.Format("Parse plugin config {0} failed: ExecuteFile {1} didn't exist", configPath, metadata.ExecuteFilePath);
Log.Warn(error);
#if (DEBUG)
{
throw new WoxException(error);
}
#endif
return null;
}
var customizedPluginConfig =
UserSettingStorage.Instance.CustomizedPluginConfigs.FirstOrDefault(o => o.ID == metadata.ID);
if (customizedPluginConfig != null && !string.IsNullOrEmpty(customizedPluginConfig.Actionword))
{
metadata.ActionKeyword = customizedPluginConfig.Actionword;
}
return metadata;
}
}
return metadatas.Select(metadata => new PluginPair()
{
Plugin = new T(),
Metadata = metadata
}).ToList();
}
}
}

View File

@@ -8,9 +8,10 @@ using Wox.Plugin.SystemPlugins;
namespace Wox.PluginLoader {
public class CSharpPluginLoader : BasePluginLoader {
public override List<PluginPair> LoadPlugin() {
public class CSharpPluginLoader : IPluginLoader
{
public List<PluginPair> LoadPlugin(List<PluginMetadata> pluginMetadatas)
{
var plugins = new List<PluginPair>();
List<PluginMetadata> metadatas = pluginMetadatas.Where(o => o.Language.ToUpper() == AllowedLanguage.CSharp.ToUpper()).ToList();

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Wox.Plugin;
namespace Wox.PluginLoader
{
public interface IPluginLoader
{
List<PluginPair> LoadPlugin(List<PluginMetadata> pluginMetadatas);
}
}

View File

@@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using Newtonsoft.Json;
using Wox.Helper;
using Wox.Infrastructure.Storage.UserSettings;
using Wox.Plugin;
using Wox.Plugin.SystemPlugins;
namespace Wox.PluginLoader {
public abstract class PluginConfigLoader {
private static string PluginPath = Path.Combine(Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath), "Plugins");
private static string PluginConfigName = "plugin.json";
private static List<PluginMetadata> pluginMetadatas = new List<PluginMetadata>();
public static List<PluginMetadata> ParsePluginsConfig()
{
pluginMetadatas.Clear();
ParseSystemPlugins();
ParseThirdPartyPlugins();
if (Plugins.DebuggerMode != null) {
PluginMetadata metadata = GetMetadataFromJson(Plugins.DebuggerMode);
if (metadata != null) pluginMetadatas.Add(metadata);
}
return pluginMetadatas;
}
private static void ParseSystemPlugins() {
pluginMetadatas.Add(new PluginMetadata() {
Name = "System Plugins",
Author = "System",
Description = "system plugins collection",
Website = "http://www.getwox.com",
Language = AllowedLanguage.CSharp,
Version = "1.0",
PluginType = PluginType.System,
ActionKeyword = "*",
ExecuteFileName = "Wox.Plugin.SystemPlugins.dll",
PluginDirecotry = Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath)
});
}
private static void ParseThirdPartyPlugins() {
if (!Directory.Exists(PluginPath))
Directory.CreateDirectory(PluginPath);
string[] directories = Directory.GetDirectories(PluginPath);
foreach (string directory in directories) {
if (File.Exists((Path.Combine(directory, "NeedDelete.txt")))) {
Directory.Delete(directory, true);
continue;
}
PluginMetadata metadata = GetMetadataFromJson(directory);
if (metadata != null) pluginMetadatas.Add(metadata);
}
}
private static PluginMetadata GetMetadataFromJson(string pluginDirectory) {
string configPath = Path.Combine(pluginDirectory, PluginConfigName);
PluginMetadata metadata;
if (!File.Exists(configPath)) {
Log.Warn(string.Format("parse plugin {0} failed: didn't find config file.", configPath));
return null;
}
try {
metadata = JsonConvert.DeserializeObject<PluginMetadata>(File.ReadAllText(configPath));
metadata.PluginType = PluginType.ThirdParty;
metadata.PluginDirecotry = pluginDirectory;
}
catch (Exception) {
string error = string.Format("Parse plugin config {0} failed: json format is not valid", configPath);
Log.Warn(error);
#if (DEBUG)
{
throw new WoxException(error);
}
#endif
return null;
}
if (!AllowedLanguage.IsAllowed(metadata.Language)) {
string error = string.Format("Parse plugin config {0} failed: invalid language {1}", configPath, metadata.Language);
Log.Warn(error);
#if (DEBUG)
{
throw new WoxException(error);
}
#endif
return null;
}
if (!File.Exists(metadata.ExecuteFilePath)) {
string error = string.Format("Parse plugin config {0} failed: ExecuteFile {1} didn't exist", configPath, metadata.ExecuteFilePath);
Log.Warn(error);
#if (DEBUG)
{
throw new WoxException(error);
}
#endif
return null;
}
var customizedPluginConfig =
UserSettingStorage.Instance.CustomizedPluginConfigs.FirstOrDefault(o => o.ID == metadata.ID);
if (customizedPluginConfig != null && !string.IsNullOrEmpty(customizedPluginConfig.Actionword))
{
metadata.ActionKeyword = customizedPluginConfig.Actionword;
}
return metadata;
}
}
}

View File

@@ -1,96 +1,56 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Microsoft.CSharp;
using Wox.Helper;
using Wox.Infrastructure;
using Wox.Infrastructure.Storage;
using Wox.Infrastructure.Storage.UserSettings;
using Wox.Plugin;
namespace Wox.PluginLoader {
public static class Plugins {
//private static string debuggerMode = null;
public static String DebuggerMode { get; private set; }
namespace Wox.PluginLoader
{
public static class Plugins
{
public static String DebuggerMode { get; private set; }
private static List<PluginPair> plugins = new List<PluginPair>();
private static List<PluginPair> plugins = new List<PluginPair>();
private static ManualResetEvent initializing = null;
public static void Init()
{
plugins.Clear();
List<PluginMetadata> pluginMetadatas = PluginConfigLoader.ParsePluginsConfig();
public static void Init() {
if (initializing != null) return;
plugins.AddRange(new CSharpPluginLoader().LoadPlugin(pluginMetadatas));
plugins.AddRange(new BasePluginLoader<PythonPlugin>().LoadPlugin(pluginMetadatas));
initializing = new ManualResetEvent(false);
plugins.Clear();
BasePluginLoader.ParsePluginsConfig();
plugins.AddRange(new PythonPluginLoader().LoadPlugin());
plugins.AddRange(new CSharpPluginLoader().LoadPlugin());
Forker forker = new Forker();
foreach (IPlugin plugin in plugins.Select(pluginPair => pluginPair.Plugin)) {
IPlugin plugin1 = plugin;
PluginPair pluginPair = plugins.FirstOrDefault(o => o.Plugin == plugin1);
if (pluginPair != null) {
PluginMetadata metadata = pluginPair.Metadata;
pluginPair.InitContext = new PluginInitContext() {
Plugins = plugins,
CurrentPluginMetadata = metadata,
ChangeQuery = s => App.Window.Dispatcher.Invoke(new Action(() => App.Window.ChangeQuery(s))),
CloseApp = () => App.Window.Dispatcher.Invoke(new Action(() => App.Window.CloseApp())),
HideApp = () => App.Window.Dispatcher.Invoke(new Action(() => App.Window.HideApp())),
ShowApp = () => App.Window.Dispatcher.Invoke(new Action(() => App.Window.ShowApp())),
ShowMsg = (title, subTitle, iconPath) => App.Window.Dispatcher.Invoke(new Action(() =>
App.Window.ShowMsg(title, subTitle, iconPath))),
OpenSettingDialog = () => App.Window.Dispatcher.Invoke(new Action(() => App.Window.OpenSettingDialog())),
ShowCurrentResultItemTooltip = (msg) => App.Window.Dispatcher.Invoke(new Action(() => App.Window.ShowCurrentResultItemTooltip(msg))),
ReloadPlugins = () => App.Window.Dispatcher.Invoke(new Action(() => Init())),
InstallPlugin = (filePath) => App.Window.Dispatcher.Invoke(new Action(() => {
PluginInstaller.Install(filePath);
})),
StartLoadingBar = () => App.Window.Dispatcher.Invoke(new Action(() => App.Window.StartLoadingBar())),
StopLoadingBar = () => App.Window.Dispatcher.Invoke(new Action(() => App.Window.StopLoadingBar())),
//ShellRun = (cmd) => (bool)App.Window.Dispatcher.Invoke(new Func<bool>(() => App.Window.ShellRun(cmd)))
};
Forker forker = new Forker();
foreach (PluginPair pluginPair in plugins)
{
PluginPair pair = pluginPair;
forker.Fork(() => pair.Plugin.Init(new PluginInitContext()
{
CurrentPluginMetadata = pair.Metadata,
API = App.Window
}));
}
pluginPair.InitContext.ShellRun = (cmd) => {
try {
return (bool)App.Window.Dispatcher.Invoke(new Func<bool>(() => App.Window.ShellRun(cmd)));
}
catch (Exception) {
return false;
}
};
forker.Join();
}
forker.Fork(() => plugin1.Init(pluginPair.InitContext));
}
}
public static List<PluginPair> AllPlugins
{
get
{
return plugins;
}
}
ThreadPool.QueueUserWorkItem(o => {
forker.Join();
initializing.Set();
initializing = null;
});
}
public static bool HitThirdpartyKeyword(Query query)
{
if (string.IsNullOrEmpty(query.ActionName)) return false;
public static List<PluginPair> AllPlugins {
get {
var init = initializing;
if (init != null) {
init.WaitOne();
}
return plugins;
}
}
return plugins.Any(o => o.Metadata.PluginType == PluginType.ThirdParty && o.Metadata.ActionKeyword == query.ActionName);
}
public static bool HitThirdpartyKeyword(Query query) {
if (string.IsNullOrEmpty(query.ActionName)) return false;
return plugins.Any(o => o.Metadata.PluginType == PluginType.ThirdParty && o.Metadata.ActionKeyword == query.ActionName);
}
public static void ActivatePluginDebugger(string path) {
DebuggerMode = path;
}
}
public static void ActivatePluginDebugger(string path)
{
DebuggerMode = path;
}
}
}

View File

@@ -0,0 +1,33 @@
using System.Collections.Generic;
using System.IO;
using Wox.JsonRPC;
using Wox.Plugin;
namespace Wox.PluginLoader
{
public class PythonPlugin : BasePlugin
{
private static string woxDirectory = Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath);
public override string SupportedLanguage
{
get { return AllowedLanguage.Python; }
}
protected override string ExecuteQuery(Query query)
{
string fileName = Path.Combine(woxDirectory, "PythonHome\\pythonw.exe");
string parameters = string.Format("{0} \"{1}\"", context.CurrentPluginMetadata.ExecuteFilePath,
string.Format(@"{{\""method\"": \""query\"", \""parameters\"": [\""{0}\""]}}",query.GetAllRemainingParameter()));
return Execute(fileName, parameters);
}
protected override string ExecuteAction(JsonRPCRequestModel rpcRequest)
{
string fileName = Path.Combine(woxDirectory, "PythonHome\\pythonw.exe");
string parameters = string.Format("{0} \"{1}\"", context.CurrentPluginMetadata.ExecuteFilePath,rpcRequest);
return Execute(fileName, parameters);
}
}
}

View File

@@ -1,55 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using Python.Runtime;
using Wox.Plugin;
using Wox.Helper;
namespace Wox.PluginLoader
{
public class PythonPluginLoader : BasePluginLoader
{
public override List<PluginPair> LoadPlugin()
{
if (!CheckPythonEnvironmentInstalled()) return new List<PluginPair>();
List<PluginPair> plugins = new List<PluginPair>();
List<PluginMetadata> metadatas = pluginMetadatas.Where(o => o.Language.ToUpper() == AllowedLanguage.Python.ToUpper()).ToList();
foreach (PluginMetadata metadata in metadatas)
{
PythonPluginWrapper python = new PythonPluginWrapper(metadata);
PluginPair pair = new PluginPair()
{
Plugin = python,
Metadata = metadata
};
plugins.Add(pair);
}
return plugins;
}
private bool CheckPythonEnvironmentInstalled() {
try
{
SetPythonHome();
PythonEngine.Initialize();
PythonEngine.Shutdown();
}
catch {
Log.Warn("Could't find python environment, all python plugins disabled.");
return false;
}
return true;
}
private void SetPythonHome()
{
//Environment.SetEnvironmentVariable("PYTHONHOME",Path.Combine(Path.GetDirectoryName(Application.ExecutablePath),"PythonHome"));
//PythonEngine.PythonHome =
//PythonEngine.ProgramName
}
}
}

View File

@@ -1,142 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Python.Runtime;
using Wox.Helper;
using Wox.Plugin;
namespace Wox.PluginLoader
{
public class PythonPluginWrapper : IPlugin
{
private PluginMetadata metadata;
private string moduleName;
public PythonPluginWrapper(PluginMetadata metadata)
{
this.metadata = metadata;
moduleName = metadata.ExecuteFileName.Replace(".py", "");
}
public List<Result> Query(Query query)
{
try
{
string jsonResult = InvokeFunc("query", query.RawQuery);
if (string.IsNullOrEmpty(jsonResult))
{
return new List<Result>();
}
List<PythonResult> o = JsonConvert.DeserializeObject<List<PythonResult>>(jsonResult);
List<Result> r = new List<Result>();
foreach (PythonResult pythonResult in o)
{
PythonResult ps = pythonResult;
if (!string.IsNullOrEmpty(ps.ActionName))
{
ps.Action = (context) =>
{
InvokeFunc(ps.ActionName, GetPythonActionContext(context), new PyString(ps.ActionPara));
return true;
};
}
r.Add(ps);
}
return r;
}
catch (Exception e)
{
#if (DEBUG)
{
throw new WoxPythonException(e.Message);
}
#endif
Log.Error(string.Format("Python Plugin {0} query failed: {1}", metadata.Name, e.Message));
}
return new List<Result>();
}
private PyObject GetPythonActionContext(ActionContext context)
{
PyDict dict = new PyDict();
PyDict specialKeyStateDict = new PyDict();
specialKeyStateDict["CtrlPressed"] = new PyString(context.SpecialKeyState.CtrlPressed.ToString());
specialKeyStateDict["AltPressed"] = new PyString(context.SpecialKeyState.AltPressed.ToString());
specialKeyStateDict["WinPressed"] = new PyString(context.SpecialKeyState.WinPressed.ToString());
specialKeyStateDict["ShiftPressed"] = new PyString(context.SpecialKeyState.ShiftPressed.ToString());
dict["SpecialKeyState"] = specialKeyStateDict;
return dict;
}
private string InvokeFunc(string func, params PyObject[] paras)
{
string json = null;
//if pythobn plugin folder name is chinese, here will deadlock.
IntPtr gs = PythonEngine.AcquireLock();
PyObject module = PythonEngine.ImportModule(moduleName);
if (module == null)
{
string error = string.Format("Python Invoke failed: {0} doesn't has module {1}",
metadata.ExecuteFilePath, moduleName);
Log.Error(error);
return json;
}
if (module.HasAttr(func))
{
try
{
PyObject res = paras.Length > 0 ? module.InvokeMethod(func, paras) : module.InvokeMethod(func);
json = Runtime.GetManagedString(res.Handle);
}
catch (Exception e)
{
string error = string.Format("Python Invoke failed: {0}", e.Message);
Log.Error(error);
#if (DEBUG)
{
throw new WoxPythonException(error);
}
#endif
}
}
else
{
string error = string.Format("Python Invoke failed: {0} doesn't has function {1}",
metadata.ExecuteFilePath, func);
Log.Error(error);
#if (DEBUG)
{
throw new WoxPythonException(error);
}
#endif
}
PythonEngine.ReleaseLock(gs);
return json;
}
private string InvokeFunc(string func, params string[] para)
{
PyObject[] paras = { };
if (para != null && para.Length > 0)
{
paras = para.Select(o => new PyString(o)).ToArray();
}
return InvokeFunc(func, paras);
}
public void Init(PluginInitContext context)
{
}
}
}

View File

@@ -22,7 +22,7 @@
<ColumnDefinition Width="32"></ColumnDefinition>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Image x:Name="imgIco" Width="32" Height="32" HorizontalAlignment="Left" >
<Image x:Name="imgIco" Width="32" Height="32" HorizontalAlignment="Left">
<Image.Resources>
<converters:ImagePathConverter x:Key="ImageConverter"/>
</Image.Resources>

View File

@@ -1,9 +1,12 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using Wox.Helper;
using Wox.Plugin;
using UserControl = System.Windows.Controls.UserControl;
@@ -23,6 +26,7 @@ namespace Wox
public void AddResults(List<Result> results)
{
if (Dirty)
{
Dirty = false;
@@ -30,6 +34,10 @@ namespace Wox
}
foreach (var result in results)
{
//ThreadPool.QueueUserWorkItem(delegate
// {
// ImageLoader.Load(Path.Combine(result.PluginDirectory, result.IcoPath));
// });
int position = GetInsertLocation(result.Score);
lbResults.Items.Insert(position, result);
}

View File

@@ -120,6 +120,7 @@
</Compile>
<Compile Include="Helper\FontHelper.cs" />
<Compile Include="Helper\Forker.cs" />
<Compile Include="Helper\ImageLoader.cs" />
<Compile Include="Helper\SyntaxSugars.cs" />
<Compile Include="Helper\WallpaperPathRetrieval.cs" />
<Compile Include="Helper\WindowIntelopHelper.cs" />
@@ -138,14 +139,17 @@
<DependentUpon>HotkeyControl.xaml</DependentUpon>
</Compile>
<Compile Include="Converters\ImagePathConverter.cs" />
<Compile Include="PluginLoader\IPluginLoader.cs" />
<Compile Include="Msg.xaml.cs">
<DependentUpon>Msg.xaml</DependentUpon>
</Compile>
<Compile Include="PluginLoader\BasePluginLoader.cs" />
<Compile Include="PluginLoader\PluginConfigLoader.cs" />
<Compile Include="PluginLoader\CSharpPluginLoader.cs" />
<Compile Include="PluginLoader\BasePluginLoader.cs" />
<Compile Include="PluginLoader\BasePlugin.cs" />
<Compile Include="JsonRPC\JsonPRCModel.cs" />
<Compile Include="PluginLoader\Plugins.cs" />
<Compile Include="PluginLoader\PythonPluginLoader.cs" />
<Compile Include="PluginLoader\PythonPluginWrapper.cs" />
<Compile Include="PluginLoader\PythonPlugin.cs" />
<Compile Include="Properties\Annotations.cs" />
<Compile Include="ResultPanel.xaml.cs">
<DependentUpon>ResultPanel.xaml</DependentUpon>
@@ -260,10 +264,6 @@
<AppDesigner Include="Properties\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Pythonnet.Runtime\Python.Runtime.csproj">
<Project>{097b4ac0-74e9-4c58-bcf8-c69746ec8271}</Project>
<Name>Python.Runtime</Name>
</ProjectReference>
<ProjectReference Include="..\Wox.Infrastructure\Wox.Infrastructure.csproj">
<Project>{4fd29318-a8ab-4d8f-aa47-60bc241b8da3}</Project>
<Name>Wox.Infrastructure</Name>