diff --git a/RetailCoder.VBE/App.cs b/RetailCoder.VBE/App.cs index 78f6f1f1d6..4ab03cd999 100644 --- a/RetailCoder.VBE/App.cs +++ b/RetailCoder.VBE/App.cs @@ -63,8 +63,8 @@ public App(IVBE vbe, _version = version; _checkVersionCommand = checkVersionCommand; - VBEEvents.SelectionChanged += _vbe_SelectionChanged; - VBEEvents.WindowFocusChange += _vbe_FocusChanged; + VBENativeServices.SelectionChanged += _vbe_SelectionChanged; + VBENativeServices.WindowFocusChange += _vbe_FocusChanged; _configService.SettingsChanged += _configService_SettingsChanged; _parser.State.StateChanged += Parser_StateChanged; @@ -73,6 +73,9 @@ public App(IVBE vbe, UiDispatcher.Initialize(); } + //TODO - This should be able to move to the appropriate handling classes now. + #region Statusbar + private void State_StatusMessageUpdate(object sender, RubberduckStatusMessageEventArgs e) { var message = e.Message; @@ -92,90 +95,79 @@ private void _vbe_SelectionChanged(object sender, SelectionChangedEventArgs e) private void _vbe_FocusChanged(object sender, WindowChangedEventArgs e) { - if (e.EventType == WindowChangedEventArgs.FocusType.GotFocus) + if (e.EventType == FocusType.GotFocus) { switch (e.Window.Type) { case WindowKind.Designer: + //Designer or control on designer form selected. RefreshSelection(e.Window); break; case WindowKind.CodeWindow: + //Caret changed in a code pane. RefreshSelection(e.CodePane); break; } - } + } + else if (e.EventType == FocusType.ChildFocus) + { + //Treeview selection changed in project window. + RefreshSelection(); + } } private ParserState _lastStatus; private Declaration _lastSelectedDeclaration; private void RefreshSelection(ICodePane pane) { - Declaration selectedDeclaration = null; - if (!pane.IsWrappingNullReference) + if (pane == null || pane.IsWrappingNullReference) { - selectedDeclaration = _parser.State.FindSelectedDeclaration(pane); - var refCount = selectedDeclaration == null ? 0 : selectedDeclaration.References.Count(); - var caption = _stateBar.GetContextSelectionCaption(_vbe.ActiveCodePane, selectedDeclaration); - _stateBar.SetContextSelectionCaption(caption, refCount); - } - - var currentStatus = _parser.State.Status; - if (ShouldEvaluateCanExecute(selectedDeclaration, currentStatus)) - { - _appMenus.EvaluateCanExecute(_parser.State); - _stateBar.EvaluateCanExecute(_parser.State); + return; } - _lastStatus = currentStatus; - _lastSelectedDeclaration = selectedDeclaration; + var selectedDeclaration = _parser.State.FindSelectedDeclaration(pane); + var caption = _stateBar.GetContextSelectionCaption(_vbe.ActiveCodePane, selectedDeclaration); + UpdateStatusbarAndCommandState(caption, selectedDeclaration); } private void RefreshSelection(IWindow window) { - if (window.IsWrappingNullReference || window.Type != WindowKind.Designer) + if (window == null || window.IsWrappingNullReference || window.Type != WindowKind.Designer) { return; } - var caption = String.Empty; - var refCount = 0; - WindowKind windowKind = _vbe.ActiveWindow.Type; - var pane = _vbe.ActiveCodePane; var component = _vbe.SelectedVBComponent; + var caption = GetComponentControlsCaption(component); + //TODO: Need to find the selected declaration for the Form\Control. + UpdateStatusbarAndCommandState(caption, null); + } - Declaration selectedDeclaration = null; - - //TODO - I doubt this is the best way to check if the SelectedVBComponent and the ActiveCodePane are the same component. - if (windowKind == WindowKind.CodeWindow || (!_vbe.SelectedVBComponent.IsWrappingNullReference - && component.ParentProject.ProjectId == pane.CodeModule.Parent.ParentProject.ProjectId - && component.Name == pane.CodeModule.Parent.Name)) - { - selectedDeclaration = _parser.State.FindSelectedDeclaration(pane); - refCount = selectedDeclaration == null ? 0 : selectedDeclaration.References.Count(); - caption = _stateBar.GetContextSelectionCaption(_vbe.ActiveCodePane, selectedDeclaration); - } - else if (windowKind == WindowKind.Designer) + private void RefreshSelection() + { + var caption = string.Empty; + var component = _vbe.SelectedVBComponent; + if (component == null || component.IsWrappingNullReference) { - caption = GetComponentControlsCaption(component); + //The user might have selected the project node in Project Explorer + //If they've chosen a folder, we'll return the project anyway + caption = !_vbe.ActiveVBProject.IsWrappingNullReference + ? _vbe.ActiveVBProject.Name + : null; } else { - if (_vbe.SelectedVBComponent.IsWrappingNullReference) - { - //The user might have selected the project node in Project Explorer - //If they've chosen a folder, we'll return the project anyway - caption = !_vbe.ActiveVBProject.IsWrappingNullReference - ? _vbe.ActiveVBProject.Name - : null; - } - else - { - caption = component.Type == ComponentType.UserForm && component.HasOpenDesigner - ? GetComponentControlsCaption(component) - : String.Format("{0}.{1} ({2})", component.ParentProject.Name, component.Name, component.Type); - } + caption = component.Type == ComponentType.UserForm && component.HasOpenDesigner + ? GetComponentControlsCaption(component) + : string.Format("{0}.{1} ({2})", component.ParentProject.Name, component.Name, component.Type); } + //TODO: Need to find the selected declaration for the selected treeview item. + UpdateStatusbarAndCommandState(caption, null); + } + private void UpdateStatusbarAndCommandState(string caption, Declaration selectedDeclaration) + { + var refCount = selectedDeclaration == null ? 0 : selectedDeclaration.References.Count(); _stateBar.SetContextSelectionCaption(caption, refCount); var currentStatus = _parser.State.Status; @@ -186,7 +178,7 @@ private void RefreshSelection(IWindow window) } _lastStatus = currentStatus; - _lastSelectedDeclaration = selectedDeclaration; + _lastSelectedDeclaration = selectedDeclaration; } private string GetComponentControlsCaption(IVBComponent component) @@ -215,6 +207,8 @@ private bool ShouldEvaluateCanExecute(Declaration selectedDeclaration, ParserSta (selectedDeclaration == null && _lastSelectedDeclaration != null); } + #endregion + private void _configService_SettingsChanged(object sender, ConfigurationChangedEventArgs e) { _config = _configService.LoadConfiguration(); @@ -366,8 +360,8 @@ public void Dispose() _parser.State.StatusMessageUpdate -= State_StatusMessageUpdate; } - VBEEvents.SelectionChanged += _vbe_SelectionChanged; - VBEEvents.WindowFocusChange += _vbe_FocusChanged; + VBENativeServices.SelectionChanged += _vbe_SelectionChanged; + VBENativeServices.WindowFocusChange += _vbe_FocusChanged; if (_configService != null) { diff --git a/RetailCoder.VBE/Extension.cs b/RetailCoder.VBE/Extension.cs index b53012ff34..4da14b26e5 100644 --- a/RetailCoder.VBE/Extension.cs +++ b/RetailCoder.VBE/Extension.cs @@ -56,7 +56,7 @@ public void OnConnection(object Application, ext_ConnectMode ConnectMode, object { var vbe = (Microsoft.Vbe.Interop.VBE) Application; _ide = new VBEditor.SafeComWrappers.VBA.VBE(vbe); - VBEEvents.HookEvents(_ide); + VBENativeServices.HookEvents(_ide); var addin = (Microsoft.Vbe.Interop.AddIn)AddInInst; _addin = new VBEditor.SafeComWrappers.VBA.AddIn(addin) { Object = this }; @@ -221,7 +221,7 @@ private void Startup() private void ShutdownAddIn() { - VBEEvents.UnhookEvents(); + VBENativeServices.UnhookEvents(); var currentDomain = AppDomain.CurrentDomain; currentDomain.AssemblyResolve -= LoadFromSameFolder; diff --git a/RetailCoder.VBE/UI/Refactorings/ExtractInterfaceDialog.Designer.cs b/RetailCoder.VBE/UI/Refactorings/ExtractInterfaceDialog.Designer.cs index b303ce54ad..7c294dd60f 100644 --- a/RetailCoder.VBE/UI/Refactorings/ExtractInterfaceDialog.Designer.cs +++ b/RetailCoder.VBE/UI/Refactorings/ExtractInterfaceDialog.Designer.cs @@ -56,21 +56,19 @@ private void InitializeComponent() this.flowLayoutPanel2.Controls.Add(this.OkButton); this.flowLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Bottom; this.flowLayoutPanel2.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; - this.flowLayoutPanel2.Location = new System.Drawing.Point(0, 364); - this.flowLayoutPanel2.Margin = new System.Windows.Forms.Padding(4); + this.flowLayoutPanel2.Location = new System.Drawing.Point(0, 296); this.flowLayoutPanel2.Name = "flowLayoutPanel2"; - this.flowLayoutPanel2.Padding = new System.Windows.Forms.Padding(11, 10, 0, 10); - this.flowLayoutPanel2.Size = new System.Drawing.Size(572, 53); + this.flowLayoutPanel2.Padding = new System.Windows.Forms.Padding(8, 8, 0, 8); + this.flowLayoutPanel2.Size = new System.Drawing.Size(459, 43); this.flowLayoutPanel2.TabIndex = 28; // // CancelDialogButton // this.CancelDialogButton.BackColor = System.Drawing.SystemColors.ButtonFace; this.CancelDialogButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.CancelDialogButton.Location = new System.Drawing.Point(457, 14); - this.CancelDialogButton.Margin = new System.Windows.Forms.Padding(4); + this.CancelDialogButton.Location = new System.Drawing.Point(373, 11); this.CancelDialogButton.Name = "CancelDialogButton"; - this.CancelDialogButton.Size = new System.Drawing.Size(100, 28); + this.CancelDialogButton.Size = new System.Drawing.Size(75, 23); this.CancelDialogButton.TabIndex = 5; this.CancelDialogButton.Text = "Cancel"; this.CancelDialogButton.UseVisualStyleBackColor = false; @@ -79,10 +77,9 @@ private void InitializeComponent() // this.OkButton.BackColor = System.Drawing.SystemColors.ButtonFace; this.OkButton.DialogResult = System.Windows.Forms.DialogResult.OK; - this.OkButton.Location = new System.Drawing.Point(349, 14); - this.OkButton.Margin = new System.Windows.Forms.Padding(4); + this.OkButton.Location = new System.Drawing.Point(292, 11); this.OkButton.Name = "OkButton"; - this.OkButton.Size = new System.Drawing.Size(100, 28); + this.OkButton.Size = new System.Drawing.Size(75, 23); this.OkButton.TabIndex = 4; this.OkButton.Text = "Ok"; this.OkButton.UseVisualStyleBackColor = false; @@ -90,8 +87,7 @@ private void InitializeComponent() // InvalidNameValidationIcon // this.InvalidNameValidationIcon.Image = global::Rubberduck.Properties.Resources.cross_circle; - this.InvalidNameValidationIcon.Location = new System.Drawing.Point(549, 102); - this.InvalidNameValidationIcon.Margin = new System.Windows.Forms.Padding(4); + this.InvalidNameValidationIcon.Location = new System.Drawing.Point(412, 83); this.InvalidNameValidationIcon.Name = "InvalidNameValidationIcon"; this.InvalidNameValidationIcon.Size = new System.Drawing.Size(16, 16); this.InvalidNameValidationIcon.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; @@ -100,19 +96,17 @@ private void InitializeComponent() // // InterfaceNameBox // - this.InterfaceNameBox.Location = new System.Drawing.Point(16, 112); - this.InterfaceNameBox.Margin = new System.Windows.Forms.Padding(4); + this.InterfaceNameBox.Location = new System.Drawing.Point(12, 91); this.InterfaceNameBox.Name = "InterfaceNameBox"; - this.InterfaceNameBox.Size = new System.Drawing.Size(541, 22); + this.InterfaceNameBox.Size = new System.Drawing.Size(437, 20); this.InterfaceNameBox.TabIndex = 0; // // NameLabel // this.NameLabel.AutoSize = true; - this.NameLabel.Location = new System.Drawing.Point(13, 91); - this.NameLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.NameLabel.Location = new System.Drawing.Point(10, 74); this.NameLabel.Name = "NameLabel"; - this.NameLabel.Size = new System.Drawing.Size(49, 17); + this.NameLabel.Size = new System.Drawing.Size(38, 13); this.NameLabel.TabIndex = 29; this.NameLabel.Text = "Name:"; // @@ -120,20 +114,20 @@ private void InitializeComponent() // this.TitleLabel.AutoSize = true; this.TitleLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Bold); - this.TitleLabel.Location = new System.Drawing.Point(20, 11); - this.TitleLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.TitleLabel.Location = new System.Drawing.Point(15, 9); this.TitleLabel.Name = "TitleLabel"; - this.TitleLabel.Padding = new System.Windows.Forms.Padding(3, 2, 3, 2); - this.TitleLabel.Size = new System.Drawing.Size(137, 22); + this.TitleLabel.Padding = new System.Windows.Forms.Padding(2, 2, 2, 2); + this.TitleLabel.Size = new System.Drawing.Size(115, 19); this.TitleLabel.TabIndex = 2; this.TitleLabel.Text = "Extract Interface"; // // InstructionsLabel // - this.InstructionsLabel.Location = new System.Drawing.Point(20, 30); + this.InstructionsLabel.Location = new System.Drawing.Point(15, 24); + this.InstructionsLabel.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.InstructionsLabel.Name = "InstructionsLabel"; - this.InstructionsLabel.Padding = new System.Windows.Forms.Padding(4); - this.InstructionsLabel.Size = new System.Drawing.Size(549, 34); + this.InstructionsLabel.Padding = new System.Windows.Forms.Padding(3, 3, 3, 3); + this.InstructionsLabel.Size = new System.Drawing.Size(412, 28); this.InstructionsLabel.TabIndex = 3; this.InstructionsLabel.Text = "Please specify interface name and members."; // @@ -144,9 +138,8 @@ private void InitializeComponent() this.DescriptionPanel.Controls.Add(this.InstructionsLabel); this.DescriptionPanel.Dock = System.Windows.Forms.DockStyle.Top; this.DescriptionPanel.Location = new System.Drawing.Point(0, 0); - this.DescriptionPanel.Margin = new System.Windows.Forms.Padding(4); this.DescriptionPanel.Name = "DescriptionPanel"; - this.DescriptionPanel.Size = new System.Drawing.Size(572, 84); + this.DescriptionPanel.Size = new System.Drawing.Size(459, 68); this.DescriptionPanel.TabIndex = 33; // // MembersGroupBox @@ -157,11 +150,11 @@ private void InitializeComponent() this.MembersGroupBox.Controls.Add(this.InterfaceMembersGridView); this.MembersGroupBox.Controls.Add(this.DeselectAllButton); this.MembersGroupBox.Controls.Add(this.SelectAllButton); - this.MembersGroupBox.Location = new System.Drawing.Point(16, 142); - this.MembersGroupBox.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.MembersGroupBox.Location = new System.Drawing.Point(12, 115); + this.MembersGroupBox.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.MembersGroupBox.Name = "MembersGroupBox"; - this.MembersGroupBox.Padding = new System.Windows.Forms.Padding(3, 2, 3, 2); - this.MembersGroupBox.Size = new System.Drawing.Size(541, 214); + this.MembersGroupBox.Padding = new System.Windows.Forms.Padding(2, 2, 2, 2); + this.MembersGroupBox.Size = new System.Drawing.Size(436, 174); this.MembersGroupBox.TabIndex = 1; this.MembersGroupBox.TabStop = false; this.MembersGroupBox.Text = "Members"; @@ -175,8 +168,8 @@ private void InitializeComponent() this.InterfaceMembersGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; this.InterfaceMembersGridView.ColumnHeadersVisible = false; this.InterfaceMembersGridView.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter; - this.InterfaceMembersGridView.Location = new System.Drawing.Point(7, 26); - this.InterfaceMembersGridView.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.InterfaceMembersGridView.Location = new System.Drawing.Point(5, 21); + this.InterfaceMembersGridView.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.InterfaceMembersGridView.MultiSelect = false; this.InterfaceMembersGridView.Name = "InterfaceMembersGridView"; this.InterfaceMembersGridView.RowHeadersVisible = false; @@ -184,25 +177,25 @@ private void InitializeComponent() this.InterfaceMembersGridView.RowTemplate.Height = 24; this.InterfaceMembersGridView.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; this.InterfaceMembersGridView.ShowEditingIcon = false; - this.InterfaceMembersGridView.Size = new System.Drawing.Size(427, 174); + this.InterfaceMembersGridView.Size = new System.Drawing.Size(320, 141); this.InterfaceMembersGridView.TabIndex = 1; // // DeselectAllButton // - this.DeselectAllButton.Location = new System.Drawing.Point(441, 64); - this.DeselectAllButton.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.DeselectAllButton.Location = new System.Drawing.Point(331, 52); + this.DeselectAllButton.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.DeselectAllButton.Name = "DeselectAllButton"; - this.DeselectAllButton.Size = new System.Drawing.Size(93, 32); + this.DeselectAllButton.Size = new System.Drawing.Size(100, 26); this.DeselectAllButton.TabIndex = 3; this.DeselectAllButton.Text = "Deselect All"; this.DeselectAllButton.UseVisualStyleBackColor = true; // // SelectAllButton // - this.SelectAllButton.Location = new System.Drawing.Point(441, 26); - this.SelectAllButton.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.SelectAllButton.Location = new System.Drawing.Point(331, 21); + this.SelectAllButton.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.SelectAllButton.Name = "SelectAllButton"; - this.SelectAllButton.Size = new System.Drawing.Size(93, 32); + this.SelectAllButton.Size = new System.Drawing.Size(100, 26); this.SelectAllButton.TabIndex = 2; this.SelectAllButton.Text = "Select All"; this.SelectAllButton.UseVisualStyleBackColor = true; @@ -210,10 +203,10 @@ private void InitializeComponent() // ExtractInterfaceDialog // this.AcceptButton = this.OkButton; - this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.CancelDialogButton; - this.ClientSize = new System.Drawing.Size(572, 417); + this.ClientSize = new System.Drawing.Size(459, 339); this.Controls.Add(this.MembersGroupBox); this.Controls.Add(this.DescriptionPanel); this.Controls.Add(this.InvalidNameValidationIcon); @@ -222,7 +215,7 @@ private void InitializeComponent() this.Controls.Add(this.flowLayoutPanel2); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); - this.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "ExtractInterfaceDialog"; diff --git a/Rubberduck.VBEEditor/Events/VBEEvents.cs b/Rubberduck.VBEEditor/Events/VBENativeServices.cs similarity index 83% rename from Rubberduck.VBEEditor/Events/VBEEvents.cs rename to Rubberduck.VBEEditor/Events/VBENativeServices.cs index 661d1bd067..2391e5c652 100644 --- a/Rubberduck.VBEEditor/Events/VBEEvents.cs +++ b/Rubberduck.VBEEditor/Events/VBENativeServices.cs @@ -9,7 +9,7 @@ namespace Rubberduck.VBEditor.Events { - public static class VBEEvents + public static class VBENativeServices { private static User32.WinEventProc _eventProc; private static IntPtr _eventHandle; @@ -66,7 +66,10 @@ public static void UnhookEvents() public static void VbeEventCallback(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) { - if (hwnd != IntPtr.Zero && idObject == (int)ObjId.Caret && eventType == (uint)WinEvent.ObjectLocationChange && hwnd.ToWindowType() == WindowType.VbaWindow) + if (hwnd != IntPtr.Zero && + idObject == (int)ObjId.Caret && + (eventType == (uint)WinEvent.ObjectLocationChange || eventType == (uint)WinEvent.ObjectCreate) && + hwnd.ToWindowType() == WindowType.VbaWindow) { OnSelectionChanged(hwnd); } @@ -83,6 +86,17 @@ public static void VbeEventCallback(IntPtr hWinEventHook, uint eventType, IntPtr DetachWindow(hwnd); } } + else if (eventType == (uint)WinEvent.ObjectFocus && idObject == (int)ObjId.Client) + { + //Test to see if it was a selection change in the project window. + var parent = User32.GetParent(hwnd); + if (parent != IntPtr.Zero && parent.ToWindowType() == WindowType.Project) + { + FocusDispatcher(_vbe, new WindowChangedEventArgs(parent, null, null, FocusType.ChildFocus)); + } + } + //This is an output window firehose, leave this here, but comment it out when done. + //if (idObject != (int)ObjId.Cursor) Debug.WriteLine("Hwnd: {0:X4} - EventType {1:X4}, idObject {2}, idChild {3}", (int)hwnd, eventType, idObject, idChild); } private static void AttachWindow(IntPtr hwnd) @@ -176,15 +190,21 @@ public enum WindowType { Indeterminate, VbaWindow, - DesignerWindow + DesignerWindow, + Project } public static WindowType ToWindowType(this IntPtr hwnd) + { + WindowType id; + return Enum.TryParse(hwnd.ToClassName(), true, out id) ? id : WindowType.Indeterminate; + } + + public static string ToClassName(this IntPtr hwnd) { var name = new StringBuilder(128); User32.GetClassName(hwnd, name, name.Capacity); - WindowType id; - return Enum.TryParse(name.ToString(), out id) ? id : WindowType.Indeterminate; + return name.ToString(); } } } diff --git a/Rubberduck.VBEEditor/Events/WindowChangedEventArgs.cs b/Rubberduck.VBEEditor/Events/WindowChangedEventArgs.cs index 0d0bf99611..6357f23824 100644 --- a/Rubberduck.VBEEditor/Events/WindowChangedEventArgs.cs +++ b/Rubberduck.VBEEditor/Events/WindowChangedEventArgs.cs @@ -3,14 +3,15 @@ namespace Rubberduck.VBEditor.Events { - public class WindowChangedEventArgs : EventArgs + public enum FocusType { - public enum FocusType - { - GotFocus, - LostFocus - } + GotFocus, + LostFocus, + ChildFocus + } + public class WindowChangedEventArgs : EventArgs + { public IntPtr Hwnd { get; private set; } public IWindow Window { get; private set; } public ICodePane CodePane { get; private set; } diff --git a/Rubberduck.VBEEditor/Rubberduck.VBEditor.csproj b/Rubberduck.VBEEditor/Rubberduck.VBEditor.csproj index bb6b162fe0..672bc3ae6a 100644 --- a/Rubberduck.VBEEditor/Rubberduck.VBEditor.csproj +++ b/Rubberduck.VBEEditor/Rubberduck.VBEditor.csproj @@ -125,7 +125,7 @@ - + diff --git a/Rubberduck.VBEEditor/SafeComWrappers/Abstract/IVBE.cs b/Rubberduck.VBEEditor/SafeComWrappers/Abstract/IVBE.cs index 0478864e60..5c1aa50b79 100644 --- a/Rubberduck.VBEEditor/SafeComWrappers/Abstract/IVBE.cs +++ b/Rubberduck.VBEEditor/SafeComWrappers/Abstract/IVBE.cs @@ -21,6 +21,7 @@ public interface IVBE : ISafeComWrapper, IEquatable IWindows Windows { get; } IHostApplication HostApplication(); + IWindow ActiveMDIChild(); bool IsInDesignMode { get; } } diff --git a/Rubberduck.VBEEditor/SafeComWrappers/VB6/VBE.cs b/Rubberduck.VBEEditor/SafeComWrappers/VB6/VBE.cs index 10515004fd..9277e8f2fe 100644 --- a/Rubberduck.VBEEditor/SafeComWrappers/VB6/VBE.cs +++ b/Rubberduck.VBEEditor/SafeComWrappers/VB6/VBE.cs @@ -109,6 +109,11 @@ public IHostApplication HostApplication() return null; } + public IWindow ActiveMDIChild() + { + throw new NotImplementedException(); + } + public bool IsInDesignMode { get { return VBProjects.All(project => project.Mode == EnvironmentMode.Design); } diff --git a/Rubberduck.VBEEditor/SafeComWrappers/VBA/VBE.cs b/Rubberduck.VBEEditor/SafeComWrappers/VBA/VBE.cs index 57fef9c3ea..11cf2309a0 100644 --- a/Rubberduck.VBEEditor/SafeComWrappers/VBA/VBE.cs +++ b/Rubberduck.VBEEditor/SafeComWrappers/VBA/VBE.cs @@ -3,11 +3,13 @@ using System.IO; using System.Linq; using System.Runtime.InteropServices; +using System.Text; using Rubberduck.VBEditor.Application; using Rubberduck.VBEditor.SafeComWrappers.Abstract; using Rubberduck.VBEditor.SafeComWrappers.MSForms; using Rubberduck.VBEditor.SafeComWrappers.Office.Core; using Rubberduck.VBEditor.SafeComWrappers.Office.Core.Abstract; +using Rubberduck.VBEditor.WindowsApi; using VB = Microsoft.Vbe.Interop; namespace Rubberduck.VBEditor.SafeComWrappers.VBA @@ -279,6 +281,33 @@ public IHostApplication HostApplication() return null; } + /// Returns the topmost MDI child window. + public IWindow ActiveMDIChild() + { + const string mdiClientClass = "MDIClient"; + const int maxCaptionLength = 512; + + IntPtr mainWindow = (IntPtr)MainWindow.HWnd; + + IntPtr mdiClient = NativeMethods.FindWindowEx(mainWindow, IntPtr.Zero, mdiClientClass, string.Empty); + + IntPtr mdiChild = NativeMethods.GetTopWindow(mdiClient); + StringBuilder mdiChildCaption = new StringBuilder(); + int captionLength = NativeMethods.GetWindowText(mdiChild, mdiChildCaption, maxCaptionLength); + + if (captionLength > 0) + { + try + { + return Windows.FirstOrDefault(win => win.Caption == mdiChildCaption.ToString()); + } + catch + { + } + } + return null; + } + /// Returns whether the host supports unit tests. public bool HostSupportsUnitTests() { diff --git a/Rubberduck.VBEEditor/WindowsApi/CodePaneSubclass.cs b/Rubberduck.VBEEditor/WindowsApi/CodePaneSubclass.cs index 5eee5941bf..46bb11e003 100644 --- a/Rubberduck.VBEEditor/WindowsApi/CodePaneSubclass.cs +++ b/Rubberduck.VBEEditor/WindowsApi/CodePaneSubclass.cs @@ -16,9 +16,9 @@ internal CodePaneSubclass(IntPtr hwnd, ICodePane pane) : base(hwnd) _pane = pane; } - protected override void DispatchFocusEvent(WindowChangedEventArgs.FocusType type) + protected override void DispatchFocusEvent(FocusType type) { - var window = VBEEvents.GetWindowInfoFromHwnd(Hwnd); + var window = VBENativeServices.GetWindowInfoFromHwnd(Hwnd); if (window == null) { return; diff --git a/Rubberduck.VBEEditor/WindowsApi/DesignerWindowSubclass.cs b/Rubberduck.VBEEditor/WindowsApi/DesignerWindowSubclass.cs index 9edf56ec2b..799d184b0b 100644 --- a/Rubberduck.VBEEditor/WindowsApi/DesignerWindowSubclass.cs +++ b/Rubberduck.VBEEditor/WindowsApi/DesignerWindowSubclass.cs @@ -1,4 +1,7 @@ using System; +using System.Diagnostics; +using Rubberduck.Common.WinAPI; +using Rubberduck.VBEditor.Events; namespace Rubberduck.VBEditor.WindowsApi { @@ -6,5 +9,18 @@ internal class DesignerWindowSubclass : FocusSource { //Stub for designer window replacement. :-) internal DesignerWindowSubclass(IntPtr hwnd) : base(hwnd) { } + + public override int SubClassProc(IntPtr hWnd, IntPtr msg, IntPtr wParam, IntPtr lParam, IntPtr uIdSubclass, IntPtr dwRefData) + { + //Any time the selected control changes in the hosted userform, the F3 Overlay has to be redrawn. This is a good proxy + //for child control selections, so raise a focus event. + if ((int) msg == (int)WM.ERASEBKGND) + { + DispatchFocusEvent(FocusType.GotFocus); + } + //This is an output window firehose, leave this here, but comment it out when done. + //Debug.WriteLine("WM: {0:X4}, wParam {1}, lParam {2}", msg, wParam, lParam); + return base.SubClassProc(hWnd, msg, wParam, lParam, uIdSubclass, dwRefData); + } } } diff --git a/Rubberduck.VBEEditor/WindowsApi/FocusSource.cs b/Rubberduck.VBEEditor/WindowsApi/FocusSource.cs index 9c6539a543..9b4e1f35f4 100644 --- a/Rubberduck.VBEEditor/WindowsApi/FocusSource.cs +++ b/Rubberduck.VBEEditor/WindowsApi/FocusSource.cs @@ -17,9 +17,9 @@ protected void OnFocusChange(WindowChangedEventArgs eventArgs) } } - protected virtual void DispatchFocusEvent(WindowChangedEventArgs.FocusType type) + protected virtual void DispatchFocusEvent(FocusType type) { - var window = VBEEvents.GetWindowInfoFromHwnd(Hwnd); + var window = VBENativeServices.GetWindowInfoFromHwnd(Hwnd); if (window == null) { return; @@ -33,10 +33,10 @@ public override int SubClassProc(IntPtr hWnd, IntPtr msg, IntPtr wParam, IntPtr { case (uint)WM.SETFOCUS: - DispatchFocusEvent(WindowChangedEventArgs.FocusType.GotFocus); + DispatchFocusEvent(FocusType.GotFocus); break; case (uint)WM.KILLFOCUS: - DispatchFocusEvent(WindowChangedEventArgs.FocusType.LostFocus); + DispatchFocusEvent(FocusType.LostFocus); break; } return base.SubClassProc(hWnd, msg, wParam, lParam, uIdSubclass, dwRefData); diff --git a/Rubberduck.VBEEditor/WindowsApi/NativeMethods.cs b/Rubberduck.VBEEditor/WindowsApi/NativeMethods.cs index 43f7dd7846..8b537f05b3 100644 --- a/Rubberduck.VBEEditor/WindowsApi/NativeMethods.cs +++ b/Rubberduck.VBEEditor/WindowsApi/NativeMethods.cs @@ -49,6 +49,16 @@ public static class NativeMethods internal static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); + /// Gets the child window at the top of the Z order. + /// + /// The window handle. + /// The child window IntPtr handle. + [DllImport("user32.dll")] + internal static extern IntPtr GetTopWindow(IntPtr hWnd); + + [DllImport("user32.dll", CharSet = CharSet.Unicode)] + internal static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string lclassName, string windowTitle); + /// Gets window caption text by handle. /// /// Handle of the window to be activated. @@ -69,15 +79,6 @@ public static string GetWindowText(this IntPtr windowHandle) return result; } - /// Gets the parent window of this item. - /// - /// The window handle. - /// The parent window IntPtr handle. - [DllImport("User32.dll")] - internal static extern IntPtr GetParent(IntPtr hWnd); - - - /// Activates the window by simulating a click. /// /// Handle of the window to be activated. diff --git a/Rubberduck.VBEEditor/WindowsApi/User32.cs b/Rubberduck.VBEEditor/WindowsApi/User32.cs index dc6a2f1170..9b182a2d70 100644 --- a/Rubberduck.VBEEditor/WindowsApi/User32.cs +++ b/Rubberduck.VBEEditor/WindowsApi/User32.cs @@ -66,5 +66,12 @@ public static class User32 /// The length of the returned class name (without the null terminator), zero on error. [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); + + /// Gets the parent window of this item. + /// + /// The window handle. + /// The parent window IntPtr handle. + [DllImport("User32.dll")] + internal static extern IntPtr GetParent(IntPtr hWnd); } }