diff --git a/ILSpy/Commands/DecompileInNewViewCommand.cs b/ILSpy/Commands/DecompileInNewViewCommand.cs index 3069d7c6b..152d0f8dd 100644 --- a/ILSpy/Commands/DecompileInNewViewCommand.cs +++ b/ILSpy/Commands/DecompileInNewViewCommand.cs @@ -28,7 +28,7 @@ using ICSharpCode.ILSpy.TreeNodes; namespace ICSharpCode.ILSpy.Commands { - [ExportContextMenuEntry(Header = nameof(Resources.DecompileToNewPanel), Icon = "images/Search", Category = nameof(Resources.Analyze), Order = 90)] + [ExportContextMenuEntry(Header = nameof(Resources.DecompileToNewPanel), InputGestureText = "MMB", Icon = "images/Search", Category = nameof(Resources.Analyze), Order = 90)] internal sealed class DecompileInNewViewCommand : IContextMenuEntry { public bool IsVisible(TextViewContext context) diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 51b0f2438..a085fea59 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -875,7 +875,12 @@ namespace ICSharpCode.ILSpy public void JumpToReference(object reference) { - JumpToReferenceAsync(reference).HandleExceptions(); + JumpToReference(reference, inNewTabPage: false); + } + + public void JumpToReference(object reference, bool inNewTabPage) + { + JumpToReferenceAsync(reference, inNewTabPage).HandleExceptions(); } /// @@ -886,6 +891,11 @@ namespace ICSharpCode.ILSpy /// The task will be marked as canceled if the decompilation is canceled. /// public Task JumpToReferenceAsync(object reference) + { + return JumpToReferenceAsync(reference, inNewTabPage: false); + } + + public Task JumpToReferenceAsync(object reference, bool inNewTabPage) { decompilationTask = TaskHelper.CompletedTask; switch (reference) { @@ -900,7 +910,7 @@ namespace ICSharpCode.ILSpy foreach (var handler in protocolHandlers) { var node = handler.Value.Resolve(protocol, file, unresolvedEntity.Handle, out bool newTabPage); if (node != null) { - SelectNode(node, newTabPage); + SelectNode(node, newTabPage || inNewTabPage); return decompilationTask; } } @@ -915,7 +925,7 @@ namespace ICSharpCode.ILSpy default: ILSpyTreeNode treeNode = FindTreeNode(reference); if (treeNode != null) - SelectNode(treeNode); + SelectNode(treeNode, inNewTabPage); break; } return decompilationTask; diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 5a66a16ea..e26704dde 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -820,7 +820,7 @@ namespace ICSharpCode.ILSpy.TextView /// /// Jumps to the definition referred to by the . /// - internal void JumpToReference(ReferenceSegment referenceSegment) + internal void JumpToReference(ReferenceSegment referenceSegment, bool openInNewTab) { object reference = referenceSegment.Reference; if (referenceSegment.IsLocal) { @@ -849,7 +849,7 @@ namespace ICSharpCode.ILSpy.TextView return; } } - MainWindow.Instance.JumpToReference(reference); + MainWindow.Instance.JumpToReference(reference, openInNewTab); } Point? mouseDownPos; @@ -866,7 +866,7 @@ namespace ICSharpCode.ILSpy.TextView Vector dragDistance = e.GetPosition(this) - mouseDownPos.Value; if (Math.Abs(dragDistance.X) < SystemParameters.MinimumHorizontalDragDistance && Math.Abs(dragDistance.Y) < SystemParameters.MinimumVerticalDragDistance - && e.ChangedButton == MouseButton.Left) + && (e.ChangedButton == MouseButton.Left || e.ChangedButton == MouseButton.Middle)) { // click without moving mouse var referenceSegment = GetReferenceSegmentAtMousePosition(); @@ -878,7 +878,7 @@ namespace ICSharpCode.ILSpy.TextView // cursor position and the mouse position. textEditor.TextArea.MouseSelectionMode = MouseSelectionMode.None; - JumpToReference(referenceSegment); + JumpToReference(referenceSegment, e.ChangedButton == MouseButton.Middle || Keyboard.Modifiers.HasFlag(ModifierKeys.Shift)); } } } diff --git a/ILSpy/TreeNodes/ILSpyTreeNode.cs b/ILSpy/TreeNodes/ILSpyTreeNode.cs index 26fda113b..b3463b956 100644 --- a/ILSpy/TreeNodes/ILSpyTreeNode.cs +++ b/ILSpy/TreeNodes/ILSpyTreeNode.cs @@ -16,9 +16,12 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System; using System.Collections.Specialized; using System.ComponentModel; using System.Linq; +using System.Windows; +using System.Windows.Threading; using ICSharpCode.Decompiler; using ICSharpCode.TreeView; @@ -69,6 +72,12 @@ namespace ICSharpCode.ILSpy.TreeNodes return false; } + public override void ActivateItemSecondary(RoutedEventArgs e) + { + MainWindow.Instance.SelectNode(this, inNewTabPage: true); + MainWindow.Instance.Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)MainWindow.Instance.RefreshDecompiledView); + } + /// /// Used to implement special save logic for some items. /// This method is called on the main thread when only a single item is selected. diff --git a/SharpTreeView/SharpTreeNode.cs b/SharpTreeView/SharpTreeNode.cs index 3bffb065b..4606e0b16 100644 --- a/SharpTreeView/SharpTreeNode.cs +++ b/SharpTreeView/SharpTreeNode.cs @@ -24,6 +24,7 @@ using System.Windows; using System.ComponentModel; using System.Collections.Specialized; using System.Windows.Media; +using System.Windows.Input; namespace ICSharpCode.TreeView { @@ -680,7 +681,14 @@ namespace ICSharpCode.TreeView public virtual void ActivateItem(RoutedEventArgs e) { } - + + /// + /// Gets called when the item is clicked with the middle mouse button. + /// + public virtual void ActivateItemSecondary(RoutedEventArgs e) + { + } + public override string ToString() { // used for keyboard navigation diff --git a/SharpTreeView/SharpTreeViewItem.cs b/SharpTreeView/SharpTreeViewItem.cs index 354b58917..dc6e85964 100644 --- a/SharpTreeView/SharpTreeViewItem.cs +++ b/SharpTreeView/SharpTreeViewItem.cs @@ -119,8 +119,17 @@ namespace ICSharpCode.TreeView } } + protected override void OnMouseUp(MouseButtonEventArgs e) + { + if (e.ChangedButton == MouseButton.Middle) { + Node.ActivateItemSecondary(e); + } else { + base.OnMouseUp(e); + } + } + #endregion - + #region Drag and Drop protected override void OnDragEnter(DragEventArgs e)