Merge branch 'V1.1.0' into dev
@ -8,7 +8,7 @@ namespace Wox.Plugin.BrowserBookmark
|
|||||||
{
|
{
|
||||||
private PluginInitContext context;
|
private PluginInitContext context;
|
||||||
|
|
||||||
// TODO: periodically refresh the cache?
|
// TODO: periodically refresh the Cache?
|
||||||
private List<Bookmark> cachedBookmarks = new List<Bookmark>();
|
private List<Bookmark> cachedBookmarks = new List<Bookmark>();
|
||||||
|
|
||||||
public void Init(PluginInitContext context)
|
public void Init(PluginInitContext context)
|
||||||
|
46
Plugins/Wox.Plugin.FindFile/FindFileContextMenuStorage.cs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Wox.Infrastructure.Storage;
|
||||||
|
|
||||||
|
namespace Wox.Plugin.FindFile
|
||||||
|
{
|
||||||
|
public class FindFileContextMenuStorage : BaseStorage<FindFileContextMenuStorage>
|
||||||
|
{
|
||||||
|
[JsonProperty]
|
||||||
|
public List<ContextMenu> ContextMenus = new List<ContextMenu>();
|
||||||
|
|
||||||
|
protected override string ConfigName
|
||||||
|
{
|
||||||
|
get { return "FindFileContextMenu"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override FindFileContextMenuStorage LoadDefaultConfig()
|
||||||
|
{
|
||||||
|
ContextMenus = new List<ContextMenu>()
|
||||||
|
{
|
||||||
|
new ContextMenu()
|
||||||
|
{
|
||||||
|
Name = "Open Containing Folder",
|
||||||
|
Command = "explorer.exe",
|
||||||
|
Argument = " /select \"{path}\""
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ContextMenu
|
||||||
|
{
|
||||||
|
[JsonProperty]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty]
|
||||||
|
public string Command { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty]
|
||||||
|
public string Argument { get; set; }
|
||||||
|
}
|
||||||
|
}
|
BIN
Plugins/Wox.Plugin.FindFile/Images/file.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
Plugins/Wox.Plugin.FindFile/Images/find.png
Normal file
After Width: | Height: | Size: 522 B |
BIN
Plugins/Wox.Plugin.FindFile/Images/folder.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
Plugins/Wox.Plugin.FindFile/Images/list.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
Plugins/Wox.Plugin.FindFile/Images/open.png
Normal file
After Width: | Height: | Size: 941 B |
BIN
Plugins/Wox.Plugin.FindFile/Images/warning.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
22
Plugins/Wox.Plugin.FindFile/MFTSearch/MFTSearchRecord.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
namespace Wox.Plugin.FindFile.MFTSearch
|
||||||
|
{
|
||||||
|
public class MFTSearchRecord
|
||||||
|
{
|
||||||
|
private USNRecord usn;
|
||||||
|
|
||||||
|
public MFTSearchRecord(USNRecord usn)
|
||||||
|
{
|
||||||
|
this.usn = usn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string FullPath
|
||||||
|
{
|
||||||
|
get { return usn.FullPath; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsFolder
|
||||||
|
{
|
||||||
|
get { return usn.IsFolder; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
258
Plugins/Wox.Plugin.FindFile/MFTSearch/MFTSearcher.cs
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
/*
|
||||||
|
* Thanks to the https://github.com/yiwenshengmei/MyEverything, we can bring MFT search to Wox
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Wox.Plugin.FindFile.MFTSearch
|
||||||
|
{
|
||||||
|
|
||||||
|
public class MFTSearcher
|
||||||
|
{
|
||||||
|
private static MFTSearcherCache cache = new MFTSearcherCache();
|
||||||
|
private static VolumeMonitor monitor = new VolumeMonitor();
|
||||||
|
|
||||||
|
private static void IndexVolume(string volume)
|
||||||
|
{
|
||||||
|
cache.EnsureVolumeExistInHashTable(volume);
|
||||||
|
EnumerateVolume(volume, cache.VolumeRecords[volume]);
|
||||||
|
monitor.Monitor(volume,cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void IndexAllVolumes()
|
||||||
|
{
|
||||||
|
foreach (DriveInfo drive in DriveInfo.GetDrives())
|
||||||
|
{
|
||||||
|
IndexVolume(drive.Name.Replace("\\", ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long IndexedFileCount
|
||||||
|
{
|
||||||
|
get { return cache.RecordsCount; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<MFTSearchRecord> Search(string item)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(item)) return new List<MFTSearchRecord>();
|
||||||
|
|
||||||
|
List<USNRecord> found = cache.FindByName(item, 100);
|
||||||
|
found.ForEach(x => FillPath(x.VolumeName, x, cache));
|
||||||
|
return found.ConvertAll(o => new MFTSearchRecord(o));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddVolumeRootRecord(string volumeName, Dictionary<ulong, USNRecord> files)
|
||||||
|
{
|
||||||
|
string rightVolumeName = string.Concat("\\\\.\\", volumeName);
|
||||||
|
rightVolumeName = string.Concat(rightVolumeName, Path.DirectorySeparatorChar);
|
||||||
|
IntPtr hRoot = PInvokeWin32.CreateFile(rightVolumeName,
|
||||||
|
0,
|
||||||
|
PInvokeWin32.FILE_SHARE_READ | PInvokeWin32.FILE_SHARE_WRITE,
|
||||||
|
IntPtr.Zero,
|
||||||
|
PInvokeWin32.OPEN_EXISTING,
|
||||||
|
PInvokeWin32.FILE_FLAG_BACKUP_SEMANTICS,
|
||||||
|
IntPtr.Zero);
|
||||||
|
|
||||||
|
if (hRoot.ToInt32() != PInvokeWin32.INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
PInvokeWin32.BY_HANDLE_FILE_INFORMATION fi = new PInvokeWin32.BY_HANDLE_FILE_INFORMATION();
|
||||||
|
bool bRtn = PInvokeWin32.GetFileInformationByHandle(hRoot, out fi);
|
||||||
|
if (bRtn)
|
||||||
|
{
|
||||||
|
UInt64 fileIndexHigh = (UInt64)fi.FileIndexHigh;
|
||||||
|
UInt64 indexRoot = (fileIndexHigh << 32) | fi.FileIndexLow;
|
||||||
|
|
||||||
|
files.Add(indexRoot, new USNRecord
|
||||||
|
{
|
||||||
|
FRN = indexRoot,
|
||||||
|
Name = volumeName,
|
||||||
|
ParentFrn = 0,
|
||||||
|
IsVolumeRoot = true,
|
||||||
|
IsFolder = true,
|
||||||
|
VolumeName = volumeName
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new IOException("GetFileInformationbyHandle() returned invalid handle",
|
||||||
|
new Win32Exception(Marshal.GetLastWin32Error()));
|
||||||
|
}
|
||||||
|
PInvokeWin32.CloseHandle(hRoot);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new IOException("Unable to get root frn entry", new Win32Exception(Marshal.GetLastWin32Error()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EnumerateVolume(string volumeName, Dictionary<ulong, USNRecord> files)
|
||||||
|
{
|
||||||
|
IntPtr medBuffer = IntPtr.Zero;
|
||||||
|
IntPtr pVolume = IntPtr.Zero;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
AddVolumeRootRecord(volumeName, files);
|
||||||
|
pVolume = GetVolumeJournalHandle(volumeName);
|
||||||
|
EnableVomuleJournal(pVolume);
|
||||||
|
|
||||||
|
SetupMFTEnumInBuffer(ref medBuffer, pVolume);
|
||||||
|
EnumerateFiles(volumeName, pVolume, medBuffer, files);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e.Message, e);
|
||||||
|
Exception innerException = e.InnerException;
|
||||||
|
while (innerException != null)
|
||||||
|
{
|
||||||
|
Console.WriteLine(innerException.Message, innerException);
|
||||||
|
innerException = innerException.InnerException;
|
||||||
|
}
|
||||||
|
throw new ApplicationException("Error in EnumerateVolume()", e);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (pVolume.ToInt32() != PInvokeWin32.INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
PInvokeWin32.CloseHandle(pVolume);
|
||||||
|
if (medBuffer != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Marshal.FreeHGlobal(medBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static IntPtr GetVolumeJournalHandle(string volumeName)
|
||||||
|
{
|
||||||
|
string vol = string.Concat("\\\\.\\", volumeName);
|
||||||
|
IntPtr pVolume = PInvokeWin32.CreateFile(vol,
|
||||||
|
PInvokeWin32.GENERIC_READ | PInvokeWin32.GENERIC_WRITE,
|
||||||
|
PInvokeWin32.FILE_SHARE_READ | PInvokeWin32.FILE_SHARE_WRITE,
|
||||||
|
IntPtr.Zero,
|
||||||
|
PInvokeWin32.OPEN_EXISTING,
|
||||||
|
0,
|
||||||
|
IntPtr.Zero);
|
||||||
|
if (pVolume.ToInt32() == PInvokeWin32.INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
throw new IOException(string.Format("CreateFile(\"{0}\") returned invalid handle", volumeName),
|
||||||
|
new Win32Exception(Marshal.GetLastWin32Error()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return pVolume;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe private static void EnableVomuleJournal(IntPtr pVolume)
|
||||||
|
{
|
||||||
|
UInt64 MaximumSize = 0x800000;
|
||||||
|
UInt64 AllocationDelta = 0x100000;
|
||||||
|
UInt32 cb;
|
||||||
|
PInvokeWin32.CREATE_USN_JOURNAL_DATA cujd;
|
||||||
|
cujd.MaximumSize = MaximumSize;
|
||||||
|
cujd.AllocationDelta = AllocationDelta;
|
||||||
|
|
||||||
|
int sizeCujd = Marshal.SizeOf(cujd);
|
||||||
|
IntPtr cujdBuffer = Marshal.AllocHGlobal(sizeCujd);
|
||||||
|
PInvokeWin32.ZeroMemory(cujdBuffer, sizeCujd);
|
||||||
|
Marshal.StructureToPtr(cujd, cujdBuffer, true);
|
||||||
|
|
||||||
|
bool fOk = PInvokeWin32.DeviceIoControl(pVolume, PInvokeWin32.FSCTL_CREATE_USN_JOURNAL,
|
||||||
|
cujdBuffer, sizeCujd, IntPtr.Zero, 0, out cb, IntPtr.Zero);
|
||||||
|
if (!fOk)
|
||||||
|
{
|
||||||
|
throw new IOException("DeviceIoControl() returned false", new Win32Exception(Marshal.GetLastWin32Error()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe internal static bool QueryUSNJournal(IntPtr pVolume, out PInvokeWin32.USN_JOURNAL_DATA ujd, out uint bytesReturned)
|
||||||
|
{
|
||||||
|
bool bOK = PInvokeWin32.DeviceIoControl(
|
||||||
|
pVolume, PInvokeWin32.FSCTL_QUERY_USN_JOURNAL,
|
||||||
|
IntPtr.Zero,
|
||||||
|
0,
|
||||||
|
out ujd,
|
||||||
|
sizeof(PInvokeWin32.USN_JOURNAL_DATA),
|
||||||
|
out bytesReturned,
|
||||||
|
IntPtr.Zero
|
||||||
|
);
|
||||||
|
return bOK;
|
||||||
|
}
|
||||||
|
unsafe private static void SetupMFTEnumInBuffer(ref IntPtr medBuffer, IntPtr pVolume)
|
||||||
|
{
|
||||||
|
uint bytesReturned = 0;
|
||||||
|
PInvokeWin32.USN_JOURNAL_DATA ujd = new PInvokeWin32.USN_JOURNAL_DATA();
|
||||||
|
|
||||||
|
bool bOk = QueryUSNJournal(pVolume, out ujd, out bytesReturned);
|
||||||
|
if (bOk)
|
||||||
|
{
|
||||||
|
PInvokeWin32.MFT_ENUM_DATA med;
|
||||||
|
med.StartFileReferenceNumber = 0;
|
||||||
|
med.LowUsn = 0;
|
||||||
|
med.HighUsn = ujd.NextUsn;
|
||||||
|
int sizeMftEnumData = Marshal.SizeOf(med);
|
||||||
|
medBuffer = Marshal.AllocHGlobal(sizeMftEnumData);
|
||||||
|
PInvokeWin32.ZeroMemory(medBuffer, sizeMftEnumData);
|
||||||
|
Marshal.StructureToPtr(med, medBuffer, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new IOException("DeviceIoControl() returned false", new Win32Exception(Marshal.GetLastWin32Error()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe private static void EnumerateFiles(string volumeName, IntPtr pVolume, IntPtr medBuffer, Dictionary<ulong, USNRecord> files)
|
||||||
|
{
|
||||||
|
IntPtr pData = Marshal.AllocHGlobal(sizeof(UInt64) + 0x10000);
|
||||||
|
PInvokeWin32.ZeroMemory(pData, sizeof(UInt64) + 0x10000);
|
||||||
|
uint outBytesReturned = 0;
|
||||||
|
|
||||||
|
while (false != PInvokeWin32.DeviceIoControl(pVolume, PInvokeWin32.FSCTL_ENUM_USN_DATA, medBuffer,
|
||||||
|
sizeof(PInvokeWin32.MFT_ENUM_DATA), pData, sizeof(UInt64) + 0x10000, out outBytesReturned,
|
||||||
|
IntPtr.Zero))
|
||||||
|
{
|
||||||
|
IntPtr pUsnRecord = new IntPtr(pData.ToInt32() + sizeof(Int64));
|
||||||
|
while (outBytesReturned > 60)
|
||||||
|
{
|
||||||
|
PInvokeWin32.USN_RECORD usn = new PInvokeWin32.USN_RECORD(pUsnRecord);
|
||||||
|
|
||||||
|
files.Add(usn.FRN, new USNRecord
|
||||||
|
{
|
||||||
|
Name = usn.FileName,
|
||||||
|
ParentFrn = usn.ParentFRN,
|
||||||
|
FRN = usn.FRN,
|
||||||
|
IsFolder = usn.IsFolder,
|
||||||
|
VolumeName = volumeName
|
||||||
|
});
|
||||||
|
|
||||||
|
pUsnRecord = new IntPtr(pUsnRecord.ToInt32() + usn.RecordLength);
|
||||||
|
outBytesReturned -= usn.RecordLength;
|
||||||
|
}
|
||||||
|
Marshal.WriteInt64(medBuffer, Marshal.ReadInt64(pData, 0));
|
||||||
|
}
|
||||||
|
Marshal.FreeHGlobal(pData);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void FillPath(string volume, USNRecord record, MFTSearcherCache db)
|
||||||
|
{
|
||||||
|
if (record == null) return;
|
||||||
|
var fdSource = db.GetVolumeRecords(volume);
|
||||||
|
string fullpath = record.Name;
|
||||||
|
FindRecordPath(record, ref fullpath, fdSource);
|
||||||
|
record.FullPath = fullpath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void FindRecordPath(USNRecord curRecord, ref string fullpath, Dictionary<ulong, USNRecord> fdSource)
|
||||||
|
{
|
||||||
|
if (curRecord.IsVolumeRoot) return;
|
||||||
|
USNRecord nextRecord = null;
|
||||||
|
if (!fdSource.TryGetValue(curRecord.ParentFrn, out nextRecord))
|
||||||
|
return;
|
||||||
|
fullpath = string.Format("{0}{1}{2}", nextRecord.Name, Path.DirectorySeparatorChar, fullpath);
|
||||||
|
FindRecordPath(nextRecord, ref fullpath, fdSource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
129
Plugins/Wox.Plugin.FindFile/MFTSearch/MFTSearcherCache.cs
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Wox.Plugin.FindFile.MFTSearch
|
||||||
|
{
|
||||||
|
internal class MFTSearcherCache
|
||||||
|
{
|
||||||
|
public Dictionary<string, Dictionary<ulong, USNRecord>> VolumeRecords = new Dictionary<string, Dictionary<ulong, USNRecord>>();
|
||||||
|
public static object locker = new object();
|
||||||
|
|
||||||
|
public MFTSearcherCache() { }
|
||||||
|
|
||||||
|
public bool ContainsVolume(string volume)
|
||||||
|
{
|
||||||
|
return VolumeRecords.ContainsKey(volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddRecord(string volume, List<USNRecord> r)
|
||||||
|
{
|
||||||
|
EnsureVolumeExistInHashTable(volume);
|
||||||
|
r.ForEach(x => VolumeRecords[volume].Add(x.FRN, x));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddRecord(string volume, USNRecord record)
|
||||||
|
{
|
||||||
|
EnsureVolumeExistInHashTable(volume);
|
||||||
|
if (!VolumeRecords[volume].ContainsKey(record.FRN))
|
||||||
|
{
|
||||||
|
lock (locker)
|
||||||
|
{
|
||||||
|
VolumeRecords[volume].Add(record.FRN, record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EnsureVolumeExistInHashTable(string volume)
|
||||||
|
{
|
||||||
|
if (!VolumeRecords.ContainsKey(volume))
|
||||||
|
VolumeRecords.Add(volume, new Dictionary<ulong, USNRecord>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool DeleteRecord(string volume, ulong frn)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
result = DeleteRecordHashTableItem(VolumeRecords, volume, frn);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool DeleteRecordHashTableItem(Dictionary<string, Dictionary<ulong, USNRecord>> hashtable, string volume, ulong frn)
|
||||||
|
{
|
||||||
|
if (hashtable.ContainsKey(volume) && hashtable[volume].ContainsKey(frn))
|
||||||
|
{
|
||||||
|
lock (locker)
|
||||||
|
{
|
||||||
|
hashtable[volume].Remove(frn);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateRecord(string volume, USNRecord record)
|
||||||
|
{
|
||||||
|
RealUpdateRecord(volume, VolumeRecords, record);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool RealUpdateRecord(string volume, Dictionary<string, Dictionary<ulong, USNRecord>> source, USNRecord record)
|
||||||
|
{
|
||||||
|
if (source.ContainsKey(volume) && source[volume].ContainsKey(record.FRN))
|
||||||
|
{
|
||||||
|
lock (locker)
|
||||||
|
{
|
||||||
|
source[volume][record.FRN] = record;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<USNRecord> FindByName(string filename, long maxResult = -1)
|
||||||
|
{
|
||||||
|
List<USNRecord> result = new List<USNRecord>();
|
||||||
|
lock (locker)
|
||||||
|
{
|
||||||
|
foreach (Dictionary<ulong, USNRecord> dictionary in VolumeRecords.Values)
|
||||||
|
{
|
||||||
|
foreach (var usnRecord in dictionary)
|
||||||
|
{
|
||||||
|
if (usnRecord.Value.Name.IndexOf(filename, StringComparison.OrdinalIgnoreCase) >= 0)
|
||||||
|
{
|
||||||
|
result.Add(usnRecord.Value);
|
||||||
|
if (maxResult > 0 && result.Count() >= maxResult) break;
|
||||||
|
}
|
||||||
|
if (maxResult > 0 && result.Count() >= maxResult) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public USNRecord FindByFrn(string volume, ulong frn)
|
||||||
|
{
|
||||||
|
if ((!VolumeRecords.ContainsKey(volume)))
|
||||||
|
throw new Exception(string.Format("DB not contain the volume: {0}", volume));
|
||||||
|
USNRecord result = null;
|
||||||
|
VolumeRecords[volume].TryGetValue(frn, out result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long RecordsCount
|
||||||
|
{
|
||||||
|
get { return VolumeRecords.Sum(x => x.Value.Count); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<ulong, USNRecord> GetVolumeRecords(string volume)
|
||||||
|
{
|
||||||
|
Dictionary<ulong, USNRecord> result = null;
|
||||||
|
VolumeRecords.TryGetValue(volume, out result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
165
Plugins/Wox.Plugin.FindFile/MFTSearch/PInvokeWin32.cs
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Wox.Plugin.FindFile.MFTSearch {
|
||||||
|
public class PInvokeWin32
|
||||||
|
{
|
||||||
|
|
||||||
|
public const UInt32 GENERIC_READ = 0x80000000;
|
||||||
|
public const UInt32 GENERIC_WRITE = 0x40000000;
|
||||||
|
public const UInt32 FILE_SHARE_READ = 0x00000001;
|
||||||
|
public const UInt32 FILE_SHARE_WRITE = 0x00000002;
|
||||||
|
public const UInt32 FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
|
||||||
|
public const UInt32 OPEN_EXISTING = 3;
|
||||||
|
public const UInt32 FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
|
||||||
|
public const Int32 INVALID_HANDLE_VALUE = -1;
|
||||||
|
public const UInt32 FSCTL_QUERY_USN_JOURNAL = 0x000900f4;
|
||||||
|
public const UInt32 FSCTL_ENUM_USN_DATA = 0x000900b3;
|
||||||
|
public const UInt32 FSCTL_CREATE_USN_JOURNAL = 0x000900e7;
|
||||||
|
public const UInt32 FSCTL_READ_USN_JOURNAL = 0x000900bb;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
public static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess,
|
||||||
|
uint dwShareMode, IntPtr lpSecurityAttributes,
|
||||||
|
uint dwCreationDisposition, uint dwFlagsAndAttributes,
|
||||||
|
IntPtr hTemplateFile);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
public static extern bool GetFileInformationByHandle(IntPtr hFile,
|
||||||
|
out BY_HANDLE_FILE_INFORMATION lpFileInformation);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
public static extern bool CloseHandle(IntPtr hObject);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
public static extern bool DeviceIoControl(IntPtr hDevice,
|
||||||
|
UInt32 dwIoControlCode,
|
||||||
|
IntPtr lpInBuffer, Int32 nInBufferSize,
|
||||||
|
out USN_JOURNAL_DATA lpOutBuffer, Int32 nOutBufferSize,
|
||||||
|
out uint lpBytesReturned, IntPtr lpOverlapped);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
public static extern bool DeviceIoControl(IntPtr hDevice,
|
||||||
|
UInt32 dwIoControlCode,
|
||||||
|
IntPtr lpInBuffer, Int32 nInBufferSize,
|
||||||
|
IntPtr lpOutBuffer, Int32 nOutBufferSize,
|
||||||
|
out uint lpBytesReturned, IntPtr lpOverlapped);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll")]
|
||||||
|
public static extern void ZeroMemory(IntPtr ptr, Int32 size);
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public struct BY_HANDLE_FILE_INFORMATION {
|
||||||
|
public uint FileAttributes;
|
||||||
|
public FILETIME CreationTime;
|
||||||
|
public FILETIME LastAccessTime;
|
||||||
|
public FILETIME LastWriteTime;
|
||||||
|
public uint VolumeSerialNumber;
|
||||||
|
public uint FileSizeHigh;
|
||||||
|
public uint FileSizeLow;
|
||||||
|
public uint NumberOfLinks;
|
||||||
|
public uint FileIndexHigh;
|
||||||
|
public uint FileIndexLow;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public struct FILETIME {
|
||||||
|
public uint DateTimeLow;
|
||||||
|
public uint DateTimeHigh;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public struct USN_JOURNAL_DATA {
|
||||||
|
public UInt64 UsnJournalID;
|
||||||
|
public Int64 FirstUsn;
|
||||||
|
public Int64 NextUsn;
|
||||||
|
public Int64 LowestValidUsn;
|
||||||
|
public Int64 MaxUsn;
|
||||||
|
public UInt64 MaximumSize;
|
||||||
|
public UInt64 AllocationDelta;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public struct MFT_ENUM_DATA {
|
||||||
|
public UInt64 StartFileReferenceNumber;
|
||||||
|
public Int64 LowUsn;
|
||||||
|
public Int64 HighUsn;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public struct CREATE_USN_JOURNAL_DATA {
|
||||||
|
public UInt64 MaximumSize;
|
||||||
|
public UInt64 AllocationDelta;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public struct READ_USN_JOURNAL_DATA {
|
||||||
|
public Int64 StartUsn;
|
||||||
|
public UInt32 ReasonMask;
|
||||||
|
public UInt32 ReturnOnlyOnClose;
|
||||||
|
public UInt64 Timeout;
|
||||||
|
public UInt64 BytesToWaitFor;
|
||||||
|
public UInt64 UsnJournalID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class USN_RECORD {
|
||||||
|
public UInt32 RecordLength;
|
||||||
|
public UInt16 MajorVersion;
|
||||||
|
public UInt16 MinorVersion;
|
||||||
|
public UInt64 FRN; // 8
|
||||||
|
public UInt64 ParentFRN; // 16
|
||||||
|
public Int64 Usn; // Need be care
|
||||||
|
public UInt64 TimeStamp; // Need Be care
|
||||||
|
public UInt32 Reason;
|
||||||
|
public UInt32 SourceInfo;
|
||||||
|
public UInt32 SecurityId;
|
||||||
|
public UInt32 FileAttributes; // 52
|
||||||
|
public UInt16 FileNameLength;
|
||||||
|
public UInt16 FileNameOffset;
|
||||||
|
public string FileName = string.Empty;
|
||||||
|
|
||||||
|
private const int RecordLength_OFFSET = 0;
|
||||||
|
private const int MajorVersion_OFFSET = 4;
|
||||||
|
private const int MinorVersion_OFFSET = 6;
|
||||||
|
private const int FileReferenceNumber_OFFSET = 8;
|
||||||
|
private const int ParentFileReferenceNumber_OFFSET = 16;
|
||||||
|
private const int Usn_OFFSET = 24;
|
||||||
|
private const int TimeStamp_OFFSET = 32;
|
||||||
|
private const int Reason_OFFSET = 40;
|
||||||
|
private const int SourceInfo_OFFSET = 44;
|
||||||
|
private const int SecurityId_OFFSET = 48;
|
||||||
|
private const int FileAttributes_OFFSET = 52;
|
||||||
|
private const int FileNameLength_OFFSET = 56;
|
||||||
|
private const int FileNameOffset_OFFSET = 58;
|
||||||
|
private const int FileName_OFFSET = 60;
|
||||||
|
|
||||||
|
public USN_RECORD(IntPtr p) {
|
||||||
|
this.RecordLength = (UInt32)Marshal.ReadInt32(p, RecordLength_OFFSET);
|
||||||
|
this.MajorVersion = (UInt16)Marshal.ReadInt16(p, MajorVersion_OFFSET);
|
||||||
|
this.MinorVersion = (UInt16)Marshal.ReadInt16(p, MinorVersion_OFFSET);
|
||||||
|
this.FRN = (UInt64)Marshal.ReadInt64(p, FileReferenceNumber_OFFSET);
|
||||||
|
this.ParentFRN = (UInt64)Marshal.ReadInt64(p, ParentFileReferenceNumber_OFFSET);
|
||||||
|
this.Usn = Marshal.ReadInt64(p, Usn_OFFSET);
|
||||||
|
this.TimeStamp = (UInt64)Marshal.ReadInt64(p, TimeStamp_OFFSET);
|
||||||
|
this.Reason = (UInt32)Marshal.ReadInt32(p, Reason_OFFSET);
|
||||||
|
this.SourceInfo = (UInt32)Marshal.ReadInt32(p, SourceInfo_OFFSET);
|
||||||
|
this.SecurityId = (UInt32)Marshal.ReadInt32(p, SecurityId_OFFSET);
|
||||||
|
this.FileAttributes = (UInt32)Marshal.ReadInt32(p, FileAttributes_OFFSET);
|
||||||
|
this.FileNameLength = (UInt16)Marshal.ReadInt16(p, FileNameLength_OFFSET);
|
||||||
|
this.FileNameOffset = (UInt16)Marshal.ReadInt16(p, FileNameOffset_OFFSET);
|
||||||
|
|
||||||
|
this.FileName = Marshal.PtrToStringUni(new IntPtr(p.ToInt32() + this.FileNameOffset), this.FileNameLength / sizeof(char));
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsFolder {
|
||||||
|
get { return 0 != (FileAttributes & PInvokeWin32.FILE_ATTRIBUTE_DIRECTORY); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
47
Plugins/Wox.Plugin.FindFile/MFTSearch/USNChangeReason.cs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Wox.Plugin.FindFile.MFTSearch
|
||||||
|
{
|
||||||
|
internal class USNChangeReason
|
||||||
|
{
|
||||||
|
public static Dictionary<string, UInt32> USN_REASONS = new Dictionary<string, UInt32> {
|
||||||
|
{"USN_REASON_DATA_OVERWRITE", 0x00000001},
|
||||||
|
{"USN_REASON_DATA_EXTEND", 0x00000002},
|
||||||
|
{"USN_REASON_DATA_TRUNCATION", 0x00000004},
|
||||||
|
{"USN_REASON_NAMED_DATA_OVERWRITE", 0x00000010},
|
||||||
|
{"USN_REASON_NAMED_DATA_EXTEND", 0x00000020},
|
||||||
|
{"USN_REASON_NAMED_DATA_TRUNCATION", 0x00000040},
|
||||||
|
{"USN_REASON_FILE_CREATE", 0x00000100},
|
||||||
|
{"USN_REASON_FILE_DELETE", 0x00000200},
|
||||||
|
{"USN_REASON_EA_CHANGE", 0x00000400},
|
||||||
|
{"USN_REASON_SECURITY_CHANGE", 0x00000800},
|
||||||
|
{"USN_REASON_RENAME_OLD_NAME", 0x00001000},
|
||||||
|
{"USN_REASON_RENAME_NEW_NAME", 0x00002000},
|
||||||
|
{"USN_REASON_INDEXABLE_CHANGE", 0x00004000},
|
||||||
|
{"USN_REASON_BASIC_INFO_CHANGE", 0x00008000},
|
||||||
|
{"USN_REASON_HARD_LINK_CHANGE", 0x00010000},
|
||||||
|
{"USN_REASON_COMPRESSION_CHANGE", 0x00020000},
|
||||||
|
{"USN_REASON_ENCRYPTION_CHANGE", 0x00040000},
|
||||||
|
{"USN_REASON_OBJECT_ID_CHANGE", 0x00080000},
|
||||||
|
{"USN_REASON_REPARSE_POINT_CHANGE", 0x00100000},
|
||||||
|
{"USN_REASON_STREAM_CHANGE", 0x00200000},
|
||||||
|
{"USN_REASON_TRANSACTED_CHANGE", 0x00400000},
|
||||||
|
{"USN_REASON_CLOSE", 0x80000000}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string ReasonPrettyFormat(UInt32 rsn)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.Append("[");
|
||||||
|
foreach (var rsnPair in USN_REASONS)
|
||||||
|
{
|
||||||
|
if ((rsnPair.Value & rsn) != 0)
|
||||||
|
sb.Append(rsnPair.Key + " ");
|
||||||
|
}
|
||||||
|
sb.Append("]");
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
Plugins/Wox.Plugin.FindFile/MFTSearch/USNRecord.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Wox.Plugin.FindFile.MFTSearch
|
||||||
|
{
|
||||||
|
public class USNRecord
|
||||||
|
{
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
public ulong FRN { get; set; }
|
||||||
|
public UInt64 ParentFrn { get; set; }
|
||||||
|
public string FullPath { get; set; }
|
||||||
|
public bool IsVolumeRoot { get; set; }
|
||||||
|
public bool IsFolder { get; set; }
|
||||||
|
public string VolumeName { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.IsNullOrEmpty(FullPath) ? Name : FullPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static USNRecord ParseUSN(string volume, PInvokeWin32.USN_RECORD usn)
|
||||||
|
{
|
||||||
|
return new USNRecord
|
||||||
|
{
|
||||||
|
FRN = usn.FRN,
|
||||||
|
Name = usn.FileName,
|
||||||
|
ParentFrn = usn.ParentFRN,
|
||||||
|
IsFolder = usn.IsFolder,
|
||||||
|
VolumeName = volume
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
139
Plugins/Wox.Plugin.FindFile/MFTSearch/VolumeMonitor.cs
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Wox.Plugin.FindFile.MFTSearch
|
||||||
|
{
|
||||||
|
internal class VolumeMonitor
|
||||||
|
{
|
||||||
|
public Action<USNRecord> RecordAddedEvent;
|
||||||
|
public Action<USNRecord> RecordDeletedEvent;
|
||||||
|
public Action<USNRecord, USNRecord> RecordRenameEvent;
|
||||||
|
|
||||||
|
public void Monitor(List<string> volumes, MFTSearcherCache db)
|
||||||
|
{
|
||||||
|
foreach (var volume in volumes)
|
||||||
|
{
|
||||||
|
Monitor(volume, db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Monitor(string volume, MFTSearcherCache db)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(volume)) throw new InvalidOperationException("Volume cant't be null or empty string.");
|
||||||
|
if (!db.ContainsVolume(volume)) throw new InvalidOperationException(string.Format("Volume {0} must be scaned first."));
|
||||||
|
|
||||||
|
ThreadPool.QueueUserWorkItem(o => MonitorThread(volume, db));
|
||||||
|
}
|
||||||
|
|
||||||
|
private PInvokeWin32.READ_USN_JOURNAL_DATA SetupInputData4JournalRead(string volume, uint reason)
|
||||||
|
{
|
||||||
|
IntPtr pMonitorVolume = MFTSearcher.GetVolumeJournalHandle(volume);
|
||||||
|
uint bytesReturned = 0;
|
||||||
|
PInvokeWin32.USN_JOURNAL_DATA ujd = new PInvokeWin32.USN_JOURNAL_DATA();
|
||||||
|
MFTSearcher.QueryUSNJournal(pMonitorVolume, out ujd, out bytesReturned);
|
||||||
|
|
||||||
|
// 构建输入参数
|
||||||
|
PInvokeWin32.READ_USN_JOURNAL_DATA rujd = new PInvokeWin32.READ_USN_JOURNAL_DATA();
|
||||||
|
rujd.StartUsn = ujd.NextUsn;
|
||||||
|
rujd.ReasonMask = reason;
|
||||||
|
rujd.ReturnOnlyOnClose = 1;
|
||||||
|
rujd.Timeout = 0;
|
||||||
|
rujd.BytesToWaitFor = 1;
|
||||||
|
rujd.UsnJournalID = ujd.UsnJournalID;
|
||||||
|
|
||||||
|
return rujd;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MonitorThread(string volume, MFTSearcherCache db)
|
||||||
|
{
|
||||||
|
IntPtr pbuffer = Marshal.AllocHGlobal(0x1000);
|
||||||
|
PInvokeWin32.READ_USN_JOURNAL_DATA rujd = SetupInputData4JournalRead(volume, 0xFFFFFFFF);
|
||||||
|
UInt32 cbRead;
|
||||||
|
IntPtr prujd;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
prujd = Marshal.AllocHGlobal(Marshal.SizeOf(rujd));
|
||||||
|
PInvokeWin32.ZeroMemory(prujd, Marshal.SizeOf(rujd));
|
||||||
|
Marshal.StructureToPtr(rujd, prujd, true);
|
||||||
|
|
||||||
|
IntPtr pVolume = MFTSearcher.GetVolumeJournalHandle(volume);
|
||||||
|
|
||||||
|
bool fok = PInvokeWin32.DeviceIoControl(pVolume,
|
||||||
|
PInvokeWin32.FSCTL_READ_USN_JOURNAL,
|
||||||
|
prujd, Marshal.SizeOf(typeof(PInvokeWin32.READ_USN_JOURNAL_DATA)),
|
||||||
|
pbuffer, 0x1000, out cbRead, IntPtr.Zero);
|
||||||
|
|
||||||
|
IntPtr pRealData = new IntPtr(pbuffer.ToInt32() + Marshal.SizeOf(typeof(Int64)));
|
||||||
|
uint offset = 0;
|
||||||
|
|
||||||
|
if (fok)
|
||||||
|
{
|
||||||
|
while (offset + Marshal.SizeOf(typeof(Int64)) < cbRead)
|
||||||
|
{
|
||||||
|
PInvokeWin32.USN_RECORD usn = new PInvokeWin32.USN_RECORD(new IntPtr(pRealData.ToInt32() + (int)offset));
|
||||||
|
ProcessUSN(usn, volume, db);
|
||||||
|
offset += usn.RecordLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Marshal.FreeHGlobal(prujd);
|
||||||
|
rujd.StartUsn = Marshal.ReadInt64(pbuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessUSN(PInvokeWin32.USN_RECORD usn, string volume, MFTSearcherCache db)
|
||||||
|
{
|
||||||
|
if (MaskEqual(usn.Reason, USNChangeReason.USN_REASONS["USN_REASON_RENAME_NEW_NAME"]))
|
||||||
|
ProcessRenameNewName(usn, volume, db);
|
||||||
|
if ((usn.Reason & USNChangeReason.USN_REASONS["USN_REASON_FILE_CREATE"]) != 0)
|
||||||
|
ProcessFileCreate(usn, volume, db);
|
||||||
|
if (MaskEqual(usn.Reason, USNChangeReason.USN_REASONS["USN_REASON_FILE_DELETE"]))
|
||||||
|
ProcessFileDelete(usn, volume, db);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessFileDelete(PInvokeWin32.USN_RECORD usn, string volume, MFTSearcherCache db)
|
||||||
|
{
|
||||||
|
var cached = db.FindByFrn(volume, usn.FRN);
|
||||||
|
if (cached != null)
|
||||||
|
{
|
||||||
|
MFTSearcher.FillPath(volume, cached, db);
|
||||||
|
var deleteok = db.DeleteRecord(volume, usn.FRN);
|
||||||
|
Debug.WriteLine(string.Format(">>>> DeleteFIle {0} {1}.", cached.FullPath, deleteok ? "successful" : "fail"));
|
||||||
|
if (RecordDeletedEvent != null)
|
||||||
|
RecordDeletedEvent(cached);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessRenameNewName(PInvokeWin32.USN_RECORD usn, string volume, MFTSearcherCache db)
|
||||||
|
{
|
||||||
|
USNRecord newRecord = USNRecord.ParseUSN(volume, usn);
|
||||||
|
db.UpdateRecord(volume, newRecord);
|
||||||
|
|
||||||
|
var oldRecord = db.FindByFrn(volume, usn.FRN);
|
||||||
|
if (oldRecord != null)
|
||||||
|
{
|
||||||
|
Debug.WriteLine(string.Format(">>>> RenameFile {0} to {1}", oldRecord.FullPath, newRecord.FullPath));
|
||||||
|
if (RecordRenameEvent != null) RecordRenameEvent(oldRecord, newRecord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessFileCreate(PInvokeWin32.USN_RECORD usn, string volume, MFTSearcherCache db)
|
||||||
|
{
|
||||||
|
USNRecord record = USNRecord.ParseUSN(volume, usn);
|
||||||
|
db.AddRecord(volume, record);
|
||||||
|
Debug.WriteLine(string.Format(">>>> NewFile: {0}", record.FullPath));
|
||||||
|
if (RecordAddedEvent != null)
|
||||||
|
RecordAddedEvent(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private bool MaskEqual(uint target, uint compare)
|
||||||
|
{
|
||||||
|
return (target & compare) != 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
114
Plugins/Wox.Plugin.FindFile/Main.cs
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.Serialization.Formatters.Binary;
|
||||||
|
using System.Text;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using Wox.Infrastructure;
|
||||||
|
using Wox.Plugin.FindFile.MFTSearch;
|
||||||
|
|
||||||
|
namespace Wox.Plugin.FindFile
|
||||||
|
{
|
||||||
|
public class Main : IPlugin, ISettingProvider
|
||||||
|
{
|
||||||
|
private PluginInitContext context;
|
||||||
|
private bool initial = false;
|
||||||
|
|
||||||
|
public List<Result> Query(Query query)
|
||||||
|
{
|
||||||
|
if (!initial)
|
||||||
|
{
|
||||||
|
return new List<Result>()
|
||||||
|
{
|
||||||
|
new Result("Wox is indexing your files, please try later.","Images/warning.png")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
string q = query.GetAllRemainingParameter();
|
||||||
|
return MFTSearcher.Search(q).Take(100).Select(t => ConvertMFTSearch(t, q)).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Init(PluginInitContext context)
|
||||||
|
{
|
||||||
|
this.context = context;
|
||||||
|
var searchtimestart = DateTime.Now;
|
||||||
|
MFTSearcher.IndexAllVolumes();
|
||||||
|
initial = true;
|
||||||
|
var searchtimeend = DateTime.Now;
|
||||||
|
Debug.WriteLine(string.Format("{0} file, indexed, {1}ms has spent.", MFTSearcher.IndexedFileCount, searchtimeend.Subtract(searchtimestart).TotalMilliseconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result ConvertMFTSearch(MFTSearchRecord record, string query)
|
||||||
|
{
|
||||||
|
string icoPath = "Images/file.png";
|
||||||
|
if (record.IsFolder)
|
||||||
|
{
|
||||||
|
icoPath = "Images/folder.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
string name = Path.GetFileName(record.FullPath);
|
||||||
|
FuzzyMatcher matcher = FuzzyMatcher.Create(query);
|
||||||
|
return new Result()
|
||||||
|
{
|
||||||
|
Title = name,
|
||||||
|
Score = matcher.Evaluate(name).Score,
|
||||||
|
SubTitle = record.FullPath,
|
||||||
|
IcoPath = icoPath,
|
||||||
|
Action = _ =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Process.Start(record.FullPath);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
context.API.ShowMsg("Can't open " + record.FullPath, string.Empty, string.Empty);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
ContextMenu = GetContextMenu(record)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Result> GetContextMenu(MFTSearchRecord record)
|
||||||
|
{
|
||||||
|
List<Result> contextMenus = new List<Result>();
|
||||||
|
|
||||||
|
if (!record.IsFolder)
|
||||||
|
{
|
||||||
|
foreach (ContextMenu contextMenu in FindFileContextMenuStorage.Instance.ContextMenus)
|
||||||
|
{
|
||||||
|
contextMenus.Add(new Result()
|
||||||
|
{
|
||||||
|
Title = contextMenu.Name,
|
||||||
|
Action = _ =>
|
||||||
|
{
|
||||||
|
string argument = contextMenu.Argument.Replace("{path}", record.FullPath);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Process.Start(contextMenu.Command,argument);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
context.API.ShowMsg("Can't start " + record.FullPath, string.Empty, string.Empty);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
IcoPath = "Images/list.png"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return contextMenus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Control CreateSettingPanel()
|
||||||
|
{
|
||||||
|
return new Setting();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
Plugins/Wox.Plugin.FindFile/Properties/AssemblyInfo.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// 有关程序集的常规信息通过以下
|
||||||
|
// 特性集控制。更改这些特性值可修改
|
||||||
|
// 与程序集关联的信息。
|
||||||
|
[assembly: AssemblyTitle("Wox.Plugin.FindFile")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("Wox.Plugin.FindFile")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © 2014")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// 将 ComVisible 设置为 false 使此程序集中的类型
|
||||||
|
// 对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型,
|
||||||
|
// 则将该类型上的 ComVisible 特性设置为 true。
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
|
||||||
|
[assembly: Guid("93b857b4-07a4-4ac1-a3ee-d038ace03ce9")]
|
||||||
|
|
||||||
|
// 程序集的版本信息由下面四个值组成:
|
||||||
|
//
|
||||||
|
// 主版本
|
||||||
|
// 次版本
|
||||||
|
// 生成号
|
||||||
|
// 修订号
|
||||||
|
//
|
||||||
|
// 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值,
|
||||||
|
// 方法是按如下所示使用“*”:
|
||||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
132
Plugins/Wox.Plugin.FindFile/Wox.Plugin.FindFile.csproj
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
<?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>{84EA88B4-71F2-4A3D-9771-D7B0244C0D81}</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>Wox.Plugin.FindFile</RootNamespace>
|
||||||
|
<AssemblyName>Wox.Plugin.FindFile</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<TargetFrameworkProfile />
|
||||||
|
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
|
||||||
|
<RestorePackages>true</RestorePackages>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>..\..\Output\Debug\Plugins\Wox.Plugin.FindFile\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>..\..\Output\Release\Plugins\Wox.Plugin.FindFile\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="log4net, Version=1.2.13.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\..\packages\log4net.2.0.3\lib\net35-full\log4net.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\..\packages\Newtonsoft.Json.6.0.5\lib\net35\Newtonsoft.Json.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="PresentationCore" />
|
||||||
|
<Reference Include="PresentationFramework" />
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Windows.Controls.Input.Toolkit">
|
||||||
|
<HintPath>..\..\packages\WPFToolkit.3.5.50211.1\lib\System.Windows.Controls.Input.Toolkit.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Windows.Controls.Layout.Toolkit">
|
||||||
|
<HintPath>..\..\packages\WPFToolkit.3.5.50211.1\lib\System.Windows.Controls.Layout.Toolkit.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="WindowsBase" />
|
||||||
|
<Reference Include="WPFToolkit, Version=3.5.40128.1, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\WPFToolkit.3.5.50211.1\lib\WPFToolkit.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="FindFileContextMenuStorage.cs" />
|
||||||
|
<Compile Include="Main.cs" />
|
||||||
|
<Compile Include="MFTSearch\MFTSearcher.cs" />
|
||||||
|
<Compile Include="MFTSearch\MFTSearcherCache.cs" />
|
||||||
|
<Compile Include="MFTSearch\MFTSearchRecord.cs" />
|
||||||
|
<Compile Include="MFTSearch\PInvokeWin32.cs" />
|
||||||
|
<Compile Include="MFTSearch\USNChangeReason.cs" />
|
||||||
|
<Compile Include="MFTSearch\USNRecord.cs" />
|
||||||
|
<Compile Include="MFTSearch\VolumeMonitor.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="Setting.xaml.cs">
|
||||||
|
<DependentUpon>Setting.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
</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>
|
||||||
|
<Content Include="Images\find.png">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="Images\folder.png">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="Images\file.png">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="Images\list.png">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="Images\open.png">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="Images\warning.png">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
<None Include="plugin.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Page Include="Setting.xaml">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.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>
|
6
Plugins/Wox.Plugin.FindFile/packages.config
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="log4net" version="2.0.3" targetFramework="net35" />
|
||||||
|
<package id="Newtonsoft.Json" version="6.0.5" targetFramework="net35" />
|
||||||
|
<package id="WPFToolkit" version="3.5.50211.1" targetFramework="net35" />
|
||||||
|
</packages>
|
12
Plugins/Wox.Plugin.FindFile/plugin.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"ID":"64EDFA42642446E5BBFF2546EE4549BB",
|
||||||
|
"ActionKeyword":"f",
|
||||||
|
"Name":"Find Files and Folders",
|
||||||
|
"Description":"Search all your files and folders in your disk",
|
||||||
|
"Author":"qianlifeng",
|
||||||
|
"Version":"1.0",
|
||||||
|
"Language":"csharp",
|
||||||
|
"Website":"http://www.getwox.com/plugin",
|
||||||
|
"ExecuteFileName":"Wox.Plugin.FindFile.dll",
|
||||||
|
"IcoPath":"Images\\find.png"
|
||||||
|
}
|
20
Plugins/Wox.Plugin.FindFile/setting.xaml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<UserControl x:Class="Wox.Plugin.FindFile.Setting"
|
||||||
|
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:controls="http://schemas.microsoft.com/wpf/2008/toolkit"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
d:DesignHeight="300" d:DesignWidth="600">
|
||||||
|
<TabControl Height="auto" Margin="10" VerticalAlignment="Top">
|
||||||
|
<TabItem Header="Context Menu">
|
||||||
|
<controls:DataGrid x:Name="menuGrid" AutoGenerateColumns="False" CanUserAddRows="True" CanUserDeleteRows="True" Height="420">
|
||||||
|
<controls:DataGrid.Columns>
|
||||||
|
<controls:DataGridTextColumn Header="Name" Width="150" Binding="{Binding Name}" />
|
||||||
|
<controls:DataGridTextColumn Header="Command" Width="250" Binding="{Binding Command}" />
|
||||||
|
<controls:DataGridTextColumn Header="Arguments" Width="*" Binding="{Binding Argument}" />
|
||||||
|
</controls:DataGrid.Columns>
|
||||||
|
</controls:DataGrid>
|
||||||
|
</TabItem>
|
||||||
|
</TabControl>
|
||||||
|
</UserControl>
|
31
Plugins/Wox.Plugin.FindFile/setting.xaml.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using System.Windows.Documents;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
|
using System.Windows.Navigation;
|
||||||
|
using System.Windows.Shapes;
|
||||||
|
|
||||||
|
namespace Wox.Plugin.FindFile
|
||||||
|
{
|
||||||
|
public partial class Setting : UserControl
|
||||||
|
{
|
||||||
|
public Setting()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
menuGrid.ItemsSource = FindFileContextMenuStorage.Instance.ContextMenus;
|
||||||
|
menuGrid.CurrentCellChanged += menuGrid_CurrentCellChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
void menuGrid_CurrentCellChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
FindFileContextMenuStorage.Instance.Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
61
README.md
@ -1,5 +1,60 @@
|
|||||||
Wox [![Build status](https://ci.appveyor.com/api/projects/status/bfktntbivg32e103/branch/dev)](https://ci.appveyor.com/project/qianlifeng/wox/branch/dev)
|
Wox [![Build status](https://ci.appveyor.com/api/projects/status/bfktntbivg32e103)](https://ci.appveyor.com/project/qianlifeng/wox)
|
||||||
|
=========
|
||||||
|
[Wox](http://www.getwox.com) is an effective launcher for windows, which was inspired by [Alfred](http://www.alfredapp.com/) and [Launchy](http://www.launchy.net/). Wox provide bundles of features let you access infomations quickly.
|
||||||
|
|
||||||
|
Features
|
||||||
|
=========
|
||||||
|
1. Search applications, files (via everything plugin) and chrome bookmarks
|
||||||
|
2. Search web contents with shortcuts (e.g. search google with `g keyword` or `youtube keyword`)
|
||||||
|
3. Search clipboard history (via clipboard plugin)
|
||||||
|
4. Themes support, get more themes from [http://www.getwox.com/theme](http://www.getwox.com/theme)
|
||||||
|
5. Plugin support, get more plugins from [http://www.getwox.com/plugin](http://www.getwox.com/plugin)
|
||||||
|
|
||||||
|
Screenshot
|
||||||
=========
|
=========
|
||||||
|
|
||||||
This is Wox Dev branch. We will develop new feature here and merge to master when we want to release a new version.
|
<a href="https://github.com/qianlifeng/Wox/wiki/Screenshot">More screenshot</a>
|
||||||
**Please send pull request to this branch if you want to contribute codes**
|
<img src="http://ww3.sinaimg.cn/large/dce48faejw1eihx6ffo8eg20zk0m8hdt.gif" />
|
||||||
|
|
||||||
|
Download
|
||||||
|
=========
|
||||||
|
|
||||||
|
Download from [release page](https://github.com/qianlifeng/Wox/releases).
|
||||||
|
|
||||||
|
* **Windows 8 users:** You have to [enable the .NET Framework 3.5 in Control Panel](http://msdn.microsoft.com/library/hh506443.aspx).
|
||||||
|
|
||||||
|
|
||||||
|
Contribute
|
||||||
|
=========
|
||||||
|
|
||||||
|
If you are a developer, please feel free to send a pull request to **Dev** branch. We still have a lot functions and bugs need to do now. Just pick one from [issues page](https://github.com/qianlifeng/Wox/issues) that you think you can fix.
|
||||||
|
|
||||||
|
If you are not a developer, you can also help Wox by contributing the Wox wiki page, for example [Wox Function Guide](https://github.com/qianlifeng/Wox/wiki/Wox-Function-Guide).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Create and Share Plugin
|
||||||
|
=========
|
||||||
|
|
||||||
|
Currently, Wox support using C# and Python to write your plugins. Please refer to [Create-plugin](https://github.com/qianlifeng/Wox/wiki/Create-plugins) page for more infomation.
|
||||||
|
You can share your plugin with <a href="http://www.getwox.com/plugin">WoxPlugins</a>. After you upload your plugin, other uses can download or search your plugin through `wpm install <plugin name>` command (this is a plugin for plugin management, which is one of the default plugins inside Wox). Please refer to [How-to-share-your-plugins](https://github.com/qianlifeng/Wox/wiki/How-to-share-your-plugins-in--getwox.com-plugin-page%3F) for details.
|
||||||
|
|
||||||
|
|
||||||
|
Create and Share Theme
|
||||||
|
=========
|
||||||
|
|
||||||
|
Wox provide an online theme editor [http://www.getwox.com/themebuilder](http://www.getwox.com/themebuilder), you build your own theme for wox.
|
||||||
|
<img src="http://ww3.sinaimg.cn/large/5d7c1fa4gw1eegm4tvpu7j20zg0in412.jpg" />
|
||||||
|
|
||||||
|
|
||||||
|
Most Asked Questions
|
||||||
|
=========
|
||||||
|
|
||||||
|
1. **How to install plugin?**
|
||||||
|
|
||||||
|
Refer to [https://github.com/qianlifeng/Wox/wiki/How-to-install-Plugins](https://github.com/qianlifeng/Wox/wiki/How-to-install-Plugins)
|
||||||
|
|
||||||
|
|
||||||
|
2. **How to open setting dialog?**
|
||||||
|
|
||||||
|
Refer to [https://github.com/qianlifeng/Wox/wiki/How-to-open-setting-dialog](https://github.com/qianlifeng/Wox/wiki/How-to-open-setting-dialog)
|
||||||
|
@ -78,7 +78,7 @@ namespace Wox.Infrastructure
|
|||||||
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
|
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
|
||||||
static extern bool UrlIs(string pszUrl, int UrlIs);
|
static extern bool UrlIs(string pszUrl, int UrlIs);
|
||||||
|
|
||||||
static void ShellExecCmdLine(IntPtr hInstance, IntPtr hwnd, string command, string startDir, global::System.Diagnostics.ProcessWindowStyle nShow, ShellExecCmdLineFlags dwSeclFlags)
|
static void ShellExecCmdLine(IntPtr hInstance, IntPtr hwnd, string command, string startDir, global::System.Diagnostics.ProcessWindowStyle nShow, ShellExecCmdLineFlags dwSeclFlags,bool runAsAdministrator = false)
|
||||||
{
|
{
|
||||||
string cmd = command;
|
string cmd = command;
|
||||||
string args = null;
|
string args = null;
|
||||||
@ -143,6 +143,7 @@ namespace Wox.Infrastructure
|
|||||||
startInfo.UseShellExecute = true;
|
startInfo.UseShellExecute = true;
|
||||||
startInfo.Arguments = args;
|
startInfo.Arguments = args;
|
||||||
startInfo.FileName = cmd;
|
startInfo.FileName = cmd;
|
||||||
|
if (runAsAdministrator) startInfo.Verb = "runas";
|
||||||
startInfo.WindowStyle = global::System.Diagnostics.ProcessWindowStyle.Normal;
|
startInfo.WindowStyle = global::System.Diagnostics.ProcessWindowStyle.Normal;
|
||||||
startInfo.ErrorDialog = (dwSeclFlags | ShellExecCmdLineFlags.SECL_NO_UI) == 0;
|
startInfo.ErrorDialog = (dwSeclFlags | ShellExecCmdLineFlags.SECL_NO_UI) == 0;
|
||||||
startInfo.ErrorDialogParentHandle = hwnd;
|
startInfo.ErrorDialogParentHandle = hwnd;
|
||||||
@ -280,17 +281,12 @@ namespace Wox.Infrastructure
|
|||||||
hresult);
|
hresult);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Start(string cmd)
|
public static void Start(string cmd, bool runAsAdministrator = false)
|
||||||
{
|
{
|
||||||
Start(cmd, false);
|
Start(cmd, false, IntPtr.Zero,runAsAdministrator);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Start(string cmd, bool showErrorDialog)
|
public static void Start(string cmd, bool showErrorDialog, IntPtr errorDialogHwnd, bool runAsAdministrator = false)
|
||||||
{
|
|
||||||
Start(cmd, false, IntPtr.Zero);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Start(string cmd, bool showErrorDialog, IntPtr errorDialogHwnd)
|
|
||||||
{
|
{
|
||||||
cmd = cmd.Trim(); // PathRemoveBlanks
|
cmd = cmd.Trim(); // PathRemoveBlanks
|
||||||
cmd = Environment.ExpandEnvironmentVariables(cmd); // SHExpandEnvironmentStrings
|
cmd = Environment.ExpandEnvironmentVariables(cmd); // SHExpandEnvironmentStrings
|
||||||
@ -306,7 +302,8 @@ namespace Wox.Infrastructure
|
|||||||
cmd,
|
cmd,
|
||||||
null, // i have no ideas about this field
|
null, // i have no ideas about this field
|
||||||
global::System.Diagnostics.ProcessWindowStyle.Normal,
|
global::System.Diagnostics.ProcessWindowStyle.Normal,
|
||||||
ShellExecCmdLineFlags.SECL__IGNORE_ERROR | ShellExecCmdLineFlags.SECL_USE_IDLIST | ShellExecCmdLineFlags.SECL_LOG_USAGE | (showErrorDialog ? 0 : ShellExecCmdLineFlags.SECL_NO_UI)
|
ShellExecCmdLineFlags.SECL__IGNORE_ERROR | ShellExecCmdLineFlags.SECL_USE_IDLIST | ShellExecCmdLineFlags.SECL_LOG_USAGE | (showErrorDialog ? 0 : ShellExecCmdLineFlags.SECL_NO_UI),
|
||||||
|
runAsAdministrator
|
||||||
);
|
);
|
||||||
if (!string.IsNullOrEmpty(home) && Directory.Exists(home)) Environment.CurrentDirectory = oldCwd;
|
if (!string.IsNullOrEmpty(home) && Directory.Exists(home)) Environment.CurrentDirectory = oldCwd;
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
@ -30,6 +31,7 @@
|
|||||||
<DefineConstants>TRACE</DefineConstants>
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="log4net, Version=1.2.13.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
|
<Reference Include="log4net, Version=1.2.13.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
|
||||||
|
@ -42,6 +42,20 @@ namespace Wox.Plugin.SystemPlugins.Program
|
|||||||
context.API.HideApp();
|
context.API.HideApp();
|
||||||
context.API.ShellRun(c.ExecutePath);
|
context.API.ShellRun(c.ExecutePath);
|
||||||
return true;
|
return true;
|
||||||
|
},
|
||||||
|
ContextMenu = new List<Result>()
|
||||||
|
{
|
||||||
|
new Result()
|
||||||
|
{
|
||||||
|
Title = "Run As Administrator",
|
||||||
|
Action = _ =>
|
||||||
|
{
|
||||||
|
context.API.HideApp();
|
||||||
|
context.API.ShellRun(c.ExecutePath,true);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
IcoPath = "Images/cmd.png"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}).ToList();
|
}).ToList();
|
||||||
}
|
}
|
||||||
@ -84,11 +98,11 @@ namespace Wox.Plugin.SystemPlugins.Program
|
|||||||
Type sourceClass;
|
Type sourceClass;
|
||||||
if (SourceTypes.TryGetValue(source.Type, out sourceClass))
|
if (SourceTypes.TryGetValue(source.Type, out sourceClass))
|
||||||
{
|
{
|
||||||
ConstructorInfo constructorInfo = sourceClass.GetConstructor(new[] {typeof (ProgramSource)});
|
ConstructorInfo constructorInfo = sourceClass.GetConstructor(new[] { typeof(ProgramSource) });
|
||||||
if (constructorInfo != null)
|
if (constructorInfo != null)
|
||||||
{
|
{
|
||||||
IProgramSource programSource =
|
IProgramSource programSource =
|
||||||
constructorInfo.Invoke(new object[] {source}) as IProgramSource;
|
constructorInfo.Invoke(new object[] { source }) as IProgramSource;
|
||||||
sources.Add(programSource);
|
sources.Add(programSource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -106,7 +120,7 @@ namespace Wox.Plugin.SystemPlugins.Program
|
|||||||
}
|
}
|
||||||
|
|
||||||
// filter duplicate program
|
// filter duplicate program
|
||||||
tempPrograms = tempPrograms.GroupBy(x => new {x.ExecutePath, x.ExecuteName})
|
tempPrograms = tempPrograms.GroupBy(x => new { x.ExecutePath, x.ExecuteName })
|
||||||
.Select(g => g.First()).ToList();
|
.Select(g => g.First()).ToList();
|
||||||
|
|
||||||
programs = tempPrograms;
|
programs = tempPrograms;
|
||||||
|
@ -9,7 +9,7 @@ namespace Wox.Plugin
|
|||||||
|
|
||||||
void PushResults(Query query,PluginMetadata plugin, List<Result> results);
|
void PushResults(Query query,PluginMetadata plugin, List<Result> results);
|
||||||
|
|
||||||
bool ShellRun(string cmd);
|
bool ShellRun(string cmd, bool runAsAdministrator = false);
|
||||||
|
|
||||||
void ChangeQuery(string query, bool requery = false);
|
void ChangeQuery(string query, bool requery = false);
|
||||||
|
|
||||||
|
@ -3,71 +3,78 @@ using System.Collections;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Wox.Plugin {
|
namespace Wox.Plugin
|
||||||
|
{
|
||||||
|
|
||||||
public class Result {
|
public class Result
|
||||||
public string Title { get; set; }
|
{
|
||||||
public string SubTitle { get; set; }
|
|
||||||
public string IcoPath { get; set; }
|
|
||||||
|
|
||||||
public string FullIcoPath
|
public string Title { get; set; }
|
||||||
{
|
public string SubTitle { get; set; }
|
||||||
get
|
public string IcoPath { get; set; }
|
||||||
{
|
|
||||||
|
public string FullIcoPath
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
if (string.IsNullOrEmpty(IcoPath)) return string.Empty;
|
if (string.IsNullOrEmpty(IcoPath)) return string.Empty;
|
||||||
if (IcoPath.StartsWith("data:"))
|
if (IcoPath.StartsWith("data:"))
|
||||||
{
|
{
|
||||||
return IcoPath;
|
return IcoPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Path.Combine(PluginDirectory, IcoPath);
|
return Path.Combine(PluginDirectory, IcoPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// return true to hide wox after select result
|
/// return true to hide wox after select result
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<ActionContext, bool> Action { get; set; }
|
public Func<ActionContext, bool> Action { get; set; }
|
||||||
public int Score { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
public int Score { get; set; }
|
||||||
/// Auto add scores for MRU items
|
|
||||||
/// </summary>
|
|
||||||
public bool AutoAjustScore { get; set; }
|
|
||||||
|
|
||||||
//todo: this should be controlled by system, not visible to users
|
/// <summary>
|
||||||
/// <summary>
|
/// Auto add scores for MRU items
|
||||||
/// Only resulsts that originQuery match with curren query will be displayed in the panel
|
/// </summary>
|
||||||
/// </summary>
|
public bool AutoAjustScore { get; set; }
|
||||||
public Query OriginQuery { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
//todo: this should be controlled by system, not visible to users
|
||||||
/// Don't set this property if you are developing a plugin
|
/// <summary>
|
||||||
/// </summary>
|
/// Only resulsts that originQuery match with curren query will be displayed in the panel
|
||||||
public string PluginDirectory { get; set; }
|
/// </summary>
|
||||||
|
public Query OriginQuery { get; set; }
|
||||||
|
|
||||||
public new bool Equals(object obj) {
|
/// <summary>
|
||||||
if (obj == null || !(obj is Result)) return false;
|
/// Don't set this property if you are developing a plugin
|
||||||
|
/// </summary>
|
||||||
|
public string PluginDirectory { get; set; }
|
||||||
|
|
||||||
Result r = (Result)obj;
|
public new bool Equals(object obj)
|
||||||
return r.Title == Title && r.SubTitle == SubTitle;
|
{
|
||||||
}
|
if (obj == null || !(obj is Result)) return false;
|
||||||
|
|
||||||
|
Result r = (Result)obj;
|
||||||
|
return r.Title == Title && r.SubTitle == SubTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Title + SubTitle;
|
||||||
|
}
|
||||||
|
|
||||||
public override string ToString() {
|
public Result()
|
||||||
return Title + SubTitle;
|
{
|
||||||
}
|
|
||||||
|
|
||||||
public Result() {
|
}
|
||||||
|
|
||||||
}
|
public Result(string Title = null, string IcoPath = null, string SubTitle = null)
|
||||||
|
{
|
||||||
|
this.Title = Title;
|
||||||
|
this.IcoPath = IcoPath;
|
||||||
|
this.SubTitle = SubTitle;
|
||||||
|
}
|
||||||
|
|
||||||
public Result(string Title = null, string IcoPath = null, string SubTitle = null) {
|
public List<Result> ContextMenu { get; set; }
|
||||||
this.Title = Title;
|
}
|
||||||
this.IcoPath = IcoPath;
|
|
||||||
this.SubTitle = SubTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
48
Wox.Test/MFTSearcherTest.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using Wox.Plugin.FindFile.MFTSearch;
|
||||||
|
|
||||||
|
namespace Wox.Test
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class MFTSearcherTest
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void MatchTest()
|
||||||
|
{
|
||||||
|
var searchtimestart = DateTime.Now;
|
||||||
|
MFTSearcher.IndexAllVolumes();
|
||||||
|
var searchtimeend = DateTime.Now;
|
||||||
|
Console.WriteLine(string.Format("{0} file indexed, {1}ms has spent.", MFTSearcher.IndexedFileCount, searchtimeend.Subtract(searchtimestart).TotalMilliseconds));
|
||||||
|
|
||||||
|
searchtimestart = DateTime.Now;
|
||||||
|
List<MFTSearchRecord> mftSearchRecords = MFTSearcher.Search("q");
|
||||||
|
searchtimeend = DateTime.Now;
|
||||||
|
Console.WriteLine(string.Format("{0} file searched, {1}ms has spent.", mftSearchRecords.Count, searchtimeend.Subtract(searchtimestart).TotalMilliseconds));
|
||||||
|
|
||||||
|
searchtimestart = DateTime.Now;
|
||||||
|
mftSearchRecords = MFTSearcher.Search("ss");
|
||||||
|
searchtimeend = DateTime.Now;
|
||||||
|
Console.WriteLine(string.Format("{0} file searched, {1}ms has spent.", mftSearchRecords.Count, searchtimeend.Subtract(searchtimestart).TotalMilliseconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void MemoryTest()
|
||||||
|
{
|
||||||
|
long oldWorkingSet = System.Diagnostics.Process.GetCurrentProcess().WorkingSet64;
|
||||||
|
MFTSearcher.IndexAllVolumes();
|
||||||
|
GC.Collect();
|
||||||
|
long newWorkingSet = System.Diagnostics.Process.GetCurrentProcess().WorkingSet64;
|
||||||
|
Console.WriteLine(string.Format("Index: {0}M", (newWorkingSet - oldWorkingSet)/(1024*1024)));
|
||||||
|
|
||||||
|
oldWorkingSet = System.Diagnostics.Process.GetCurrentProcess().WorkingSet64;
|
||||||
|
List<MFTSearchRecord> mftSearchRecords = MFTSearcher.Search("q");
|
||||||
|
newWorkingSet = System.Diagnostics.Process.GetCurrentProcess().WorkingSet64;
|
||||||
|
Console.WriteLine(string.Format("Search: {0}M", (newWorkingSet - oldWorkingSet) / (1024 * 1024)));
|
||||||
|
Console.WriteLine(string.Format("Search results: {0}",mftSearchRecords.Count));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -44,6 +44,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="FuzzyMatcherTest.cs" />
|
<Compile Include="FuzzyMatcherTest.cs" />
|
||||||
|
<Compile Include="MFTSearcherTest.cs" />
|
||||||
<Compile Include="QueryTest.cs" />
|
<Compile Include="QueryTest.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@ -51,6 +52,10 @@
|
|||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Plugins\Wox.Plugin.FindFile\Wox.Plugin.FindFile.csproj">
|
||||||
|
<Project>{84EA88B4-71F2-4A3D-9771-D7B0244C0D81}</Project>
|
||||||
|
<Name>Wox.Plugin.FindFile</Name>
|
||||||
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\Wox.Infrastructure\Wox.Infrastructure.csproj">
|
<ProjectReference Include="..\Wox.Infrastructure\Wox.Infrastructure.csproj">
|
||||||
<Project>{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}</Project>
|
<Project>{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}</Project>
|
||||||
<Name>Wox.Infrastructure</Name>
|
<Name>Wox.Infrastructure</Name>
|
||||||
|
9
Wox.sln
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 2013
|
# Visual Studio 2013
|
||||||
VisualStudioVersion = 12.0.21005.1
|
VisualStudioVersion = 12.0.30723.0
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wox.Test", "Wox.Test\Wox.Test.csproj", "{FF742965-9A80-41A5-B042-D6C7D3A21708}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wox.Test", "Wox.Test\Wox.Test.csproj", "{FF742965-9A80-41A5-B042-D6C7D3A21708}"
|
||||||
EndProject
|
EndProject
|
||||||
@ -19,6 +19,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wox.Plugin.PluginManagement
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wox.Plugin.BrowserBookmark", "Plugins\Wox.Plugin.BrowserBookmark\Wox.Plugin.BrowserBookmark.csproj", "{9B130CC5-14FB-41FF-B310-0A95B6894C37}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wox.Plugin.BrowserBookmark", "Plugins\Wox.Plugin.BrowserBookmark\Wox.Plugin.BrowserBookmark.csproj", "{9B130CC5-14FB-41FF-B310-0A95B6894C37}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wox.Plugin.FindFile", "Plugins\Wox.Plugin.FindFile\Wox.Plugin.FindFile.csproj", "{84EA88B4-71F2-4A3D-9771-D7B0244C0D81}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -53,6 +55,10 @@ Global
|
|||||||
{9B130CC5-14FB-41FF-B310-0A95B6894C37}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{9B130CC5-14FB-41FF-B310-0A95B6894C37}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{9B130CC5-14FB-41FF-B310-0A95B6894C37}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{9B130CC5-14FB-41FF-B310-0A95B6894C37}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{9B130CC5-14FB-41FF-B310-0A95B6894C37}.Release|Any CPU.Build.0 = Release|Any CPU
|
{9B130CC5-14FB-41FF-B310-0A95B6894C37}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{84EA88B4-71F2-4A3D-9771-D7B0244C0D81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{84EA88B4-71F2-4A3D-9771-D7B0244C0D81}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{84EA88B4-71F2-4A3D-9771-D7B0244C0D81}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{84EA88B4-71F2-4A3D-9771-D7B0244C0D81}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@ -60,5 +66,6 @@ Global
|
|||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
{049490F0-ECD2-4148-9B39-2135EC346EBE} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
{049490F0-ECD2-4148-9B39-2135EC346EBE} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||||
{9B130CC5-14FB-41FF-B310-0A95B6894C37} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
{9B130CC5-14FB-41FF-B310-0A95B6894C37} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||||
|
{84EA88B4-71F2-4A3D-9771-D7B0244C0D81} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
@ -10,21 +10,11 @@ namespace Wox.Commands
|
|||||||
{
|
{
|
||||||
internal static class CommandFactory
|
internal static class CommandFactory
|
||||||
{
|
{
|
||||||
private static PluginCommand pluginCmd;
|
private static PluginCommand pluginCmd = new PluginCommand();
|
||||||
private static SystemCommand systemCmd;
|
private static SystemCommand systemCmd = new SystemCommand();
|
||||||
|
|
||||||
public static void DispatchCommand(Query query)
|
public static void DispatchCommand(Query query)
|
||||||
{
|
{
|
||||||
//lazy init command instance.
|
|
||||||
if (pluginCmd == null)
|
|
||||||
{
|
|
||||||
pluginCmd = new PluginCommand();
|
|
||||||
}
|
|
||||||
if (systemCmd == null)
|
|
||||||
{
|
|
||||||
systemCmd = new SystemCommand();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Plugins.HitThirdpartyKeyword(query))
|
if (Plugins.HitThirdpartyKeyword(query))
|
||||||
{
|
{
|
||||||
pluginCmd.Dispatch(query);
|
pluginCmd.Dispatch(query);
|
||||||
|
@ -12,19 +12,21 @@ namespace Wox.Commands
|
|||||||
{
|
{
|
||||||
public class SystemCommand : BaseCommand
|
public class SystemCommand : BaseCommand
|
||||||
{
|
{
|
||||||
|
private IEnumerable<PluginPair> allSytemPlugins = Plugins.AllPlugins.Where(o => o.Metadata.PluginType == PluginType.System);
|
||||||
|
|
||||||
public override void Dispatch(Query query)
|
public override void Dispatch(Query query)
|
||||||
{
|
{
|
||||||
var allSytemPlugins = Plugins.AllPlugins.Where(o => o.Metadata.PluginType == PluginType.System);
|
var queryPlugins = allSytemPlugins;
|
||||||
if (UserSettingStorage.Instance.WebSearches.Exists(o => o.ActionWord == query.ActionName && o.Enabled))
|
if (UserSettingStorage.Instance.WebSearches.Exists(o => o.ActionWord == query.ActionName && o.Enabled))
|
||||||
{
|
{
|
||||||
//websearch mode
|
//websearch mode
|
||||||
allSytemPlugins = new List<PluginPair>()
|
queryPlugins = new List<PluginPair>()
|
||||||
{
|
{
|
||||||
allSytemPlugins.First(o => ((ISystemPlugin)o.Plugin).ID == "565B73353DBF4806919830B9202EE3BF")
|
allSytemPlugins.First(o => ((ISystemPlugin)o.Plugin).ID == "565B73353DBF4806919830B9202EE3BF")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (PluginPair pair in allSytemPlugins)
|
foreach (PluginPair pair in queryPlugins)
|
||||||
{
|
{
|
||||||
PluginPair pair1 = pair;
|
PluginPair pair1 = pair;
|
||||||
ThreadPool.QueueUserWorkItem(state =>
|
ThreadPool.QueueUserWorkItem(state =>
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using Wox.Plugin;
|
||||||
|
|
||||||
namespace Wox.Converters
|
namespace Wox.Converters
|
||||||
{
|
{
|
||||||
@ -21,4 +23,23 @@ namespace Wox.Converters
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class ContextMenuEmptyToWidthConverter : ConvertorBase<ContextMenuEmptyToWidthConverter>
|
||||||
|
{
|
||||||
|
public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
List<Result> results = value as List<Result>;
|
||||||
|
return results == null || results.Count == 0 ? 0 : 17;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object ProvideValue(IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
BIN
Wox/Images/menu.png
Normal file
After Width: | Height: | Size: 346 B |
BIN
Wox/Images/open.png
Normal file
After Width: | Height: | Size: 941 B |
@ -22,7 +22,8 @@
|
|||||||
<StackPanel Orientation="Vertical">
|
<StackPanel Orientation="Vertical">
|
||||||
<TextBox Style="{DynamicResource QueryBoxStyle}" PreviewDragOver="TbQuery_OnPreviewDragOver" AllowDrop="True" Grid.Row="0" x:Name="tbQuery" PreviewKeyDown="TbQuery_OnPreviewKeyDown" TextChanged="TextBoxBase_OnTextChanged" />
|
<TextBox Style="{DynamicResource QueryBoxStyle}" PreviewDragOver="TbQuery_OnPreviewDragOver" AllowDrop="True" Grid.Row="0" x:Name="tbQuery" PreviewKeyDown="TbQuery_OnPreviewKeyDown" TextChanged="TextBoxBase_OnTextChanged" />
|
||||||
<Line Style="{DynamicResource PendingLineStyle}" x:Name="progressBar" Y1="0" Y2="0" X2="100" Grid.Row="1" Height="2" StrokeThickness="1"></Line>
|
<Line Style="{DynamicResource PendingLineStyle}" x:Name="progressBar" Y1="0" Y2="0" X2="100" Grid.Row="1" Height="2" StrokeThickness="1"></Line>
|
||||||
<wox:ResultPanel x:Name="resultCtrl" />
|
<wox:ResultPanel x:Name="pnlResult" />
|
||||||
|
<wox:ResultPanel x:Name="pnlContextMenu" Visibility="Collapsed" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</Window>
|
</Window>
|
@ -140,6 +140,13 @@ namespace Wox
|
|||||||
results.ForEach(o =>
|
results.ForEach(o =>
|
||||||
{
|
{
|
||||||
o.PluginDirectory = plugin.PluginDirectory;
|
o.PluginDirectory = plugin.PluginDirectory;
|
||||||
|
if (o.ContextMenu != null)
|
||||||
|
{
|
||||||
|
o.ContextMenu.ForEach(t =>
|
||||||
|
{
|
||||||
|
t.PluginDirectory = plugin.PluginDirectory;
|
||||||
|
});
|
||||||
|
}
|
||||||
o.OriginQuery = query;
|
o.OriginQuery = query;
|
||||||
});
|
});
|
||||||
OnUpdateResultView(results);
|
OnUpdateResultView(results);
|
||||||
@ -147,7 +154,6 @@ namespace Wox
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
public MainWindow()
|
public MainWindow()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@ -159,7 +165,9 @@ namespace Wox
|
|||||||
|
|
||||||
progressBar.ToolTip = toolTip;
|
progressBar.ToolTip = toolTip;
|
||||||
InitialTray();
|
InitialTray();
|
||||||
resultCtrl.OnMouseClickItem += AcceptSelect;
|
pnlResult.LeftMouseClickEvent += SelectResult;
|
||||||
|
pnlContextMenu.LeftMouseClickEvent += SelectResult;
|
||||||
|
pnlResult.RightMouseClickEvent += pnlResult_RightMouseClickEvent;
|
||||||
|
|
||||||
ThreadPool.SetMaxThreads(30, 10);
|
ThreadPool.SetMaxThreads(30, 10);
|
||||||
try
|
try
|
||||||
@ -176,7 +184,15 @@ namespace Wox
|
|||||||
|
|
||||||
globalHotkey.hookedKeyboardCallback += KListener_hookedKeyboardCallback;
|
globalHotkey.hookedKeyboardCallback += KListener_hookedKeyboardCallback;
|
||||||
|
|
||||||
this.Closing += MainWindow_Closing;
|
Closing += MainWindow_Closing;
|
||||||
|
//since MainWIndow implement IPublicAPI, so we need to finish ctor MainWindow object before
|
||||||
|
//PublicAPI invoke in plugin init methods. E.g FolderPlugin
|
||||||
|
ThreadPool.QueueUserWorkItem(o => Plugins.Init());
|
||||||
|
}
|
||||||
|
|
||||||
|
void pnlResult_RightMouseClickEvent(Result result)
|
||||||
|
{
|
||||||
|
ShowContextMenu(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
||||||
@ -204,8 +220,6 @@ namespace Wox
|
|||||||
Top = UserSettingStorage.Instance.WindowTop;
|
Top = UserSettingStorage.Instance.WindowTop;
|
||||||
}
|
}
|
||||||
|
|
||||||
Plugins.Init();
|
|
||||||
|
|
||||||
InitProgressbarAnimation();
|
InitProgressbarAnimation();
|
||||||
|
|
||||||
//only works for win7+
|
//only works for win7+
|
||||||
@ -299,21 +313,22 @@ namespace Wox
|
|||||||
|
|
||||||
lastQuery = tbQuery.Text;
|
lastQuery = tbQuery.Text;
|
||||||
toolTip.IsOpen = false;
|
toolTip.IsOpen = false;
|
||||||
resultCtrl.Dirty = true;
|
pnlResult.Dirty = true;
|
||||||
Dispatcher.DelayInvoke("UpdateSearch",
|
Dispatcher.DelayInvoke("UpdateSearch",
|
||||||
o =>
|
o =>
|
||||||
{
|
{
|
||||||
Dispatcher.DelayInvoke("ClearResults", i =>
|
Dispatcher.DelayInvoke("ClearResults", i =>
|
||||||
{
|
{
|
||||||
// first try to use clear method inside resultCtrl, which is more closer to the add new results
|
// first try to use clear method inside pnlResult, which is more closer to the add new results
|
||||||
// and this will not bring splash issues.After waiting 30ms, if there still no results added, we
|
// and this will not bring splash issues.After waiting 30ms, if there still no results added, we
|
||||||
// must clear the result. otherwise, it will be confused why the query changed, but the results
|
// must clear the result. otherwise, it will be confused why the query changed, but the results
|
||||||
// didn't.
|
// didn't.
|
||||||
if (resultCtrl.Dirty) resultCtrl.Clear();
|
if (pnlResult.Dirty) pnlResult.Clear();
|
||||||
}, TimeSpan.FromMilliseconds(100), null);
|
}, TimeSpan.FromMilliseconds(100), null);
|
||||||
queryHasReturn = false;
|
queryHasReturn = false;
|
||||||
var q = new Query(lastQuery);
|
var q = new Query(lastQuery);
|
||||||
CommandFactory.DispatchCommand(q);
|
CommandFactory.DispatchCommand(q);
|
||||||
|
BackToResultMode();
|
||||||
if (Plugins.HitThirdpartyKeyword(q))
|
if (Plugins.HitThirdpartyKeyword(q))
|
||||||
{
|
{
|
||||||
Dispatcher.DelayInvoke("ShowProgressbar", originQuery =>
|
Dispatcher.DelayInvoke("ShowProgressbar", originQuery =>
|
||||||
@ -327,6 +342,12 @@ namespace Wox
|
|||||||
}, TimeSpan.FromMilliseconds(ShouldNotDelayQuery ? 0 : 150));
|
}, TimeSpan.FromMilliseconds(ShouldNotDelayQuery ? 0 : 150));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void BackToResultMode()
|
||||||
|
{
|
||||||
|
pnlResult.Visibility = Visibility.Visible;
|
||||||
|
pnlContextMenu.Visibility = Visibility.Collapsed;
|
||||||
|
}
|
||||||
|
|
||||||
private bool ShouldNotDelayQuery
|
private bool ShouldNotDelayQuery
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@ -426,7 +447,7 @@ namespace Wox
|
|||||||
ShowWox(false);
|
ShowWox(false);
|
||||||
if (!tbQuery.Text.StartsWith(">"))
|
if (!tbQuery.Text.StartsWith(">"))
|
||||||
{
|
{
|
||||||
resultCtrl.Clear();
|
pnlResult.Clear();
|
||||||
ChangeQuery(">");
|
ChangeQuery(">");
|
||||||
}
|
}
|
||||||
tbQuery.CaretIndex = tbQuery.Text.Length;
|
tbQuery.CaretIndex = tbQuery.Text.Length;
|
||||||
@ -436,7 +457,7 @@ namespace Wox
|
|||||||
|
|
||||||
private void updateCmdMode()
|
private void updateCmdMode()
|
||||||
{
|
{
|
||||||
var currentSelectedItem = resultCtrl.GetActiveResult();
|
var currentSelectedItem = pnlResult.GetActiveResult();
|
||||||
if (currentSelectedItem != null)
|
if (currentSelectedItem != null)
|
||||||
{
|
{
|
||||||
ignoreTextChange = true;
|
ignoreTextChange = true;
|
||||||
@ -452,7 +473,14 @@ namespace Wox
|
|||||||
switch (key)
|
switch (key)
|
||||||
{
|
{
|
||||||
case Key.Escape:
|
case Key.Escape:
|
||||||
HideWox();
|
if (IsInContextMenuMode)
|
||||||
|
{
|
||||||
|
BackToResultMode();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HideWox();
|
||||||
|
}
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -479,14 +507,14 @@ namespace Wox
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.PageDown:
|
case Key.PageDown:
|
||||||
resultCtrl.SelectNextPage();
|
pnlResult.SelectNextPage();
|
||||||
if (IsCMDMode) updateCmdMode();
|
if (IsCMDMode) updateCmdMode();
|
||||||
toolTip.IsOpen = false;
|
toolTip.IsOpen = false;
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.PageUp:
|
case Key.PageUp:
|
||||||
resultCtrl.SelectPrevPage();
|
pnlResult.SelectPrevPage();
|
||||||
if (IsCMDMode) updateCmdMode();
|
if (IsCMDMode) updateCmdMode();
|
||||||
toolTip.IsOpen = false;
|
toolTip.IsOpen = false;
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
@ -508,29 +536,68 @@ namespace Wox
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.Enter:
|
case Key.Enter:
|
||||||
AcceptSelect(resultCtrl.GetActiveResult());
|
Result activeResult = GetActiveResult();
|
||||||
|
if (globalHotkey.CheckModifiers().ShiftPressed)
|
||||||
|
{
|
||||||
|
ShowContextMenu(activeResult);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SelectResult(activeResult);
|
||||||
|
}
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool IsInContextMenuMode
|
||||||
|
{
|
||||||
|
get { return pnlContextMenu.Visibility == Visibility.Visible; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result GetActiveResult()
|
||||||
|
{
|
||||||
|
if (IsInContextMenuMode)
|
||||||
|
{
|
||||||
|
return pnlContextMenu.GetActiveResult();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return pnlResult.GetActiveResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void SelectPrevItem()
|
private void SelectPrevItem()
|
||||||
{
|
{
|
||||||
resultCtrl.SelectPrev();
|
if (IsInContextMenuMode)
|
||||||
if (IsCMDMode) updateCmdMode();
|
{
|
||||||
|
pnlContextMenu.SelectPrev();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pnlResult.SelectPrev();
|
||||||
|
if (IsCMDMode) updateCmdMode();
|
||||||
|
}
|
||||||
toolTip.IsOpen = false;
|
toolTip.IsOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SelectNextItem()
|
private void SelectNextItem()
|
||||||
{
|
{
|
||||||
resultCtrl.SelectNext();
|
if (IsInContextMenuMode)
|
||||||
if (IsCMDMode) updateCmdMode();
|
{
|
||||||
|
pnlContextMenu.SelectNext();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pnlResult.SelectNext();
|
||||||
|
if (IsCMDMode) updateCmdMode();
|
||||||
|
}
|
||||||
toolTip.IsOpen = false;
|
toolTip.IsOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AcceptSelect(Result result)
|
private void SelectResult(Result result)
|
||||||
{
|
{
|
||||||
if (!resultCtrl.Dirty && result != null)
|
if (result != null)
|
||||||
{
|
{
|
||||||
if (result.Action != null)
|
if (result.Action != null)
|
||||||
{
|
{
|
||||||
@ -562,7 +629,20 @@ namespace Wox
|
|||||||
if (o.AutoAjustScore) o.Score += UserSelectedRecordStorage.Instance.GetSelectedCount(o);
|
if (o.AutoAjustScore) o.Score += UserSelectedRecordStorage.Instance.GetSelectedCount(o);
|
||||||
});
|
});
|
||||||
List<Result> l = list.Where(o => o.OriginQuery != null && o.OriginQuery.RawQuery == lastQuery).ToList();
|
List<Result> l = list.Where(o => o.OriginQuery != null && o.OriginQuery.RawQuery == lastQuery).ToList();
|
||||||
Dispatcher.Invoke(new Action(() => resultCtrl.AddResults(l)));
|
Dispatcher.Invoke(new Action(() =>
|
||||||
|
pnlResult.AddResults(l))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowContextMenu(Result result)
|
||||||
|
{
|
||||||
|
if (result.ContextMenu != null && result.ContextMenu.Count > 0)
|
||||||
|
{
|
||||||
|
pnlContextMenu.Clear();
|
||||||
|
pnlContextMenu.AddResults(result.ContextMenu);
|
||||||
|
pnlContextMenu.Visibility = Visibility.Visible;
|
||||||
|
pnlResult.Visibility = Visibility.Collapsed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -604,14 +684,14 @@ namespace Wox
|
|||||||
this.Opacity = this.AllowsTransparency ? UserSettingStorage.Instance.Opacity : 1;
|
this.Opacity = this.AllowsTransparency ? UserSettingStorage.Instance.Opacity : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ShellRun(string cmd)
|
public bool ShellRun(string cmd, bool runAsAdministrator = false)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(cmd))
|
if (string.IsNullOrEmpty(cmd))
|
||||||
throw new ArgumentNullException();
|
throw new ArgumentNullException();
|
||||||
|
|
||||||
Wox.Infrastructure.WindowsShellRun.Start(cmd);
|
WindowsShellRun.Start(cmd, runAsAdministrator);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Resources;
|
using System.Resources;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
|
||||||
|
// 有关程序集的常规信息通过以下
|
||||||
|
// 特性集控制。更改这些特性值可修改
|
||||||
|
// 与程序集关联的信息。
|
||||||
[assembly: AssemblyTitle("Wox")]
|
[assembly: AssemblyTitle("Wox")]
|
||||||
[assembly: AssemblyDescription("https://github.com/qianlifeng/Wox")]
|
[assembly: AssemblyDescription("https://github.com/qianlifeng/Wox")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
@ -12,10 +15,41 @@ using System.Windows;
|
|||||||
[assembly: AssemblyCopyright("The MIT License (MIT)")]
|
[assembly: AssemblyCopyright("The MIT License (MIT)")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// 将 ComVisible 设置为 false 使此程序集中的类型
|
||||||
|
// 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型,
|
||||||
|
// 则将该类型上的 ComVisible 特性设置为 true。
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
//若要开始生成可本地化的应用程序,请在
|
||||||
|
//<PropertyGroup> 中的 .csproj 文件中
|
||||||
|
//设置 <UICulture>CultureYouAreCodingWith</UICulture>。例如,如果您在源文件中
|
||||||
|
//使用的是美国英语,请将 <UICulture> 设置为 en-US。然后取消
|
||||||
|
//对以下 NeutralResourceLanguage 特性的注释。更新
|
||||||
|
//以下行中的“en-US”以匹配项目文件中的 UICulture 设置。
|
||||||
|
|
||||||
|
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
|
||||||
|
|
||||||
|
|
||||||
[assembly: ThemeInfo(
|
[assembly: ThemeInfo(
|
||||||
ResourceDictionaryLocation.None,
|
ResourceDictionaryLocation.None, //主题特定资源词典所处位置
|
||||||
ResourceDictionaryLocation.SourceAssembly
|
//(在页面或应用程序资源词典中
|
||||||
|
// 未找到某个资源的情况下使用)
|
||||||
|
ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置
|
||||||
|
//(在页面、应用程序或任何主题特定资源词典中
|
||||||
|
// 未找到某个资源的情况下使用)
|
||||||
)]
|
)]
|
||||||
[assembly: AssemblyVersion("1.0.*")]
|
|
||||||
[assembly: AssemblyFileVersion("1.0.*")]
|
|
||||||
|
// 程序集的版本信息由下面四个值组成:
|
||||||
|
//
|
||||||
|
// 主版本
|
||||||
|
// 次版本
|
||||||
|
// 生成号
|
||||||
|
// 修订号
|
||||||
|
//
|
||||||
|
// 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值,
|
||||||
|
// 方法是按如下所示使用“*”:
|
||||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
@ -7,7 +7,7 @@
|
|||||||
mc:Ignorable="d" d:DesignWidth="100" d:DesignHeight="100">
|
mc:Ignorable="d" d:DesignWidth="100" d:DesignHeight="100">
|
||||||
|
|
||||||
<!-- set max height of listbox to allow 6 results showed at once -->
|
<!-- set max height of listbox to allow 6 results showed at once -->
|
||||||
<ListBox x:Name="lbResults" MaxHeight="300" PreviewMouseDown="LbResults_OnPreviewMouseDown" Style="{DynamicResource BaseListboxStyle}" SelectionChanged ="lbResults_SelectionChanged" Focusable="False" KeyboardNavigation.DirectionalNavigation="Cycle" SelectionMode="Single" VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Standard">
|
<ListBox x:Name="lbResults" MaxHeight="300" HorizontalContentAlignment="Stretch" PreviewMouseDown="LbResults_OnPreviewMouseDown" Style="{DynamicResource BaseListboxStyle}" SelectionChanged ="lbResults_SelectionChanged" Focusable="False" KeyboardNavigation.DirectionalNavigation="Cycle" SelectionMode="Single" VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Standard">
|
||||||
<ListBox.Resources>
|
<ListBox.Resources>
|
||||||
<!--SelectedItem with focus-->
|
<!--SelectedItem with focus-->
|
||||||
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="{DynamicResource ItemSelectedBackgroundColor}"/>
|
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="{DynamicResource ItemSelectedBackgroundColor}"/>
|
||||||
@ -17,13 +17,14 @@
|
|||||||
<ListBox.ItemTemplate>
|
<ListBox.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<!-- a result item height is 50 including margin -->
|
<!-- a result item height is 50 including margin -->
|
||||||
<Grid HorizontalAlignment="Stretch" Height="40" VerticalAlignment="Stretch" Margin="5" Cursor="Hand">
|
<Grid HorizontalAlignment="Stretch" Height="40" VerticalAlignment="Stretch" Margin="5" Cursor="Hand">
|
||||||
<Grid.Resources>
|
<Grid.Resources>
|
||||||
<converters:ImagePathConverter x:Key="ImageConverter" />
|
<converters:ImagePathConverter x:Key="ImageConverter" />
|
||||||
</Grid.Resources>
|
</Grid.Resources>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="32"></ColumnDefinition>
|
<ColumnDefinition Width="32"></ColumnDefinition>
|
||||||
<ColumnDefinition/>
|
<ColumnDefinition/>
|
||||||
|
<ColumnDefinition x:Name="contextMenuDefinition" Width="0"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<Image x:Name="imgIco" Width="32" Height="32" HorizontalAlignment="Left" Source="{Binding FullIcoPath,Converter={StaticResource ImageConverter},IsAsync=True}" >
|
<Image x:Name="imgIco" Width="32" Height="32" HorizontalAlignment="Left" Source="{Binding FullIcoPath,Converter={StaticResource ImageConverter},IsAsync=True}" >
|
||||||
</Image>
|
</Image>
|
||||||
@ -32,14 +33,16 @@
|
|||||||
<RowDefinition></RowDefinition>
|
<RowDefinition></RowDefinition>
|
||||||
<RowDefinition Height="Auto" x:Name="SubTitleRowDefinition"></RowDefinition>
|
<RowDefinition Height="Auto" x:Name="SubTitleRowDefinition"></RowDefinition>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<TextBlock Style="{DynamicResource ItemTitleStyle}" VerticalAlignment="Center" ToolTip="{Binding Title}" x:Name="tbTitle" Text="{Binding Title}"></TextBlock>
|
<TextBlock Style="{DynamicResource ItemTitleStyle}" DockPanel.Dock="Left" VerticalAlignment="Center" ToolTip="{Binding Title}" x:Name="tbTitle" Text="{Binding Title}"></TextBlock>
|
||||||
<TextBlock Style="{DynamicResource ItemSubTitleStyle}" ToolTip="{Binding SubTitle}" Visibility="{Binding SubTitle, Converter={converters:StringNullOrEmptyToVisibilityConverter}}" Grid.Row="1" x:Name="tbSubTitle" Text="{Binding SubTitle}"></TextBlock>
|
<TextBlock Style="{DynamicResource ItemSubTitleStyle}" ToolTip="{Binding SubTitle}" Visibility="{Binding SubTitle, Converter={converters:StringNullOrEmptyToVisibilityConverter}}" Grid.Row="1" x:Name="tbSubTitle" Text="{Binding SubTitle}"></TextBlock>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Image Grid.Column="2" VerticalAlignment="Center" Margin="5 0 0 0" Width="12" x:Name="contextMenu" Source="Images/menu.png" ToolTip="Shift + Enter to open context menu"></Image>
|
||||||
</Grid>
|
</Grid>
|
||||||
<DataTemplate.Triggers>
|
<DataTemplate.Triggers>
|
||||||
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}, Path=IsSelected}" Value="True">
|
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}, Path=IsSelected}" Value="True">
|
||||||
<Setter TargetName="tbTitle" Property="Style" Value="{DynamicResource ItemTitleSelectedStyle}"/>
|
<Setter TargetName="tbTitle" Property="Style" Value="{DynamicResource ItemTitleSelectedStyle}"/>
|
||||||
<Setter TargetName="tbSubTitle" Property="Style" Value="{DynamicResource ItemSubTitleSelectedStyle}"/>
|
<Setter TargetName="tbSubTitle" Property="Style" Value="{DynamicResource ItemSubTitleSelectedStyle}"/>
|
||||||
|
<Setter TargetName="contextMenuDefinition" Property="Width" Value="{Binding ContextMenu, Converter={converters:ContextMenuEmptyToWidthConverter}}"/>
|
||||||
</DataTrigger>
|
</DataTrigger>
|
||||||
</DataTemplate.Triggers>
|
</DataTemplate.Triggers>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
@ -14,11 +14,18 @@ namespace Wox
|
|||||||
{
|
{
|
||||||
public partial class ResultPanel : UserControl
|
public partial class ResultPanel : UserControl
|
||||||
{
|
{
|
||||||
public event Action<Result> OnMouseClickItem;
|
public event Action<Result> LeftMouseClickEvent;
|
||||||
|
public event Action<Result> RightMouseClickEvent;
|
||||||
|
|
||||||
protected virtual void OnOnMouseClickItem(Result result)
|
protected virtual void OnRightMouseClick(Result result)
|
||||||
{
|
{
|
||||||
Action<Result> handler = OnMouseClickItem;
|
Action<Result> handler = RightMouseClickEvent;
|
||||||
|
if (handler != null) handler(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnLeftMouseClick(Result result)
|
||||||
|
{
|
||||||
|
Action<Result> handler = LeftMouseClickEvent;
|
||||||
if (handler != null) handler(result);
|
if (handler != null) handler(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,9 +140,13 @@ namespace Wox
|
|||||||
private void LbResults_OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
|
private void LbResults_OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
|
||||||
{
|
{
|
||||||
var item = ItemsControl.ContainerFromElement(lbResults, e.OriginalSource as DependencyObject) as ListBoxItem;
|
var item = ItemsControl.ContainerFromElement(lbResults, e.OriginalSource as DependencyObject) as ListBoxItem;
|
||||||
if (item != null)
|
if (item != null && e.ChangedButton == MouseButton.Left)
|
||||||
{
|
{
|
||||||
OnOnMouseClickItem(item.DataContext as Result);
|
OnLeftMouseClick(item.DataContext as Result);
|
||||||
|
}
|
||||||
|
if (item != null && e.ChangedButton == MouseButton.Right)
|
||||||
|
{
|
||||||
|
OnRightMouseClick(item.DataContext as Result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
15
Wox/app.manifest
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
|
||||||
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||||
|
<security>
|
||||||
|
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
||||||
|
</requestedPrivileges>
|
||||||
|
</security>
|
||||||
|
</trustInfo>
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<application>
|
||||||
|
</application>
|
||||||
|
</compatibility>
|
||||||
|
</asmv1:assembly>
|