Skip to content

Commit 612c5bc

Browse files
Merge pull request #3591 from miloush/history
Navigate visible history
2 parents f8b4823 + c80c7de commit 612c5bc

39 files changed

+256
-22
lines changed

ICSharpCode.Decompiler/Metadata/MetadataFile.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,12 @@ public string Name {
7373
if (value == null)
7474
{
7575
var metadata = Metadata;
76-
value = metadata.IsAssembly
77-
? metadata.GetString(metadata.GetAssemblyDefinition().Name)
78-
: metadata.GetString(metadata.GetModuleDefinition().Name);
76+
if (metadata.IsAssembly)
77+
value = metadata.GetString(metadata.GetAssemblyDefinition().Name);
78+
else if (metadata.DebugMetadataHeader == null) // standalone debug metadata does not contain module table
79+
value = metadata.GetString(metadata.GetModuleDefinition().Name);
80+
else
81+
value = "debug metadata";
7982
value = LazyInit.GetOrSet(ref name, value);
8083
}
8184
return value;

ICSharpCode.ILSpyX/TreeView/SharpTreeNode.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ public virtual object? Text {
126126
get { return null; }
127127
}
128128

129+
public virtual object? NavigationText {
130+
get { return Text; }
131+
}
132+
129133
public virtual object? Icon {
130134
get { return null; }
131135
}

ILSpy/AssemblyTree/AssemblyTreeModel.cs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -853,15 +853,24 @@ public void RefreshDecompiledView()
853853

854854
#endregion
855855

856-
public void NavigateHistory(bool forward)
856+
public void NavigateHistory(bool forward, NavigationState? toState = null)
857857
{
858858
try
859859
{
860860
TabPageModel tabPage = DockWorkspace.ActiveTabPage;
861-
var state = tabPage.GetState();
862-
if (state != null)
863-
history.UpdateCurrent(new NavigationState(tabPage, state));
864-
var newState = forward ? history.GoForward() : history.GoBack();
861+
var currentState = tabPage.GetState();
862+
if (currentState != null)
863+
history.UpdateCurrent(new NavigationState(tabPage, currentState));
864+
865+
NavigationState newState;
866+
do
867+
{
868+
newState = forward ? history.GoForward() : history.GoBack();
869+
} while (newState != null && toState != null && toState != newState);
870+
871+
if (newState == null)
872+
return;
873+
865874
navigatingToState = newState;
866875

867876
TabPageModel activeTabPage = newState.TabPage;
@@ -884,6 +893,8 @@ public void NavigateHistory(bool forward)
884893
}
885894
}
886895

896+
public NavigationState[] GetNavigateHistory(bool forward) => forward ? history.ForwardList : history.BackList;
897+
887898
public bool CanNavigateBack => history.CanNavigateBack;
888899

889900
public bool CanNavigateForward => history.CanNavigateForward;

ILSpy/Commands/BrowseBackCommand.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
1717
// DEALINGS IN THE SOFTWARE.
1818

19+
using System.Collections;
1920
using System.Composition;
21+
using System.Linq;
2022
using System.Windows.Input;
2123

2224
using ICSharpCode.ILSpy.AssemblyTree;
@@ -26,7 +28,7 @@ namespace ICSharpCode.ILSpy
2628
{
2729
[ExportToolbarCommand(ToolTip = nameof(Resources.Back), ToolbarIcon = "Images/Back", ToolbarCategory = nameof(Resources.Navigation), ToolbarOrder = 0)]
2830
[Shared]
29-
sealed class BrowseBackCommand : CommandWrapper
31+
sealed class BrowseBackCommand : CommandWrapper, IProvideParameterList
3032
{
3133
readonly AssemblyTreeModel assemblyTreeModel;
3234

@@ -49,8 +51,15 @@ protected override void OnExecute(object sender, ExecutedRoutedEventArgs e)
4951
if (assemblyTreeModel.CanNavigateBack)
5052
{
5153
e.Handled = true;
52-
assemblyTreeModel.NavigateHistory(false);
54+
assemblyTreeModel.NavigateHistory(false, e.Parameter as NavigationState);
5355
}
5456
}
57+
58+
public IEnumerable ParameterList => assemblyTreeModel.GetNavigateHistory(false).Reverse();
59+
60+
public object GetParameterText(object parameter)
61+
{
62+
return (parameter as NavigationState)?.NavigationText;
63+
}
5564
}
5665
}

ILSpy/Commands/BrowseForwardCommand.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
1717
// DEALINGS IN THE SOFTWARE.
1818

19+
using System.Collections;
1920
using System.Composition;
21+
using System.Linq;
2022
using System.Windows.Input;
2123

2224
using ICSharpCode.ILSpy.AssemblyTree;
@@ -26,7 +28,7 @@ namespace ICSharpCode.ILSpy
2628
{
2729
[ExportToolbarCommand(ToolTip = nameof(Resources.Forward), ToolbarIcon = "Images/Forward", ToolbarCategory = nameof(Resources.Navigation), ToolbarOrder = 1)]
2830
[Shared]
29-
sealed class BrowseForwardCommand : CommandWrapper
31+
sealed class BrowseForwardCommand : CommandWrapper, IProvideParameterList
3032
{
3133
private readonly AssemblyTreeModel assemblyTreeModel;
3234

@@ -49,9 +51,15 @@ protected override void OnExecute(object sender, ExecutedRoutedEventArgs e)
4951
if (assemblyTreeModel.CanNavigateForward)
5052
{
5153
e.Handled = true;
52-
assemblyTreeModel.NavigateHistory(true);
54+
assemblyTreeModel.NavigateHistory(true, e.Parameter as NavigationState);
5355
}
5456
}
5557

58+
public IEnumerable ParameterList => assemblyTreeModel.GetNavigateHistory(true).Reverse();
59+
60+
public object GetParameterText(object parameter)
61+
{
62+
return (parameter as NavigationState)?.NavigationText;
63+
}
5664
}
5765
}

ILSpy/Commands/SimpleCommand.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
// DEALINGS IN THE SOFTWARE.
1818

1919
using System;
20+
using System.Collections;
2021
using System.Windows.Data;
2122
using System.Windows.Input;
2223

@@ -41,4 +42,10 @@ public interface IProvideParameterBinding
4142
{
4243
Binding ParameterBinding { get; }
4344
}
45+
46+
public interface IProvideParameterList
47+
{
48+
IEnumerable ParameterList { get; }
49+
object GetParameterText(object parameter);
50+
}
4451
}

ILSpy/Controls/MainToolBar.xaml.cs

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,20 @@
1616
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
1717
// DEALINGS IN THE SOFTWARE.
1818

19+
using System;
20+
using System.ComponentModel;
1921
using System.Composition;
2022
using System.Linq;
2123
using System.Windows;
2224
using System.Windows.Controls;
2325
using System.Windows.Controls.Primitives;
2426
using System.Windows.Data;
2527
using System.Windows.Input;
28+
using System.Windows.Media;
2629
using System.Windows.Threading;
2730

2831
using ICSharpCode.ILSpy.Themes;
32+
using ICSharpCode.ILSpyX.TreeView;
2933

3034
using TomsToolbox.Composition;
3135

@@ -85,7 +89,8 @@ static void InitToolbar(ToolBar toolBar, IExportProvider exportProvider)
8589
}
8690
}
8791

88-
static Button CreateToolbarItem(IExport<ICommand, IToolbarCommandMetadata> commandExport)
92+
93+
static UIElement CreateToolbarItem(IExport<ICommand, IToolbarCommandMetadata> commandExport)
8994
{
9095
var command = commandExport.Value;
9196

@@ -108,9 +113,106 @@ static Button CreateToolbarItem(IExport<ICommand, IToolbarCommandMetadata> comma
108113
parameterBinding.ParameterBinding);
109114
}
110115

116+
if (command is IProvideParameterList parameterList)
117+
{
118+
toolbarItem.Margin = new Thickness(2, 0, 0, 0);
119+
120+
var dropDownPanel = new StackPanel { Orientation = Orientation.Horizontal };
121+
122+
var dropDownToggle = new ToggleButton {
123+
Style = ThemeManager.Current.CreateToolBarToggleButtonStyle(),
124+
Content = "▾",
125+
Padding = new Thickness(0),
126+
MinWidth = 0,
127+
Margin = new Thickness(0, 0, 2, 0)
128+
};
129+
130+
var contextMenu = new ContextMenu {
131+
PlacementTarget = dropDownPanel,
132+
Tag = command
133+
};
134+
135+
ContextMenuService.SetPlacement(toolbarItem, PlacementMode.Bottom);
136+
toolbarItem.ContextMenu = contextMenu;
137+
toolbarItem.ContextMenuOpening += (_, _) =>
138+
PrepareParameterList(contextMenu);
139+
dropDownToggle.Checked += (_, _) => {
140+
PrepareParameterList(contextMenu);
141+
contextMenu.Placement = PlacementMode.Bottom;
142+
contextMenu.SetCurrentValue(ContextMenu.IsOpenProperty, true);
143+
};
144+
145+
BindingOperations.SetBinding(dropDownToggle, ToggleButton.IsCheckedProperty,
146+
new Binding(nameof(contextMenu.IsOpen)) { Source = contextMenu });
147+
148+
BindingOperations.SetBinding(dropDownToggle, IsEnabledProperty,
149+
new Binding(nameof(IsEnabled)) { Source = toolbarItem });
150+
151+
// When the toggle button is checked, clicking it to uncheck will dismiss the menu first
152+
// which unchecks the toggle button via binding above and the click is used to open it again.
153+
// This is a workaround to ignore the click to uncheck the already unchecked toggle button.
154+
// We have to ensure the dismissing click is on the toggle button, otherwise the flag
155+
// will not get cleared and menu will not open next time.
156+
Mouse.AddPreviewMouseDownOutsideCapturedElementHandler(contextMenu, (_, e) => {
157+
var point = e.GetPosition(dropDownToggle);
158+
dropDownToggle.Tag = dropDownToggle.InputHitTest(point);
159+
});
160+
dropDownToggle.PreviewMouseLeftButtonDown += (_, e) => {
161+
e.Handled = dropDownToggle.Tag != null;
162+
dropDownToggle.Tag = null;
163+
};
164+
165+
dropDownPanel.Children.Add(toolbarItem);
166+
dropDownPanel.Children.Add(dropDownToggle);
167+
return dropDownPanel;
168+
}
169+
111170
return toolbarItem;
112171
}
113172

173+
static void PrepareParameterList(ContextMenu menu)
174+
{
175+
const int maximumParameterListCount = 20;
176+
177+
var command = (ICommand)menu.Tag;
178+
var parameterList = (IProvideParameterList)command;
179+
180+
menu.Items.Clear();
181+
foreach (var parameter in parameterList.ParameterList)
182+
{
183+
MenuItem parameterItem = new MenuItem();
184+
parameterItem.Command = CommandWrapper.Unwrap(command);
185+
parameterItem.CommandParameter = parameter;
186+
parameterItem.CommandTarget = menu.PlacementTarget;
187+
parameterItem.InputGestureText = " ";
188+
189+
var headerPresenter = new ContentPresenter { RecognizesAccessKey = false };
190+
parameterItem.Header = headerPresenter;
191+
192+
var header = parameterList.GetParameterText(parameter);
193+
switch (header)
194+
{
195+
case SharpTreeNode node:
196+
headerPresenter.Content = node.NavigationText;
197+
if (node.Icon is ImageSource icon)
198+
parameterItem.Icon = new Image {
199+
Width = 16,
200+
Height = 16,
201+
Source = icon
202+
};
203+
break;
204+
205+
default:
206+
headerPresenter.Content = header;
207+
break;
208+
}
209+
210+
menu.Items.Add(parameterItem);
211+
if (menu.Items.Count >= maximumParameterListCount)
212+
break;
213+
}
214+
}
215+
114216
void MainWindow_KeyDown(object sender, KeyEventArgs e)
115217
{
116218
if (e.Handled || e.KeyboardDevice.Modifiers != ModifierKeys.Alt || e.Key != Key.System)

ILSpy/Metadata/CoffHeaderTreeNode.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ public CoffHeaderTreeNode(PEFile module)
4343

4444
public override object Text => "COFF Header";
4545

46+
public override object NavigationText => $"{Text} ({module.Name})";
47+
4648
public override object Icon => Images.Header;
4749

4850
public override bool View(TabPageModel tabPage)

ILSpy/Metadata/DataDirectoriesTreeNode.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ public DataDirectoriesTreeNode(PEFile module)
3535

3636
public override object Text => "Data Directories";
3737

38+
public override object NavigationText => $"{Text} ({module.Name})";
39+
3840
public override object Icon => Images.ListFolder;
3941
public override object ExpandedIcon => Images.ListFolderOpen;
4042

ILSpy/Metadata/DebugDirectory/DebugDirectoryEntryTreeNode.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ public DebugDirectoryEntryTreeNode(PEFile module, DebugDirectoryEntry entry)
4040

4141
override public object Text => entry.Type.ToString();
4242

43+
public override object NavigationText => $"{Text} ({module.Name})";
44+
4345
public override object Icon => Images.MetadataTable;
4446

4547
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)

0 commit comments

Comments
 (0)