Provide support of HotKeys to manage thumbnails and corresponding EVE clients - implemented w/o GUI required to set hotkeys

This commit is contained in:
Anton Kasyanov
2016-06-15 01:59:20 +03:00
parent fa4086aba7
commit d136da3da1
10 changed files with 269 additions and 366 deletions

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using Newtonsoft.Json;
namespace EveOPreview.Configuration
@@ -35,6 +36,7 @@ namespace EveOPreview.Configuration
this.PerClientLayout = new Dictionary<string, Dictionary<string, Point>>();
this.FlatLayout = new Dictionary<string, Point>();
this.ClientLayout = new Dictionary<string, ClientLayout>();
this.ClientHotkey = new Dictionary<string, string>();
}
public bool MinimizeToTray { get; set; }
@@ -66,6 +68,8 @@ namespace EveOPreview.Configuration
private Dictionary<string, Point> FlatLayout { get; set; }
[JsonProperty]
private Dictionary<string, ClientLayout> ClientLayout { get; set; }
[JsonProperty]
private Dictionary<string, string> ClientHotkey { get; set; }
public Point GetThumbnailLocation(string currentClient, string activeClient, Point defaultLocation)
{
@@ -129,5 +133,23 @@ namespace EveOPreview.Configuration
{
this.ClientLayout[currentClient] = layout;
}
public Keys GetClientHotkey(string currentClient)
{
string hotkey;
if (this.ClientHotkey.TryGetValue(currentClient, out hotkey))
{
// Protect from incorrect values
object rawValue = (new KeysConverter()).ConvertFromInvariantString(hotkey);
return rawValue != null ? (Keys)rawValue : Keys.None;
}
return Keys.None;
}
public void SetClientHotkey(string currentClient, Keys hotkey)
{
this.ClientHotkey[currentClient] = (new KeysConverter()).ConvertToInvariantString(hotkey);
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Drawing;
using System.Windows.Forms;
namespace EveOPreview.Configuration
{
@@ -32,5 +33,8 @@ namespace EveOPreview.Configuration
ClientLayout GetClientLayout(string currentClient);
void SetClientLayout(string currentClient, ClientLayout layout);
Keys GetClientHotkey(string currentClient);
void SetClientHotkey(string currentClient, Keys hotkey);
}
}

View File

@@ -119,8 +119,8 @@
<Compile Include="UI\Interface\IThumbnailDescriptionViewFactory.cs" />
<Compile Include="UI\Interface\ViewZoomAnchor.cs" />
<Compile Include="Configuration\ApplicationConfiguration.cs" />
<None Include="Hotkeys\Hotkey.cs" />
<None Include="Hotkeys\HotkeyNativeMethods.cs" />
<Compile Include="Hotkeys\HotkeyHandler.cs" />
<Compile Include="Hotkeys\HotkeyHandlerNativeMethods.cs" />
<Compile Include="UI\Implementation\MainForm.cs">
<SubType>Form</SubType>
</Compile>

View File

@@ -1,332 +0,0 @@
using System;
using System.Windows.Forms;
using System.ComponentModel;
using System.Xml.Serialization;
using System.Runtime.InteropServices;
namespace EveOPreview
{
public class Hotkey : IMessageFilter
{
private static int _currentId;
private const int MaxId = 0xBFFF;
[XmlElement("keyCode")]
private Keys _keyCode;
[XmlElement("shift")]
private bool _shift;
[XmlElement("control")]
private bool _control;
[XmlElement("alt")]
private bool _alt;
[XmlElement("windows")]
private bool _windows;
[XmlIgnore]
private int _id;
[XmlIgnore]
private bool _isRegistered;
[XmlIgnore]
private Control _windowControl;
public event HandledEventHandler Pressed;
public Hotkey()
: this(Keys.None, false, false, false, false)
{
}
public Hotkey(Keys keyCode, bool shift, bool control, bool alt, bool windows)
{
// Assign properties
this.KeyCode = keyCode;
this.Shift = shift;
this.Control = control;
this.Alt = alt;
this.Windows = windows;
// Register us as a message filter
Application.AddMessageFilter(this);
}
~Hotkey()
{
// Unregister the hotkey if necessary
if (this.IsRegistered)
{
this.Unregister();
}
}
public Hotkey Clone()
{
// Clone the whole object
return new Hotkey(this._keyCode, this._shift, this._control, this._alt, this._windows);
}
public bool GetCanRegister(Control windowControl)
{
// Handle any exceptions: they mean "no, you can't register" :)
try
{
// Attempt to register
if (this.Register(windowControl))
{
// Unregister and say we managed it
this.Unregister();
return true;
}
}
catch (Win32Exception)
{
}
catch (NotSupportedException)
{
}
return false;
}
public bool Register(Control windowControl)
{
// Check that we have not registered
if (this._isRegistered)
{
throw new NotSupportedException("You cannot register a hotkey that is already registered");
}
// We can't register an empty hotkey
if (this.IsEmpty)
{
throw new NotSupportedException("You cannot register an empty hotkey");
}
// Get an ID for the hotkey and increase current ID
this._id = Hotkey._currentId;
Hotkey._currentId = Hotkey._currentId + 1 % Hotkey.MaxId;
// Translate modifier keys into unmanaged version
uint modifiers = (this.Alt ? HotkeyNativeMethods.MOD_ALT : 0) | (this.Control ? HotkeyNativeMethods.MOD_CONTROL : 0) |
(this.Shift ? HotkeyNativeMethods.MOD_SHIFT : 0) | (this.Windows ? HotkeyNativeMethods.MOD_WIN : 0);
// Register the hotkey
if (HotkeyNativeMethods.RegisterHotKey(windowControl.Handle, this._id, modifiers, _keyCode) == 0)
{
// Is the error that the hotkey is registered?
if (Marshal.GetLastWin32Error() != HotkeyNativeMethods.ERROR_HOTKEY_ALREADY_REGISTERED)
{
throw new Win32Exception();
}
return false;
}
// Save the control reference and register state
this._isRegistered = true;
this._windowControl = windowControl;
// We successfully registered
return true;
}
public void Unregister()
{
// Check that we have registered
if (!this._isRegistered)
{
throw new NotSupportedException("You cannot unregister a hotkey that is not registered");
}
// It's possible that the control itself has died: in that case, no need to unregister!
if (!this._windowControl.IsDisposed)
{
// Clean up after ourselves
if (HotkeyNativeMethods.UnregisterHotKey(this._windowControl.Handle, this._id) == 0)
{
throw new Win32Exception();
}
}
// Clear the control reference and register state
this._isRegistered = false;
this._windowControl = null;
}
private void Reregister()
{
// Only do something if the key is already registered
if (!this._isRegistered)
{
return;
}
// Save control reference
Control windowControl = this._windowControl;
// Unregister and then reregister again
this.Unregister();
this.Register(windowControl);
}
public bool PreFilterMessage(ref Message message)
{
// Only process WM_HOTKEY messages
if (message.Msg != HotkeyNativeMethods.WM_HOTKEY)
{
return false;
}
// Check that the ID is our key and we are registerd
return this._isRegistered && (message.WParam.ToInt32() == this._id) && this.OnPressed();
}
private bool OnPressed()
{
// Fire the event if we can
HandledEventArgs handledEventArgs = new HandledEventArgs(false);
this.Pressed?.Invoke(this, handledEventArgs);
// Return whether we handled the event or not
return handledEventArgs.Handled;
}
public override string ToString()
{
// We can be empty
if (this.IsEmpty)
{
return "(none)";
}
// Build key name
string keyName = Enum.GetName(typeof(Keys), this._keyCode) ?? " ";
switch (this._keyCode)
{
case Keys.D0:
case Keys.D1:
case Keys.D2:
case Keys.D3:
case Keys.D4:
case Keys.D5:
case Keys.D6:
case Keys.D7:
case Keys.D8:
case Keys.D9:
// Strip the first character
keyName = keyName.Substring(1);
break;
}
// Build modifiers
string modifiers = "";
if (this._shift)
{
modifiers += "Shift+";
}
if (this._control)
{
modifiers += "Control+";
}
if (this._alt)
{
modifiers += "Alt+";
}
if (this._windows)
{
modifiers += "Windows+";
}
// Return result
return modifiers + keyName;
}
public bool IsEmpty
{
get
{
return this._keyCode == Keys.None;
}
}
public bool IsRegistered
{
get
{
return this._isRegistered;
}
}
public Keys KeyCode
{
get
{
return this._keyCode;
}
set
{
// Save and reregister
this._keyCode = value;
this.Reregister();
}
}
public bool Shift
{
get
{
return this._shift;
}
set
{
// Save and reregister
this._shift = value;
this.Reregister();
}
}
public bool Control
{
get
{
return this._control;
}
set
{
// Save and reregister
this._control = value;
this.Reregister();
}
}
public bool Alt
{
get
{
return this._alt;
}
set
{
// Save and reregister
this._alt = value;
this.Reregister();
}
}
public bool Windows
{
get
{
return this._windows;
}
set
{
// Save and reregister
this._windows = value;
this.Reregister();
}
}
}
}

View File

@@ -0,0 +1,159 @@
using System;
using System.Windows.Forms;
using System.ComponentModel;
namespace EveOPreview.UI
{
class HotkeyHandler : IMessageFilter, IDisposable
{
private static int _currentId;
private const int MaxId = 0xBFFF;
#region Private fields
private readonly int _hotkeyId;
private readonly IntPtr _hotkeyTarget;
#endregion
public HotkeyHandler(IntPtr target, Keys hotkey)
{
this._hotkeyId = HotkeyHandler._currentId;
HotkeyHandler._currentId = (HotkeyHandler._currentId + 1) & HotkeyHandler.MaxId;
this._hotkeyTarget = target;
// Assign properties
this.IsRegistered = false;
this.KeyCode = hotkey;
}
public void Dispose()
{
if (this.IsRegistered)
{
this.Unregister();
}
GC.SuppressFinalize(this);
}
~HotkeyHandler()
{
// Unregister the hotkey if necessary
if (this.IsRegistered)
{
try
{
this.Unregister();
}
catch (Exception)
{
// Please no exceptions in the finalizer thread
}
}
}
public bool IsRegistered { get; private set; }
public Keys KeyCode { get; private set; }
public event HandledEventHandler Pressed;
public bool CanRegister()
{
// Any exception means "no, you can't register"
try
{
// Attempt to register
if (this.Register())
{
// Unregister and say we managed it
this.Unregister();
return true;
}
}
catch (Win32Exception)
{
}
catch (NotSupportedException)
{
}
return false;
}
public bool Register()
{
// Check that we have not registered
if (this.IsRegistered)
{
throw new NotSupportedException("This hotkey is already registered");
}
if (this.KeyCode == Keys.None)
{
throw new NotSupportedException("Cannot register an empty hotkey");
}
// Remove all modifiers from the 'main' hotkey
uint key = (uint)this.KeyCode & (~(uint)Keys.Alt) & (~(uint)Keys.Control) & (~(uint)Keys.Shift);
// Get unmanaged version of the modifiers code
uint modifiers = (this.KeyCode.HasFlag(Keys.Alt) ? HotkeyHandlerNativeMethods.MOD_ALT : 0)
| (this.KeyCode.HasFlag(Keys.Control) ? HotkeyHandlerNativeMethods.MOD_CONTROL : 0)
| (this.KeyCode.HasFlag(Keys.Shift) ? HotkeyHandlerNativeMethods.MOD_SHIFT : 0);
// Register the hotkey
if (!HotkeyHandlerNativeMethods.RegisterHotKey(this._hotkeyTarget, this._hotkeyId, modifiers, key))
{
return false;
}
Application.AddMessageFilter(this);
this.IsRegistered = true;
// We successfully registered
return true;
}
public void Unregister()
{
// Check that we have registered
if (!this.IsRegistered)
{
throw new NotSupportedException("This hotkey was not registered");
}
Application.RemoveMessageFilter(this);
// Clean up after ourselves
if (!HotkeyHandlerNativeMethods.UnregisterHotKey(this._hotkeyTarget, this._hotkeyId))
{
throw new Win32Exception();
}
this.IsRegistered = false;
}
#region IMessageFilter
public bool PreFilterMessage(ref Message message)
{
return this.IsRegistered
&& (message.Msg == HotkeyHandlerNativeMethods.WM_HOTKEY)
&& (message.WParam.ToInt32() == this._hotkeyId)
&& this.OnPressed();
}
#endregion
private bool OnPressed()
{
// Fire the event if we can
HandledEventArgs handledEventArgs = new HandledEventArgs(false);
this.Pressed?.Invoke(this, handledEventArgs);
// Return whether we handled the event or not
return handledEventArgs.Handled;
}
}
}

View File

@@ -0,0 +1,23 @@
using System;
using System.Runtime.InteropServices;
namespace EveOPreview.UI
{
static class HotkeyHandlerNativeMethods
{
[DllImport("user32.dll")]
public static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
[DllImport("user32.dll")]
public static extern bool UnregisterHotKey(IntPtr hWnd, int id);
public const uint WM_HOTKEY = 0x0312;
public const uint MOD_ALT = 0x1;
public const uint MOD_CONTROL = 0x2;
public const uint MOD_SHIFT = 0x4;
public const uint MOD_WIN = 0x8;
public const uint ERROR_HOTKEY_ALREADY_REGISTERED = 1409;
}
}

View File

@@ -1,24 +0,0 @@
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace EveOPreview
{
static class HotkeyNativeMethods
{
[DllImport("user32.dll", SetLastError = true)]
public static extern int RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, Keys vk);
[DllImport("user32.dll", SetLastError = true)]
public static extern int UnregisterHotKey(IntPtr hWnd, int id);
public const uint WM_HOTKEY = 0x312;
public const uint MOD_ALT = 0x1;
public const uint MOD_CONTROL = 0x2;
public const uint MOD_SHIFT = 0x4;
public const uint MOD_WIN = 0x8;
public const uint ERROR_HOTKEY_ALREADY_REGISTERED = 1409;
}
}

View File

@@ -215,6 +215,8 @@ namespace EveOPreview.UI
view.ThumbnailLostFocus = this.ThumbnailViewLostFocus;
view.ThumbnailActivated = this.ThumbnailActivated;
view.RegisterHotkey(this._configuration.GetClientHotkey(processTitle));
this._thumbnailViews.Add(processHandle, view);
this.ApplyClientLayout(processHandle, processTitle);
@@ -224,13 +226,7 @@ namespace EveOPreview.UI
else if ((view != null) && (processTitle != view.Title)) // update thumbnail title
{
view.Title = processTitle;
// TODO Shortcuts should be handled at manager level
//string value;
//if (_flatLayoutShortcuts.TryGetValue(processTitle, out value))
//{
// view.RegisterShortcut(value);
//}
view.RegisterHotkey(this._configuration.GetClientHotkey(processTitle));
this.ApplyClientLayout(processHandle, processTitle);
@@ -261,7 +257,8 @@ namespace EveOPreview.UI
this._thumbnailViews.Remove(processHandle);
// TODO Remove hotkey here
view.UnregisterHotkey();
view.ThumbnailResized = null;
view.ThumbnailMoved = null;
view.ThumbnailFocused = null;

View File

@@ -1,4 +1,5 @@
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
@@ -24,6 +25,7 @@ namespace EveOPreview.UI
private IntPtr _thumbnailHandle;
private Size _baseSize;
private Point _baseLocation;
private HotkeyHandler _hotkeyHandler;
#endregion
public ThumbnailView()
@@ -207,6 +209,47 @@ namespace EveOPreview.UI
this.Location = this._baseLocation;
}
public void RegisterHotkey(Keys hotkey)
{
if (this._hotkeyHandler != null)
{
this.UnregisterHotkey();
}
if (hotkey == Keys.None)
{
return;
}
this._hotkeyHandler = new HotkeyHandler(this.Handle, hotkey);
this._hotkeyHandler.Pressed += HotkeyPressed_Handler;
try
{
this._hotkeyHandler.Register();
System.Diagnostics.Debug.WriteLine("Registered shortcut for " + this.Title);
}
catch (Exception)
{
System.Diagnostics.Debug.WriteLine("Failed to register shortcut for " + this.Title);
// There can be a lot of possible exception reasons here
// In case of any of them the hotkey setting is silently ignored
}
}
public void UnregisterHotkey()
{
if (this._hotkeyHandler == null)
{
return;
}
this._hotkeyHandler.Unregister();
this._hotkeyHandler.Pressed -= HotkeyPressed_Handler;
this._hotkeyHandler.Dispose();
this._hotkeyHandler = null;
}
public void Refresh(bool forceRefresh)
{
// To prevent flickering the old broken thumbnail is removed AFTER the new shiny one is created
@@ -327,6 +370,13 @@ namespace EveOPreview.UI
//this.Refresh();
}
}
private void HotkeyPressed_Handler(object sender, HandledEventArgs e)
{
this.ThumbnailActivated?.Invoke(this.Id);
e.Handled = true;
}
#endregion
private void RegisterThumbnail()

View File

@@ -1,5 +1,6 @@
using System;
using System.Drawing;
using System.Windows.Forms;
namespace EveOPreview.UI
{
@@ -24,6 +25,9 @@ namespace EveOPreview.UI
void ZoomIn(ViewZoomAnchor anchor, int zoomFactor);
void ZoomOut();
void RegisterHotkey(Keys hotkey);
void UnregisterHotkey();
void Refresh(bool forceRefresh);
Action<IntPtr> ThumbnailResized { get; set; }