Browse Source

Move menu/toolbar logic from MainWindow to separate service.

pull/3274/head
tom-englert 9 months ago committed by tom-englert
parent
commit
f8883211b8
  1. 17
      ILSpy/AssemblyTree/AssemblyListPaneModel.cs
  2. 10
      ILSpy/Commands/DecompileAllCommand.cs
  3. 15
      ILSpy/Commands/DisassembleAllCommand.cs
  4. 6
      ILSpy/Commands/GeneratePdbContextMenuEntry.cs
  5. 11
      ILSpy/Commands/SaveCodeContextMenuEntry.cs
  6. 2
      ILSpy/ContextMenuEntry.cs
  7. 7
      ILSpy/Docking/DockWorkspace.cs
  8. 6
      ILSpy/Languages/ILLanguage.cs
  9. 432
      ILSpy/MainWindow.xaml.cs
  10. 2
      ILSpy/Options/OptionsDialog.xaml.cs
  11. 3
      ILSpy/SolutionWriter.cs
  12. 3
      ILSpy/TreeNodes/AssemblyTreeNode.cs
  13. 357
      ILSpy/Util/MenuService.cs
  14. 15
      ILSpy/Util/ResourceHelper.cs
  15. 10
      ILSpy/Util/SettingsService.cs

17
ILSpy/AssemblyTree/AssemblyListPaneModel.cs

@ -766,20 +766,21 @@ namespace ICSharpCode.ILSpy.AssemblyTree @@ -766,20 +766,21 @@ namespace ICSharpCode.ILSpy.AssemblyTree
if (SelectedItems.Count == 0 && refreshInProgress)
return;
var activeTabPage = DockWorkspace.Instance.ActiveTabPage;
if (recordHistory)
{
var tabPage = DockWorkspace.Instance.ActiveTabPage;
var currentState = tabPage.GetState();
var currentState = activeTabPage.GetState();
if (currentState != null)
history.UpdateCurrent(new NavigationState(tabPage, currentState));
history.Record(new NavigationState(tabPage, SelectedItems));
history.UpdateCurrent(new NavigationState(activeTabPage, currentState));
history.Record(new NavigationState(activeTabPage, SelectedItems));
}
DockWorkspace.Instance.ActiveTabPage.SupportsLanguageSwitching = true;
activeTabPage.SupportsLanguageSwitching = true;
if (SelectedItems.Count == 1)
{
if (SelectedItem is ILSpyTreeNode node && node.View(DockWorkspace.Instance.ActiveTabPage))
if (SelectedItem is ILSpyTreeNode node && node.View(activeTabPage))
return;
}
if (newState?.ViewedUri != null)
@ -787,9 +788,9 @@ namespace ICSharpCode.ILSpy.AssemblyTree @@ -787,9 +788,9 @@ namespace ICSharpCode.ILSpy.AssemblyTree
MainWindow.Instance.NavigateTo(new(newState.ViewedUri, null), recordHistory: false);
return;
}
var options = MainWindow.Instance.CreateDecompilationOptions();
var options = SettingsService.Instance.CreateDecompilationOptions(activeTabPage);
options.TextViewState = newState;
DockWorkspace.Instance.ActiveTabPage.ShowTextViewAsync(textView => textView.DecompileAsync(this.CurrentLanguage, this.SelectedNodes, options));
activeTabPage.ShowTextViewAsync(textView => textView.DecompileAsync(this.CurrentLanguage, this.SelectedNodes, options));
}
public void RefreshDecompiledView()

10
ILSpy/Commands/DecompileAllCommand.cs

@ -26,6 +26,7 @@ using System.Linq; @@ -26,6 +26,7 @@ using System.Linq;
using System.Threading.Tasks;
using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy.Docking;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpyX;
@ -59,7 +60,7 @@ namespace ICSharpCode.ILSpy @@ -59,7 +60,7 @@ namespace ICSharpCode.ILSpy
{
try
{
var options = MainWindow.Instance.CreateDecompilationOptions();
var options = SettingsService.Instance.CreateDecompilationOptions(DockWorkspace.Instance.ActiveTabPage);
options.CancellationToken = ct;
options.FullDecompilation = true;
new CSharpLanguage().DecompileAssembly(asm, new PlainTextOutput(writer), options);
@ -96,8 +97,9 @@ namespace ICSharpCode.ILSpy @@ -96,8 +97,9 @@ namespace ICSharpCode.ILSpy
const int numRuns = 100;
var language = SettingsService.Instance.SessionSettings.LanguageSettings.Language;
var nodes = MainWindow.Instance.AssemblyTreeModel.SelectedNodes.ToArray();
var options = MainWindow.Instance.CreateDecompilationOptions();
Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
DockWorkspace dockWorkspace = DockWorkspace.Instance;
var options = SettingsService.Instance.CreateDecompilationOptions(dockWorkspace.ActiveTabPage);
dockWorkspace.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
options.CancellationToken = ct;
Stopwatch w = Stopwatch.StartNew();
for (int i = 0; i < numRuns; ++i)
@ -112,7 +114,7 @@ namespace ICSharpCode.ILSpy @@ -112,7 +114,7 @@ namespace ICSharpCode.ILSpy
double msPerRun = w.Elapsed.TotalMilliseconds / numRuns;
output.Write($"Average time: {msPerRun.ToString("f1")}ms\n");
return output;
}, ct)).Then(output => Docking.DockWorkspace.Instance.ShowText(output)).HandleExceptions();
}, ct)).Then(output => dockWorkspace.ShowText(output)).HandleExceptions();
}
}
}

15
ILSpy/Commands/DisassembleAllCommand.cs

@ -26,7 +26,6 @@ using System.Threading.Tasks; @@ -26,7 +26,6 @@ using System.Threading.Tasks;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpyX;
namespace ICSharpCode.ILSpy
{
@ -41,12 +40,14 @@ namespace ICSharpCode.ILSpy @@ -41,12 +40,14 @@ namespace ICSharpCode.ILSpy
public override void Execute(object parameter)
{
Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
AvalonEditTextOutput output = new AvalonEditTextOutput();
var dockWorkspace = Docking.DockWorkspace.Instance;
dockWorkspace.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
AvalonEditTextOutput output = new();
Parallel.ForEach(
Partitioner.Create(MainWindow.Instance.AssemblyTreeModel.CurrentAssemblyList.GetAssemblies(), loadBalance: true),
new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct },
delegate (LoadedAssembly asm) {
new() { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct },
asm => {
if (!asm.HasLoadError)
{
Stopwatch w = Stopwatch.StartNew();
@ -55,7 +56,7 @@ namespace ICSharpCode.ILSpy @@ -55,7 +56,7 @@ namespace ICSharpCode.ILSpy
{
try
{
var options = MainWindow.Instance.CreateDecompilationOptions();
var options = SettingsService.Instance.CreateDecompilationOptions(dockWorkspace.ActiveTabPage);
options.FullDecompilation = true;
options.CancellationToken = ct;
new ILLanguage().DecompileAssembly(asm, new Decompiler.PlainTextOutput(writer), options);
@ -79,7 +80,7 @@ namespace ICSharpCode.ILSpy @@ -79,7 +80,7 @@ namespace ICSharpCode.ILSpy
}
});
return output;
}, ct)).Then(output => Docking.DockWorkspace.Instance.ShowText(output)).HandleExceptions();
}, ct)).Then(dockWorkspace.ShowText).HandleExceptions();
}
}
}

6
ILSpy/Commands/GeneratePdbContextMenuEntry.cs

@ -29,6 +29,7 @@ using ICSharpCode.Decompiler.CSharp; @@ -29,6 +29,7 @@ using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.ILSpy.Docking;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.TreeNodes;
@ -73,9 +74,10 @@ namespace ICSharpCode.ILSpy @@ -73,9 +74,10 @@ namespace ICSharpCode.ILSpy
dlg.InitialDirectory = Path.GetDirectoryName(assembly.FileName);
if (dlg.ShowDialog() != true)
return;
DecompilationOptions options = MainWindow.Instance.CreateDecompilationOptions();
DockWorkspace dockWorkspace = DockWorkspace.Instance;
DecompilationOptions options = SettingsService.Instance.CreateDecompilationOptions(dockWorkspace.ActiveTabPage);
string fileName = dlg.FileName;
Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
dockWorkspace.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
AvalonEditTextOutput output = new AvalonEditTextOutput();
Stopwatch stopwatch = Stopwatch.StartNew();
options.CancellationToken = ct;

11
ILSpy/Commands/SaveCodeContextMenuEntry.cs

@ -32,6 +32,8 @@ using Microsoft.Win32; @@ -32,6 +32,8 @@ using Microsoft.Win32;
using ICSharpCode.ILSpyX.TreeView;
using System.ComponentModel.Composition;
using ICSharpCode.ILSpy.Docking;
namespace ICSharpCode.ILSpy.TextView
{
@ -61,8 +63,11 @@ namespace ICSharpCode.ILSpy.TextView @@ -61,8 +63,11 @@ namespace ICSharpCode.ILSpy.TextView
public static void Execute(IReadOnlyList<SharpTreeNode> selectedNodes)
{
var currentLanguage = SettingsService.Instance.SessionSettings.LanguageSettings.Language;
var tabPage = Docking.DockWorkspace.Instance.ActiveTabPage;
var settingsService = SettingsService.Instance;
var dockWorkspace = Docking.DockWorkspace.Instance;
var currentLanguage = settingsService.SessionSettings.LanguageSettings.Language;
var tabPage = dockWorkspace.ActiveTabPage;
tabPage.ShowTextView(textView => {
if (selectedNodes.Count == 1 && selectedNodes[0] is ILSpyTreeNode singleSelection)
{
@ -87,7 +92,7 @@ namespace ICSharpCode.ILSpy.TextView @@ -87,7 +92,7 @@ namespace ICSharpCode.ILSpy.TextView
// Fallback: if nobody was able to handle the request, use default behavior.
// try to save all nodes to disk.
var options = MainWindow.Instance.CreateDecompilationOptions();
var options = settingsService.CreateDecompilationOptions(dockWorkspace.ActiveTabPage);
options.FullDecompilation = true;
textView.SaveToDisk(currentLanguage, selectedNodes.OfType<ILSpyTreeNode>(), options);
});

2
ILSpy/ContextMenuEntry.cs

@ -345,7 +345,7 @@ namespace ICSharpCode.ILSpy @@ -345,7 +345,7 @@ namespace ICSharpCode.ILSpy
needSeparatorForCategory = false;
}
MenuItem menuItem = new MenuItem();
menuItem.Header = MainWindow.GetResourceString(entryPair.Metadata.Header);
menuItem.Header = ResourceHelper.GetString(entryPair.Metadata.Header);
menuItem.InputGestureText = entryPair.Metadata.InputGestureText;
if (!string.IsNullOrEmpty(entryPair.Metadata.Icon))
{

7
ILSpy/Docking/DockWorkspace.cs

@ -34,7 +34,9 @@ using ICSharpCode.ILSpy.Analyzers; @@ -34,7 +34,9 @@ using ICSharpCode.ILSpy.Analyzers;
using ICSharpCode.ILSpy.Search;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.ViewModels;
using ICSharpCode.ILSpyX.Extensions;
using TomsToolbox.Composition;
using TomsToolbox.Wpf;
namespace ICSharpCode.ILSpy.Docking
@ -43,6 +45,8 @@ namespace ICSharpCode.ILSpy.Docking @@ -43,6 +45,8 @@ namespace ICSharpCode.ILSpy.Docking
{
private static SessionSettings SessionSettings => SettingsService.Instance.SessionSettings;
private readonly IExportProvider exportProvider = App.ExportProvider;
public static readonly DockWorkspace Instance = new();
private DockWorkspace()
@ -148,6 +152,9 @@ namespace ICSharpCode.ILSpy.Docking @@ -148,6 +152,9 @@ namespace ICSharpCode.ILSpy.Docking
public void InitializeLayout(DockingManager manager)
{
var toolPanes = exportProvider.GetExportedValues<ToolPaneModel>("ToolPane").OrderBy(item => item.Title);
ToolPanes.AddRange(toolPanes);
manager.LayoutUpdateStrategy = this;
XmlLayoutSerializer serializer = new XmlLayoutSerializer(manager);
serializer.LayoutSerializationCallback += LayoutSerializationCallback;

6
ILSpy/Languages/ILLanguage.cs

@ -29,6 +29,7 @@ using ICSharpCode.Decompiler.Metadata; @@ -29,6 +29,7 @@ using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Solution;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
using ICSharpCode.ILSpy.Docking;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpyX;
@ -197,7 +198,10 @@ namespace ICSharpCode.ILSpy @@ -197,7 +198,10 @@ namespace ICSharpCode.ILSpy
public override RichText GetRichTextTooltip(IEntity entity)
{
var output = new AvalonEditTextOutput() { IgnoreNewLineAndIndent = true };
var disasm = CreateDisassembler(output, MainWindow.Instance.CreateDecompilationOptions());
var settingsService = SettingsService.Instance;
var dockWorkspace = DockWorkspace.Instance;
var disasm = CreateDisassembler(output, settingsService.CreateDecompilationOptions(dockWorkspace.ActiveTabPage));
MetadataFile module = entity.ParentModule?.MetadataFile;
if (module == null)
{

432
ILSpy/MainWindow.xaml.cs

@ -17,43 +17,36 @@ @@ -17,43 +17,36 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Navigation;
using AvalonDock.Layout.Serialization;
using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy.AppEnv;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.Commands;
using ICSharpCode.ILSpy.Docking;
using ICSharpCode.ILSpy.Search;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.Themes;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpy.Updates;
using ICSharpCode.ILSpy.ViewModels;
using ICSharpCode.ILSpyX.FileLoaders;
using ICSharpCode.ILSpyX.Settings;
using ICSharpCode.ILSpyX.Extensions;
using ICSharpCode.ILSpyX.TreeView;
using Microsoft.Win32;
using ICSharpCode.ILSpyX.TreeView;
using TomsToolbox.Composition;
using Screen = System.Windows.Forms.Screen;
namespace ICSharpCode.ILSpy
{
@ -76,13 +69,6 @@ namespace ICSharpCode.ILSpy @@ -76,13 +69,6 @@ namespace ICSharpCode.ILSpy
}
}
public DecompilationOptions CreateDecompilationOptions()
{
var decompilerView = DockWorkspace.Instance.ActiveTabPage.Content as IProgress<DecompilationProgress>;
return new(AssemblyTreeModel.CurrentLanguageVersion, SettingsService.Instance.DecompilerSettings, SettingsService.Instance.DisplaySettings) { Progress = decompilerView };
}
public MainWindow()
{
instance = this;
@ -104,33 +90,16 @@ namespace ICSharpCode.ILSpy @@ -104,33 +90,16 @@ namespace ICSharpCode.ILSpy
Thread.CurrentThread.CurrentUICulture = new CultureInfo(sessionSettings.CurrentCulture);
}
InitializeComponent();
InitToolPanes();
DockWorkspace.Instance.InitializeLayout(dockManager);
MessageBus<DockWorkspaceActiveTabPageChangedEventArgs>.Subscribers += DockWorkspace_ActiveTabPageChanged;
MenuService.Instance.Init(mainMenu, toolBar, InputBindings);
InitMainMenu();
InitWindowMenu();
InitToolbar();
InitFileLoaders();
this.Loaded += MainWindow_Loaded;
}
private void DockWorkspace_ActiveTabPageChanged(object sender, EventArgs e)
{
DockWorkspace dock = DockWorkspace.Instance;
var windowMenuItem = mainMenu.Items.OfType<MenuItem>().First(m => (string)m.Tag == nameof(Properties.Resources._Window));
foreach (MenuItem menuItem in windowMenuItem.Items.OfType<MenuItem>())
{
if (menuItem.IsCheckable && menuItem.Tag is TabPageModel)
{
menuItem.IsChecked = menuItem.Tag == dock.ActiveTabPage;
}
}
}
void SetWindowBounds(Rect bounds)
{
this.Left = bounds.Left;
@ -139,367 +108,6 @@ namespace ICSharpCode.ILSpy @@ -139,367 +108,6 @@ namespace ICSharpCode.ILSpy
this.Height = bounds.Height;
}
#region Toolbar extensibility
void InitToolbar()
{
int navigationPos = 0;
int openPos = 1;
var toolbarCommands = App.ExportProvider.GetExports<ICommand, IToolbarCommandMetadata>("ToolbarCommand");
foreach (var commandGroup in toolbarCommands.OrderBy(c => c.Metadata.ToolbarOrder).GroupBy(c => Properties.Resources.ResourceManager.GetString(c.Metadata.ToolbarCategory)))
{
if (commandGroup.Key == Properties.Resources.ResourceManager.GetString("Navigation"))
{
foreach (var command in commandGroup)
{
toolBar.Items.Insert(navigationPos++, MakeToolbarItem(command));
openPos++;
}
}
else if (commandGroup.Key == Properties.Resources.ResourceManager.GetString("Open"))
{
foreach (var command in commandGroup)
{
toolBar.Items.Insert(openPos++, MakeToolbarItem(command));
}
}
else
{
toolBar.Items.Add(new Separator());
foreach (var command in commandGroup)
{
toolBar.Items.Add(MakeToolbarItem(command));
}
}
}
}
Button MakeToolbarItem(IExport<ICommand, IToolbarCommandMetadata> command)
{
return new Button {
Style = ThemeManager.Current.CreateToolBarButtonStyle(),
Command = CommandWrapper.Unwrap(command.Value),
ToolTip = Properties.Resources.ResourceManager.GetString(command.Metadata.ToolTip),
Tag = command.Metadata.Tag,
Content = new System.Windows.Controls.Image {
Width = 16,
Height = 16,
Source = Images.Load(command.Value, command.Metadata.ToolbarIcon)
}
};
}
#endregion
#region Main Menu extensibility
void InitMainMenu()
{
var mainMenuCommands = App.ExportProvider.GetExports<ICommand, IMainMenuCommandMetadata>("MainMenuCommand");
// Start by constructing the individual flat menus
var parentMenuItems = new Dictionary<string, MenuItem>();
var menuGroups = mainMenuCommands.OrderBy(c => c.Metadata.MenuOrder).GroupBy(c => c.Metadata.ParentMenuID);
foreach (var menu in menuGroups)
{
// Get or add the target menu item and add all items grouped by menu category
var parentMenuItem = GetOrAddParentMenuItem(menu.Key, menu.Key);
foreach (var category in menu.GroupBy(c => c.Metadata.MenuCategory))
{
if (parentMenuItem.Items.Count > 0)
{
parentMenuItem.Items.Add(new Separator { Tag = category.Key });
}
foreach (var entry in category)
{
if (menuGroups.Any(g => g.Key == entry.Metadata.MenuID))
{
var menuItem = GetOrAddParentMenuItem(entry.Metadata.MenuID, entry.Metadata.Header);
// replace potential dummy text with real name
menuItem.Header = GetResourceString(entry.Metadata.Header);
parentMenuItem.Items.Add(menuItem);
}
else
{
MenuItem menuItem = new MenuItem();
menuItem.Command = CommandWrapper.Unwrap(entry.Value);
menuItem.Tag = entry.Metadata.MenuID;
menuItem.Header = GetResourceString(entry.Metadata.Header);
if (!string.IsNullOrEmpty(entry.Metadata.MenuIcon))
{
menuItem.Icon = new Image {
Width = 16,
Height = 16,
Source = Images.Load(entry.Value, entry.Metadata.MenuIcon)
};
}
menuItem.IsEnabled = entry.Metadata.IsEnabled;
if (entry.Value is ToggleableCommand toggle)
{
menuItem.IsCheckable = true;
menuItem.SetBinding(MenuItem.IsCheckedProperty, new Binding("IsChecked") { Source = entry.Value, Mode = BindingMode.OneWay });
}
menuItem.InputGestureText = entry.Metadata.InputGestureText;
parentMenuItem.Items.Add(menuItem);
}
}
}
}
foreach (var (key, item) in parentMenuItems)
{
if (item.Parent == null)
{
mainMenu.Items.Add(item);
}
}
MenuItem GetOrAddParentMenuItem(string menuID, string resourceKey)
{
if (!parentMenuItems.TryGetValue(menuID, out var parentMenuItem))
{
var topLevelMenuItem = mainMenu.Items.OfType<MenuItem>().FirstOrDefault(m => (string)m.Tag == menuID);
if (topLevelMenuItem == null)
{
parentMenuItem = new MenuItem();
parentMenuItem.Header = GetResourceString(resourceKey);
parentMenuItem.Tag = menuID;
parentMenuItems.Add(menuID, parentMenuItem);
}
else
{
parentMenuItems.Add(menuID, topLevelMenuItem);
parentMenuItem = topLevelMenuItem;
}
}
return parentMenuItem;
}
}
internal static string GetResourceString(string key)
{
if (string.IsNullOrEmpty(key))
{
return null;
}
string value = Properties.Resources.ResourceManager.GetString(key);
if (!string.IsNullOrEmpty(value))
{
return value;
}
return key;
}
#endregion
#region Tool Pane extensibility
private void InitToolPanes()
{
var toolPanes = App.ExportProvider.GetExportedValues<ToolPaneModel>("ToolPane").OrderBy(item => item.Title);
DockWorkspace.Instance.ToolPanes.AddRange(toolPanes);
}
private void InitWindowMenu()
{
var windowMenuItem = mainMenu.Items.OfType<MenuItem>().First(m => (string)m.Tag == nameof(Properties.Resources._Window));
Separator separatorBeforeTools, separatorBeforeDocuments;
windowMenuItem.Items.Add(separatorBeforeTools = new Separator());
windowMenuItem.Items.Add(separatorBeforeDocuments = new Separator());
var dock = DockWorkspace.Instance;
dock.ToolPanes.CollectionChanged += ToolsChanged;
dock.TabPages.CollectionChanged += TabsChanged;
ToolsChanged(dock.ToolPanes, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
TabsChanged(dock.TabPages, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
void ToolsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
int endIndex = windowMenuItem.Items.IndexOf(separatorBeforeDocuments);
int startIndex = windowMenuItem.Items.IndexOf(separatorBeforeTools) + 1;
int insertionIndex;
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
insertionIndex = Math.Min(endIndex, startIndex + e.NewStartingIndex);
foreach (ToolPaneModel pane in e.NewItems)
{
MenuItem menuItem = CreateMenuItem(pane);
windowMenuItem.Items.Insert(insertionIndex, menuItem);
insertionIndex++;
}
break;
case NotifyCollectionChangedAction.Remove:
foreach (ToolPaneModel pane in e.OldItems)
{
for (int i = endIndex - 1; i >= startIndex; i--)
{
MenuItem item = (MenuItem)windowMenuItem.Items[i];
if (pane == item.Tag)
{
windowMenuItem.Items.RemoveAt(i);
item.Tag = null;
endIndex--;
break;
}
}
}
break;
case NotifyCollectionChangedAction.Replace:
break;
case NotifyCollectionChangedAction.Move:
break;
case NotifyCollectionChangedAction.Reset:
for (int i = endIndex - 1; i >= startIndex; i--)
{
MenuItem item = (MenuItem)windowMenuItem.Items[0];
item.Tag = null;
windowMenuItem.Items.RemoveAt(i);
endIndex--;
}
insertionIndex = endIndex;
foreach (ToolPaneModel pane in dock.ToolPanes)
{
MenuItem menuItem = CreateMenuItem(pane);
windowMenuItem.Items.Insert(insertionIndex, menuItem);
insertionIndex++;
}
break;
}
MenuItem CreateMenuItem(ToolPaneModel pane)
{
MenuItem menuItem = new MenuItem();
menuItem.Command = pane.AssociatedCommand ?? new ToolPaneCommand(pane.ContentId);
menuItem.Header = pane.Title;
menuItem.Tag = pane;
var shortcutKey = pane.ShortcutKey;
if (shortcutKey != null)
{
InputBindings.Add(new InputBinding(menuItem.Command, shortcutKey));
menuItem.InputGestureText = shortcutKey.GetDisplayStringForCulture(CultureInfo.CurrentUICulture);
}
if (!string.IsNullOrEmpty(pane.Icon))
{
menuItem.Icon = new Image {
Width = 16,
Height = 16,
Source = Images.Load(pane, pane.Icon)
};
}
return menuItem;
}
}
void TabsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
int endIndex = windowMenuItem.Items.Count;
int startIndex = windowMenuItem.Items.IndexOf(separatorBeforeDocuments) + 1;
int insertionIndex;
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
insertionIndex = Math.Min(endIndex, startIndex + e.NewStartingIndex);
foreach (TabPageModel pane in e.NewItems)
{
MenuItem menuItem = CreateMenuItem(pane);
pane.PropertyChanged += TabPageChanged;
windowMenuItem.Items.Insert(insertionIndex, menuItem);
insertionIndex++;
}
break;
case NotifyCollectionChangedAction.Remove:
foreach (TabPageModel pane in e.OldItems)
{
for (int i = endIndex - 1; i >= startIndex; i--)
{
MenuItem item = (MenuItem)windowMenuItem.Items[i];
if (pane == item.Tag)
{
windowMenuItem.Items.RemoveAt(i);
pane.PropertyChanged -= TabPageChanged;
item.Tag = null;
endIndex--;
break;
}
}
}
break;
case NotifyCollectionChangedAction.Replace:
break;
case NotifyCollectionChangedAction.Move:
break;
case NotifyCollectionChangedAction.Reset:
for (int i = endIndex - 1; i >= startIndex; i--)
{
MenuItem item = (MenuItem)windowMenuItem.Items[i];
windowMenuItem.Items.RemoveAt(i);
((TabPageModel)item.Tag).PropertyChanged -= TabPageChanged;
endIndex--;
}
insertionIndex = endIndex;
foreach (TabPageModel pane in dock.TabPages)
{
MenuItem menuItem = CreateMenuItem(pane);
pane.PropertyChanged += TabPageChanged;
windowMenuItem.Items.Insert(insertionIndex, menuItem);
insertionIndex++;
}
break;
}
MenuItem CreateMenuItem(TabPageModel pane)
{
MenuItem menuItem = new MenuItem();
menuItem.Command = new TabPageCommand(pane);
menuItem.Header = pane.Title.Length > 20 ? pane.Title.Substring(20) + "..." : pane.Title;
menuItem.Tag = pane;
menuItem.IsCheckable = true;
return menuItem;
}
}
static void TabPageChanged(object sender, PropertyChangedEventArgs e)
{
var windowMenuItem = Instance.mainMenu.Items.OfType<MenuItem>().First(m => (string)m.Tag == nameof(Properties.Resources._Window));
foreach (MenuItem menuItem in windowMenuItem.Items.OfType<MenuItem>())
{
if (menuItem.IsCheckable && menuItem.Tag == sender)
{
string title = ((TabPageModel)sender).Title;
menuItem.Header = title.Length > 20 ? title.Substring(0, 20) + "..." : title;
}
}
}
}
public void ShowInTopPane(string title, object content)
{
var model = DockWorkspace.Instance.ToolPanes.OfType<LegacyToolPaneModel>().FirstOrDefault(p => p.Content == content);
if (model == null)
{
model = new LegacyToolPaneModel(title, content, LegacyToolPaneLocation.Top);
DockWorkspace.Instance.ToolPanes.Add(model);
}
model.Show();
}
public void ShowInBottomPane(string title, object content)
{
var model = DockWorkspace.Instance.ToolPanes.OfType<LegacyToolPaneModel>().FirstOrDefault(p => p.Content == content);
if (model == null)
{
model = new LegacyToolPaneModel(title, content, LegacyToolPaneLocation.Bottom);
DockWorkspace.Instance.ToolPanes.Add(model);
}
model.Show();
}
#endregion
#region File Loader extensibility
void InitFileLoaders()
@ -514,20 +122,22 @@ namespace ICSharpCode.ILSpy @@ -514,20 +122,22 @@ namespace ICSharpCode.ILSpy
#endregion
#region Message Hook
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
PresentationSource source = PresentationSource.FromVisual(this);
var source = PresentationSource.FromVisual(this);
var sessionSettings = SettingsService.Instance.SessionSettings;
// Validate and Set Window Bounds
Rect bounds = Rect.Transform(sessionSettings.WindowBounds, source.CompositionTarget.TransformToDevice);
var boundsRect = new System.Drawing.Rectangle((int)bounds.Left, (int)bounds.Top, (int)bounds.Width, (int)bounds.Height);
Rect bounds = Rect.Transform(sessionSettings.WindowBounds, source?.CompositionTarget?.TransformToDevice ?? Matrix.Identity);
var boundsRect = new Rectangle((int)bounds.Left, (int)bounds.Top, (int)bounds.Width, (int)bounds.Height);
bool boundsOK = false;
foreach (var screen in System.Windows.Forms.Screen.AllScreens)
foreach (var screen in Screen.AllScreens)
{
var intersection = System.Drawing.Rectangle.Intersect(boundsRect, screen.WorkingArea);
var intersection = Rectangle.Intersect(boundsRect, screen.WorkingArea);
if (intersection.Width > 10 && intersection.Height > 10)
boundsOK = true;
}
@ -853,23 +463,5 @@ namespace ICSharpCode.ILSpy @@ -853,23 +463,5 @@ namespace ICSharpCode.ILSpy
return loadedAssy.FileName;
}
public void SetStatus(string status, Brush foreground)
{
if (this.statusBar.Visibility == Visibility.Collapsed)
this.statusBar.Visibility = Visibility.Visible;
this.statusLabel.Foreground = foreground;
this.statusLabel.Text = status;
}
public ItemCollection GetMainMenuItems()
{
return mainMenu.Items;
}
public ItemCollection GetToolBarItems()
{
return toolBar.Items;
}
}
}

2
ILSpy/Options/OptionsDialog.xaml.cs

@ -59,7 +59,7 @@ namespace ICSharpCode.ILSpy.Options @@ -59,7 +59,7 @@ namespace ICSharpCode.ILSpy.Options
ILSpySettings settings = ILSpySettings.Load();
foreach (var optionPage in optionPages.OrderBy(p => p.Metadata.Order))
{
var tabItem = new TabItemViewModel(MainWindow.GetResourceString(optionPage.Metadata.Title), optionPage.Value);
var tabItem = new TabItemViewModel(Util.ResourceHelper.GetString(optionPage.Metadata.Title), optionPage.Value);
tabControl.Items.Add(tabItem);

3
ILSpy/SolutionWriter.cs

@ -28,6 +28,7 @@ using System.Threading.Tasks; @@ -28,6 +28,7 @@ using System.Threading.Tasks;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Solution;
using ICSharpCode.Decompiler.Util;
using ICSharpCode.ILSpy.Docking;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpyX;
@ -212,7 +213,7 @@ namespace ICSharpCode.ILSpy @@ -212,7 +213,7 @@ namespace ICSharpCode.ILSpy
using (var projectFileWriter = new StreamWriter(projectFileName))
{
var projectFileOutput = new PlainTextOutput(projectFileWriter);
var options = MainWindow.Instance.CreateDecompilationOptions();
var options = SettingsService.Instance.CreateDecompilationOptions(DockWorkspace.Instance.ActiveTabPage);
options.FullDecompilation = true;
options.CancellationToken = ct;
options.SaveAsProjectDirectory = targetDirectory;

3
ILSpy/TreeNodes/AssemblyTreeNode.cs

@ -32,6 +32,7 @@ using ICSharpCode.Decompiler.Metadata; @@ -32,6 +32,7 @@ using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.Controls.TreeView;
using ICSharpCode.ILSpy.Docking;
using ICSharpCode.ILSpy.Metadata;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.ViewModels;
@ -546,7 +547,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -546,7 +547,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
dlg.Filter = language.Name + " project|*" + language.ProjectFileExtension + "|" + language.Name + " single file|*" + language.FileExtension + "|All files|*.*";
if (dlg.ShowDialog() == true)
{
DecompilationOptions options = MainWindow.Instance.CreateDecompilationOptions();
var options = SettingsService.Instance.CreateDecompilationOptions(DockWorkspace.Instance.ActiveTabPage);
options.FullDecompilation = true;
if (dlg.FilterIndex == 1)
{

357
ILSpy/Util/MenuService.cs

@ -0,0 +1,357 @@ @@ -0,0 +1,357 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using ICSharpCode.ILSpy.Commands;
using ICSharpCode.ILSpy.Docking;
using ICSharpCode.ILSpy.Themes;
using ICSharpCode.ILSpy.ViewModels;
using TomsToolbox.Composition;
namespace ICSharpCode.ILSpy.Util
{
internal class MenuService
{
public static readonly MenuService Instance = new();
public void InitMainMenu(Menu mainMenu)
{
var mainMenuCommands = App.ExportProvider.GetExports<ICommand, IMainMenuCommandMetadata>("MainMenuCommand");
// Start by constructing the individual flat menus
var parentMenuItems = new Dictionary<string, MenuItem>();
var menuGroups = mainMenuCommands.OrderBy(c => c.Metadata?.MenuOrder).GroupBy(c => c.Metadata?.ParentMenuID).ToArray();
foreach (var menu in menuGroups)
{
// Get or add the target menu item and add all items grouped by menu category
var parentMenuItem = GetOrAddParentMenuItem(menu.Key, menu.Key);
foreach (var category in menu.GroupBy(c => c.Metadata?.MenuCategory))
{
if (parentMenuItem.Items.Count > 0)
{
parentMenuItem.Items.Add(new Separator { Tag = category.Key });
}
foreach (var entry in category)
{
if (menuGroups.Any(g => g.Key == entry.Metadata?.MenuID))
{
var menuItem = GetOrAddParentMenuItem(entry.Metadata?.MenuID, entry.Metadata?.Header);
// replace potential dummy text with real name
menuItem.Header = ResourceHelper.GetString(entry.Metadata?.Header);
parentMenuItem.Items.Add(menuItem);
}
else
{
MenuItem menuItem = new MenuItem();
menuItem.Command = CommandWrapper.Unwrap(entry.Value);
menuItem.Tag = entry.Metadata?.MenuID;
menuItem.Header = ResourceHelper.GetString(entry.Metadata?.Header);
if (!string.IsNullOrEmpty(entry.Metadata?.MenuIcon))
{
menuItem.Icon = new Image {
Width = 16,
Height = 16,
Source = Images.Load(entry.Value, entry.Metadata.MenuIcon)
};
}
menuItem.IsEnabled = entry.Metadata?.IsEnabled ?? false;
if (entry.Value is ToggleableCommand toggle)
{
menuItem.IsCheckable = true;
menuItem.SetBinding(MenuItem.IsCheckedProperty, new Binding("IsChecked") { Source = entry.Value, Mode = BindingMode.OneWay });
}
menuItem.InputGestureText = entry.Metadata?.InputGestureText;
parentMenuItem.Items.Add(menuItem);
}
}
}
}
foreach (var (key, item) in parentMenuItems)
{
if (item.Parent == null)
{
mainMenu.Items.Add(item);
}
}
MenuItem GetOrAddParentMenuItem(string menuID, string resourceKey)
{
if (!parentMenuItems.TryGetValue(menuID, out var parentMenuItem))
{
var topLevelMenuItem = mainMenu.Items.OfType<MenuItem>().FirstOrDefault(m => (string)m.Tag == menuID);
if (topLevelMenuItem == null)
{
parentMenuItem = new MenuItem();
parentMenuItem.Header = ResourceHelper.GetString(resourceKey);
parentMenuItem.Tag = menuID;
parentMenuItems.Add(menuID, parentMenuItem);
}
else
{
parentMenuItems.Add(menuID, topLevelMenuItem);
parentMenuItem = topLevelMenuItem;
}
}
return parentMenuItem;
}
}
public void InitWindowMenu(Menu mainMenu, InputBindingCollection inputBindings)
{
var windowMenuItem = mainMenu.Items.OfType<MenuItem>().First(m => (string)m.Tag == nameof(Properties.Resources._Window));
var separatorBeforeTools = new Separator();
var separatorBeforeDocuments = new Separator();
windowMenuItem.Items.Add(separatorBeforeTools);
windowMenuItem.Items.Add(separatorBeforeDocuments);
var dock = DockWorkspace.Instance;
dock.ToolPanes.CollectionChanged += ToolsChanged;
dock.TabPages.CollectionChanged += TabsChanged;
MessageBus<DockWorkspaceActiveTabPageChangedEventArgs>.Subscribers += ActiveTabPageChanged;
ToolsChanged(dock.ToolPanes, new(NotifyCollectionChangedAction.Reset));
TabsChanged(dock.TabPages, new(NotifyCollectionChangedAction.Reset));
void ToolsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
int endIndex = windowMenuItem.Items.IndexOf(separatorBeforeDocuments);
int startIndex = windowMenuItem.Items.IndexOf(separatorBeforeTools) + 1;
int insertionIndex;
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
insertionIndex = Math.Min(endIndex, startIndex + e.NewStartingIndex);
foreach (ToolPaneModel pane in e.NewItems)
{
MenuItem menuItem = CreateMenuItem(pane);
windowMenuItem.Items.Insert(insertionIndex, menuItem);
insertionIndex++;
}
break;
case NotifyCollectionChangedAction.Remove:
foreach (ToolPaneModel pane in e.OldItems)
{
for (int i = endIndex - 1; i >= startIndex; i--)
{
MenuItem item = (MenuItem)windowMenuItem.Items[i];
if (pane == item.Tag)
{
windowMenuItem.Items.RemoveAt(i);
item.Tag = null;
endIndex--;
break;
}
}
}
break;
case NotifyCollectionChangedAction.Replace:
break;
case NotifyCollectionChangedAction.Move:
break;
case NotifyCollectionChangedAction.Reset:
for (int i = endIndex - 1; i >= startIndex; i--)
{
MenuItem item = (MenuItem)windowMenuItem.Items[0];
item.Tag = null;
windowMenuItem.Items.RemoveAt(i);
endIndex--;
}
insertionIndex = endIndex;
foreach (ToolPaneModel pane in dock.ToolPanes)
{
MenuItem menuItem = CreateMenuItem(pane);
windowMenuItem.Items.Insert(insertionIndex, menuItem);
insertionIndex++;
}
break;
}
MenuItem CreateMenuItem(ToolPaneModel pane)
{
MenuItem menuItem = new MenuItem();
menuItem.Command = pane.AssociatedCommand ?? new ToolPaneCommand(pane.ContentId);
menuItem.Header = pane.Title;
menuItem.Tag = pane;
var shortcutKey = pane.ShortcutKey;
if (shortcutKey != null)
{
inputBindings.Add(new InputBinding(menuItem.Command, shortcutKey));
menuItem.InputGestureText = shortcutKey.GetDisplayStringForCulture(CultureInfo.CurrentUICulture);
}
if (!string.IsNullOrEmpty(pane.Icon))
{
menuItem.Icon = new Image {
Width = 16,
Height = 16,
Source = Images.Load(pane, pane.Icon)
};
}
return menuItem;
}
}
void TabsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
int endIndex = windowMenuItem.Items.Count;
int startIndex = windowMenuItem.Items.IndexOf(separatorBeforeDocuments) + 1;
int insertionIndex;
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
insertionIndex = Math.Min(endIndex, startIndex + e.NewStartingIndex);
foreach (TabPageModel pane in e.NewItems)
{
MenuItem menuItem = CreateMenuItem(pane);
pane.PropertyChanged += TabPageChanged;
windowMenuItem.Items.Insert(insertionIndex, menuItem);
insertionIndex++;
}
break;
case NotifyCollectionChangedAction.Remove:
foreach (TabPageModel pane in e.OldItems)
{
for (int i = endIndex - 1; i >= startIndex; i--)
{
MenuItem item = (MenuItem)windowMenuItem.Items[i];
if (pane == item.Tag)
{
windowMenuItem.Items.RemoveAt(i);
pane.PropertyChanged -= TabPageChanged;
item.Tag = null;
endIndex--;
break;
}
}
}
break;
case NotifyCollectionChangedAction.Replace:
break;
case NotifyCollectionChangedAction.Move:
break;
case NotifyCollectionChangedAction.Reset:
for (int i = endIndex - 1; i >= startIndex; i--)
{
MenuItem item = (MenuItem)windowMenuItem.Items[i];
windowMenuItem.Items.RemoveAt(i);
((TabPageModel)item.Tag).PropertyChanged -= TabPageChanged;
endIndex--;
}
insertionIndex = endIndex;
foreach (TabPageModel pane in dock.TabPages)
{
MenuItem menuItem = CreateMenuItem(pane);
pane.PropertyChanged += TabPageChanged;
windowMenuItem.Items.Insert(insertionIndex, menuItem);
insertionIndex++;
}
break;
}
MenuItem CreateMenuItem(TabPageModel pane)
{
MenuItem menuItem = new MenuItem();
menuItem.Command = new TabPageCommand(pane);
menuItem.Header = pane.Title.Length > 20 ? pane.Title.Substring(20) + "..." : pane.Title;
menuItem.Tag = pane;
menuItem.IsCheckable = true;
menuItem.IsChecked = menuItem.Tag == dock.ActiveTabPage;
return menuItem;
}
}
void TabPageChanged(object sender, PropertyChangedEventArgs e)
{
var windowMenu = mainMenu.Items.OfType<MenuItem>().First(m => (string)m.Tag == nameof(Properties.Resources._Window));
foreach (MenuItem menuItem in windowMenu.Items.OfType<MenuItem>())
{
if (menuItem.IsCheckable && menuItem.Tag == sender)
{
string title = ((TabPageModel)sender).Title;
menuItem.Header = title.Length > 20 ? title.Substring(0, 20) + "..." : title;
}
}
}
void ActiveTabPageChanged(object sender, EventArgs e)
{
foreach (MenuItem menuItem in windowMenuItem.Items.OfType<MenuItem>())
{
if (menuItem.IsCheckable && menuItem.Tag is TabPageModel)
{
menuItem.IsChecked = menuItem.Tag == dock.ActiveTabPage;
}
}
}
}
public void InitToolbar(ToolBar toolBar)
{
int navigationPos = 0;
int openPos = 1;
var toolbarCommands = App.ExportProvider.GetExports<ICommand, IToolbarCommandMetadata>("ToolbarCommand");
foreach (var commandGroup in toolbarCommands.OrderBy(c => c.Metadata.ToolbarOrder).GroupBy(c => Properties.Resources.ResourceManager.GetString(c.Metadata.ToolbarCategory)))
{
if (commandGroup.Key == Properties.Resources.ResourceManager.GetString("Navigation"))
{
foreach (var command in commandGroup)
{
toolBar.Items.Insert(navigationPos++, MakeToolbarItem(command));
openPos++;
}
}
else if (commandGroup.Key == Properties.Resources.ResourceManager.GetString("Open"))
{
foreach (var command in commandGroup)
{
toolBar.Items.Insert(openPos++, MakeToolbarItem(command));
}
}
else
{
toolBar.Items.Add(new Separator());
foreach (var command in commandGroup)
{
toolBar.Items.Add(MakeToolbarItem(command));
}
}
}
}
Button MakeToolbarItem(IExport<ICommand, IToolbarCommandMetadata> command)
{
return new Button {
Style = ThemeManager.Current.CreateToolBarButtonStyle(),
Command = CommandWrapper.Unwrap(command.Value),
ToolTip = Properties.Resources.ResourceManager.GetString(command.Metadata.ToolTip),
Tag = command.Metadata.Tag,
Content = new Image {
Width = 16,
Height = 16,
Source = Images.Load(command.Value, command.Metadata.ToolbarIcon)
}
};
}
public void Init(Menu mainMenu, ToolBar toolBar, InputBindingCollection inputBindings)
{
InitMainMenu(mainMenu);
InitWindowMenu(mainMenu, inputBindings);
InitToolbar(toolBar);
}
}
}

15
ILSpy/Util/ResourceHelper.cs

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
namespace ICSharpCode.ILSpy.Util
{
internal static class ResourceHelper
{
internal static string GetString(string key)
{
if (string.IsNullOrEmpty(key))
return null;
string value = Properties.Resources.ResourceManager.GetString(key);
return !string.IsNullOrEmpty(value) ? value : key;
}
}
}

10
ILSpy/Util/SettingsService.cs

@ -1,5 +1,8 @@ @@ -1,5 +1,8 @@
using ICSharpCode.Decompiler;
using System;
using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy.Options;
using ICSharpCode.ILSpy.ViewModels;
using ICSharpCode.ILSpyX;
using ICSharpCode.ILSpyX.Settings;
@ -30,5 +33,10 @@ namespace ICSharpCode.ILSpy.Util @@ -30,5 +33,10 @@ namespace ICSharpCode.ILSpy.Util
public DisplaySettings DisplaySettings { get; }
public AssemblyListManager AssemblyListManager { get; }
public DecompilationOptions CreateDecompilationOptions(TabPageModel tabPage)
{
return new(SessionSettings.LanguageSettings.LanguageVersion, DecompilerSettings, DisplaySettings) { Progress = tabPage.Content as IProgress<DecompilationProgress> };
}
}
}

Loading…
Cancel
Save