66using System . Runtime . InteropServices . ComTypes ;
77using System . Threading . Tasks ;
88using System . Windows . Forms ;
9+ using System . Windows . Input ;
910using Microsoft . Vbe . Interop ;
1011using NLog ;
1112using Rubberduck . Common ;
1718using Rubberduck . UI . Command . MenuItems ;
1819using Infralution . Localization . Wpf ;
1920using Rubberduck . Common . Dispatch ;
21+ using Rubberduck . Common . Hotkeys ;
22+ using Rubberduck . UI . Command ;
23+ using Hotkey = Rubberduck . Common . Hotkeys . Hotkey ;
2024
2125namespace 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}
0 commit comments