Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
retailcoder committed Nov 30, 2014
2 parents 17e5d52 + da2d3b3 commit 7149649
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 10 deletions.
27 changes: 27 additions & 0 deletions RetailCoder.VBE/Extensions/CodePaneExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
using System.Runtime.InteropServices;
using Microsoft.Vbe.Interop;
using System;
using Rubberduck.UI;

namespace Rubberduck.Extensions
{
/// <summary> VBE Code Pane extensions. </summary>
[ComVisible(false)]
public static class CodePaneExtensions
{
/// <summary> A CodePane extension method that gets the current selection. </summary>
/// <returns> The selection. </returns>
public static Selection GetSelection(this CodePane code)
{
int startLine;
Expand All @@ -17,6 +22,10 @@ public static Selection GetSelection(this CodePane code)
return new Selection(startLine, startColumn, endLine, endColumn);
}

/// <summary> A CodePane extension method that selected procedure. </summary>
///
/// <param name="selection"> The selection. </param>
/// <returns> A Selection object representing the procedure the cursor is currently in. </returns>
public static Selection SelectedProcedure(this CodePane code, Selection selection)
{
vbext_ProcKind kind;
Expand Down Expand Up @@ -49,5 +58,23 @@ public static void SetSelection(this CodePane codePane, Selection selection)
codePane.SetSelection(selection.StartLine, selection.StartColumn, selection.EndLine, selection.EndColumn);
codePane.Window.SetFocus();
}

/// <summary> A CodePane extension method that forces focus onto the CodePane. This patches a bug in VBE.Interop.</summary>
public static void ForceFocus(this CodePane codePane)
{
codePane.Show();

IntPtr mainWindowHandle = codePane.VBE.MainWindow.Handle();
var childWindowFinder = new NativeWindowMethods.ChildWindowFinder(codePane.Window.Caption);

NativeWindowMethods.EnumChildWindows(mainWindowHandle, childWindowFinder.EnumWindowsProcToChildWindowByCaption);
IntPtr handle = childWindowFinder.ResultHandle;

if (handle != IntPtr.Zero)
{
NativeWindowMethods.ActivateWindow(handle, mainWindowHandle);
}

}
}
}
3 changes: 2 additions & 1 deletion RetailCoder.VBE/Rubberduck.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,10 @@
<Compile Include="Reflection\Attributes.cs" />
<Compile Include="Extensions\CodePaneExtensions.cs" />
<Compile Include="Reflection\Member.cs" />
<Compile Include="UI\NativeWindowMethods.cs" />
<Compile Include="UI\ToDoItems\ToDoExplorerDockablePresenter.cs" />
<Compile Include="UI\ToDoItems\ToDoItemClickEventArgs.cs" />
<Compile Include="UI\WindowExtensions.cs" />
<Compile Include="UI\UnitTesting\TestExplorerDockablePresenter.cs" />
<Compile Include="UnitTesting\TestOutcome.cs" />
<Compile Include="UnitTesting\TestResult.cs" />
Expand Down Expand Up @@ -263,7 +265,6 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Include="ClassDiagram1.cd" />
<None Include="Resources\rubberduck.config" />
<None Include="Resources\tick-circle.png" />
</ItemGroup>
Expand Down
12 changes: 7 additions & 5 deletions RetailCoder.VBE/UI/CodeExplorer/CodeExplorerDockablePresenter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
using Microsoft.Vbe.Interop;
using Rubberduck.VBA.Parser;
using Rubberduck.VBA.Parser.Grammar;
using System;
using Rubberduck.UI;
using Rubberduck.Extensions;

namespace Rubberduck.UI.CodeExplorer
{
Expand Down Expand Up @@ -56,15 +59,14 @@ private void NavigateExplorerTreeNode(object sender, SyntaxTreeNodeClickEventArg
return;
}

var codePane = vbComponent.CodeModule.CodePane;
var selection = instruction.Selection;

if (selection.StartLine != 0)
{
vbComponent.CodeModule.CodePane
.SetSelection(selection.StartLine, selection.StartColumn, selection.EndLine, selection.EndColumn + 1);
codePane.SetSelection(selection.StartLine, selection.StartColumn, selection.EndLine, selection.EndColumn + 1);
codePane.ForceFocus();
}

vbComponent.CodeModule.CodePane.Show();
vbComponent.CodeModule.CodePane.Window.SetFocus();
}

private void RefreshExplorerTreeView()
Expand Down
146 changes: 146 additions & 0 deletions RetailCoder.VBE/UI/NativeWindowMethods.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;

namespace Rubberduck.UI
{
[ComVisible(false)]
/// <summary> Collection of WinAPI methods and extensions to handle native windows. </summary>
// Special Thank You to Carlos Quintero for supplying the project with the original code this file is based on.
public static class NativeWindowMethods
{
/// <summary> Sends a message to the OS. </summary>
///
/// <param name="hWnd"> The window handle. </param>
/// <param name="wMsg"> The message. </param>
/// <param name="wParam"> The parameter. </param>
/// <param name="lParam"> The parameter. </param>
/// <returns> An IntPtr handle. </returns>
[DllImport("user32", EntryPoint = "SendMessageW", ExactSpelling = true)]
public static extern IntPtr SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);

/// <summary> EnumChildWindows delegate. </summary>
///
/// <param name="hwnd"> Main Window Handle</param>
/// <param name="lParam"> Application defined parameter. Unused. </param>
/// <returns> An int. </returns>
public delegate int EnumChildWindowsDelegate(IntPtr hwnd, IntPtr lParam);

/// <summary> WinAPI method to Enumerate Child Windows </summary>
///
/// <param name="parentWindowHandle"> Handle of the parent window. </param>
/// <param name="lpEnumFunction"> The enum delegate function. </param>
/// <param name="lParam"> The parameter. </param>
/// <returns> An int. </returns>
[DllImport("user32", ExactSpelling = true, CharSet = CharSet.Unicode)]
public static extern int EnumChildWindows(IntPtr parentWindowHandle, EnumChildWindowsDelegate lpEnumFunction, IntPtr lParam);

/// <summary> Gets window text. </summary>
///
/// <param name="hWnd"> The window handle. </param>
/// <param name="lpString"> The return string. </param>
/// <param name="nMaxCount"> Number of maximums. </param>
/// <returns> Integer Success Code </returns>
[DllImport("user32", EntryPoint = "GetWindowTextW", ExactSpelling = true, CharSet = CharSet.Unicode)]
public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

/// <summary> Gets the parent window of this item. </summary>
///
/// <param name="hWnd"> The window handle. </param>
/// <returns> The parent window IntPtr handle. </returns>
[DllImport("User32.dll")]
public static extern IntPtr GetParent(IntPtr hWnd);

/// <summary> Gets window caption text by handle. </summary>
///
/// <param name="windowHandle"> Handle of the window to be activated. </param>
/// <returns> The window caption text. </returns>
public static string GetWindowTextByHwnd(IntPtr windowHandle)
{
const int MAX_BUFFER = 300;

StringBuilder bufferStringBuilder = null;
int charactersCount = 0;
string result = null;

bufferStringBuilder = new StringBuilder(MAX_BUFFER + 1);

charactersCount = GetWindowText(windowHandle, bufferStringBuilder, MAX_BUFFER);
if (charactersCount > 0)
{
result = bufferStringBuilder.ToString(0, charactersCount);
}

return result;
}

/// <summary>Activates the window by simulating a click.</summary>
///
/// <param name="windowHandle"> Handle of the window to be activated. </param>
/// <param name="parentWindowHandle"> Handle of the parent window. </param>
public static void ActivateWindow(IntPtr windowHandle, IntPtr parentWindowHandle)
{
const int WM_MOUSEACTIVATE = 0x21;
const int HTCAPTION = 2;
const int WM_LBUTTONDOWN = 0x201;

SendMessage(windowHandle, WM_MOUSEACTIVATE, parentWindowHandle, new IntPtr(HTCAPTION + WM_LBUTTONDOWN * 0x10000));
}

internal static void EnumChildWindows(IntPtr parentWindowHandle, EnumChildWindowsDelegate callBackEnumWindows)
{
int result;

result = EnumChildWindows(parentWindowHandle, callBackEnumWindows, IntPtr.Zero);

if (result != 0)
{
System.Diagnostics.Debug.WriteLine("EnumChildWindows failed");
}
}

internal class ChildWindowFinder
{
private IntPtr _resultHandle = IntPtr.Zero;
private string _caption;

internal ChildWindowFinder(string caption)
{
_caption = caption;
}

public int EnumWindowsProcToChildWindowByCaption(IntPtr windowHandle, IntPtr param)
{
string caption;
int result;

// By default it will continue enumeration after this call
result = 1;

caption = NativeWindowMethods.GetWindowTextByHwnd(windowHandle);


if (_caption == caption)
{
// Found
_resultHandle = windowHandle;

// Stop enumeration after this call
result = 0;
}
return result;
}

public IntPtr ResultHandle
{
get
{
return _resultHandle;
}
}
}
}
}
4 changes: 2 additions & 2 deletions RetailCoder.VBE/UI/ToDoItems/ToDoExplorerDockablePresenter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

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

var codePane = component.CodeModule.CodePane;

codePane.Show();
codePane.SetSelection(e.Selection.LineNumber);
codePane.Window.SetFocus();
codePane.ForceFocus();
}
}
}
19 changes: 19 additions & 0 deletions RetailCoder.VBE/UI/WindowExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using Microsoft.Vbe.Interop;

namespace Rubberduck.UI
{
[ComVisible(false)]
public static class WindowExtensions
{
public static IntPtr Handle(this Window window)
{
return (IntPtr)window.HWnd;
}
}
}
5 changes: 3 additions & 2 deletions RetailCoder.VBE/UnitTesting/TestEngine.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using System.Runtime.InteropServices;
using Microsoft.Vbe.Interop;
using Rubberduck.UI;
using System;
using System.Collections.Generic;
using System.Linq;
using Rubberduck.UI.UnitTesting;
using Rubberduck.Extensions;

namespace Rubberduck.UnitTesting
{
Expand Down Expand Up @@ -240,8 +242,7 @@ void OnExplorerGoToSelectedTest(object sender, SelectedTestEventArgs e)
if (codeModule.Find(signature, ref startLine, ref startColumn, ref endLine, ref endColumn))
{
codeModule.CodePane.SetSelection(startLine, startColumn, endLine, endColumn);
codeModule.CodePane.Show();
codeModule.CodePane.Window.SetFocus();
codeModule.CodePane.ForceFocus();
}
}

Expand Down

0 comments on commit 7149649

Please sign in to comment.