Skip to content

Commit f55ce1b

Browse files
committed
2 parents 9c56a93 + b0984bc commit f55ce1b

File tree

62 files changed

+5848
-3672
lines changed

Some content is hidden

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

62 files changed

+5848
-3672
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ This library makes localizing WPF applications at runtime using resx files a bre
9292

9393
> Licensed under [The Code Project Open License](http://www.codeproject.com/info/cpol10.aspx).
9494
95+
###[EventHook](https://github.com/justcoding121/Windows-User-Action-Hook)
96+
97+
> A one stop library for global windows user actions such mouse, keyboard, clipboard, website visit & print events.
98+
99+
This library allows Rubberduck to detect righ-click actions in the active code pane, to dynamically enable/disable menu commands depending on the current context/selection. We're also using it to capture keypresses, to trigger a reparse of the current module as it's being modified.
100+
95101
##Icons
96102

97103
We didn't come up with these icons ourselves! Here's who did what:

RetailCoder.VBE/App.cs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,11 @@ public App(VBE vbe, IMessageBox messageBox,
8484

8585
private void _hooks_MessageReceived(object sender, HookEventArgs e)
8686
{
87-
if (sender is MouseHookWrapper)
87+
if (sender is MouseHook)
8888
{
89-
// right-click detected
9089
_appMenus.EvaluateCanExecute(_parser.State);
9190
}
92-
if (sender is KeyboardHookWrapper)
91+
if (sender is KeyboardHook)
9392
{
9493
var pane = _vbe.ActiveCodePane;
9594
if (pane == null)
@@ -121,10 +120,12 @@ public void Startup()
121120

122121
Task.Delay(1000).ContinueWith(t =>
123122
{
124-
_parser.State.OnParseRequested(this);
125-
_hooks.HookHotkeys();
126-
_hooks.Attach();
127-
});
123+
UiDispatcher.Invoke(() =>
124+
{
125+
_parser.State.OnParseRequested(this);
126+
_hooks.HookHotkeys();
127+
});
128+
}).ConfigureAwait(false);
128129
}
129130

130131
#region sink handlers. todo: move to another class

RetailCoder.VBE/Common/DeclarationExtensions.cs

Lines changed: 0 additions & 2 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

RetailCoder.VBE/Common/Hotkeys/Hotkey.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ private static Keys GetKey(string keyCode)
147147
result = Keys.Return;
148148
break;
149149
default:
150-
if (!String.IsNullOrEmpty(keyCode))
150+
if (!string.IsNullOrEmpty(keyCode))
151151
{
152152
result = (Keys)Enum.Parse(typeof(Keys), keyCode);
153153
}
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+
}

RetailCoder.VBE/Common/KeyboardHookWrapper.cs

Lines changed: 0 additions & 66 deletions
This file was deleted.

RetailCoder.VBE/Common/MouseHook.cs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
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 MouseHook : IAttachable
10+
{
11+
private readonly VBE _vbe;
12+
private IntPtr _hookId;
13+
private readonly User32.HookProc _callback;
14+
15+
public MouseHook(VBE vbe)
16+
{
17+
_vbe = vbe;
18+
_callback = HookCallback;
19+
}
20+
21+
private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
22+
{
23+
try
24+
{
25+
var pane = _vbe.ActiveCodePane;
26+
if (User32.IsVbeWindowActive((IntPtr)_vbe.MainWindow.HWnd) && nCode >= 0 && pane != null)
27+
{
28+
var button = (WM)wParam;
29+
if (button == WM.RBUTTONDOWN || button == WM.LBUTTONDOWN)
30+
{
31+
// handle right-click to evaluate commands' CanExecute before the context menu is shown;
32+
// handle left-click to do the same before the Rubberduck menu is drawn, too.
33+
OnMessageReceived();
34+
}
35+
}
36+
37+
return User32.CallNextHookEx(_hookId, nCode, wParam, lParam);
38+
}
39+
catch (Exception exception)
40+
{
41+
Debug.WriteLine(exception);
42+
}
43+
44+
return IntPtr.Zero;
45+
}
46+
47+
private void OnMessageReceived()
48+
{
49+
var handler = MessageReceived;
50+
if (handler != null)
51+
{
52+
handler.Invoke(this, HookEventArgs.Empty);
53+
}
54+
}
55+
56+
public bool IsAttached { get; private set; }
57+
public event EventHandler<HookEventArgs> MessageReceived;
58+
59+
public void Attach()
60+
{
61+
if (IsAttached)
62+
{
63+
return;
64+
}
65+
66+
var handle = Kernel32.GetModuleHandle("user32");
67+
if (handle == IntPtr.Zero)
68+
{
69+
throw new Win32Exception();
70+
}
71+
_hookId = User32.SetWindowsHookEx(WindowsHook.MOUSE_LL, _callback, handle, 0);
72+
if (_hookId == IntPtr.Zero)
73+
{
74+
throw new Win32Exception();
75+
}
76+
77+
IsAttached = true;
78+
Debug.WriteLine("{0}: {1}", GetType().Name, IsAttached ? "Attached" : "Detached");
79+
}
80+
81+
public void Detach()
82+
{
83+
if (!IsAttached)
84+
{
85+
return;
86+
}
87+
88+
if (!User32.UnhookWindowsHookEx(_hookId))
89+
{
90+
throw new Win32Exception();
91+
}
92+
93+
IsAttached = false;
94+
Debug.WriteLine("{0}: {1}", GetType().Name, IsAttached ? "Attached" : "Detached");
95+
}
96+
}
97+
}

0 commit comments

Comments
 (0)