Skip to content

Commit 9f7dd04

Browse files
committed
Merge pull request #1259 from retailcoder/next
Hotkeys work
2 parents f3e88a7 + f250fdf commit 9f7dd04

32 files changed

+1061
-734
lines changed

RetailCoder.VBE/API/ParserState.cs

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,33 @@
99
namespace Rubberduck.API
1010
{
1111
[ComVisible(true)]
12-
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
1312
public interface IParserState
1413
{
1514
void Initialize(VBE vbe);
1615

1716
void Parse();
1817
void BeginParse();
1918

20-
Declaration[] AllDeclarations
21-
{
22-
get;
23-
}
19+
Declaration[] AllDeclarations { get; }
20+
Declaration[] UserDeclarations { get; }
21+
}
2422

25-
Declaration[] UserDeclarations
26-
{
27-
get;
28-
}
23+
[ComVisible(true)]
24+
[Guid("3D8EAA28-8983-44D5-83AF-2EEC4C363079")]
25+
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
26+
public interface IParserStateEvents
27+
{
28+
void OnParsed();
29+
void OnReady();
30+
void OnError();
2931
}
3032

3133
[ComVisible(true)]
3234
[Guid(ClassId)]
3335
[ProgId(ProgId)]
36+
[ClassInterface(ClassInterfaceType.AutoDual)]
3437
[ComDefaultInterface(typeof(IParserState))]
38+
[ComSourceInterfaces(typeof(IParserStateEvents))]
3539
[EditorBrowsable(EditorBrowsableState.Always)]
3640
public class ParserState : IParserState
3741
{
@@ -73,6 +77,10 @@ public void BeginParse()
7377
_state.OnParseRequested(this);
7478
}
7579

80+
public event Action OnParsed;
81+
public event Action OnReady;
82+
public event Action OnError;
83+
7684
private void _state_StateChanged(object sender, System.EventArgs e)
7785
{
7886
_allDeclarations = _state.AllDeclarations
@@ -82,6 +90,24 @@ private void _state_StateChanged(object sender, System.EventArgs e)
8290
_userDeclarations = _state.AllUserDeclarations
8391
.Select(item => new Declaration(item))
8492
.ToArray();
93+
94+
var errorHandler = OnError;
95+
if (_state.Status == Parsing.VBA.ParserState.Error && errorHandler != null)
96+
{
97+
errorHandler.Invoke();
98+
}
99+
100+
var parsedHandler = OnParsed;
101+
if (_state.Status == Parsing.VBA.ParserState.Parsed && parsedHandler != null)
102+
{
103+
parsedHandler.Invoke();
104+
}
105+
106+
var readyHandler = OnReady;
107+
if (_state.Status == Parsing.VBA.ParserState.Ready && readyHandler != null)
108+
{
109+
readyHandler.Invoke();
110+
}
85111
}
86112

87113
private Declaration[] _allDeclarations;

RetailCoder.VBE/App.cs

Lines changed: 105 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Runtime.InteropServices.ComTypes;
77
using System.Threading.Tasks;
88
using System.Windows.Forms;
9+
using System.Windows.Input;
910
using Microsoft.Vbe.Interop;
1011
using NLog;
1112
using Rubberduck.Common;
@@ -17,6 +18,9 @@
1718
using Rubberduck.UI.Command.MenuItems;
1819
using Infralution.Localization.Wpf;
1920
using Rubberduck.Common.Dispatch;
21+
using Rubberduck.Common.Hotkeys;
22+
using Rubberduck.UI.Command;
23+
using Hotkey = Rubberduck.Common.Hotkeys.Hotkey;
2024

2125
namespace Rubberduck
2226
{
@@ -31,6 +35,7 @@ public class App : IDisposable
3135
private readonly RubberduckCommandBar _stateBar;
3236
private readonly IIndenter _indenter;
3337
private readonly IRubberduckHooks _hooks;
38+
private readonly IEnumerable<ICommand> _appCommands;
3439

3540
private readonly Logger _logger;
3641

@@ -40,15 +45,21 @@ public class App : IDisposable
4045
private readonly int _projectsEventsCookie;
4146

4247
private readonly IDictionary<VBComponents, Tuple<IConnectionPoint, int>> _componentsEventsConnectionPoints =
43-
new Dictionary<VBComponents, Tuple<IConnectionPoint, int>>();
48+
new Dictionary<VBComponents, Tuple<IConnectionPoint, int>>();
49+
50+
private IReadOnlyDictionary<string, RubberduckHotkey> _hotkeyNameMap;
51+
52+
private IReadOnlyDictionary<RubberduckHotkey, ICommand> _hotkeyActions;
53+
private IReadOnlyDictionary<string, ICommand> _secondKeyActions;
4454

4555
public App(VBE vbe, IMessageBox messageBox,
4656
IRubberduckParser parser,
4757
IGeneralConfigService configService,
4858
IAppMenu appMenus,
4959
RubberduckCommandBar stateBar,
50-
IIndenter indenter/*
51-
IRubberduckHooks hooks*/)
60+
IIndenter indenter,
61+
IRubberduckHooks hooks,
62+
IEnumerable<ICommand> appCommands)
5263
{
5364
_vbe = vbe;
5465
_messageBox = messageBox;
@@ -58,10 +69,11 @@ IIndenter indenter/*
5869
_appMenus = appMenus;
5970
_stateBar = stateBar;
6071
_indenter = indenter;
61-
//_hooks = hooks;
72+
_hooks = hooks;
73+
_appCommands = appCommands;
6274
_logger = LogManager.GetCurrentClassLogger();
6375

64-
//_hooks.MessageReceived += hooks_MessageReceived;
76+
_hooks.MessageReceived += hooks_MessageReceived;
6577
_configService.LanguageChanged += ConfigServiceLanguageChanged;
6678
_parser.State.StateChanged += Parser_StateChanged;
6779
_stateBar.Refresh += _stateBar_Refresh;
@@ -81,6 +93,18 @@ IIndenter indenter/*
8193
UiDispatcher.Initialize();
8294
}
8395

96+
public void Startup()
97+
{
98+
CleanReloadConfig();
99+
100+
_appMenus.Initialize();
101+
_appMenus.Localize();
102+
103+
//_hooks.AddHook(new LowLevelKeyboardHook(_vbe));
104+
HookHotkeys();
105+
_hooks.Attach();
106+
}
107+
84108
async void sink_ProjectRemoved(object sender, DispatcherEventArgs<VBProject> e)
85109
{
86110
Debug.WriteLine(string.Format("Project '{0}' was removed.", e.Item.Name));
@@ -220,56 +244,68 @@ async void sink_ProjectActivated(object sender, DispatcherEventArgs<VBProject> e
220244
private bool _isAwaitingTwoStepKey;
221245
private bool _skipKeyUp;
222246

223-
private async void hooks_MessageReceived(object sender, HookEventArgs e)
247+
private void hooks_MessageReceived(object sender, HookEventArgs e)
224248
{
225249
if (sender is LowLevelKeyboardHook)
226250
{
227-
if (_skipKeyUp)
228-
{
229-
_skipKeyUp = false;
230-
return;
231-
}
232-
233-
if (_isAwaitingTwoStepKey)
234-
{
235-
// todo: use _firstStepHotKey and e.Key to run 2-step hotkey action
236-
if (_firstStepHotKey == Keys.I && e.Key == Keys.M)
237-
{
238-
_indenter.IndentCurrentModule();
239-
}
240-
241-
AwaitNextKey();
242-
return;
243-
}
244-
245-
var component = _vbe.ActiveCodePane.CodeModule.Parent;
246-
_parser.ParseComponent(component);
247-
248-
AwaitNextKey();
251+
HandleLowLevelKeyhook(e);
249252
return;
250253
}
251-
252-
var hotKey = sender as IHotKey;
253-
if (hotKey == null)
254+
255+
var hotKey = sender as IHotkey;
256+
if (hotKey != null)
257+
{
258+
HandleHotkey(hotKey);
259+
}
260+
else
254261
{
255262
AwaitNextKey();
256-
return;
257263
}
264+
}
258265

259-
if (hotKey.IsTwoStepHotKey)
266+
private void HandleHotkey(IHotkey hotkey)
267+
{
268+
if (hotkey.IsTwoStepHotkey)
260269
{
261-
_firstStepHotKey = hotKey.HotKeyInfo.Keys;
262-
AwaitNextKey(true, hotKey.HotKeyInfo);
270+
_firstStepHotKey = hotkey.HotkeyInfo.Keys;
271+
AwaitNextKey(true, hotkey.HotkeyInfo);
263272
}
264273
else
265274
{
266-
// todo: use e.Key to run 1-step hotkey action
267275
_firstStepHotKey = Keys.None;
276+
_hotkeyActions[_hotkeyNameMap[hotkey.Key]].Execute(null);
277+
AwaitNextKey();
278+
}
279+
}
280+
281+
private void HandleLowLevelKeyhook(HookEventArgs e)
282+
{
283+
if (_skipKeyUp)
284+
{
285+
_skipKeyUp = false;
286+
return;
287+
}
288+
289+
if (_isAwaitingTwoStepKey)
290+
{
291+
// todo: use _firstStepHotKey and e.Key to run 2-step hotkey action
292+
if (_firstStepHotKey == Keys.I && e.Key == Keys.M)
293+
{
294+
_indenter.IndentCurrentModule();
295+
}
296+
268297
AwaitNextKey();
298+
return;
269299
}
300+
301+
var component = _vbe.ActiveCodePane.CodeModule.Parent;
302+
_parser.ParseComponent(component);
303+
304+
AwaitNextKey();
305+
return;
270306
}
271307

272-
private void AwaitNextKey(bool eatNextKey = false, HotKeyInfo info = default(HotKeyInfo))
308+
private void AwaitNextKey(bool eatNextKey = false, HotkeyInfo info = default(HotkeyInfo))
273309
{
274310
_isAwaitingTwoStepKey = eatNextKey;
275311
foreach (var hook in _hooks.Hooks.OfType<ILowLevelKeyboardHook>())
@@ -299,22 +335,42 @@ private void Parser_StateChanged(object sender, EventArgs e)
299335
_appMenus.EvaluateCanExecute(_parser.State);
300336
}
301337

302-
public void Startup()
338+
private void HookHotkeys()
303339
{
304-
CleanReloadConfig();
305-
306-
_appMenus.Initialize();
307-
_appMenus.Localize();
308-
309-
//_hooks.AddHook(new LowLevelKeyboardHook(_vbe));
310-
//_hooks.AddHook(new HotKey((IntPtr)_vbe.MainWindow.HWnd, "%^R", Keys.R));
311-
//_hooks.AddHook(new HotKey((IntPtr)_vbe.MainWindow.HWnd, "%^I", Keys.I));
312-
//_hooks.Attach();
340+
var settings = _config.UserSettings.GeneralSettings.HotkeySettings;
341+
foreach (var hotkey in settings.Where(hotkey => hotkey.IsEnabled))
342+
{
343+
_hooks.AddHook(new Hotkey((IntPtr)_vbe.MainWindow.HWnd, hotkey.ToString()));
344+
}
313345
}
314346

315347
private void CleanReloadConfig()
316348
{
317349
LoadConfig();
350+
var hotkeys = _config.UserSettings.GeneralSettings.HotkeySettings
351+
.Where(hotkey => hotkey.IsEnabled).ToList();
352+
353+
_hotkeyNameMap = hotkeys
354+
.ToDictionary(
355+
hotkey => hotkey.ToString(),
356+
hotkey => (RubberduckHotkey)Enum.Parse(typeof(RubberduckHotkey), hotkey.Name));
357+
358+
_hotkeyActions = (from hotkey in hotkeys
359+
let value = (RubberduckHotkey)Enum.Parse(typeof(RubberduckHotkey),hotkey.Name)
360+
where string.IsNullOrEmpty(hotkey.Key2)
361+
select new
362+
{
363+
Hotkey = value,
364+
Command = _appCommands.OfType<IHotkeyCommand>()
365+
.SingleOrDefault(command => command.Hotkey == (RubberduckHotkey)Enum.Parse(typeof(RubberduckHotkey),hotkey.Name))
366+
})
367+
.ToDictionary(kvp => kvp.Hotkey, kvp => (ICommand)kvp.Command);
368+
369+
_secondKeyActions = hotkeys
370+
.Where(hotkey => !string.IsNullOrEmpty(hotkey.Key2))
371+
.ToDictionary(
372+
hotkey => hotkey.Key2,
373+
hotkey => hotkey.Command);
318374
}
319375

320376
private void ConfigServiceLanguageChanged(object sender, EventArgs e)
@@ -354,8 +410,8 @@ public void Dispose()
354410
item.Value.Item1.Unadvise(item.Value.Item2);
355411
}
356412

357-
//_hooks.MessageReceived -= hooks_MessageReceived;
358-
//_hooks.Dispose();
413+
_hooks.MessageReceived -= hooks_MessageReceived;
414+
_hooks.Dispose();
359415
}
360416
}
361417
}

RetailCoder.VBE/Common/HotKey.cs renamed to RetailCoder.VBE/Common/Hotkeys/Hotkey.cs

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,31 @@
11
using System;
22
using System.Collections.Generic;
33
using System.ComponentModel;
4-
using System.Linq;
5-
using System.Text;
64
using System.Windows.Forms;
75
using Rubberduck.Common.WinAPI;
86

9-
namespace Rubberduck.Common
7+
namespace Rubberduck.Common.Hotkeys
108
{
11-
public class HotKey : IHotKey
9+
public class Hotkey : IHotkey
1210
{
1311
private readonly string _key;
1412
private readonly IntPtr _hWndVbe;
1513

16-
public HotKey(IntPtr hWndVbe, string key, Keys secondKey = Keys.None)
14+
public Hotkey(IntPtr hWndVbe, string key, Keys secondKey = Keys.None)
1715
{
1816
_hWndVbe = hWndVbe;
1917

20-
IsTwoStepHotKey = secondKey != Keys.None;
18+
IsTwoStepHotkey = secondKey != Keys.None;
2119
_key = key;
2220
Combo = GetCombo(key);
2321
SecondKey = secondKey;
2422
}
2523

26-
public HotKeyInfo HotKeyInfo { get; private set; }
24+
public string Key { get { return _key; } }
25+
public HotkeyInfo HotkeyInfo { get; private set; }
2726
public Keys Combo { get; private set; }
2827
public Keys SecondKey { get; private set; }
29-
public bool IsTwoStepHotKey { get; private set; }
28+
public bool IsTwoStepHotkey { get; private set; }
3029
public bool IsAttached { get; private set; }
3130

3231
public void Attach()
@@ -50,8 +49,8 @@ public void Detach()
5049
throw new InvalidOperationException("Hook is already detached.");
5150
}
5251

53-
User32.UnregisterHotKey(_hWndVbe, HotKeyInfo.HookId);
54-
Kernel32.GlobalDeleteAtom(HotKeyInfo.HookId);
52+
User32.UnregisterHotKey(_hWndVbe, HotkeyInfo.HookId);
53+
Kernel32.GlobalDeleteAtom(HotkeyInfo.HookId);
5554

5655
IsAttached = false;
5756
}
@@ -70,7 +69,7 @@ private void HookKey(Keys key, uint shift)
7069
throw new Win32Exception("HotKey was not registered.");
7170
}
7271

73-
HotKeyInfo = new HotKeyInfo(hookId, Combo);
72+
HotkeyInfo = new HotkeyInfo(hookId, Combo);
7473
IsAttached = true;
7574
}
7675

@@ -111,9 +110,7 @@ private static uint GetModifierValue(ref string key)
111110

112111
private static Keys GetCombo(string key)
113112
{
114-
var combo = key;
115-
var modifiers = GetModifierValue(ref combo);
116-
return (Keys)Enum.Parse(typeof(Keys), combo) // will break with special keys, e.g. {f12}
113+
return (Keys)Enum.Parse(typeof(Keys), key.Trim('%', '^', '+')) // will break with special keys, e.g. {f12}
117114
| (key.Contains("%") ? Keys.Alt : Keys.None)
118115
| (key.Contains("^") ? Keys.Control : Keys.None)
119116
| (key.Contains("+") ? Keys.Shift : Keys.None);

0 commit comments

Comments
 (0)