-
Notifications
You must be signed in to change notification settings - Fork 12
/
HotkeyControl.cs
317 lines (285 loc) · 10.9 KB
/
HotkeyControl.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
using System;
using System.Collections;
using System.Windows.Forms;
//
// Hotkey selection control, written by [email protected], 2006-08-03
// Please mail me if you find a bug.
//
namespace exscape
{
/// <summary>
/// A simple control that allows the user to select pretty much any valid hotkey combination
/// </summary>
public class HotkeyControl : TextBox
{
// These variables store the current hotkey and modifier(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, as it would screw up when the user is typing.
private ArrayList needNonShiftModifier = null;
private ArrayList needNonAltGrModifier = null;
private ContextMenu dummy = new ContextMenu();
/// <summary>
/// Used to make sure that there is no right-click menu available
/// </summary>
public override ContextMenu ContextMenu
{
get
{
return dummy;
}
set
{
base.ContextMenu = dummy;
}
}
/// <summary>
/// Forces the control to be non-multiline
/// </summary>
public override bool Multiline
{
get
{
return base.Multiline;
}
set
{
// Ignore what the user wants; force Multiline to false
base.Multiline = false;
}
}
/// <summary>
/// Creates a new HotkeyControl
/// </summary>
public HotkeyControl()
{
this.ContextMenu = dummy; // Disable right-clicking
this.Text = "None";
// Handle events that occurs when keys are pressed
this.KeyPress += new KeyPressEventHandler(HotkeyControl_KeyPress);
this.KeyUp += new KeyEventHandler(HotkeyControl_KeyUp);
this.KeyDown += new KeyEventHandler(HotkeyControl_KeyDown);
// Fill the ArrayLists that contain all invalid hotkey combinations
needNonShiftModifier = new ArrayList();
needNonAltGrModifier = new ArrayList();
PopulateModifierLists();
}
/// <summary>
/// Populates the ArrayLists specifying disallowed hotkeys
/// such as Shift+A, Ctrl+Alt+4 (would produce a dollar sign) etc
/// </summary>
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);
}
/// <summary>
/// Resets this hotkey control to None
/// </summary>
public new void Clear()
{
this.Hotkey = Keys.None;
this.HotkeyModifiers = Keys.None;
}
/// <summary>
/// Fires when a key is pushed down. Here, we'll want to update the text in the box
/// to notify the user what combination is currently pressed.
/// </summary>
void HotkeyControl_KeyDown(object sender, KeyEventArgs e)
{
// Clear the current hotkey
if (e.KeyCode == Keys.Back || e.KeyCode == Keys.Delete)
{
ResetHotkey();
return;
}
else
{
this._modifiers = e.Modifiers;
this._hotkey = e.KeyCode;
Redraw();
}
}
/// <summary>
/// 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.
/// </summary>
void HotkeyControl_KeyUp(object sender, KeyEventArgs e)
{
if (this._hotkey == Keys.None && Control.ModifierKeys == Keys.None)
{
ResetHotkey();
return;
}
}
/// <summary>
/// Prevents the letter/whatever entered to show up in the TextBox
/// Without this, a "A" key press would appear as "aControl, Alt + A"
/// </summary>
void HotkeyControl_KeyPress(object sender, KeyPressEventArgs e)
{
e.Handled = true;
}
/// <summary>
/// Handles some misc keys, such as Ctrl+Delete and Shift+Insert
/// </summary>
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.Delete || keyData == (Keys.Control | Keys.Delete))
{
ResetHotkey();
return true;
}
if (keyData == (Keys.Shift | Keys.Insert)) // Paste
return true; // Don't allow
// Allow the rest
return base.ProcessCmdKey(ref msg, keyData);
}
/// <summary>
/// Clears the current hotkey and resets the TextBox
/// </summary>
public void ResetHotkey()
{
this._hotkey = Keys.None;
this._modifiers = Keys.None;
Redraw();
}
/// <summary>
/// Used to get/set the hotkey (e.g. Keys.A)
/// </summary>
public Keys Hotkey
{
get
{
return this._hotkey;
}
set
{
this._hotkey = value;
Redraw(true);
}
}
/// <summary>
/// Used to get/set the modifier keys (e.g. Keys.Alt | Keys.Control)
/// </summary>
public Keys HotkeyModifiers
{
get
{
return this._modifiers;
}
set
{
this._modifiers = value;
Redraw(true);
}
}
/// <summary>
/// Helper function
/// </summary>
private void Redraw()
{
Redraw(false);
}
/// <summary>
/// Redraws the TextBox when necessary.
/// </summary>
/// <param name="bCalledProgramatically">Specifies whether this function was called by the Hotkey/HotkeyModifiers properties or by the user.</param>
private void Redraw(bool bCalledProgramatically)
{
// No hotkey set
if (this._hotkey == Keys.None)
{
this.Text = "None";
return;
}
// LWin/RWin doesn't work as hotkeys (neither do they work as modifier keys in .NET 2.0)
if (this._hotkey == Keys.LWin || this._hotkey == Keys.RWin)
{
this.Text = "None";
return;
}
// Only validate input if it comes from the user
if (bCalledProgramatically == false)
{
// No modifier or shift only, AND a hotkey that needs another modifier
if ((this._modifiers == Keys.Shift || this._modifiers == Keys.None) &&
this.needNonShiftModifier.Contains((int)this._hotkey))
{
if (this._modifiers == Keys.None)
{
// Set Ctrl+Alt as the modifier unless Ctrl+Alt+<key> won't work...
if (needNonAltGrModifier.Contains((int)this._hotkey) == false)
this._modifiers = Keys.Alt | Keys.Control;
else // ... in that case, use Shift+Alt instead.
this._modifiers = 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;
this.Text = this._modifiers.ToString() + " + Invalid key";
return;
}
}
// Check all Ctrl+Alt keys
if ((this._modifiers == (Keys.Alt | Keys.Control)) &&
this.needNonAltGrModifier.Contains((int)this._hotkey))
{
// Ctrl+Alt+4 etc won't work; reset hotkey and tell the user
this._hotkey = Keys.None;
this.Text = this._modifiers.ToString() + " + Invalid key";
return;
}
}
if (this._modifiers == Keys.None)
{
if (this._hotkey == Keys.None)
{
this.Text = "None";
return;
}
else
{
// We get here if we've got a hotkey that is valid without a modifier,
// like F1-F12, Media-keys etc.
this.Text = this._hotkey.ToString();
return;
}
}
// I have no idea why this is needed, but it is. 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;
this.Text = this._modifiers.ToString() + " + " + this._hotkey.ToString();
}
}
}