Skip to content

Commit 7149649

Browse files
committed
2 parents 17e5d52 + da2d3b3 commit 7149649

File tree

7 files changed

+206
-10
lines changed

7 files changed

+206
-10
lines changed

RetailCoder.VBE/Extensions/CodePaneExtensions.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
using System.Runtime.InteropServices;
22
using Microsoft.Vbe.Interop;
3+
using System;
4+
using Rubberduck.UI;
35

46
namespace Rubberduck.Extensions
57
{
8+
/// <summary> VBE Code Pane extensions. </summary>
69
[ComVisible(false)]
710
public static class CodePaneExtensions
811
{
12+
/// <summary> A CodePane extension method that gets the current selection. </summary>
13+
/// <returns> The selection. </returns>
914
public static Selection GetSelection(this CodePane code)
1015
{
1116
int startLine;
@@ -17,6 +22,10 @@ public static Selection GetSelection(this CodePane code)
1722
return new Selection(startLine, startColumn, endLine, endColumn);
1823
}
1924

25+
/// <summary> A CodePane extension method that selected procedure. </summary>
26+
///
27+
/// <param name="selection"> The selection. </param>
28+
/// <returns> A Selection object representing the procedure the cursor is currently in. </returns>
2029
public static Selection SelectedProcedure(this CodePane code, Selection selection)
2130
{
2231
vbext_ProcKind kind;
@@ -49,5 +58,23 @@ public static void SetSelection(this CodePane codePane, Selection selection)
4958
codePane.SetSelection(selection.StartLine, selection.StartColumn, selection.EndLine, selection.EndColumn);
5059
codePane.Window.SetFocus();
5160
}
61+
62+
/// <summary> A CodePane extension method that forces focus onto the CodePane. This patches a bug in VBE.Interop.</summary>
63+
public static void ForceFocus(this CodePane codePane)
64+
{
65+
codePane.Show();
66+
67+
IntPtr mainWindowHandle = codePane.VBE.MainWindow.Handle();
68+
var childWindowFinder = new NativeWindowMethods.ChildWindowFinder(codePane.Window.Caption);
69+
70+
NativeWindowMethods.EnumChildWindows(mainWindowHandle, childWindowFinder.EnumWindowsProcToChildWindowByCaption);
71+
IntPtr handle = childWindowFinder.ResultHandle;
72+
73+
if (handle != IntPtr.Zero)
74+
{
75+
NativeWindowMethods.ActivateWindow(handle, mainWindowHandle);
76+
}
77+
78+
}
5279
}
5380
}

RetailCoder.VBE/Rubberduck.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,10 @@
105105
<Compile Include="Reflection\Attributes.cs" />
106106
<Compile Include="Extensions\CodePaneExtensions.cs" />
107107
<Compile Include="Reflection\Member.cs" />
108+
<Compile Include="UI\NativeWindowMethods.cs" />
108109
<Compile Include="UI\ToDoItems\ToDoExplorerDockablePresenter.cs" />
109110
<Compile Include="UI\ToDoItems\ToDoItemClickEventArgs.cs" />
111+
<Compile Include="UI\WindowExtensions.cs" />
110112
<Compile Include="UI\UnitTesting\TestExplorerDockablePresenter.cs" />
111113
<Compile Include="UnitTesting\TestOutcome.cs" />
112114
<Compile Include="UnitTesting\TestResult.cs" />
@@ -263,7 +265,6 @@
263265
</EmbeddedResource>
264266
</ItemGroup>
265267
<ItemGroup>
266-
<None Include="ClassDiagram1.cd" />
267268
<None Include="Resources\rubberduck.config" />
268269
<None Include="Resources\tick-circle.png" />
269270
</ItemGroup>

RetailCoder.VBE/UI/CodeExplorer/CodeExplorerDockablePresenter.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
using Microsoft.Vbe.Interop;
55
using Rubberduck.VBA.Parser;
66
using Rubberduck.VBA.Parser.Grammar;
7+
using System;
8+
using Rubberduck.UI;
9+
using Rubberduck.Extensions;
710

811
namespace Rubberduck.UI.CodeExplorer
912
{
@@ -56,15 +59,14 @@ private void NavigateExplorerTreeNode(object sender, SyntaxTreeNodeClickEventArg
5659
return;
5760
}
5861

62+
var codePane = vbComponent.CodeModule.CodePane;
5963
var selection = instruction.Selection;
64+
6065
if (selection.StartLine != 0)
6166
{
62-
vbComponent.CodeModule.CodePane
63-
.SetSelection(selection.StartLine, selection.StartColumn, selection.EndLine, selection.EndColumn + 1);
67+
codePane.SetSelection(selection.StartLine, selection.StartColumn, selection.EndLine, selection.EndColumn + 1);
68+
codePane.ForceFocus();
6469
}
65-
66-
vbComponent.CodeModule.CodePane.Show();
67-
vbComponent.CodeModule.CodePane.Window.SetFocus();
6870
}
6971

7072
private void RefreshExplorerTreeView()
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using System.Runtime.InteropServices;
7+
8+
namespace Rubberduck.UI
9+
{
10+
[ComVisible(false)]
11+
/// <summary> Collection of WinAPI methods and extensions to handle native windows. </summary>
12+
// Special Thank You to Carlos Quintero for supplying the project with the original code this file is based on.
13+
public static class NativeWindowMethods
14+
{
15+
/// <summary> Sends a message to the OS. </summary>
16+
///
17+
/// <param name="hWnd"> The window handle. </param>
18+
/// <param name="wMsg"> The message. </param>
19+
/// <param name="wParam"> The parameter. </param>
20+
/// <param name="lParam"> The parameter. </param>
21+
/// <returns> An IntPtr handle. </returns>
22+
[DllImport("user32", EntryPoint = "SendMessageW", ExactSpelling = true)]
23+
public static extern IntPtr SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
24+
25+
/// <summary> EnumChildWindows delegate. </summary>
26+
///
27+
/// <param name="hwnd"> Main Window Handle</param>
28+
/// <param name="lParam"> Application defined parameter. Unused. </param>
29+
/// <returns> An int. </returns>
30+
public delegate int EnumChildWindowsDelegate(IntPtr hwnd, IntPtr lParam);
31+
32+
/// <summary> WinAPI method to Enumerate Child Windows </summary>
33+
///
34+
/// <param name="parentWindowHandle"> Handle of the parent window. </param>
35+
/// <param name="lpEnumFunction"> The enum delegate function. </param>
36+
/// <param name="lParam"> The parameter. </param>
37+
/// <returns> An int. </returns>
38+
[DllImport("user32", ExactSpelling = true, CharSet = CharSet.Unicode)]
39+
public static extern int EnumChildWindows(IntPtr parentWindowHandle, EnumChildWindowsDelegate lpEnumFunction, IntPtr lParam);
40+
41+
/// <summary> Gets window text. </summary>
42+
///
43+
/// <param name="hWnd"> The window handle. </param>
44+
/// <param name="lpString"> The return string. </param>
45+
/// <param name="nMaxCount"> Number of maximums. </param>
46+
/// <returns> Integer Success Code </returns>
47+
[DllImport("user32", EntryPoint = "GetWindowTextW", ExactSpelling = true, CharSet = CharSet.Unicode)]
48+
public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
49+
50+
/// <summary> Gets the parent window of this item. </summary>
51+
///
52+
/// <param name="hWnd"> The window handle. </param>
53+
/// <returns> The parent window IntPtr handle. </returns>
54+
[DllImport("User32.dll")]
55+
public static extern IntPtr GetParent(IntPtr hWnd);
56+
57+
/// <summary> Gets window caption text by handle. </summary>
58+
///
59+
/// <param name="windowHandle"> Handle of the window to be activated. </param>
60+
/// <returns> The window caption text. </returns>
61+
public static string GetWindowTextByHwnd(IntPtr windowHandle)
62+
{
63+
const int MAX_BUFFER = 300;
64+
65+
StringBuilder bufferStringBuilder = null;
66+
int charactersCount = 0;
67+
string result = null;
68+
69+
bufferStringBuilder = new StringBuilder(MAX_BUFFER + 1);
70+
71+
charactersCount = GetWindowText(windowHandle, bufferStringBuilder, MAX_BUFFER);
72+
if (charactersCount > 0)
73+
{
74+
result = bufferStringBuilder.ToString(0, charactersCount);
75+
}
76+
77+
return result;
78+
}
79+
80+
/// <summary>Activates the window by simulating a click.</summary>
81+
///
82+
/// <param name="windowHandle"> Handle of the window to be activated. </param>
83+
/// <param name="parentWindowHandle"> Handle of the parent window. </param>
84+
public static void ActivateWindow(IntPtr windowHandle, IntPtr parentWindowHandle)
85+
{
86+
const int WM_MOUSEACTIVATE = 0x21;
87+
const int HTCAPTION = 2;
88+
const int WM_LBUTTONDOWN = 0x201;
89+
90+
SendMessage(windowHandle, WM_MOUSEACTIVATE, parentWindowHandle, new IntPtr(HTCAPTION + WM_LBUTTONDOWN * 0x10000));
91+
}
92+
93+
internal static void EnumChildWindows(IntPtr parentWindowHandle, EnumChildWindowsDelegate callBackEnumWindows)
94+
{
95+
int result;
96+
97+
result = EnumChildWindows(parentWindowHandle, callBackEnumWindows, IntPtr.Zero);
98+
99+
if (result != 0)
100+
{
101+
System.Diagnostics.Debug.WriteLine("EnumChildWindows failed");
102+
}
103+
}
104+
105+
internal class ChildWindowFinder
106+
{
107+
private IntPtr _resultHandle = IntPtr.Zero;
108+
private string _caption;
109+
110+
internal ChildWindowFinder(string caption)
111+
{
112+
_caption = caption;
113+
}
114+
115+
public int EnumWindowsProcToChildWindowByCaption(IntPtr windowHandle, IntPtr param)
116+
{
117+
string caption;
118+
int result;
119+
120+
// By default it will continue enumeration after this call
121+
result = 1;
122+
123+
caption = NativeWindowMethods.GetWindowTextByHwnd(windowHandle);
124+
125+
126+
if (_caption == caption)
127+
{
128+
// Found
129+
_resultHandle = windowHandle;
130+
131+
// Stop enumeration after this call
132+
result = 0;
133+
}
134+
return result;
135+
}
136+
137+
public IntPtr ResultHandle
138+
{
139+
get
140+
{
141+
return _resultHandle;
142+
}
143+
}
144+
}
145+
}
146+
}

RetailCoder.VBE/UI/ToDoItems/ToDoExplorerDockablePresenter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
namespace Rubberduck.UI.ToDoItems
1212
{
13+
/// <summary> (Not COM visible) Presenter for the Todo Explorer. </summary>
1314
[ComVisible(false)]
1415
public class ToDoExplorerDockablePresenter : DockablePresenterBase
1516
{
@@ -67,9 +68,8 @@ private void NavigateToDoItem(object sender, ToDoItemClickEventArgs e)
6768

6869
var codePane = component.CodeModule.CodePane;
6970

70-
codePane.Show();
7171
codePane.SetSelection(e.Selection.LineNumber);
72-
codePane.Window.SetFocus();
72+
codePane.ForceFocus();
7373
}
7474
}
7575
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using System.Runtime.InteropServices;
7+
using Microsoft.Vbe.Interop;
8+
9+
namespace Rubberduck.UI
10+
{
11+
[ComVisible(false)]
12+
public static class WindowExtensions
13+
{
14+
public static IntPtr Handle(this Window window)
15+
{
16+
return (IntPtr)window.HWnd;
17+
}
18+
}
19+
}

RetailCoder.VBE/UnitTesting/TestEngine.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
using System.Runtime.InteropServices;
22
using Microsoft.Vbe.Interop;
3+
using Rubberduck.UI;
34
using System;
45
using System.Collections.Generic;
56
using System.Linq;
67
using Rubberduck.UI.UnitTesting;
8+
using Rubberduck.Extensions;
79

810
namespace Rubberduck.UnitTesting
911
{
@@ -240,8 +242,7 @@ void OnExplorerGoToSelectedTest(object sender, SelectedTestEventArgs e)
240242
if (codeModule.Find(signature, ref startLine, ref startColumn, ref endLine, ref endColumn))
241243
{
242244
codeModule.CodePane.SetSelection(startLine, startColumn, endLine, endColumn);
243-
codeModule.CodePane.Show();
244-
codeModule.CodePane.Window.SetFocus();
245+
codeModule.CodePane.ForceFocus();
245246
}
246247
}
247248

0 commit comments

Comments
 (0)