Spring – Rev 1
?pathlinks?
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using System.Windows.Forms;
using Gma.System.MouseKeyHook;
using Spring.Main;
using Spring.Properties;
namespace Spring.MouseKeyboard
{
public class Key : IDisposable
{
#region Public Events & Delegates
public event EventHandler<KeyComboConfiguredEventArgs> KeyComboConfigured;
public event EventHandler<KeySlowConfiguredEventArgs> KeySlowConfigured;
public event EventHandler<KeySpeedConfiguredEventArgs> KeySpeedConfigured;
public event EventHandler<KeyMessageEventArgs> KeyMessage;
#endregion
#region Public Enums, Properties and Fields
public bool IsConfigured => _isConfigured;
public bool IsSpeedConfigured => _isSpeedConfigured;
public bool IsSlowConfigured => _isSlowConfigured;
public ConcurrentQueue<KeyAction> KeyCombo { get; private set; }
public ConcurrentQueue<KeyAction> SpeedCombo { get; private set; }
public ConcurrentQueue<KeyAction> SlowCombo { get; private set; }
public BindingType CurrentBindingType { get; set; }
#endregion
#region Private Delegates, Events, Enums, Properties, Indexers and Fields
private MainForm Form { get; }
private IKeyboardMouseEvents KeyboardMouseEvents { get; }
private Configuration.Configuration Configuration { get; set; }
private bool _disposing;
private volatile bool _isConfigured;
private volatile bool _isSlowConfigured;
private volatile bool _isSpeedConfigured;
private BufferBlock<KeyAction> _keyCombo;
private BufferBlock<KeyAction> _slowCombo;
private BufferBlock<KeyAction> _speedCombo;
#endregion
#region Constructors, Destructors and Finalizers
public Key()
{
_keyCombo = new BufferBlock<KeyAction>();
_speedCombo = new BufferBlock<KeyAction>();
_slowCombo = new BufferBlock<KeyAction>();
}
public Key(MainForm form,
Configuration.Configuration springConfiguration,
IKeyboardMouseEvents keyboardMouseEvents) : this()
{
Form = form;
KeyboardMouseEvents = keyboardMouseEvents;
Configuration = springConfiguration;
Configuration.PropertyChanged += Configuration_PropertyChanged;
Form.BindKeyButton.Click += BindKeyButton_Click;
// Bind speed and slow buttons.
Form.BindSlowButton.Click += BindSlowButton_Click;
Form.BindSpeedButton.Click += BindSpeedButton_Click;
}
public void Dispose()
{
if (_disposing)
{
return;
}
_disposing = true;
Configuration.PropertyChanged -= Configuration_PropertyChanged;
Form.BindKeyButton.Click -= BindKeyButton_Click;
// Bind speed and slow buttons.
Form.BindSlowButton.Click -= BindSlowButton_Click;
Form.BindSpeedButton.Click -= BindSpeedButton_Click;
}
#endregion
#region Event Handlers
private void Configuration_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Configuration = (Configuration.Configuration) sender;
}
private void BindSpeedButton_Click(object sender, EventArgs e)
{
if (_isSpeedConfigured)
{
CurrentBindingType = BindingType.Speed;
_isSpeedConfigured = false;
Configuration.BoundSpeedButton.Clear();
Interlocked.Exchange(ref _speedCombo, new BufferBlock<KeyAction>());
KeyMessage?.Invoke(this, new KeyMessageEventArgs(Strings.Keys_unbound));
return;
}
CurrentBindingType = BindingType.Speed;
_isSpeedConfigured = false;
Configuration.BoundSpeedButton.Clear();
Interlocked.Exchange(ref _speedCombo, new BufferBlock<KeyAction>());
Start();
}
private void BindSlowButton_Click(object sender, EventArgs e)
{
if (_isSlowConfigured)
{
CurrentBindingType = BindingType.Slow;
_isSlowConfigured = false;
Configuration.BoundSlowButton.Clear();
Interlocked.Exchange(ref _slowCombo, new BufferBlock<KeyAction>());
KeyMessage?.Invoke(this, new KeyMessageEventArgs(Strings.Keys_unbound));
return;
}
CurrentBindingType = BindingType.Slow;
_isSlowConfigured = false;
Configuration.BoundSlowButton.Clear();
Interlocked.Exchange(ref _slowCombo, new BufferBlock<KeyAction>());
Start();
}
private void BindKeyButton_Click(object sender, EventArgs e)
{
if (_isConfigured)
{
CurrentBindingType = BindingType.Combos;
_isConfigured = false;
Configuration.BoundButton.Clear();
_keyCombo.Complete();
Interlocked.Exchange(ref _keyCombo, new BufferBlock<KeyAction>());
KeyMessage?.Invoke(this, new KeyMessageEventArgs(Strings.Keys_unbound));
return;
}
CurrentBindingType = BindingType.Combos;
_isConfigured = false;
Configuration.BoundButton.Clear();
_keyCombo.Complete();
Interlocked.Exchange(ref _keyCombo, new BufferBlock<KeyAction>());
Start();
}
private async void SpringKeyApplicationHook_MouseWheel(object sender, MouseEventArgs e)
{
var direction = e.Delta > 0 ? MouseScrollDirection.Up : MouseScrollDirection.Down;
switch (CurrentBindingType)
{
case BindingType.Combos:
await _keyCombo.SendAsync(new KeyActionMouseScrollDirection(direction));
break;
case BindingType.Speed:
await _speedCombo.SendAsync(new KeyActionMouseScrollDirection(direction));
break;
case BindingType.Slow:
await _slowCombo.SendAsync(new KeyActionMouseScrollDirection(direction));
break;
}
KeyboardMouseEvents.MouseWheel -= SpringKeyApplicationHook_MouseWheel;
Stop();
}
private async void SpringKeyApplicationHook_KeyDown(object sender, KeyEventArgs e)
{
e.SuppressKeyPress = true;
switch (CurrentBindingType)
{
case BindingType.Combos:
await _keyCombo.SendAsync(new KeyActionKeys(e.KeyCode));
break;
case BindingType.Speed:
await _speedCombo.SendAsync(new KeyActionKeys(e.KeyCode));
break;
case BindingType.Slow:
await _slowCombo.SendAsync(new KeyActionKeys(e.KeyCode));
break;
}
}
private void SpringKeyApplicationHook_KeyUp(object sender, KeyEventArgs e)
{
e.SuppressKeyPress = true;
KeyboardMouseEvents.KeyDown -= SpringKeyApplicationHook_KeyDown;
KeyboardMouseEvents.KeyUp -= SpringKeyApplicationHook_KeyUp;
Stop();
}
#endregion
#region Public Methods
public async Task Load()
{
foreach (var binding in Enum.GetValues(typeof(BindingType))
.OfType<BindingType>())
{
switch (binding)
{
case BindingType.Combos:
LoadBindingType(Configuration.BoundButton, _keyCombo);
break;
case BindingType.Slow:
LoadBindingType(Configuration.BoundSlowButton, _slowCombo);
break;
case BindingType.Speed:
LoadBindingType(Configuration.BoundSpeedButton, _speedCombo);
break;
}
BindKeyType(binding);
}
// Set up the key combination.
foreach (var key in Configuration.BoundButton)
{
if (Enum.TryParse<MouseScrollDirection>(key, out var mouseScrollDirection))
{
await _keyCombo.SendAsync(new KeyActionMouseScrollDirection(mouseScrollDirection));
continue;
}
if (Enum.TryParse<Keys>(key, out var springKey))
{
await _keyCombo.SendAsync(new KeyActionKeys(springKey));
continue;
}
if (Enum.TryParse<MouseButtons>(key, out var springButton))
{
await _keyCombo.SendAsync(new KeyActionMouseButtons(springButton));
}
}
_keyCombo.Complete();
if (ConfigureBinding(ref _keyCombo, out var keyComboButton, out var keyCombo))
{
Configuration.BoundButton = keyComboButton;
KeyCombo = keyCombo;
_isConfigured = true;
KeyComboConfigured?.Invoke(this, new KeyComboConfiguredEventArgs());
}
}
#endregion
#region Private Methods
private static void LoadBindingType(IEnumerable<string> keys, ITargetBlock<KeyAction> store)
{
foreach (var key in keys)
{
if (Enum.TryParse<MouseScrollDirection>(key, out var mouseScrollDirection))
{
store.Post(new KeyActionMouseScrollDirection(mouseScrollDirection));
continue;
}
if (Enum.TryParse<Keys>(key, out var springKey))
{
store.Post(new KeyActionKeys(springKey));
continue;
}
if (Enum.TryParse<MouseButtons>(key, out var springButton))
{
store.Post(new KeyActionMouseButtons(springButton));
}
}
store.Complete();
}
private void Start()
{
KeyMessage?.Invoke(this, new KeyMessageEventArgs(Strings.Binding_keys));
KeyboardMouseEvents.KeyUp += SpringKeyApplicationHook_KeyUp;
KeyboardMouseEvents.KeyDown += SpringKeyApplicationHook_KeyDown;
KeyboardMouseEvents.MouseWheel += SpringKeyApplicationHook_MouseWheel;
}
private void Stop()
{
KeyboardMouseEvents.KeyUp -= SpringKeyApplicationHook_KeyUp;
KeyboardMouseEvents.KeyDown -= SpringKeyApplicationHook_KeyDown;
KeyboardMouseEvents.MouseWheel -= SpringKeyApplicationHook_MouseWheel;
BindKeyType(CurrentBindingType);
CurrentBindingType = BindingType.None;
KeyMessage?.Invoke(this, new KeyMessageEventArgs(Strings.Keys_bound));
}
private void BindKeyType(BindingType currentBindingType)
{
switch (currentBindingType)
{
case BindingType.Slow:
if (!ConfigureBinding(ref _slowCombo, out var slowComboButton, out var slowCombo))
{
return;
}
Configuration.BoundSlowButton = slowComboButton;
SlowCombo = slowCombo;
_isSlowConfigured = true;
KeySlowConfigured?.Invoke(this, new KeySlowConfiguredEventArgs());
break;
case BindingType.Speed:
if (!ConfigureBinding(ref _speedCombo, out var speedComboButton, out var speedCombo))
{
return;
}
Configuration.BoundSpeedButton = speedComboButton;
SpeedCombo = speedCombo;
_isSpeedConfigured = true;
KeySpeedConfigured?.Invoke(this, new KeySpeedConfiguredEventArgs());
break;
case BindingType.Combos:
if (!ConfigureBinding(ref _keyCombo, out var keyComboButton, out var keyCombo))
{
return;
}
Configuration.BoundButton = keyComboButton;
KeyCombo = keyCombo;
_isConfigured = true;
KeyComboConfigured?.Invoke(this, new KeyComboConfiguredEventArgs());
break;
}
}
private static bool ConfigureBinding(ref BufferBlock<KeyAction> bufferBlock,
out List<string> keys,
out ConcurrentQueue<KeyAction> queue)
{
if (!bufferBlock.TryReceiveAll(out var keyComboKeys))
{
queue = null;
keys = null;
return false;
}
var uniqueKeyComboKeys = keyComboKeys.Distinct();
var comboKeys = uniqueKeyComboKeys as KeyAction[] ?? uniqueKeyComboKeys.ToArray();
keys = new List<string>();
foreach (var key in comboKeys)
{
switch (key)
{
case KeyActionKeys keyActionKeys:
keys.Add(keyActionKeys.Keys.ToString());
break;
case KeyActionMouseButtons keyActionMouseButtons:
keys.Add(keyActionMouseButtons.MouseButtons.ToString());
break;
case KeyActionMouseScrollDirection keyActionMouseScrollDirection:
keys.Add(keyActionMouseScrollDirection.Direction.ToString());
break;
}
}
queue = new ConcurrentQueue<KeyAction>(comboKeys);
return true;
}
#endregion
}
}