Skip to content

Commit 364a2b5

Browse files
committed
Merge pull request #1307 from retailcoder/next
MouseHook / CanExecute checks and selective reparse
2 parents b356f79 + e78d93c commit 364a2b5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+877
-529
lines changed

RetailCoder.VBE/App.cs

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Globalization;
55
using System.Linq;
66
using System.Runtime.InteropServices.ComTypes;
7+
using System.Threading.Tasks;
78
using System.Windows.Forms;
89
using Microsoft.Vbe.Interop;
910
using NLog;
@@ -16,6 +17,7 @@
1617
using Rubberduck.UI.Command.MenuItems;
1718
using Infralution.Localization.Wpf;
1819
using Rubberduck.Common.Dispatch;
20+
using Rubberduck.Common.Hotkeys;
1921

2022
namespace Rubberduck
2123
{
@@ -41,6 +43,8 @@ public class App : IDisposable
4143
private readonly IDictionary<VBComponents, Tuple<IConnectionPoint, int>> _componentsEventsConnectionPoints =
4244
new Dictionary<VBComponents, Tuple<IConnectionPoint, int>>();
4345

46+
private readonly IDictionary<Type, Action> _hookActions;
47+
4448
public App(VBE vbe, IMessageBox messageBox,
4549
IRubberduckParser parser,
4650
IGeneralConfigService configService,
@@ -78,18 +82,41 @@ public App(VBE vbe, IMessageBox messageBox,
7882

7983
_projectsEventsConnectionPoint.Advise(sink, out _projectsEventsCookie);
8084

85+
_hookActions = new Dictionary<Type, Action>
86+
{
87+
{ typeof(MouseHook), HandleMouseMessage },
88+
//{ typeof(KeyboardHook), HandleKeyboardMessage },
89+
};
90+
91+
8192
UiDispatcher.Initialize();
8293
}
8394

8495
private void _hooks_MessageReceived(object sender, HookEventArgs e)
8596
{
86-
if (sender is MouseHookWrapper)
97+
var hookType = sender.GetType();
98+
Action action;
99+
if (_hookActions.TryGetValue(hookType, out action))
87100
{
88-
// right-click detected
89-
_appMenus.EvaluateCanExecute(_parser.State);
101+
action.Invoke();
90102
}
91103
}
92104

105+
private void HandleMouseMessage()
106+
{
107+
_appMenus.EvaluateCanExecute(_parser.State);
108+
}
109+
110+
private void HandleKeyboardMessage()
111+
{
112+
var pane = _vbe.ActiveCodePane;
113+
if (pane == null)
114+
{
115+
return;
116+
}
117+
_parser.State.OnParseRequested(this, pane.CodeModule.Parent);
118+
}
119+
93120
private void _configService_SettingsChanged(object sender, EventArgs e)
94121
{
95122
// also updates the ShortcutKey text
@@ -109,8 +136,15 @@ public void Startup()
109136
_appMenus.Initialize();
110137
_appMenus.Localize();
111138

112-
_hooks.HookHotkeys();
113-
_hooks.Attach();
139+
Task.Delay(1000).ContinueWith(t =>
140+
{
141+
// run this on UI thread
142+
UiDispatcher.Invoke(() =>
143+
{
144+
_parser.State.OnParseRequested(this);
145+
_hooks.HookHotkeys();
146+
});
147+
}, new StaTaskScheduler()).ConfigureAwait(false);
114148
}
115149

116150
#region sink handlers. todo: move to another class
@@ -256,20 +290,12 @@ async void sink_ProjectActivated(object sender, DispatcherEventArgs<VBProject> e
256290

257291
private void _stateBar_Refresh(object sender, EventArgs e)
258292
{
293+
// handles "refresh" button click on "Rubberduck" command bar
259294
_parser.State.OnParseRequested(sender);
260295
}
261296

262297
private void Parser_StateChanged(object sender, EventArgs e)
263298
{
264-
if (_parser.State.Status != ParserState.Ready)
265-
{
266-
_hooks.Detach();
267-
}
268-
else
269-
{
270-
_hooks.Attach();
271-
}
272-
273299
Debug.WriteLine("App handles StateChanged ({0}), evaluating menu states...", _parser.State.Status);
274300
_appMenus.EvaluateCanExecute(_parser.State);
275301
}

RetailCoder.VBE/Common/DeclarationExtensions.cs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -401,8 +401,6 @@ public static Declaration FindInterfaceMember(this IEnumerable<Declaration> decl
401401
public static Declaration FindTarget(this IEnumerable<Declaration> declarations, QualifiedSelection selection)
402402
{
403403
var items = declarations.ToList();
404-
Debug.Assert(!items.Any(item => item.IsBuiltIn));
405-
406404
return items.SingleOrDefault(item => item.IsSelected(selection) || item.References.Any(reference => reference.IsSelected(selection)));
407405
}
408406

@@ -419,25 +417,23 @@ public static Declaration FindTarget(this IEnumerable<Declaration> declarations,
419417
var items = declarations.ToList();
420418

421419
var target = items
422-
.Where(item => !item.IsBuiltIn)
420+
.Where(item => !item.IsBuiltIn && validDeclarationTypes.Contains(item.DeclarationType))
423421
.SingleOrDefault(item => item.IsSelected(selection)
424422
|| item.References.Any(r => r.IsSelected(selection)));
425423

426-
if (target != null && validDeclarationTypes.Contains(target.DeclarationType))
424+
if (target != null)
427425
{
428426
return target;
429427
}
430428

431-
target = null;
432-
433429
var targets = items
434430
.Where(item => !item.IsBuiltIn
435431
&& item.ComponentName == selection.QualifiedName.ComponentName
436432
&& validDeclarationTypes.Contains(item.DeclarationType));
437433

438434
var currentSelection = new Selection(0, 0, int.MaxValue, int.MaxValue);
439435

440-
foreach (var declaration in targets)
436+
foreach (var declaration in targets.Where(item => item.Context != null))
441437
{
442438
var activeSelection = new Selection(declaration.Context.Start.Line,
443439
declaration.Context.Start.Column,

RetailCoder.VBE/Common/Hotkeys/Hotkey.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ private static uint GetModifierValue(ref string key)
129129

130130
private static Keys GetCombo(string key)
131131
{
132-
return (Keys)Enum.Parse(typeof(Keys), key.Trim('%', '^', '+')) // will break with special keys, e.g. {f12}
132+
return GetKey(key.Trim('%', '^', '+')) // will break with special keys, e.g. {f12}
133133
| (key.Contains("%") ? Keys.Alt : Keys.None)
134134
| (key.Contains("^") ? Keys.Control : Keys.None)
135135
| (key.Contains("+") ? Keys.Shift : Keys.None);
@@ -146,8 +146,11 @@ private static Keys GetKey(string keyCode)
146146
case "~":
147147
result = Keys.Return;
148148
break;
149+
case "`":
150+
result = Keys.Oemtilde;
151+
break;
149152
default:
150-
if (!String.IsNullOrEmpty(keyCode))
153+
if (!string.IsNullOrEmpty(keyCode))
151154
{
152155
result = (Keys)Enum.Parse(typeof(Keys), keyCode);
153156
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
using System;
2+
using System.ComponentModel;
3+
using System.Diagnostics;
4+
using Microsoft.Vbe.Interop;
5+
using Rubberduck.Common.WinAPI;
6+
7+
namespace Rubberduck.Common
8+
{
9+
public class KeyboardHook : IAttachable
10+
{
11+
private readonly VBE _vbe;
12+
private IntPtr _hookId;
13+
14+
private readonly User32.HookProc _callback;
15+
16+
public KeyboardHook(VBE vbe)
17+
{
18+
_vbe = vbe;
19+
_callback = HookCallback;
20+
}
21+
22+
private int _lastLineIndex;
23+
private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
24+
{
25+
try
26+
{
27+
var pane = _vbe.ActiveCodePane;
28+
if (User32.IsVbeWindowActive((IntPtr)_vbe.MainWindow.HWnd) && pane != null && (WM)wParam == WM.KEYUP)
29+
{
30+
int startLine;
31+
int endLine;
32+
int startColumn;
33+
int endColumn;
34+
35+
// not using extension method because a QualifiedSelection would be overkill:
36+
pane.GetSelection(out startLine, out startColumn, out endLine, out endColumn);
37+
if (startLine != _lastLineIndex)
38+
{
39+
// if the current line has changed, let the KEYDOWN be written to the IDE, and notify on KEYUP:
40+
_lastLineIndex = startLine;
41+
if (nCode >= 0)
42+
{
43+
//var key = (Keys)Marshal.ReadInt32(lParam);
44+
OnMessageReceived();
45+
}
46+
}
47+
}
48+
49+
return User32.CallNextHookEx(_hookId, nCode, wParam, lParam);
50+
}
51+
catch (Exception exception)
52+
{
53+
Debug.WriteLine(exception);
54+
}
55+
56+
return IntPtr.Zero;
57+
}
58+
59+
private void OnMessageReceived()
60+
{
61+
var handler = MessageReceived;
62+
if (handler != null)
63+
{
64+
handler.Invoke(this, HookEventArgs.Empty);
65+
}
66+
}
67+
68+
public bool IsAttached { get; private set; }
69+
public event EventHandler<HookEventArgs> MessageReceived;
70+
71+
public void Attach()
72+
{
73+
if (IsAttached)
74+
{
75+
return;
76+
}
77+
78+
var handle = Kernel32.GetModuleHandle("user32");
79+
if (handle == IntPtr.Zero)
80+
{
81+
throw new Win32Exception();
82+
}
83+
84+
_hookId = User32.SetWindowsHookEx(WindowsHook.KEYBOARD_LL, _callback, handle, 0);
85+
if (_hookId == IntPtr.Zero)
86+
{
87+
throw new Win32Exception();
88+
}
89+
IsAttached = true;
90+
Debug.WriteLine("{0}: {1}", GetType().Name, IsAttached ? "Attached" : "Detached");
91+
}
92+
93+
public void Detach()
94+
{
95+
if (!IsAttached)
96+
{
97+
return;
98+
}
99+
100+
if (!User32.UnhookWindowsHookEx(_hookId))
101+
{
102+
throw new Win32Exception();
103+
}
104+
105+
IsAttached = false;
106+
Debug.WriteLine("{0}: {1}", GetType().Name, IsAttached ? "Attached" : "Detached");
107+
}
108+
}
109+
}

0 commit comments

Comments
 (0)