From 209e3f0338093063fd372e02e8d125831a4fc993 Mon Sep 17 00:00:00 2001 From: Terry MacDonald Date: Tue, 27 Apr 2021 22:19:44 +1200 Subject: [PATCH] [WIP] Partway through swap to NHotkey Also trying to port Willy-Kimura's HKL HotkeySelector code over as well cause it is ace! --- DisplayMagician/DisplayMagician.csproj | 12 +- DisplayMagician/HotkeySelector.cs | 537 ++++++++++++++++++ DisplayMagician/Program.cs | 5 +- .../DisplayMagicianShared.csproj | 9 +- 4 files changed, 553 insertions(+), 10 deletions(-) create mode 100644 DisplayMagician/HotkeySelector.cs diff --git a/DisplayMagician/DisplayMagician.csproj b/DisplayMagician/DisplayMagician.csproj index 68ba785..821dcb2 100644 --- a/DisplayMagician/DisplayMagician.csproj +++ b/DisplayMagician/DisplayMagician.csproj @@ -108,6 +108,9 @@ + + Component + @@ -262,9 +265,6 @@ 1.2.0.1 - - 1.9.0 - 1.11.33 @@ -292,6 +292,12 @@ 13.0.1 + + 2.1.0 + + + 2.1.0 + 4.7.9 diff --git a/DisplayMagician/HotkeySelector.cs b/DisplayMagician/HotkeySelector.cs new file mode 100644 index 0000000..8903483 --- /dev/null +++ b/DisplayMagician/HotkeySelector.cs @@ -0,0 +1,537 @@ +#region Copyright + +/* + * Developer : Willy Kimura (WK). + * Library : HotkeySelector. + * License : MIT. + * + * Copied and repurposed to help with DisplayMagician by Terry MacDonald + */ + +#endregion + +using System; +using System.Collections.Generic; +using System.Collections; +using System.Diagnostics; +using System.Windows.Forms; +using System.ComponentModel; + +namespace DisplayMagician +{ + /// + /// Provides support for enabling standard Windows controls + /// and User controls to select hotkeys at runtime. + /// Combined with the class, + /// you can easily enable the selection and registering of + /// hotkeys for a seamless end-user experience. + /// + [DebuggerStepThrough] + [Description("Provides support for enabling standard Windows controls " + + "and User controls to select hotkeys at runtime.")] + public partial class HotkeySelector : Component + { + #region Constructor + + /// + /// Initializes a new instance of the class. + /// + public HotkeySelector() { } + + /// + /// Initializes a new instance of the class. + /// + public HotkeySelector(IContainer container) + { + container.Add(this); + + //InitializeComponent(); + } + + #endregion + + #region Fields + + // These variables store the selected hotkey and modifier key(s). + private Keys _hotkey = Keys.None; + //private Keys _modifiers = Keys.None; + + // ArrayLists used to enforce the use of proper modifiers. + // Shift+A isn't a valid hotkey, for instance. + private ArrayList _needNonShiftModifier = null; + private ArrayList _needNonAltGrModifier = null; + + // Stores the list of enabled hotkey selection controls. + private List _controls = new List(); + + #endregion + + #region Properties + + #region Public + + /// + /// Gets or sets the text to be displayed in a + /// control when no hotkey has been set. + /// (Preferred default text is "None") + /// + public string EmptyHotkeyText { get; set; } = "None"; + + /// + /// Gets or sets the text to be displayed in a control + /// when an invalid or unsupported hotkey is pressed. + /// (Preferred default text is "(Unsupported)") + /// + public string InvalidHotkeyText { get; set; } = "Unsupported"; + + #endregion + + #endregion + + #region Methods + + #region Public + + /// + /// Enables a control for hotkey selection and previewing. + /// This will make use of the control's Text property to + /// preview the current hotkey selected. + /// + /// The control to enable. + public bool Enable(Control control) + { + try + { + control.Text = EmptyHotkeyText; + + control.KeyPress += new KeyPressEventHandler(OnKeyPress); + control.KeyDown += new KeyEventHandler(OnKeyDown); + control.KeyUp += new KeyEventHandler(OnKeyUp); + + ResetModifiers(); + + try + { + _controls.Add(control); + } + catch (Exception) { } + + return true; + } + catch (Exception) + { + return false; + } + } + + /// + /// Enables a control for hotkey selection and previewing. + /// This will make use of the control's Text property to + /// preview the current hotkey selected. + /// + /// The control to enable. + /// Assign the default Keys to be previewed in the control. + public bool Enable(Control control, Keys hotkey) + { + try + { + Enable(control); + + _hotkey = hotkey; + + Refresh(control); + + return true; + } + catch (Exception) + { + return false; + } + } + + /// + /// Disables a control for hotkey selection and previewing. + /// + /// The control to disable. + /// Clear the control's previewed keys? + public bool Disable(Control control, bool clearKeys = true) + { + try + { + control.KeyPress -= OnKeyPress; + control.KeyDown -= OnKeyDown; + control.KeyUp -= OnKeyUp; + + if (clearKeys) + control.Text = string.Empty; + + try + { + if (_controls.Contains(control)) + _controls.Remove(control); + } + catch (Exception) { } + + return true; + } + catch (Exception) + { + return false; + } + } + + /// + /// Gets a value indicating whether a specific + /// control is enabled for hotkey selection. + /// + /// The control to determine. + public bool IsEnabled(Control control) + { + if (_controls.Contains(control)) + return true; + else + return false; + } + + /// + /// Sets a hotkey selection to be previewed in a control. + /// Thsi does not automatically enable the control for + /// hotkey selection. For this, please use the method. + /// + /// The control to set. + /// Provide a standard key selection. + /// Provide a modifier key selection. + public bool Set(Control control, Keys hotkey) + { + try + { + _hotkey = hotkey; + + Refresh(control); + + return true; + } + catch (Exception) + { + return false; + } + } + + /// + /// Clears the currently previewed hotkey + /// selection displayed in a control. + /// + public void Clear(Control control) + { + this._hotkey = Keys.None; + + Refresh(control); + } + + /// + /// (Variant of the method) + /// Clears the currently previewed hotkey + /// selection displayed in a control. + /// + public void Reset(Control control) + { + this._hotkey = Keys.None; + + Refresh(control); + } + + /// + /// [Helper] Converts keys or key combinations to their string types. + /// + /// The hotkey to convert. + public string Convert(Keys hotkey) + { + try + { + _hotkey = hotkey; + + string parsedHotkey = string.Empty; + + // No modifier or shift only, and a hotkey that needs another modifier. + if ((_hotkey == Keys.Shift || _hotkey == Keys.None)) + { + if (_needNonShiftModifier != null && _needNonShiftModifier.Contains((int)this._hotkey)) + { + if (this._hotkey == Keys.None) + { + // Set Ctrl+Alt as the modifier unless Ctrl+Alt+ won't work. + if (_needNonAltGrModifier.Contains((int)this._hotkey) == false) + { + this._hotkey = Keys.Alt | Keys.Control; + } + else + { + // ...In that case, use Shift+Alt instead. + this._hotkey = Keys.Alt | Keys.Shift; + } + } + else + { + // User pressed Shift and an invalid key (e.g. a letter or a number), + // that needs another set of modifier keys. + this._hotkey = Keys.None; + } + } + } + + // Without this code, pressing only Ctrl + // will show up as "Control + ControlKey", etc. + if (this._hotkey == Keys.Menu || /* Alt */ + this._hotkey == Keys.ShiftKey || + this._hotkey == Keys.ControlKey) + { + this._hotkey = Keys.None; + } + + if (this._hotkey == Keys.None) + { + // LWin/RWin don't work as hotkeys... + // (neither do they work as modifier keys in .NET 2.0). + if (_hotkey == Keys.None || _hotkey == Keys.LWin || _hotkey == Keys.RWin) + { + parsedHotkey = string.Empty; + } + else + { + parsedHotkey = this._hotkey.ToString(); + } + } + else + { + parsedHotkey = this._hotkey.ToString(); + } + + return parsedHotkey; + } + catch (Exception) + { + return string.Empty; + } + } + + #endregion + + #region Private + + /// + /// Resets the hotkey modifiers to their defaults. + /// + private void ResetModifiers() + { + // Fill the ArrayLists that contain + // all invalid hotkey combinations. + _needNonShiftModifier = new ArrayList(); + _needNonAltGrModifier = new ArrayList(); + + PopulateModifierLists(); + } + + /// + /// Populates the ArrayLists specifying disallowed Hotkeys + /// such as Shift+A, Ctrl+Alt+4 (produces 'dollar' sign). + /// + private void PopulateModifierLists() + { + // Shift + 0 - 9, A - Z. + for (Keys k = Keys.D0; k <= Keys.Z; k++) + _needNonShiftModifier.Add((int)k); + + // Shift + Numpad keys. + for (Keys k = Keys.NumPad0; k <= Keys.NumPad9; k++) + _needNonShiftModifier.Add((int)k); + + // Shift + Misc (,;<./ etc). + for (Keys k = Keys.Oem1; k <= Keys.OemBackslash; k++) + _needNonShiftModifier.Add((int)k); + + // Shift + Space, PgUp, PgDn, End, Home. + for (Keys k = Keys.Space; k <= Keys.Home; k++) + _needNonShiftModifier.Add((int)k); + + // Misc keys that we can't loop through. + _needNonShiftModifier.Add((int)Keys.Insert); + _needNonShiftModifier.Add((int)Keys.Help); + _needNonShiftModifier.Add((int)Keys.Multiply); + _needNonShiftModifier.Add((int)Keys.Add); + _needNonShiftModifier.Add((int)Keys.Subtract); + _needNonShiftModifier.Add((int)Keys.Divide); + _needNonShiftModifier.Add((int)Keys.Decimal); + _needNonShiftModifier.Add((int)Keys.Return); + _needNonShiftModifier.Add((int)Keys.Escape); + _needNonShiftModifier.Add((int)Keys.NumLock); + _needNonShiftModifier.Add((int)Keys.Scroll); + _needNonShiftModifier.Add((int)Keys.Pause); + + // Ctrl+Alt + 0 - 9. + for (Keys k = Keys.D0; k <= Keys.D9; k++) + _needNonAltGrModifier.Add((int)k); + } + + /// + /// Refreshes the previewed hotkey combination displayed in a control. + /// + /// + /// The control providing hotkey selection. + /// + private void Refresh(Control control) + { + Refresh(control, false); + } + + /// + /// Refreshes the previewed hotkey combination displayed in a control. + /// + /// + /// The control providing hotkey selection. + /// + /// + /// Specifies whether this function is + /// called internally or by the user. + /// + private void Refresh(Control control, bool internalCall) + { + try + { + string parsedHotkey = string.Empty; + + // No hotkey set. + if (this._hotkey == Keys.None) + { + control.Text = EmptyHotkeyText; + + return; + } + + // LWin/RWin don't work as hotkeys... + // (neither do they work as modifier keys in .NET 2.0). + if (this._hotkey == Keys.LWin || this._hotkey == Keys.RWin) + { + control.Text = InvalidHotkeyText; + + return; + } + + // Only validate input if it comes from the user. + if (internalCall == false) + { + // No modifier or shift only, and a hotkey that needs another modifier. + if ((this._hotkey == Keys.Shift || this._hotkey == Keys.None) && + this._needNonShiftModifier.Contains((int)this._hotkey)) + { + if (this._hotkey == Keys.None) + { + // Set Ctrl+Alt as the modifier unless Ctrl+Alt+ won't work. + if (_needNonAltGrModifier.Contains((int)this._hotkey) == false) + { + this._hotkey |= Keys.Alt | Keys.Control; + } + else + { + // ...In that case, use Shift+Alt instead. + this._hotkey |= Keys.Alt | Keys.Shift; + } + } + else + { + // User pressed Shift and an invalid key (e.g. a letter or a number), + // that needs another set of modifier keys. + this._hotkey = Keys.None; + + control.Text = this._hotkey.ToString() + $" + {InvalidHotkeyText}"; + + return; + } + } + } + + // Without this code, pressing only Ctrl + // will show up as "Control + ControlKey", etc. + if (this._hotkey == Keys.Menu || /* Alt */ + this._hotkey == Keys.ShiftKey || + this._hotkey == Keys.ControlKey) + { + this._hotkey = Keys.None; + } + + // A final compilation of the processed keys in string format. + parsedHotkey = this._hotkey.ToString(); + + control.Text = parsedHotkey; + + return; + } + catch (Exception) { } + } + + #endregion + + #endregion + + #region Events + + #region Private + + /// + /// Fires when a key is pressed down. Here, we'll want to update the Text + /// property to notify the user what key combination is currently pressed. + /// + private void OnKeyDown(object sender, KeyEventArgs e) + { + if (e.KeyData == Keys.Delete || e.KeyData == (Keys.Control | Keys.Delete)) + Reset((Control)sender); + + if (e.KeyData == (Keys.Shift | Keys.Insert)) + { + this._hotkey = Keys.Shift; + + e.Handled = true; + } + + // Clear the current hotkey. + if (e.KeyCode == Keys.Back || e.KeyCode == Keys.Delete) + { + Reset((Control)sender); + + return; + } + else + { + this._hotkey = e.KeyCode; + + Refresh((Control)sender); + } + } + + /// + /// Fires when all keys are released. If the current hotkey isn't valid, reset it. + /// Otherwise, do nothing and keep the Text and hotkey as it was. + /// + private void OnKeyUp(object sender, KeyEventArgs e) + { + if (this._hotkey == Keys.None && Control.ModifierKeys == Keys.None) + { + Reset((Control)sender); + + return; + } + } + + /// + /// Prevents anything entered in Input controls from being displayed. + /// Without this, a "A" key press would appear as "aControl, Alt + A". + /// + private void OnKeyPress(object sender, KeyPressEventArgs e) + { + e.Handled = true; + } + + #endregion + + #endregion + } +} diff --git a/DisplayMagician/Program.cs b/DisplayMagician/Program.cs index 1e87e73..8f0c3e5 100644 --- a/DisplayMagician/Program.cs +++ b/DisplayMagician/Program.cs @@ -17,7 +17,7 @@ using DesktopNotifications; using System.Runtime.Serialization; using NLog.Config; using System.Collections.Generic; -using WK.Libraries.HotkeyListenerNS; +using NHotkey.WindowsForms; namespace DisplayMagician { @@ -45,7 +45,6 @@ namespace DisplayMagician { public static bool WaitingForGameToExit = false; public static ProgramSettings AppProgramSettings; public static MainForm AppMainForm; - public static HotkeyListener HotkeyListener; private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); private static SharedLogger sharedLogger; @@ -206,8 +205,6 @@ namespace DisplayMagician { Application.SetCompatibleTextRenderingDefault(false); ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; - // Create a program-wide HotKeyListener - HotkeyListener = new HotkeyListener(); logger.Debug($"Setting up commandline processing configuration"); var app = new CommandLineApplication diff --git a/DisplayMagicianShared/DisplayMagicianShared.csproj b/DisplayMagicianShared/DisplayMagicianShared.csproj index b804433..110cdbd 100644 --- a/DisplayMagicianShared/DisplayMagicianShared.csproj +++ b/DisplayMagicianShared/DisplayMagicianShared.csproj @@ -104,9 +104,6 @@ 1.2.0.1 - - 1.9.0 - 1.0.2.1-beta @@ -121,6 +118,12 @@ 12.0.3 + + 2.1.0 + + + 2.1.0 + 4.7.8