Add Context menu

This commit is contained in:
qianlifeng
2014-10-23 18:39:11 +08:00
parent 80e38fc430
commit 755e7bc232
14 changed files with 433 additions and 343 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@@ -36,7 +36,7 @@ namespace Wox.Plugin.FindFile
MFTSearcher.IndexAllVolumes(); MFTSearcher.IndexAllVolumes();
initial = true; initial = true;
var searchtimeend = DateTime.Now; var searchtimeend = DateTime.Now;
Debug.WriteLine(string.Format("{0} file, {1} folder indexed, {2}ms has spent.", MFTSearcher.IndexedFileCount, MFTSearcher.IndexedFolderCount, searchtimeend.Subtract(searchtimestart).TotalMilliseconds)); Debug.WriteLine(string.Format("{0} file, indexed, {1}ms has spent.", MFTSearcher.IndexedRecordsCount, searchtimeend.Subtract(searchtimestart).TotalMilliseconds));
} }
private Result ConvertMFTSearch(MFTSearchRecord record, string query) private Result ConvertMFTSearch(MFTSearchRecord record, string query)
@@ -67,8 +67,57 @@ namespace Wox.Plugin.FindFile
return false; return false;
} }
return true; return true;
} },
ContextMenu = GetContextMenu(record)
}; };
} }
private List<Result> GetContextMenu(MFTSearchRecord record)
{
List<Result> contextMenus = new List<Result>();
contextMenus.Add(new Result()
{
Title = "Open",
Action = _ =>
{
try
{
Process.Start(record.FullPath);
}
catch
{
context.API.ShowMsg("Can't open " + record.FullPath, string.Empty, string.Empty);
return false;
}
return true;
},
IcoPath = "Images/edit.png"
});
if (!record.IsFolder)
{
contextMenus.Add(new Result()
{
Title = "Open Containing Folder",
Action = _ =>
{
try
{
Process.Start("explorer.exe", string.Format("/select, \"{0}\"", record.FullPath));
}
catch
{
context.API.ShowMsg("Can't open " + record.FullPath, string.Empty, string.Empty);
return false;
}
return true;
},
IcoPath = "Images/folder.png"
});
}
return contextMenus;
}
} }
} }

View File

@@ -54,7 +54,12 @@
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Images\find.png" /> <Content Include="Images\edit.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Images\find.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Images\folder.png"> <Content Include="Images\folder.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>

View File

@@ -9,7 +9,6 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using MyEverything;
namespace Wox.Infrastructure.MFTSearch namespace Wox.Infrastructure.MFTSearch
{ {
@@ -23,8 +22,8 @@ namespace Wox.Infrastructure.MFTSearch
List<USNRecord> files; List<USNRecord> files;
List<USNRecord> folders; List<USNRecord> folders;
EnumerateVolume(volume, out files, out folders); EnumerateVolume(volume, out files, out folders);
cache.AddRecord(volume, files, USNRecordType.File); //cache.AddRecord(files);
cache.AddRecord(volume, folders, USNRecordType.Folder); //cache.AddRecord(folders);
} }
public static void IndexAllVolumes() public static void IndexAllVolumes()
@@ -35,13 +34,9 @@ namespace Wox.Infrastructure.MFTSearch
} }
} }
public static long IndexedFileCount public static long IndexedRecordsCount
{ {
get { return cache.FileCount; } get { return cache.RecordsCount; }
}
public static long IndexedFolderCount
{
get { return cache.FolderCount; }
} }
public static List<MFTSearchRecord> Search(string item) public static List<MFTSearchRecord> Search(string item)
@@ -49,7 +44,7 @@ namespace Wox.Infrastructure.MFTSearch
if (string.IsNullOrEmpty(item)) return new List<MFTSearchRecord>(); if (string.IsNullOrEmpty(item)) return new List<MFTSearchRecord>();
List<USNRecord> found = cache.FindByName(item); List<USNRecord> found = cache.FindByName(item);
found.ForEach(x => FillPath(x.VolumeName, x, cache)); found.ForEach(x => FillPath(x, cache));
return found.ConvertAll(o => new MFTSearchRecord(o)); return found.ConvertAll(o => new MFTSearchRecord(o));
} }
@@ -255,14 +250,16 @@ namespace Wox.Infrastructure.MFTSearch
} }
Marshal.FreeHGlobal(pData); Marshal.FreeHGlobal(pData);
} }
internal static void FillPath(string volume, USNRecord record, MFTSearcherCache db)
internal static void FillPath(USNRecord record, MFTSearcherCache db)
{ {
if (record == null) return; if (record == null) return;
var fdSource = db.GetFolderSource(volume); var fdSource = db.GetAllRecords();
string fullpath = record.Name; string fullpath = record.Name;
FindRecordPath(record, ref fullpath, fdSource); FindRecordPath(record, ref fullpath, fdSource);
record.FullPath = fullpath; record.FullPath = fullpath;
} }
private static void FindRecordPath(USNRecord curRecord, ref string fullpath, Dictionary<ulong, USNRecord> fdSource) private static void FindRecordPath(USNRecord curRecord, ref string fullpath, Dictionary<ulong, USNRecord> fdSource)
{ {
if (curRecord.IsVolumeRoot) return; if (curRecord.IsVolumeRoot) return;

View File

@@ -1,133 +1,60 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using MyEverything;
namespace Wox.Infrastructure.MFTSearch namespace Wox.Infrastructure.MFTSearch
{ {
internal class MFTSearcherCache internal class MFTSearcherCache
{ {
private Dictionary<string, Dictionary<ulong, USNRecord>> _volumes_files = new Dictionary<string, Dictionary<ulong, USNRecord>>(); private Dictionary<ulong, USNRecord> records = new Dictionary<ulong, USNRecord>(100000);
private Dictionary<string, Dictionary<ulong, USNRecord>> _volumes_folders = new Dictionary<string, Dictionary<ulong, USNRecord>>(); private Lookup<string, string> recordsLookup;
public MFTSearcherCache() { } public MFTSearcherCache() { }
public bool ContainsVolume(string volume) public void AddRecord(List<USNRecord> record)
{ {
return _volumes_files.ContainsKey(volume) && _volumes_folders.ContainsKey(volume); record.ForEach(AddRecord);
} }
public void AddRecord(string volume, List<USNRecord> r, USNRecordType type)
public void AddRecord(USNRecord record)
{ {
if (type == USNRecordType.File) if(!records.ContainsKey(record.FRN)) records.Add(record.FRN, record);
}
public bool DeleteRecord(ulong frn)
{
return records.Remove(frn);
}
public void UpdateRecord(USNRecord record)
{
USNRecord firstOrDefault = records[record.FRN];
if (firstOrDefault != null)
{ {
CheckHashTableKey(_volumes_files, volume); firstOrDefault.Name = record.Name;
r.ForEach(x => _volumes_files[volume].Add(x.FRN, x)); firstOrDefault.FullPath = record.FullPath;
} firstOrDefault.VolumeName = record.VolumeName;
else
{
CheckHashTableKey(_volumes_folders, volume);
r.ForEach(x => _volumes_folders[volume].Add(x.FRN, x));
}
}
public void AddRecord(string volume, USNRecord record, USNRecordType type)
{
if (type == USNRecordType.File)
{
CheckHashTableKey(_volumes_files, volume);
_volumes_files[volume].Add(record.FRN, record);
}
else
{
CheckHashTableKey(_volumes_folders, volume);
_volumes_folders[volume].Add(record.FRN, record);
}
}
private void CheckHashTableKey(Dictionary<string, Dictionary<ulong, USNRecord>> hashtable, string key)
{
if (!hashtable.ContainsKey(key))
hashtable.Add(key, new Dictionary<ulong, USNRecord>());
}
public bool DeleteRecord(string volume, ulong frn)
{
bool result = false;
result = DeleteRecordHashTableItem(_volumes_files, volume, frn);
if (!result) result = DeleteRecordHashTableItem(_volumes_folders, 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))
{
hashtable[volume].Remove(frn);
return true;
}
else
{
return false;
}
}
public void UpdateRecord(string volume, USNRecord record, USNRecordType type)
{
if (type == USNRecordType.File)
RealUpdateRecord(volume, _volumes_files, record);
else
RealUpdateRecord(volume, _volumes_folders, record);
}
private bool RealUpdateRecord(string volume, Dictionary<string, Dictionary<ulong, USNRecord>> source, USNRecord record)
{
if (source.ContainsKey(volume) && source[volume].ContainsKey(record.FRN))
{
source[volume][record.FRN] = record;
return true;
}
else
{
return false;
} }
} }
public List<USNRecord> FindByName(string filename) public List<USNRecord> FindByName(string filename)
{ {
filename = filename.ToLower(); filename = filename.ToLower();
var fileQuery = from filesInVolumeDic in _volumes_files.Values var query = from file in records.Values
from eachFilePair in filesInVolumeDic where file.Name.ToLower().Contains(filename)
where eachFilePair.Value.Name.ToLower().Contains(filename) select file;
select eachFilePair.Value; return query.ToList();
var folderQuery = from fldsInVolumeDic in _volumes_folders.Values
from eachFldPair in fldsInVolumeDic
where eachFldPair.Value.Name.ToLower().Contains(filename)
select eachFldPair.Value;
List<USNRecord> result = new List<USNRecord>();
result.AddRange(fileQuery);
result.AddRange(folderQuery);
return result;
} }
public USNRecord FindByFrn(string volume, ulong frn)
public long RecordsCount
{ {
if ((!_volumes_files.ContainsKey(volume)) || (!_volumes_folders.ContainsKey(volume))) get { return records.Count; }
throw new Exception(string.Format("DB not contain the volume: {0}", volume));
USNRecord result = null;
_volumes_files[volume].TryGetValue(frn, out result);
if (result != null) return result;
_volumes_folders[volume].TryGetValue(frn, out result);
return result;
} }
public long FileCount
public Dictionary<ulong, USNRecord> GetAllRecords()
{ {
get { return _volumes_files.Sum(x => x.Value.Count); } return records;
}
public long FolderCount
{
get { return _volumes_folders.Sum(x => x.Value.Count); }
}
public Dictionary<ulong, USNRecord> GetFolderSource(string volume)
{
Dictionary<ulong, USNRecord> result = null;
_volumes_folders.TryGetValue(volume, out result);
return result;
} }
} }
} }

View File

@@ -1,5 +1,4 @@
using System; using System;
using MyEverything;
namespace Wox.Infrastructure.MFTSearch namespace Wox.Infrastructure.MFTSearch
{ {

View File

@@ -1,158 +1,158 @@
using System; //using System;
using System.Collections.Generic; //using System.Collections.Generic;
using System.Linq; //using System.Linq;
using System.Text; //using System.Text;
using System.Runtime.InteropServices; //using System.Runtime.InteropServices;
using System.Threading; //using System.Threading;
using System.Diagnostics; //using System.Diagnostics;
using System.IO; //using System.IO;
using Wox.Infrastructure.MFTSearch; //using Wox.Infrastructure.MFTSearch;
namespace MyEverything //namespace MyEverything
{ //{
internal class VolumeMonitor // internal class VolumeMonitor
{ // {
public Action<USNRecord> RecordAddedEvent; // public Action<USNRecord> RecordAddedEvent;
public Action<USNRecord> RecordDeletedEvent; // public Action<USNRecord> RecordDeletedEvent;
public Action<USNRecord, USNRecord> RecordRenameEvent; // public Action<USNRecord, USNRecord> RecordRenameEvent;
public void Monitor(List<string> volumes, MFTSearcherCache db) // public void Monitor(List<string> volumes, MFTSearcherCache db)
{ // {
foreach (var volume in volumes) // foreach (var volume in volumes)
{ // {
if (string.IsNullOrEmpty(volume)) throw new InvalidOperationException("Volume cant't be null or empty string."); // 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.")); // if (!db.ContainsVolume(volume)) throw new InvalidOperationException(string.Format("Volume {0} must be scaned first."));
Thread th = new Thread(new ParameterizedThreadStart(MonitorThread)); // Thread th = new Thread(new ParameterizedThreadStart(MonitorThread));
th.Start(new Dictionary<string, object> { { "Volume", volume }, { "MFTSearcherCache", db } }); // th.Start(new Dictionary<string, object> { { "Volume", volume }, { "MFTSearcherCache", db } });
} // }
} // }
private PInvokeWin32.READ_USN_JOURNAL_DATA SetupInputData4JournalRead(string volume, uint reason) // private PInvokeWin32.READ_USN_JOURNAL_DATA SetupInputData4JournalRead(string volume, uint reason)
{ // {
IntPtr pMonitorVolume = MFTSearcher.GetVolumeJournalHandle(volume); // IntPtr pMonitorVolume = MFTSearcher.GetVolumeJournalHandle(volume);
uint bytesReturned = 0; // uint bytesReturned = 0;
PInvokeWin32.USN_JOURNAL_DATA ujd = new PInvokeWin32.USN_JOURNAL_DATA(); // PInvokeWin32.USN_JOURNAL_DATA ujd = new PInvokeWin32.USN_JOURNAL_DATA();
Wox.Infrastructure.MFTSearch.MFTSearcher.QueryUSNJournal(pMonitorVolume, out ujd, out bytesReturned); // Wox.Infrastructure.MFTSearch.MFTSearcher.QueryUSNJournal(pMonitorVolume, out ujd, out bytesReturned);
// 构建输入参数 // // 构建输入参数
PInvokeWin32.READ_USN_JOURNAL_DATA rujd = new PInvokeWin32.READ_USN_JOURNAL_DATA(); // PInvokeWin32.READ_USN_JOURNAL_DATA rujd = new PInvokeWin32.READ_USN_JOURNAL_DATA();
rujd.StartUsn = ujd.NextUsn; // rujd.StartUsn = ujd.NextUsn;
rujd.ReasonMask = reason; // rujd.ReasonMask = reason;
rujd.ReturnOnlyOnClose = 1; // rujd.ReturnOnlyOnClose = 1;
rujd.Timeout = 0; // rujd.Timeout = 0;
rujd.BytesToWaitFor = 1; // rujd.BytesToWaitFor = 1;
rujd.UsnJournalID = ujd.UsnJournalID; // rujd.UsnJournalID = ujd.UsnJournalID;
return rujd; // return rujd;
} // }
private void MonitorThread(object param) // private void MonitorThread(object param)
{ // {
MFTSearcherCache db = (param as Dictionary<string, object>)["MFTSearcherCache"] as MFTSearcherCache; // MFTSearcherCache db = (param as Dictionary<string, object>)["MFTSearcherCache"] as MFTSearcherCache;
string volume = (param as Dictionary<string, object>)["Volume"] as string; // string volume = (param as Dictionary<string, object>)["Volume"] as string;
IntPtr pbuffer = Marshal.AllocHGlobal(0x1000); // IntPtr pbuffer = Marshal.AllocHGlobal(0x1000);
PInvokeWin32.READ_USN_JOURNAL_DATA rujd = SetupInputData4JournalRead(volume, 0xFFFFFFFF); // PInvokeWin32.READ_USN_JOURNAL_DATA rujd = SetupInputData4JournalRead(volume, 0xFFFFFFFF);
UInt32 cbRead; // UInt32 cbRead;
IntPtr prujd; // IntPtr prujd;
while (true) // while (true)
{ // {
prujd = Marshal.AllocHGlobal(Marshal.SizeOf(rujd)); // prujd = Marshal.AllocHGlobal(Marshal.SizeOf(rujd));
PInvokeWin32.ZeroMemory(prujd, Marshal.SizeOf(rujd)); // PInvokeWin32.ZeroMemory(prujd, Marshal.SizeOf(rujd));
Marshal.StructureToPtr(rujd, prujd, true); // Marshal.StructureToPtr(rujd, prujd, true);
Debug.WriteLine(string.Format("\nMoniting on {0}......", volume)); // Debug.WriteLine(string.Format("\nMoniting on {0}......", volume));
IntPtr pVolume = Wox.Infrastructure.MFTSearch.MFTSearcher.GetVolumeJournalHandle(volume); // IntPtr pVolume = Wox.Infrastructure.MFTSearch.MFTSearcher.GetVolumeJournalHandle(volume);
bool fok = PInvokeWin32.DeviceIoControl(pVolume, // bool fok = PInvokeWin32.DeviceIoControl(pVolume,
PInvokeWin32.FSCTL_READ_USN_JOURNAL, // PInvokeWin32.FSCTL_READ_USN_JOURNAL,
prujd, Marshal.SizeOf(typeof(PInvokeWin32.READ_USN_JOURNAL_DATA)), // prujd, Marshal.SizeOf(typeof(PInvokeWin32.READ_USN_JOURNAL_DATA)),
pbuffer, 0x1000, out cbRead, IntPtr.Zero); // pbuffer, 0x1000, out cbRead, IntPtr.Zero);
IntPtr pRealData = new IntPtr(pbuffer.ToInt32() + Marshal.SizeOf(typeof(Int64))); // IntPtr pRealData = new IntPtr(pbuffer.ToInt32() + Marshal.SizeOf(typeof(Int64)));
uint offset = 0; // uint offset = 0;
if (fok) // if (fok)
{ // {
while (offset + Marshal.SizeOf(typeof(Int64)) < cbRead) // while (offset + Marshal.SizeOf(typeof(Int64)) < cbRead)
{ // {
PInvokeWin32.USN_RECORD usn = new PInvokeWin32.USN_RECORD(new IntPtr(pRealData.ToInt32() + (int)offset)); // PInvokeWin32.USN_RECORD usn = new PInvokeWin32.USN_RECORD(new IntPtr(pRealData.ToInt32() + (int)offset));
ProcessUSN(usn, volume, db); // ProcessUSN(usn, volume, db);
offset += usn.RecordLength; // offset += usn.RecordLength;
} // }
} // }
Marshal.FreeHGlobal(prujd); // Marshal.FreeHGlobal(prujd);
rujd.StartUsn = Marshal.ReadInt64(pbuffer); // rujd.StartUsn = Marshal.ReadInt64(pbuffer);
} // }
} // }
private void ProcessUSN(PInvokeWin32.USN_RECORD usn, string volume, MFTSearcherCache db) // private void ProcessUSN(PInvokeWin32.USN_RECORD usn, string volume, MFTSearcherCache db)
{ // {
var dbCached = db.FindByFrn(volume, usn.FRN); // var dbCached = db.FindByFrn(volume, usn.FRN);
Wox.Infrastructure.MFTSearch.MFTSearcher.FillPath(volume, dbCached, db); // Wox.Infrastructure.MFTSearch.MFTSearcher.FillPath(volume, dbCached, db);
Debug.WriteLine(string.Format("------USN[frn={0}]------", usn.FRN)); // Debug.WriteLine(string.Format("------USN[frn={0}]------", usn.FRN));
Debug.WriteLine(string.Format("FileName={0}, USNChangeReason={1}", usn.FileName, USNChangeReason.ReasonPrettyFormat(usn.Reason))); // Debug.WriteLine(string.Format("FileName={0}, USNChangeReason={1}", usn.FileName, USNChangeReason.ReasonPrettyFormat(usn.Reason)));
Debug.WriteLine(string.Format("FileName[Cached]={0}", dbCached == null ? "NoCache" : dbCached.FullPath)); // Debug.WriteLine(string.Format("FileName[Cached]={0}", dbCached == null ? "NoCache" : dbCached.FullPath));
Debug.WriteLine("--------------------------------------"); // Debug.WriteLine("--------------------------------------");
if (MaskEqual(usn.Reason, USNChangeReason.USN_REASONS["USN_REASON_RENAME_NEW_NAME"])) // if (MaskEqual(usn.Reason, USNChangeReason.USN_REASONS["USN_REASON_RENAME_NEW_NAME"]))
ProcessRenameNewName(usn, volume, db); // ProcessRenameNewName(usn, volume, db);
if ((usn.Reason & USNChangeReason.USN_REASONS["USN_REASON_FILE_CREATE"]) != 0) // if ((usn.Reason & USNChangeReason.USN_REASONS["USN_REASON_FILE_CREATE"]) != 0)
ProcessFileCreate(usn, volume, db); // ProcessFileCreate(usn, volume, db);
if (MaskEqual(usn.Reason, USNChangeReason.USN_REASONS["USN_REASON_FILE_DELETE"])) // if (MaskEqual(usn.Reason, USNChangeReason.USN_REASONS["USN_REASON_FILE_DELETE"]))
ProcessFileDelete(usn, volume, db); // ProcessFileDelete(usn, volume, db);
} // }
private void ProcessFileDelete(PInvokeWin32.USN_RECORD usn, string volume, MFTSearcherCache db) // private void ProcessFileDelete(PInvokeWin32.USN_RECORD usn, string volume, MFTSearcherCache db)
{ // {
var cached = db.FindByFrn(volume, usn.FRN); // var cached = db.FindByFrn(volume, usn.FRN);
if (cached == null) // if (cached == null)
{ // {
return; // return;
} // }
else // else
{ // {
Wox.Infrastructure.MFTSearch.MFTSearcher.FillPath(volume, cached, db); // Wox.Infrastructure.MFTSearch.MFTSearcher.FillPath(volume, cached, db);
var deleteok = db.DeleteRecord(volume, usn.FRN); // var deleteok = db.DeleteRecord(volume, usn.FRN);
Debug.WriteLine(string.Format(">>>> File {0} deleted {1}.", cached.FullPath, deleteok ? "successful" : "fail")); // Debug.WriteLine(string.Format(">>>> File {0} deleted {1}.", cached.FullPath, deleteok ? "successful" : "fail"));
if (RecordDeletedEvent != null) // if (RecordDeletedEvent != null)
RecordDeletedEvent(cached); // RecordDeletedEvent(cached);
} // }
} // }
private void ProcessRenameNewName(PInvokeWin32.USN_RECORD usn, string volume, MFTSearcherCache db) // private void ProcessRenameNewName(PInvokeWin32.USN_RECORD usn, string volume, MFTSearcherCache db)
{ // {
USNRecord newRecord = USNRecord.ParseUSN(volume, usn); // USNRecord newRecord = USNRecord.ParseUSN(volume, usn);
//string fullpath = newRecord.Name; // //string fullpath = newRecord.Name;
//db.FindRecordPath(newRecord, ref fullpath, db.GetFolderSource(volume)); // //db.FindRecordPath(newRecord, ref fullpath, db.GetFolderSource(volume));
//newRecord.FullPath = fullpath; // //newRecord.FullPath = fullpath;
var oldRecord = db.FindByFrn(volume, usn.FRN); // var oldRecord = db.FindByFrn(volume, usn.FRN);
Wox.Infrastructure.MFTSearch.MFTSearcher.FillPath(volume, oldRecord, db); // Wox.Infrastructure.MFTSearch.MFTSearcher.FillPath(volume, oldRecord, db);
Wox.Infrastructure.MFTSearch.MFTSearcher.FillPath(volume, newRecord, db); // Wox.Infrastructure.MFTSearch.MFTSearcher.FillPath(volume, newRecord, db);
Debug.WriteLine(string.Format(">>>> RenameFile {0} to {1}", oldRecord.FullPath, newRecord.FullPath)); // Debug.WriteLine(string.Format(">>>> RenameFile {0} to {1}", oldRecord.FullPath, newRecord.FullPath));
db.UpdateRecord(volume, newRecord, // db.UpdateRecord(volume, newRecord,
usn.IsFolder ? USNRecordType.Folder : USNRecordType.File); // usn.IsFolder ? USNRecordType.Folder : USNRecordType.File);
if (RecordRenameEvent != null) RecordRenameEvent(oldRecord, newRecord); // if (RecordRenameEvent != null) RecordRenameEvent(oldRecord, newRecord);
if (newRecord.FullPath.Contains("$RECYCLE.BIN")) // if (newRecord.FullPath.Contains("$RECYCLE.BIN"))
{ // {
Debug.WriteLine(string.Format(">>>> Means {0} moved to recycle.", oldRecord.FullPath)); // Debug.WriteLine(string.Format(">>>> Means {0} moved to recycle.", oldRecord.FullPath));
} // }
} // }
private void ProcessFileCreate(PInvokeWin32.USN_RECORD usn, string volume, MFTSearcherCache db) // private void ProcessFileCreate(PInvokeWin32.USN_RECORD usn, string volume, MFTSearcherCache db)
{ // {
USNRecord record = USNRecord.ParseUSN(volume, usn); // USNRecord record = USNRecord.ParseUSN(volume, usn);
//string fullpath = record.Name; // //string fullpath = record.Name;
//db.FindRecordPath(record, ref fullpath, db.GetFolderSource(volume)); // //db.FindRecordPath(record, ref fullpath, db.GetFolderSource(volume));
//record.FullPath = fullpath; // //record.FullPath = fullpath;
db.AddRecord(volume, record, usn.IsFolder ? USNRecordType.Folder : USNRecordType.File); // db.AddRecord(volume, record, usn.IsFolder ? USNRecordType.Folder : USNRecordType.File);
Wox.Infrastructure.MFTSearch.MFTSearcher.FillPath(volume, record, db); // Wox.Infrastructure.MFTSearch.MFTSearcher.FillPath(volume, record, db);
Debug.WriteLine(string.Format(">>>> NewFile: {0}", record.FullPath)); // Debug.WriteLine(string.Format(">>>> NewFile: {0}", record.FullPath));
if (RecordAddedEvent != null) // if (RecordAddedEvent != null)
RecordAddedEvent(record); // RecordAddedEvent(record);
} // }
private bool MaskEqual(uint target, uint compare) // private bool MaskEqual(uint target, uint compare)
{ // {
return (target & compare) != 0; // return (target & compare) != 0;
} // }
} // }
} //}

View File

@@ -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;
}
}
} }

View File

@@ -16,7 +16,7 @@ namespace Wox.Test
var searchtimestart = DateTime.Now; var searchtimestart = DateTime.Now;
MFTSearcher.IndexAllVolumes(); MFTSearcher.IndexAllVolumes();
var searchtimeend = DateTime.Now; var searchtimeend = DateTime.Now;
Console.WriteLine(string.Format("{0} file indexed, {1}ms has spent.", MFTSearcher.IndexedFileCount, searchtimeend.Subtract(searchtimestart).TotalMilliseconds)); Console.WriteLine(string.Format("{0} file indexed, {1}ms has spent.", MFTSearcher.IndexedRecordsCount, searchtimeend.Subtract(searchtimestart).TotalMilliseconds));
searchtimestart = DateTime.Now; searchtimestart = DateTime.Now;
List<MFTSearchRecord> mftSearchRecords = MFTSearcher.Search("q"); List<MFTSearchRecord> mftSearchRecords = MFTSearcher.Search("q");
@@ -28,5 +28,19 @@ namespace Wox.Test
searchtimeend = DateTime.Now; searchtimeend = DateTime.Now;
Console.WriteLine(string.Format("{0} file searched, {1}ms has spent.", mftSearchRecords.Count, searchtimeend.Subtract(searchtimestart).TotalMilliseconds)); 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();
long newWorkingSet = System.Diagnostics.Process.GetCurrentProcess().WorkingSet64;
Console.WriteLine(string.Format("Index: {0}M", (newWorkingSet - oldWorkingSet)/(1024*1024)));
oldWorkingSet = System.Diagnostics.Process.GetCurrentProcess().WorkingSet64;
MFTSearcher.Search("q");
newWorkingSet = System.Diagnostics.Process.GetCurrentProcess().WorkingSet64;
Console.WriteLine(string.Format("Search: {0}M", (newWorkingSet - oldWorkingSet) / (1024 * 1024)));
}
} }
} }

View File

@@ -17,16 +17,17 @@ namespace Wox.Commands
public override void Dispatch(Query query) public override void Dispatch(Query query)
{ {
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 =>

View File

@@ -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>

View File

@@ -141,6 +141,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);
@@ -148,7 +155,6 @@ namespace Wox
#endregion #endregion
public MainWindow() public MainWindow()
{ {
InitializeComponent(); InitializeComponent();
@@ -160,7 +166,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
@@ -180,6 +188,11 @@ namespace Wox
this.Closing += MainWindow_Closing; this.Closing += MainWindow_Closing;
} }
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)
{ {
UserSettingStorage.Instance.WindowLeft = Left; UserSettingStorage.Instance.WindowLeft = Left;
@@ -300,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 =>
@@ -328,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
@@ -463,7 +483,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;
@@ -473,7 +493,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;
@@ -489,7 +509,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;
@@ -516,14 +543,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;
@@ -545,29 +572,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)
{ {
@@ -599,7 +665,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;
} }
} }

View File

@@ -17,7 +17,7 @@
<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>

View File

@@ -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);
} }
} }