mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 19:57:57 +01:00
319 lines
10 KiB
C#
319 lines
10 KiB
C#
// 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.Management;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
using System.Threading;
|
|
using System.Windows.Input;
|
|
using ManagedCommon;
|
|
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
|
|
using Microsoft.PowerToys.Settings.UI.Library.ViewModels.Commands;
|
|
|
|
namespace Microsoft.PowerToys.Settings.UI.Library
|
|
{
|
|
public class KeysDataModel : INotifyPropertyChanged
|
|
{
|
|
[JsonPropertyName("originalKeys")]
|
|
public string OriginalKeys { get; set; }
|
|
|
|
[JsonPropertyName("secondKeyOfChord")]
|
|
public uint SecondKeyOfChord { get; set; }
|
|
|
|
[JsonPropertyName("newRemapKeys")]
|
|
public string NewRemapKeys { get; set; }
|
|
|
|
[JsonPropertyName("unicodeText")]
|
|
public string NewRemapString { get; set; }
|
|
|
|
[JsonPropertyName("runProgramFilePath")]
|
|
public string RunProgramFilePath { get; set; }
|
|
|
|
[JsonPropertyName("runProgramArgs")]
|
|
public string RunProgramArgs { get; set; }
|
|
|
|
[JsonPropertyName("openUri")]
|
|
public string OpenUri { get; set; }
|
|
|
|
[JsonPropertyName("operationType")]
|
|
public int OperationType { get; set; }
|
|
|
|
private enum KeyboardManagerEditorType
|
|
{
|
|
KeyEditor = 0,
|
|
ShortcutEditor,
|
|
}
|
|
|
|
public const string CommaSeparator = "<comma>";
|
|
|
|
private static Process editor;
|
|
private ICommand _editShortcutItemCommand;
|
|
|
|
public ICommand EditShortcutItem => _editShortcutItemCommand ?? (_editShortcutItemCommand = new RelayCommand<object>(OnEditShortcutItem));
|
|
|
|
public event PropertyChangedEventHandler PropertyChanged;
|
|
|
|
protected virtual void OnPropertyChanged(string propertyName)
|
|
{
|
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
|
}
|
|
|
|
public void OnEditShortcutItem(object parameter)
|
|
{
|
|
OpenEditor((int)KeyboardManagerEditorType.ShortcutEditor);
|
|
}
|
|
|
|
private async void OpenEditor(int type)
|
|
{
|
|
if (editor != null)
|
|
{
|
|
BringProcessToFront(editor);
|
|
return;
|
|
}
|
|
|
|
const string PowerToyName = KeyboardManagerSettings.ModuleName;
|
|
const string KeyboardManagerEditorPath = "KeyboardManagerEditor\\PowerToys.KeyboardManagerEditor.exe";
|
|
try
|
|
{
|
|
if (editor != null && editor.HasExited)
|
|
{
|
|
Logger.LogInfo($"Previous instance of {PowerToyName} editor exited");
|
|
editor = null;
|
|
}
|
|
|
|
if (editor != null)
|
|
{
|
|
Logger.LogInfo($"The {PowerToyName} editor instance {editor.Id} exists. Bringing the process to the front");
|
|
BringProcessToFront(editor);
|
|
return;
|
|
}
|
|
|
|
string path = Path.Combine(Environment.CurrentDirectory, KeyboardManagerEditorPath);
|
|
Logger.LogInfo($"Starting {PowerToyName} editor from {path}");
|
|
|
|
// InvariantCulture: type represents the KeyboardManagerEditorType enum value
|
|
editor = Process.Start(path, $"{type.ToString(CultureInfo.InvariantCulture)} {Environment.ProcessId}");
|
|
|
|
await editor.WaitForExitAsync();
|
|
|
|
editor = null;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
editor = null;
|
|
Logger.LogError($"Exception encountered when opening an {PowerToyName} editor", e);
|
|
}
|
|
}
|
|
|
|
private static void BringProcessToFront(Process process)
|
|
{
|
|
if (process == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
IntPtr handle = process.MainWindowHandle;
|
|
if (NativeMethods.IsIconic(handle))
|
|
{
|
|
NativeMethods.ShowWindow(handle, NativeMethods.SWRESTORE);
|
|
}
|
|
|
|
NativeMethods.SetForegroundWindow(handle);
|
|
}
|
|
|
|
private static List<string> MapKeysOnlyChord(uint secondKeyOfChord)
|
|
{
|
|
var result = new List<string>();
|
|
if (secondKeyOfChord <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
result.Add(Helper.GetKeyName(secondKeyOfChord));
|
|
|
|
return result;
|
|
}
|
|
|
|
private static List<string> MapKeys(string stringOfKeys, uint secondKeyOfChord, bool splitChordsWithComma = false)
|
|
{
|
|
if (stringOfKeys == null)
|
|
{
|
|
return new List<string>();
|
|
}
|
|
|
|
if (secondKeyOfChord > 0)
|
|
{
|
|
var keys = stringOfKeys.Split(';');
|
|
return keys.Take(keys.Length - 1)
|
|
.Select(uint.Parse)
|
|
.Select(Helper.GetKeyName)
|
|
.ToList();
|
|
}
|
|
else
|
|
{
|
|
if (splitChordsWithComma)
|
|
{
|
|
var keys = stringOfKeys.Split(';')
|
|
.Select(uint.Parse)
|
|
.Select(Helper.GetKeyName)
|
|
.ToList();
|
|
keys.Insert(keys.Count - 1, CommaSeparator);
|
|
return keys;
|
|
}
|
|
else
|
|
{
|
|
return stringOfKeys
|
|
.Split(';')
|
|
.Select(uint.Parse)
|
|
.Select(Helper.GetKeyName)
|
|
.ToList();
|
|
}
|
|
}
|
|
}
|
|
|
|
private static List<string> MapKeys(string stringOfKeys)
|
|
{
|
|
return MapKeys(stringOfKeys, 0);
|
|
}
|
|
|
|
public List<string> GetMappedOriginalKeys(bool ignoreSecondKeyInChord, bool splitChordsWithComma = false)
|
|
{
|
|
if (ignoreSecondKeyInChord && SecondKeyOfChord > 0)
|
|
{
|
|
return MapKeys(OriginalKeys, SecondKeyOfChord);
|
|
}
|
|
else
|
|
{
|
|
return MapKeys(OriginalKeys, 0, splitChordsWithComma);
|
|
}
|
|
}
|
|
|
|
public List<string> GetMappedOriginalKeysOnlyChord()
|
|
{
|
|
return MapKeysOnlyChord(SecondKeyOfChord);
|
|
}
|
|
|
|
public List<string> GetMappedOriginalKeys()
|
|
{
|
|
return GetMappedOriginalKeys(false);
|
|
}
|
|
|
|
public List<string> GetMappedOriginalKeysWithSplitChord()
|
|
{
|
|
return GetMappedOriginalKeys(false, true);
|
|
}
|
|
|
|
public bool IsRunProgram
|
|
{
|
|
get
|
|
{
|
|
return OperationType == 1;
|
|
}
|
|
}
|
|
|
|
public bool IsOpenURI
|
|
{
|
|
get
|
|
{
|
|
return OperationType == 2;
|
|
}
|
|
}
|
|
|
|
public bool IsOpenUriOrIsRunProgram
|
|
{
|
|
get
|
|
{
|
|
return IsOpenURI || IsRunProgram;
|
|
}
|
|
}
|
|
|
|
public bool HasChord
|
|
{
|
|
get
|
|
{
|
|
return SecondKeyOfChord > 0;
|
|
}
|
|
}
|
|
|
|
public List<string> GetMappedNewRemapKeys(int runProgramMaxLength)
|
|
{
|
|
if (IsRunProgram)
|
|
{
|
|
// we're going to just pretend this is a "key" if we have a RunProgramFilePath
|
|
if (string.IsNullOrEmpty(RunProgramFilePath))
|
|
{
|
|
return new List<string>();
|
|
}
|
|
else
|
|
{
|
|
return new List<string> { FormatFakeKeyForDisplay(runProgramMaxLength) };
|
|
}
|
|
}
|
|
else if (IsOpenURI)
|
|
{
|
|
// we're going to just pretend this is a "key" if we have a RunProgramFilePath
|
|
if (string.IsNullOrEmpty(OpenUri))
|
|
{
|
|
return new List<string>();
|
|
}
|
|
else
|
|
{
|
|
if (OpenUri.Length > runProgramMaxLength)
|
|
{
|
|
return new List<string> { $"{OpenUri.Substring(0, runProgramMaxLength - 3)}..." };
|
|
}
|
|
else
|
|
{
|
|
return new List<string> { OpenUri };
|
|
}
|
|
}
|
|
}
|
|
|
|
return (string.IsNullOrEmpty(NewRemapString) || NewRemapString == "*Unsupported*") ? MapKeys(NewRemapKeys) : new List<string> { NewRemapString };
|
|
}
|
|
|
|
// Instead of doing something fancy pants, we 'll just display the RunProgramFilePath data when it's IsRunProgram
|
|
// It truncates the start of the program to run, if it's long and truncates the end of the args if it's long
|
|
// e.g.: c:\MyCool\PathIs\Long\software.exe myArg1 myArg2 myArg3 -> (something like) "...ng\software.exe myArg1..."
|
|
// the idea is you get the most important part of the program to run and some of the args in case that the only thing thats different,
|
|
// e.g: "...path\software.exe cool1.txt" and "...path\software.exe cool3.txt"
|
|
private string FormatFakeKeyForDisplay(int runProgramMaxLength)
|
|
{
|
|
// was going to use this:
|
|
var fakeKey = Environment.ExpandEnvironmentVariables(RunProgramFilePath);
|
|
try
|
|
{
|
|
if (File.Exists(fakeKey))
|
|
{
|
|
fakeKey = Path.GetFileName(Environment.ExpandEnvironmentVariables(RunProgramFilePath));
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
|
|
fakeKey = $"{fakeKey} {RunProgramArgs}".Trim();
|
|
|
|
if (fakeKey.Length > runProgramMaxLength)
|
|
{
|
|
fakeKey = $"{fakeKey.Substring(0, runProgramMaxLength - 3)}...";
|
|
}
|
|
|
|
return fakeKey;
|
|
}
|
|
|
|
public string ToJsonString()
|
|
{
|
|
return JsonSerializer.Serialize(this);
|
|
}
|
|
}
|
|
}
|