2025-03-14 08:12:15 +00:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
2020-07-02 20:44:48 +00:00
|
|
|
|
using System.Linq;
|
2025-03-26 07:33:49 +00:00
|
|
|
|
using System.Windows.Controls;
|
|
|
|
|
|
using Flow.Launcher.Plugin.ProcessKiller.ViewModels;
|
|
|
|
|
|
using Flow.Launcher.Plugin.ProcessKiller.Views;
|
2020-07-02 20:44:48 +00:00
|
|
|
|
|
|
|
|
|
|
namespace Flow.Launcher.Plugin.ProcessKiller
|
|
|
|
|
|
{
|
2025-03-26 07:33:49 +00:00
|
|
|
|
public class Main : IPlugin, IPluginI18n, IContextMenu, ISettingProvider
|
2020-07-02 20:44:48 +00:00
|
|
|
|
{
|
2025-03-13 06:01:02 +00:00
|
|
|
|
private readonly ProcessHelper processHelper = new();
|
2020-07-02 20:44:48 +00:00
|
|
|
|
|
2020-07-02 22:16:33 +00:00
|
|
|
|
private static PluginInitContext _context;
|
|
|
|
|
|
|
2025-03-26 07:33:49 +00:00
|
|
|
|
internal Settings Settings;
|
|
|
|
|
|
|
|
|
|
|
|
private SettingsViewModel _viewModel;
|
|
|
|
|
|
|
2020-07-02 22:16:33 +00:00
|
|
|
|
public void Init(PluginInitContext context)
|
|
|
|
|
|
{
|
|
|
|
|
|
_context = context;
|
2025-03-26 07:33:49 +00:00
|
|
|
|
Settings = context.API.LoadSettingJsonStorage<Settings>();
|
|
|
|
|
|
_viewModel = new SettingsViewModel(Settings);
|
2020-07-02 22:16:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-02 20:44:48 +00:00
|
|
|
|
public List<Result> Query(Query query)
|
|
|
|
|
|
{
|
2023-04-23 16:45:38 +00:00
|
|
|
|
return CreateResultsFromQuery(query);
|
2020-07-02 20:44:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-02 22:16:33 +00:00
|
|
|
|
public string GetTranslatedPluginTitle()
|
|
|
|
|
|
{
|
|
|
|
|
|
return _context.API.GetTranslation("flowlauncher_plugin_processkiller_plugin_name");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public string GetTranslatedPluginDescription()
|
|
|
|
|
|
{
|
|
|
|
|
|
return _context.API.GetTranslation("flowlauncher_plugin_processkiller_plugin_description");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-02 23:43:35 +00:00
|
|
|
|
public List<Result> LoadContextMenus(Result result)
|
|
|
|
|
|
{
|
|
|
|
|
|
var menuOptions = new List<Result>();
|
|
|
|
|
|
var processPath = result.SubTitle;
|
|
|
|
|
|
|
|
|
|
|
|
// get all non-system processes whose file path matches that of the given result (processPath)
|
2020-07-03 14:07:24 +00:00
|
|
|
|
var similarProcesses = processHelper.GetSimilarProcesses(processPath);
|
2020-07-02 23:43:35 +00:00
|
|
|
|
|
2023-04-23 16:45:38 +00:00
|
|
|
|
if (similarProcesses.Any())
|
2020-07-02 23:43:35 +00:00
|
|
|
|
{
|
2020-10-11 20:40:29 +00:00
|
|
|
|
menuOptions.Add(new Result
|
2020-07-02 23:43:35 +00:00
|
|
|
|
{
|
2020-10-11 20:40:29 +00:00
|
|
|
|
Title = _context.API.GetTranslation("flowlauncher_plugin_processkiller_kill_instances"),
|
|
|
|
|
|
SubTitle = processPath,
|
|
|
|
|
|
Action = _ =>
|
2020-07-02 23:43:35 +00:00
|
|
|
|
{
|
2020-10-11 20:40:29 +00:00
|
|
|
|
foreach (var p in similarProcesses)
|
|
|
|
|
|
{
|
2025-03-14 08:12:15 +00:00
|
|
|
|
processHelper.TryKill(_context, p);
|
2020-10-11 20:40:29 +00:00
|
|
|
|
}
|
2020-07-02 23:43:35 +00:00
|
|
|
|
|
2020-10-11 20:40:29 +00:00
|
|
|
|
return true;
|
|
|
|
|
|
},
|
|
|
|
|
|
IcoPath = processPath
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2020-07-02 23:43:35 +00:00
|
|
|
|
|
|
|
|
|
|
return menuOptions;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-04-23 16:45:38 +00:00
|
|
|
|
private List<Result> CreateResultsFromQuery(Query query)
|
2020-07-02 20:44:48 +00:00
|
|
|
|
{
|
2025-03-14 08:12:15 +00:00
|
|
|
|
// Get all non-system processes
|
|
|
|
|
|
var allPocessList = processHelper.GetMatchingProcesses();
|
|
|
|
|
|
if (!allPocessList.Any())
|
2023-04-23 16:45:38 +00:00
|
|
|
|
{
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-14 08:12:15 +00:00
|
|
|
|
// Filter processes based on search term
|
|
|
|
|
|
var searchTerm = query.Search;
|
|
|
|
|
|
var processlist = new List<ProcessResult>();
|
2025-04-15 15:42:08 +00:00
|
|
|
|
var processWindowTitle =
|
|
|
|
|
|
Settings.ShowWindowTitle || Settings.PutVisibleWindowProcessesTop ?
|
2025-04-15 13:21:47 +00:00
|
|
|
|
ProcessHelper.GetProcessesWithNonEmptyWindowTitle() :
|
|
|
|
|
|
new Dictionary<int, string>();
|
2025-03-14 08:12:15 +00:00
|
|
|
|
if (string.IsNullOrWhiteSpace(searchTerm))
|
2020-07-02 20:44:48 +00:00
|
|
|
|
{
|
2025-03-14 08:12:15 +00:00
|
|
|
|
foreach (var p in allPocessList)
|
2025-03-14 03:39:16 +00:00
|
|
|
|
{
|
2025-03-14 08:12:15 +00:00
|
|
|
|
var progressNameIdTitle = ProcessHelper.GetProcessNameIdTitle(p);
|
|
|
|
|
|
|
|
|
|
|
|
if (processWindowTitle.TryGetValue(p.Id, out var windowTitle))
|
2025-03-14 03:39:16 +00:00
|
|
|
|
{
|
|
|
|
|
|
// Add score to prioritize processes with visible windows
|
2025-04-15 15:42:08 +00:00
|
|
|
|
// Use window title for those processes if enabled
|
|
|
|
|
|
processlist.Add(new ProcessResult(
|
|
|
|
|
|
p,
|
|
|
|
|
|
Settings.PutVisibleWindowProcessesTop ? 200 : 0,
|
|
|
|
|
|
Settings.ShowWindowTitle ? windowTitle : progressNameIdTitle,
|
|
|
|
|
|
null,
|
|
|
|
|
|
progressNameIdTitle));
|
2025-03-14 08:12:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-04-15 15:42:08 +00:00
|
|
|
|
processlist.Add(new ProcessResult(
|
|
|
|
|
|
p,
|
|
|
|
|
|
0,
|
|
|
|
|
|
progressNameIdTitle,
|
|
|
|
|
|
null,
|
|
|
|
|
|
progressNameIdTitle));
|
2025-03-14 03:39:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-03-14 08:12:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var p in allPocessList)
|
|
|
|
|
|
{
|
|
|
|
|
|
var progressNameIdTitle = ProcessHelper.GetProcessNameIdTitle(p);
|
|
|
|
|
|
|
|
|
|
|
|
if (processWindowTitle.TryGetValue(p.Id, out var windowTitle))
|
|
|
|
|
|
{
|
|
|
|
|
|
// Get max score from searching process name, window title and process id
|
|
|
|
|
|
var windowTitleMatch = _context.API.FuzzySearch(searchTerm, windowTitle);
|
|
|
|
|
|
var processNameIdMatch = _context.API.FuzzySearch(searchTerm, progressNameIdTitle);
|
|
|
|
|
|
var score = Math.Max(windowTitleMatch.Score, processNameIdMatch.Score);
|
|
|
|
|
|
if (score > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Add score to prioritize processes with visible windows
|
2025-04-15 15:42:08 +00:00
|
|
|
|
// Use window title for those processes
|
2025-03-26 07:33:49 +00:00
|
|
|
|
if (Settings.PutVisibleWindowProcessesTop)
|
|
|
|
|
|
{
|
|
|
|
|
|
score += 200;
|
|
|
|
|
|
}
|
2025-04-15 15:42:08 +00:00
|
|
|
|
processlist.Add(new ProcessResult(
|
|
|
|
|
|
p,
|
|
|
|
|
|
score,
|
|
|
|
|
|
Settings.ShowWindowTitle ? windowTitle : progressNameIdTitle,
|
|
|
|
|
|
score == windowTitleMatch.Score ? windowTitleMatch : null,
|
|
|
|
|
|
progressNameIdTitle));
|
2025-03-14 08:12:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
var processNameIdMatch = _context.API.FuzzySearch(searchTerm, progressNameIdTitle);
|
|
|
|
|
|
var score = processNameIdMatch.Score;
|
|
|
|
|
|
if (score > 0)
|
|
|
|
|
|
{
|
2025-04-15 15:42:08 +00:00
|
|
|
|
processlist.Add(new ProcessResult(
|
|
|
|
|
|
p,
|
|
|
|
|
|
score,
|
|
|
|
|
|
progressNameIdTitle,
|
|
|
|
|
|
processNameIdMatch,
|
|
|
|
|
|
progressNameIdTitle));
|
2025-03-14 08:12:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var results = new List<Result>();
|
|
|
|
|
|
foreach (var pr in processlist)
|
|
|
|
|
|
{
|
|
|
|
|
|
var p = pr.Process;
|
|
|
|
|
|
var path = processHelper.TryGetProcessFilename(p);
|
2020-07-02 20:44:48 +00:00
|
|
|
|
results.Add(new Result()
|
|
|
|
|
|
{
|
|
|
|
|
|
IcoPath = path,
|
2025-03-14 08:12:15 +00:00
|
|
|
|
Title = pr.Title,
|
|
|
|
|
|
TitleToolTip = pr.Tooltip,
|
2020-07-02 20:44:48 +00:00
|
|
|
|
SubTitle = path,
|
2025-03-14 08:12:15 +00:00
|
|
|
|
TitleHighlightData = pr.TitleMatch?.MatchData,
|
|
|
|
|
|
Score = pr.Score,
|
|
|
|
|
|
ContextData = p.ProcessName,
|
2022-01-13 02:52:13 +00:00
|
|
|
|
AutoCompleteText = $"{_context.CurrentPluginMetadata.ActionKeyword}{Plugin.Query.TermSeparator}{p.ProcessName}",
|
2020-07-02 20:44:48 +00:00
|
|
|
|
Action = (c) =>
|
|
|
|
|
|
{
|
2025-03-14 08:12:15 +00:00
|
|
|
|
processHelper.TryKill(_context, p);
|
2025-03-26 12:23:18 +00:00
|
|
|
|
// Re-query to refresh process list
|
2025-03-13 06:16:38 +00:00
|
|
|
|
_context.API.ReQuery();
|
2025-03-26 12:23:18 +00:00
|
|
|
|
return true;
|
2020-07-02 20:44:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-14 08:12:15 +00:00
|
|
|
|
// Order results by process name for processes without visible windows
|
|
|
|
|
|
var sortedResults = results.OrderBy(x => x.Title).ToList();
|
2020-09-23 21:22:55 +00:00
|
|
|
|
|
2020-07-02 23:10:41 +00:00
|
|
|
|
// When there are multiple results AND all of them are instances of the same executable
|
|
|
|
|
|
// add a quick option to kill them all at the top of the results.
|
2020-10-11 20:55:35 +00:00
|
|
|
|
var firstResult = sortedResults.FirstOrDefault(x => !string.IsNullOrEmpty(x.SubTitle));
|
2025-03-14 08:12:15 +00:00
|
|
|
|
if (processlist.Count > 1 && !string.IsNullOrEmpty(searchTerm) && sortedResults.All(r => r.SubTitle == firstResult?.SubTitle))
|
2020-07-02 20:44:48 +00:00
|
|
|
|
{
|
2020-09-23 21:22:55 +00:00
|
|
|
|
sortedResults.Insert(1, new Result()
|
2020-07-02 20:44:48 +00:00
|
|
|
|
{
|
2020-10-11 20:55:35 +00:00
|
|
|
|
IcoPath = firstResult?.IcoPath,
|
2025-03-14 08:12:15 +00:00
|
|
|
|
Title = string.Format(_context.API.GetTranslation("flowlauncher_plugin_processkiller_kill_all"), firstResult?.ContextData),
|
|
|
|
|
|
SubTitle = string.Format(_context.API.GetTranslation("flowlauncher_plugin_processkiller_kill_all_count"), processlist.Count),
|
2020-07-02 20:44:48 +00:00
|
|
|
|
Score = 200,
|
|
|
|
|
|
Action = (c) =>
|
|
|
|
|
|
{
|
2025-03-14 08:12:15 +00:00
|
|
|
|
foreach (var p in processlist)
|
2020-07-02 20:44:48 +00:00
|
|
|
|
{
|
2025-03-14 08:12:15 +00:00
|
|
|
|
processHelper.TryKill(_context, p.Process);
|
2020-07-02 20:44:48 +00:00
|
|
|
|
}
|
2025-03-26 12:23:18 +00:00
|
|
|
|
// Re-query to refresh process list
|
2025-03-13 06:16:38 +00:00
|
|
|
|
_context.API.ReQuery();
|
2025-03-26 12:23:18 +00:00
|
|
|
|
return true;
|
2020-07-02 20:44:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-09-23 21:22:55 +00:00
|
|
|
|
return sortedResults;
|
2020-07-02 23:43:35 +00:00
|
|
|
|
}
|
2025-03-26 07:33:49 +00:00
|
|
|
|
|
|
|
|
|
|
public Control CreateSettingPanel()
|
|
|
|
|
|
{
|
|
|
|
|
|
return new SettingsControl(_viewModel);
|
|
|
|
|
|
}
|
2020-07-02 20:44:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|