2020-08-17 10:00:56 -07:00
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System ;
using System.Collections.Generic ;
using System.ComponentModel ;
using System.Diagnostics ;
using System.Globalization ;
using System.IO ;
using System.Linq ;
using System.Text.RegularExpressions ;
using System.Windows.Controls ;
using Microsoft.Plugin.Indexer.DriveDetection ;
using Microsoft.Plugin.Indexer.SearchHelper ;
2020-10-22 09:45:48 -07:00
using Microsoft.PowerToys.Settings.UI.Library ;
2020-08-18 13:19:35 -07:00
using Microsoft.Search.Interop ;
2020-08-17 10:00:56 -07:00
using Wox.Infrastructure.Logger ;
using Wox.Infrastructure.Storage ;
using Wox.Plugin ;
namespace Microsoft.Plugin.Indexer
{
internal class Main : ISettingProvider , IPlugin , ISavable , IPluginI18n , IContextMenu , IDisposable , IDelayedExecutionPlugin
{
// This variable contains metadata about the Plugin
private PluginInitContext _context ;
// This variable contains information about the context menus
private IndexerSettings _settings ;
// Contains information about the plugin stored in json format
private PluginJsonStorage < IndexerSettings > _storage ;
// To access Windows Search functionalities
private static readonly OleDBSearch _search = new OleDBSearch ( ) ;
private readonly WindowsSearchAPI _api = new WindowsSearchAPI ( _search ) ;
// To obtain information regarding the drives that are indexed
private readonly IndexerDriveDetection _driveDetection = new IndexerDriveDetection ( new RegistryWrapper ( ) ) ;
// Reserved keywords in oleDB
2020-08-21 12:21:42 -07:00
private readonly string reservedStringPattern = @"^[\/\\\$\%]+$|^.*[<>].*$" ;
2020-08-17 10:00:56 -07:00
private string WarningIconPath { get ; set ; }
private IContextMenu _contextMenuLoader ;
private bool disposedValue ;
// To save the configurations of plugins
public void Save ( )
{
_storage . Save ( ) ;
}
// This function uses the Windows indexer and returns the list of results obtained
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "We want to keep the process alive but will log the exception")]
public List < Result > Query ( Query query , bool isFullQuery )
{
var results = new List < Result > ( ) ;
if ( ! string . IsNullOrEmpty ( query . Search ) )
{
var searchQuery = query . Search ;
if ( _settings . MaxSearchCount < = 0 )
{
_settings . MaxSearchCount = 30 ;
}
var regexMatch = Regex . Match ( searchQuery , reservedStringPattern ) ;
if ( ! regexMatch . Success )
{
try
{
if ( _driveDetection . DisplayWarning ( ) )
{
results . Add ( new Result
{
2020-09-02 15:24:59 -07:00
Title = Properties . Resources . Microsoft_plugin_indexer_drivedetectionwarning ,
SubTitle = Properties . Resources . Microsoft_plugin_indexer_disable_warning_in_settings ,
2020-08-17 10:00:56 -07:00
IcoPath = WarningIconPath ,
Action = e = >
{
try
{
Process . Start ( GetWindowsSearchSettingsProcessInfo ( ) ) ;
}
catch ( Exception ex )
{
2020-09-23 16:32:06 -07:00
Log . Exception ( $"Unable to launch Windows Search Settings: {ex.Message}" , ex , GetType ( ) ) ;
2020-08-17 10:00:56 -07:00
}
return true ;
} ,
} ) ;
}
2020-08-18 13:19:35 -07:00
// This uses the Microsoft.Search.Interop assembly
var searchManager = new CSearchManager ( ) ;
var searchResultsList = _api . Search ( searchQuery , searchManager , isFullQuery , maxCount : _settings . MaxSearchCount ) . ToList ( ) ;
2020-08-17 10:00:56 -07:00
// If the delayed execution query is not required (since the SQL query is fast) return empty results
if ( searchResultsList . Count = = 0 & & isFullQuery )
{
return new List < Result > ( ) ;
}
foreach ( var searchResult in searchResultsList )
{
var path = searchResult . Path ;
2020-09-02 15:24:59 -07:00
var toolTipTitle = string . Format ( CultureInfo . CurrentCulture , "{0} : {1}" , Properties . Resources . Microsoft_plugin_indexer_name , searchResult . Title ) ;
var toolTipText = string . Format ( CultureInfo . CurrentCulture , "{0} : {1}" , Properties . Resources . Microsoft_plugin_indexer_path , path ) ;
2020-08-17 10:00:56 -07:00
string workingDir = null ;
if ( _settings . UseLocationAsWorkingDir )
{
workingDir = Path . GetDirectoryName ( path ) ;
}
Result r = new Result ( ) ;
r . Title = searchResult . Title ;
2020-09-08 11:51:24 -07:00
r . SubTitle = Properties . Resources . Microsoft_plugin_indexer_subtitle_header + ": " + path ;
2020-08-17 10:00:56 -07:00
r . IcoPath = path ;
r . ToolTipData = new ToolTipData ( toolTipTitle , toolTipText ) ;
r . Action = c = >
{
bool hide ;
try
{
Process . Start ( new ProcessStartInfo
{
FileName = path ,
UseShellExecute = true ,
WorkingDirectory = workingDir ,
} ) ;
hide = true ;
}
catch ( Win32Exception )
{
var name = $"Plugin: {_context.CurrentPluginMetadata.Name}" ;
2020-09-08 11:51:24 -07:00
var msg = Properties . Resources . Microsoft_plugin_indexer_file_open_failed ;
2020-08-17 10:00:56 -07:00
_context . API . ShowMsg ( name , msg , string . Empty ) ;
hide = false ;
}
return hide ;
} ;
r . ContextData = searchResult ;
// If the result is a directory, then it's display should show a directory.
if ( Directory . Exists ( path ) )
{
r . QueryTextDisplay = path ;
}
results . Add ( r ) ;
}
}
catch ( InvalidOperationException )
{
// The connection has closed, internal error of ExecuteReader()
// Not showing this exception to the users
}
catch ( Exception ex )
{
2020-09-23 16:32:06 -07:00
Log . Exception ( "Something failed" , ex , GetType ( ) ) ;
2020-08-17 10:00:56 -07:00
}
}
}
return results ;
}
// This function uses the Windows indexer and returns the list of results obtained. This version is required to implement the interface
public List < Result > Query ( Query query )
{
return Query ( query , false ) ;
}
public void Init ( PluginInitContext context )
{
// initialize the context of the plugin
_context = context ;
_contextMenuLoader = new ContextMenuLoader ( context ) ;
_storage = new PluginJsonStorage < IndexerSettings > ( ) ;
_settings = _storage . Load ( ) ;
_context . API . ThemeChanged + = OnThemeChanged ;
UpdateIconPath ( _context . API . GetCurrentTheme ( ) ) ;
}
// Todo : Update with theme based IconPath
private void UpdateIconPath ( Theme theme )
{
if ( theme = = Theme . Light | | theme = = Theme . HighContrastWhite )
{
WarningIconPath = "Images/Warning.light.png" ;
}
else
{
WarningIconPath = "Images/Warning.dark.png" ;
}
}
private void OnThemeChanged ( Theme currentTheme , Theme newTheme )
{
UpdateIconPath ( newTheme ) ;
}
// Set the Plugin Title
public string GetTranslatedPluginTitle ( )
{
2020-09-08 11:51:24 -07:00
return Properties . Resources . Microsoft_plugin_indexer_plugin_name ;
2020-08-17 10:00:56 -07:00
}
// Set the plugin Description
public string GetTranslatedPluginDescription ( )
{
2020-09-08 11:51:24 -07:00
return Properties . Resources . Microsoft_plugin_indexer_plugin_description ;
2020-08-17 10:00:56 -07:00
}
public List < ContextMenuResult > LoadContextMenus ( Result selectedResult )
{
return _contextMenuLoader . LoadContextMenus ( selectedResult ) ;
}
public void UpdateSettings ( PowerLauncherSettings settings )
{
_driveDetection . IsDriveDetectionWarningCheckBoxSelected = settings . Properties . DisableDriveDetectionWarning ;
}
public Control CreateSettingPanel ( )
{
throw new NotImplementedException ( ) ;
}
// Returns the Process Start Information for the new Windows Search Settings
public static ProcessStartInfo GetWindowsSearchSettingsProcessInfo ( )
{
var ps = new ProcessStartInfo ( "ms-settings:cortana-windowssearch" )
{
UseShellExecute = true ,
Verb = "open" ,
} ;
return ps ;
}
protected virtual void Dispose ( bool disposing )
{
if ( ! disposedValue )
{
if ( disposing )
{
}
disposedValue = true ;
}
}
public void Dispose ( )
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose ( disposing : true ) ;
GC . SuppressFinalize ( this ) ;
}
}
}