Flow.Launcher/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs

460 lines
20 KiB
C#
Raw Permalink Normal View History

using System;
2020-05-25 08:58:04 +00:00
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Plugin.SharedCommands;
using Flow.Launcher.Plugin.Explorer.Search;
2021-01-26 09:48:06 +00:00
using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Input;
2020-06-21 22:43:59 +00:00
using MessageBox = System.Windows.Forms.MessageBox;
using MessageBoxIcon = System.Windows.Forms.MessageBoxIcon;
using MessageBoxButton = System.Windows.Forms.MessageBoxButtons;
using DialogResult = System.Windows.Forms.DialogResult;
2021-01-26 10:14:39 +00:00
using Flow.Launcher.Plugin.Explorer.ViewModels;
2020-05-25 08:58:04 +00:00
namespace Flow.Launcher.Plugin.Explorer
{
internal class ContextMenu : IContextMenu
{
private PluginInitContext Context { get; set; }
private Settings Settings { get; set; }
2021-01-26 10:14:39 +00:00
private SettingsViewModel ViewModel { get; set; }
public ContextMenu(PluginInitContext context, Settings settings, SettingsViewModel vm)
{
Context = context;
Settings = settings;
2021-01-26 10:14:39 +00:00
ViewModel = vm;
}
2020-05-25 08:58:04 +00:00
public List<Result> LoadContextMenus(Result selectedResult)
{
var contextMenus = new List<Result>();
if (selectedResult.ContextData is SearchResult record)
{
if (record.Type == ResultType.File && !string.IsNullOrEmpty(Settings.EditorPath))
2020-05-25 08:58:04 +00:00
contextMenus.Add(CreateOpenWithEditorResult(record));
if (record.Type == ResultType.Folder && record.WindowsIndexed)
2022-08-17 00:52:53 +00:00
{
contextMenus.Add(CreateAddToIndexSearchExclusionListResult(record));
2022-08-17 00:52:53 +00:00
contextMenus.Add(CreateOpenWithShellResult(record));
}
contextMenus.Add(CreateOpenContainingFolderResult(record));
if (record.WindowsIndexed)
{
contextMenus.Add(CreateOpenWindowsIndexingOptions());
}
2020-05-25 08:58:04 +00:00
var icoPath = (record.Type == ResultType.File) ? Constants.FileImagePath : Constants.FolderImagePath;
var fileOrFolder = (record.Type == ResultType.File) ? "file" : "folder";
if (Settings.QuickAccessLinks.All(x => !x.Path.Equals(record.FullPath, StringComparison.OrdinalIgnoreCase)))
{
contextMenus.Add(new Result
{
Title = Context.API.GetTranslation("plugin_explorer_add_to_quickaccess_title"),
SubTitle = string.Format(Context.API.GetTranslation("plugin_explorer_add_to_quickaccess_subtitle"), fileOrFolder),
Action = (context) =>
{
Settings.QuickAccessLinks.Add(new AccessLink
{
Path = record.FullPath, Type = record.Type
});
2021-01-26 19:17:24 +00:00
Context.API.ShowMsg(Context.API.GetTranslation("plugin_explorer_addfilefoldersuccess"),
string.Format(
Context.API.GetTranslation("plugin_explorer_addfilefoldersuccess_detail"),
fileOrFolder),
Constants.ExplorerIconImageFullPath);
2021-01-26 10:14:39 +00:00
2021-01-26 19:17:24 +00:00
ViewModel.Save();
2021-01-26 10:14:39 +00:00
2021-01-26 19:17:24 +00:00
return true;
},
SubTitleToolTip = Context.API.GetTranslation("plugin_explorer_contextmenu_titletooltip"),
TitleToolTip = Context.API.GetTranslation("plugin_explorer_contextmenu_titletooltip"),
2022-12-12 04:08:22 +00:00
IcoPath = Constants.QuickAccessImagePath,
Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\ue718"),
});
}
else
{
contextMenus.Add(new Result
{
Title = Context.API.GetTranslation("plugin_explorer_remove_from_quickaccess_title"),
SubTitle = string.Format(Context.API.GetTranslation("plugin_explorer_remove_from_quickaccess_subtitle"), fileOrFolder),
Action = (context) =>
{
Settings.QuickAccessLinks.Remove(Settings.QuickAccessLinks.FirstOrDefault(x => x.Path == record.FullPath));
Context.API.ShowMsg(Context.API.GetTranslation("plugin_explorer_removefilefoldersuccess"),
string.Format(
Context.API.GetTranslation("plugin_explorer_removefilefoldersuccess_detail"),
fileOrFolder),
Constants.ExplorerIconImageFullPath);
ViewModel.Save();
return true;
},
SubTitleToolTip = Context.API.GetTranslation("plugin_explorer_contextmenu_remove_titletooltip"),
TitleToolTip = Context.API.GetTranslation("plugin_explorer_contextmenu_remove_titletooltip"),
2022-12-12 04:08:22 +00:00
IcoPath = Constants.RemoveQuickAccessImagePath,
Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\uecc9")
});
}
2020-05-25 08:58:04 +00:00
contextMenus.Add(new Result
{
2020-06-08 10:31:48 +00:00
Title = Context.API.GetTranslation("plugin_explorer_copypath"),
2020-05-25 08:58:04 +00:00
SubTitle = $"Copy the current {fileOrFolder} path to clipboard",
Action = _ =>
2020-05-25 08:58:04 +00:00
{
try
{
Clipboard.SetText(record.FullPath);
return true;
}
catch (Exception e)
{
var message = "Fail to set text in clipboard";
LogException(message, e);
Context.API.ShowMsg(message);
2020-05-25 08:58:04 +00:00
return false;
}
},
2022-12-12 04:08:22 +00:00
IcoPath = Constants.CopyImagePath,
Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\ue8c8")
2020-05-25 08:58:04 +00:00
});
contextMenus.Add(new Result
{
2020-06-08 10:31:48 +00:00
Title = Context.API.GetTranslation("plugin_explorer_copyfilefolder") + $" {fileOrFolder}",
2020-05-25 08:58:04 +00:00
SubTitle = $"Copy the {fileOrFolder} to clipboard",
Action = _ =>
2020-05-25 08:58:04 +00:00
{
try
{
Clipboard.SetFileDropList(new System.Collections.Specialized.StringCollection
{
record.FullPath
});
2020-05-25 08:58:04 +00:00
return true;
}
catch (Exception e)
{
var message = $"Fail to set {fileOrFolder} in clipboard";
LogException(message, e);
Context.API.ShowMsg(message);
2020-05-25 08:58:04 +00:00
return false;
}
},
2022-12-12 04:08:22 +00:00
IcoPath = icoPath,
Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\uf12b")
2020-05-25 08:58:04 +00:00
});
if (record.Type is ResultType.File or ResultType.Folder)
2020-05-25 08:58:04 +00:00
contextMenus.Add(new Result
{
2020-06-08 10:31:48 +00:00
Title = Context.API.GetTranslation("plugin_explorer_deletefilefolder") + $" {fileOrFolder}",
SubTitle = Context.API.GetTranslation("plugin_explorer_deletefilefolder_subtitle") + $" {fileOrFolder}",
2020-05-25 08:58:04 +00:00
Action = (context) =>
{
try
{
2020-06-21 22:43:59 +00:00
if (MessageBox.Show(
string.Format(Context.API.GetTranslation("plugin_explorer_deletefilefolderconfirm"), fileOrFolder),
string.Empty,
MessageBoxButton.YesNo,
MessageBoxIcon.Warning)
2020-06-21 22:43:59 +00:00
== DialogResult.No)
return false;
2020-05-25 08:58:04 +00:00
if (record.Type == ResultType.File)
File.Delete(record.FullPath);
else
Directory.Delete(record.FullPath, true);
2020-06-21 22:43:59 +00:00
_ = Task.Run(() =>
2020-06-21 22:43:59 +00:00
{
Context.API.ShowMsg(Context.API.GetTranslation("plugin_explorer_deletefilefoldersuccess"),
string.Format(Context.API.GetTranslation("plugin_explorer_deletefilefoldersuccess_detail"), fileOrFolder),
Constants.ExplorerIconImageFullPath);
2020-06-21 22:43:59 +00:00
});
2020-05-25 08:58:04 +00:00
}
catch (Exception e)
{
var message = $"Fail to delete {fileOrFolder} at {record.FullPath}";
LogException(message, e);
Context.API.ShowMsgError(message);
2020-05-25 08:58:04 +00:00
return false;
}
return true;
},
2022-12-12 04:08:22 +00:00
IcoPath = Constants.DeleteFileFolderImagePath,
Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\ue74d")
2020-05-25 08:58:04 +00:00
});
if (record.Type is not ResultType.Volume)
{
contextMenus.Add(new Result()
{
Title = Context.API.GetTranslation("plugin_explorer_show_contextmenu_title"),
IcoPath = Constants.ShowContextMenuImagePath,
Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\ue700"),
Action = _ =>
{
if (record.Type is ResultType.Volume)
return false;
var screenWithMouseCursor = System.Windows.Forms.Screen.FromPoint(System.Windows.Forms.Cursor.Position);
var xOfScreenCenter = screenWithMouseCursor.WorkingArea.Left + screenWithMouseCursor.WorkingArea.Width / 2;
var yOfScreenCenter = screenWithMouseCursor.WorkingArea.Top + screenWithMouseCursor.WorkingArea.Height / 2;
var showPosition = new System.Drawing.Point(xOfScreenCenter, yOfScreenCenter);
switch (record.Type)
{
case ResultType.File:
{
var fileInfos = new FileInfo[]
{
new(record.FullPath)
};
new Peter.ShellContextMenu().ShowContextMenu(fileInfos, showPosition);
break;
}
case ResultType.Folder:
{
var directoryInfos = new DirectoryInfo[]
{
new(record.FullPath)
};
new Peter.ShellContextMenu().ShowContextMenu(directoryInfos, showPosition);
break;
}
}
return false;
2022-11-29 10:50:53 +00:00
},
});
}
2020-05-25 08:58:04 +00:00
if (record.Type == ResultType.File && CanRunAsDifferentUser(record.FullPath))
contextMenus.Add(new Result
{
2020-06-08 10:31:48 +00:00
Title = Context.API.GetTranslation("plugin_explorer_runasdifferentuser"),
SubTitle = Context.API.GetTranslation("plugin_explorer_runasdifferentuser_subtitle"),
2020-05-25 08:58:04 +00:00
Action = (context) =>
{
try
{
_ = Task.Run(() => ShellCommand.RunAsDifferentUser(record.FullPath.SetProcessStartInfo()));
2020-05-25 08:58:04 +00:00
}
catch (FileNotFoundException e)
{
var name = "Plugin: Folder";
var message = $"File not found: {e.Message}";
Context.API.ShowMsgError(name, message);
2020-05-25 08:58:04 +00:00
}
return true;
},
IcoPath = Constants.DifferentUserIconImagePath
2020-05-25 08:58:04 +00:00
});
}
return contextMenus;
}
private Result CreateOpenContainingFolderResult(SearchResult record)
{
return new Result
{
2020-06-08 10:31:48 +00:00
Title = Context.API.GetTranslation("plugin_explorer_opencontainingfolder"),
SubTitle = Context.API.GetTranslation("plugin_explorer_opencontainingfolder_subtitle"),
2020-05-25 08:58:04 +00:00
Action = _ =>
{
try
{
Context.API.OpenDirectory(Path.GetDirectoryName(record.FullPath), record.FullPath);
2020-05-25 08:58:04 +00:00
}
catch (Exception e)
{
var message = $"Fail to open file at {record.FullPath}";
LogException(message, e);
Context.API.ShowMsgError(message);
2020-05-25 08:58:04 +00:00
return false;
}
return true;
},
2022-12-12 04:08:22 +00:00
IcoPath = Constants.FolderImagePath,
Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\ue838")
2020-05-25 08:58:04 +00:00
};
}
2020-05-25 08:58:04 +00:00
private Result CreateOpenWithEditorResult(SearchResult record)
{
2022-08-16 22:51:03 +00:00
string editorPath = Settings.EditorPath;
2020-06-08 10:31:48 +00:00
2022-08-17 00:52:53 +00:00
var name = $"{Context.API.GetTranslation("plugin_explorer_openwitheditor")} {Path.GetFileNameWithoutExtension(editorPath)}";
2020-05-25 08:58:04 +00:00
return new Result
{
Title = name,
Action = _ =>
{
try
{
2022-12-13 17:18:57 +00:00
Process.Start(new ProcessStartInfo()
{
FileName = editorPath,
ArgumentList = { record.FullPath }
});
2020-05-25 08:58:04 +00:00
return true;
}
catch (Exception e)
{
2022-12-13 11:47:05 +00:00
var raw_message = Context.API.GetTranslation("plugin_explorer_openwitheditor_error");
var message = string.Format(raw_message, record.FullPath, Path.GetFileNameWithoutExtension(editorPath), editorPath);
2020-05-25 08:58:04 +00:00
LogException(message, e);
Context.API.ShowMsgError(message);
2020-05-25 08:58:04 +00:00
return false;
}
},
IcoPath = Constants.FileImagePath
2020-05-25 08:58:04 +00:00
};
}
2022-08-17 00:52:53 +00:00
private Result CreateOpenWithShellResult(SearchResult record)
{
string shellPath = Settings.ShellPath;
var name = $"{Context.API.GetTranslation("plugin_explorer_openwithshell")} {Path.GetFileNameWithoutExtension(shellPath)}";
return new Result
{
Title = name,
Action = _ =>
{
try
{
Process.Start(new ProcessStartInfo()
{
FileName = shellPath, WorkingDirectory = record.FullPath
2022-08-17 00:52:53 +00:00
});
return true;
}
catch (Exception e)
{
2022-12-13 11:47:05 +00:00
var raw_message = Context.API.GetTranslation("plugin_explorer_openwithshell_error");
var message = string.Format(raw_message, record.FullPath, Path.GetFileNameWithoutExtension(shellPath), shellPath);
2022-08-17 00:52:53 +00:00
LogException(message, e);
Context.API.ShowMsgError(message);
return false;
}
},
IcoPath = Constants.FileImagePath
};
}
2020-05-25 08:58:04 +00:00
private Result CreateAddToIndexSearchExclusionListResult(SearchResult record)
{
return new Result
{
2020-06-08 10:31:48 +00:00
Title = Context.API.GetTranslation("plugin_explorer_excludefromindexsearch"),
SubTitle = Context.API.GetTranslation("plugin_explorer_path") + " " + record.FullPath,
Action = _ =>
{
if (!Settings.IndexSearchExcludedSubdirectoryPaths.Any(x => x.Path == record.FullPath))
Settings.IndexSearchExcludedSubdirectoryPaths.Add(new AccessLink
{
Path = record.FullPath
});
Task.Run(() =>
{
Context.API.ShowMsg(Context.API.GetTranslation("plugin_explorer_excludedfromindexsearch_msg"),
Context.API.GetTranslation("plugin_explorer_path") +
" " + record.FullPath, Constants.ExplorerIconImageFullPath);
// so the new path can be persisted to storage and not wait till next ViewModel save.
Context.API.SaveAppAllSettings();
});
return false;
},
IcoPath = Constants.ExcludeFromIndexImagePath
};
}
2020-06-08 10:31:48 +00:00
private Result CreateOpenWindowsIndexingOptions()
{
return new Result
{
2020-06-08 10:31:48 +00:00
Title = Context.API.GetTranslation("plugin_explorer_openindexingoptions"),
SubTitle = Context.API.GetTranslation("plugin_explorer_openindexingoptions_subtitle"),
Action = _ =>
{
try
{
var psi = new ProcessStartInfo
{
FileName = "control.exe",
UseShellExecute = true,
Arguments = "srchadmin.dll"
};
Process.Start(psi);
return true;
}
catch (Exception e)
{
2020-06-08 10:31:48 +00:00
var message = Context.API.GetTranslation("plugin_explorer_openindexingoptions_errormsg");
LogException(message, e);
Context.API.ShowMsgError(message);
return false;
}
},
IcoPath = Constants.IndexingOptionsIconImagePath
};
}
2020-05-25 08:58:04 +00:00
public void LogException(string message, Exception e)
{
Log.Exception($"|Flow.Launcher.Plugin.Folder.ContextMenu|{message}", e);
}
private bool CanRunAsDifferentUser(string path)
{
switch (Path.GetExtension(path))
{
case ".exe":
case ".bat":
case ".msi":
return true;
default:
return false;
}
}
}
}