Design: Use the plain string for both config and hotkey parsing and make use of KeyConverter, ModifierKeysConverter to make it more rubust. Find corresponding label (to display operation result) and callback (to do the real work) based on textbox name. In this case, they should be named as `<Feature-Name><Control-Name>`. ShowLogsTextBox, ShowLogsLabel, ShowLogsCallback. If this requirement doesn't meet, throw an exception at runtime. Logic: How to input keys: 1. put focus in the corresponding textbox 2. press the key combination you want to use 3. when you think it is ready, release all keys 4. the textbox shows your input How to change keys: 1. put focus in the corresponding textbox 2. press BackSpace to clear content 3. re-input new one How to deactivate: 1. clear content in the textbox 2. press OK button Meaning of label color: - Green: this combination is not occupied by other programs and register successfully - Yellow: this combination is occupied by other programs and you have to change to another one - Transparent without color: initial status Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
174 lines
6.1 KiB
C#
174 lines
6.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Linq;
|
|
using System.Windows.Forms;
|
|
using System.Windows.Input;
|
|
using GlobalHotKey;
|
|
|
|
namespace Shadowsocks.Util
|
|
{
|
|
public static class HotKeys
|
|
{
|
|
private static HotKeyManager _hotKeyManager;
|
|
|
|
public delegate void HotKeyCallBackHandler();
|
|
// map key and corresponding handler function
|
|
private static Dictionary<HotKey, HotKeyCallBackHandler> _keymap = new Dictionary<HotKey, HotKeyCallBackHandler>();
|
|
|
|
public static void Init()
|
|
{
|
|
_hotKeyManager = new HotKeyManager();
|
|
_hotKeyManager.KeyPressed += HotKeyManagerPressed;
|
|
}
|
|
|
|
public static void Destroy() => _hotKeyManager.Dispose();
|
|
|
|
private static void HotKeyManagerPressed(object sender, KeyPressedEventArgs e)
|
|
{
|
|
var hotkey = e.HotKey;
|
|
HotKeyCallBackHandler callback;
|
|
if (_keymap.TryGetValue(hotkey, out callback))
|
|
callback();
|
|
}
|
|
|
|
public static bool IsHotkeyExists( HotKey hotKey )
|
|
{
|
|
if (hotKey == null) throw new ArgumentNullException(nameof(hotKey));
|
|
return _keymap.Any( v => v.Key.Equals( hotKey ) );
|
|
}
|
|
|
|
public static bool IsCallbackExists( HotKeyCallBackHandler cb, out HotKey hotkey)
|
|
{
|
|
if (cb == null) throw new ArgumentNullException(nameof(cb));
|
|
try
|
|
{
|
|
var key = _keymap.First(x => x.Value == cb).Key;
|
|
hotkey = key;
|
|
return true;
|
|
}
|
|
catch (InvalidOperationException)
|
|
{
|
|
// not found
|
|
hotkey = null;
|
|
return false;
|
|
}
|
|
}
|
|
public static string HotKey2Str( HotKey key )
|
|
{
|
|
if (key == null) throw new ArgumentNullException(nameof(key));
|
|
return HotKey2Str( key.Key, key.Modifiers );
|
|
}
|
|
|
|
public static string HotKey2Str( Key key, ModifierKeys modifier )
|
|
{
|
|
if (!Enum.IsDefined(typeof(Key), key))
|
|
throw new InvalidEnumArgumentException(nameof(key), (int) key, typeof(Key));
|
|
try
|
|
{
|
|
ModifierKeysConverter mkc = new ModifierKeysConverter();
|
|
var keyStr = Enum.GetName(typeof(Key), key);
|
|
var modifierStr = mkc.ConvertToInvariantString(modifier);
|
|
|
|
return $"{modifierStr}+{keyStr}";
|
|
}
|
|
catch (NotSupportedException)
|
|
{
|
|
// converter exception
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public static HotKey Str2HotKey( string s ) {
|
|
try
|
|
{
|
|
if (s.IsNullOrEmpty()) return null;
|
|
int offset = s.LastIndexOf("+", StringComparison.OrdinalIgnoreCase);
|
|
if (offset <= 0) return null;
|
|
string modifierStr = s.Substring(0, offset).Trim();
|
|
string keyStr = s.Substring(offset + 1).Trim();
|
|
|
|
KeyConverter kc = new KeyConverter();
|
|
ModifierKeysConverter mkc = new ModifierKeysConverter();
|
|
Key key = (Key) kc.ConvertFrom(keyStr.ToUpper());
|
|
ModifierKeys modifier = (ModifierKeys) mkc.ConvertFrom(modifierStr.ToUpper());
|
|
|
|
return new HotKey(key, modifier);
|
|
}
|
|
catch (NotSupportedException)
|
|
{
|
|
// converter exception
|
|
return null;
|
|
}
|
|
catch (NullReferenceException)
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public static bool Regist( HotKey key, HotKeyCallBackHandler callBack )
|
|
{
|
|
if (key == null)
|
|
throw new ArgumentNullException(nameof(key));
|
|
if (callBack == null)
|
|
throw new ArgumentNullException(nameof(callBack));
|
|
try
|
|
{
|
|
_hotKeyManager.Register(key);
|
|
_keymap[key] = callBack;
|
|
return true;
|
|
}
|
|
catch (ArgumentException)
|
|
{
|
|
// already called this method with the specific hotkey
|
|
// return success silently
|
|
return true;
|
|
}
|
|
catch (Win32Exception)
|
|
{
|
|
// this hotkey already registered by other programs
|
|
// notify user to change key
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public static bool Regist(Key key, ModifierKeys modifiers, HotKeyCallBackHandler callBack)
|
|
{
|
|
if (!Enum.IsDefined(typeof(Key), key))
|
|
throw new InvalidEnumArgumentException(nameof(key), (int) key, typeof(Key));
|
|
try
|
|
{
|
|
var hotkey = _hotKeyManager.Register(key, modifiers);
|
|
_keymap[hotkey] = callBack;
|
|
return true;
|
|
}
|
|
catch (ArgumentException)
|
|
{
|
|
// already called this method with the specific hotkey
|
|
// return success silently
|
|
return true;
|
|
}
|
|
catch (Win32Exception)
|
|
{
|
|
// already registered by other programs
|
|
// notify user to change key
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public static void UnRegist(HotKey key)
|
|
{
|
|
if (key == null)
|
|
throw new ArgumentNullException(nameof(key));
|
|
_hotKeyManager.Unregister(key);
|
|
if(_keymap.ContainsKey(key))
|
|
_keymap.Remove(key);
|
|
}
|
|
|
|
public static IEnumerable<TControl> GetChildControls<TControl>(this Control control) where TControl : Control
|
|
{
|
|
var children = control.Controls.Count > 0 ? control.Controls.OfType<TControl>() : Enumerable.Empty<TControl>();
|
|
return children.SelectMany(c => GetChildControls<TControl>(c)).Concat(children);
|
|
}
|
|
}
|
|
} |