diff --git a/ILSpy/AboutPage.cs b/ILSpy/AboutPage.cs index f11c412b9..92114b5ee 100644 --- a/ILSpy/AboutPage.cs +++ b/ILSpy/AboutPage.cs @@ -3,6 +3,7 @@ using System; using System.ComponentModel; +using System.ComponentModel.Composition; using System.Diagnostics; using System.IO; using System.Linq; @@ -14,13 +15,23 @@ using System.Windows.Data; using System.Windows.Input; using System.Xml; using System.Xml.Linq; + using ICSharpCode.Decompiler; using ICSharpCode.ILSpy.TextView; namespace ICSharpCode.ILSpy { - static class AboutPage + [ExportMainMenuCommand(Menu = "_Help", Header = "_About", Order = 99999)] + sealed class AboutPage : SimpleCommand { + [Import] + DecompilerTextView decompilerTextView = null; + + public override void Execute(object parameter) + { + Display(decompilerTextView); + } + static readonly Uri UpdateUrl = new Uri("http://www.ilspy.net/updates.xml"); static AvailableVersionInfo latestAvailableVersion; diff --git a/ILSpy/Commands.cs b/ILSpy/Commands.cs index eb9e0ca02..78b153a1e 100644 --- a/ILSpy/Commands.cs +++ b/ILSpy/Commands.cs @@ -6,6 +6,15 @@ using System.Windows.Input; namespace ICSharpCode.ILSpy { + [ExportMainMenuCommand(Menu = "_File", Header = "E_xit", Order = 99999, Category = "Exit")] + sealed class ExitCommand : SimpleCommand + { + public override void Execute(object parameter) + { + MainWindow.Instance.Close(); + } + } + [ExportToolbarCommand(ToolTip = "Back", Icon = "Images/Back.png", Category = "Navigation")] sealed class BrowseBackCommand : CommandWrapper { public BrowseBackCommand() : base(NavigationCommands.BrowseBack) {} @@ -50,4 +59,19 @@ namespace ICSharpCode.ILSpy return wrappedCommand.CanExecute(parameter); } } + + public abstract class SimpleCommand : ICommand + { + public event EventHandler CanExecuteChanged { + add { CommandManager.RequerySuggested += value; } + remove { CommandManager.RequerySuggested -= value; } + } + + public abstract void Execute(object parameter); + + public virtual bool CanExecute(object parameter) + { + return true; + } + } } diff --git a/ILSpy/ExportCommandAttribute.cs b/ILSpy/ExportCommandAttribute.cs index 33538524c..12eab3f45 100644 --- a/ILSpy/ExportCommandAttribute.cs +++ b/ILSpy/ExportCommandAttribute.cs @@ -22,7 +22,7 @@ namespace ICSharpCode.ILSpy public class ExportToolbarCommandAttribute : ExportAttribute { public ExportToolbarCommandAttribute() - : base(typeof(ICommand)) + : base("ToolbarCommand", typeof(ICommand)) { } @@ -32,4 +32,32 @@ namespace ICSharpCode.ILSpy public double Order { get; set; } } #endregion + + #region Main Menu + public interface IMainMenuCommandMetadata + { + string Icon { get; } + string Header { get; } + string Menu { get; } + string Category { get; } + + double Order { get; } + } + + [MetadataAttribute] + [AttributeUsage(AttributeTargets.Class, AllowMultiple=false)] + public class ExportMainMenuCommandAttribute : ExportAttribute + { + public ExportMainMenuCommandAttribute() + : base("MainMenuCommand", typeof(ICommand)) + { + } + + public string Icon { get; set; } + public string Header { get; set; } + public string Menu { get; set; } + public string Category { get; set; } + public double Order { get; set; } + } + #endregion } diff --git a/ILSpy/MainWindow.xaml b/ILSpy/MainWindow.xaml index aa7058009..bf18485d7 100644 --- a/ILSpy/MainWindow.xaml +++ b/ILSpy/MainWindow.xaml @@ -33,7 +33,7 @@ - + @@ -52,8 +52,6 @@ - - @@ -63,9 +61,6 @@ - - - - + @@ -156,7 +151,7 @@ - + 0 && sessionSettings.SplitterPosition < 1) { leftColumn.Width = new GridLength(sessionSettings.SplitterPosition, GridUnitType.Star); @@ -86,28 +93,38 @@ namespace ICSharpCode.ILSpy } sessionSettings.FilterSettings.PropertyChanged += filterSettings_PropertyChanged; - App.CompositionContainer.ComposeParts(this); + InitMainMenu(); + InitToolbar(); + + this.Loaded += new RoutedEventHandler(MainWindow_Loaded); + } + + #region Toolbar extensibility + [ImportMany("ToolbarCommand", typeof(ICommand))] + Lazy[] toolbarCommands = null; + + void InitToolbar() + { int navigationPos = 0; int openPos = 1; - foreach (var commandGroup in ToolbarCommands.GroupBy(c => c.Metadata.Category)) { + foreach (var commandGroup in toolbarCommands.OrderBy(c => c.Metadata.Order).GroupBy(c => c.Metadata.Category)) { if (commandGroup.Key == "Navigation") { - foreach (var command in commandGroup.OrderBy(c => c.Metadata.Order)) { + foreach (var command in commandGroup) { toolBar.Items.Insert(navigationPos++, MakeToolbarItem(command)); openPos++; } } else if (commandGroup.Key == "Open") { - foreach (var command in commandGroup.OrderBy(c => c.Metadata.Order)) { + foreach (var command in commandGroup) { toolBar.Items.Insert(openPos++, MakeToolbarItem(command)); } } else { toolBar.Items.Add(new Separator()); - foreach (var command in commandGroup.OrderBy(c => c.Metadata.Order)) { + foreach (var command in commandGroup) { toolBar.Items.Add(MakeToolbarItem(command)); } } } - this.Loaded += new RoutedEventHandler(MainWindow_Loaded); } Button MakeToolbarItem(Lazy command) @@ -122,9 +139,41 @@ namespace ICSharpCode.ILSpy } }; } + #endregion - [ImportMany] - internal Lazy[] ToolbarCommands { get; set; } + #region Main Menu extensibility + [ImportMany("MainMenuCommand", typeof(ICommand))] + Lazy[] mainMenuCommands = null; + + void InitMainMenu() + { + foreach (var topLevelMenu in mainMenuCommands.OrderBy(c => c.Metadata.Order).GroupBy(c => c.Metadata.Menu)) { + var topLevelMenuItem = mainMenu.Items.OfType().FirstOrDefault(m => (m.Header as string) == topLevelMenu.Key); + foreach (var category in topLevelMenu.GroupBy(c => c.Metadata.Category)) { + if (topLevelMenuItem == null) { + topLevelMenuItem = new MenuItem(); + topLevelMenuItem.Header = topLevelMenu.Key; + mainMenu.Items.Add(topLevelMenuItem); + } else { + topLevelMenuItem.Items.Add(new Separator()); + } + foreach (var entry in category) { + MenuItem menuItem = new MenuItem(); + menuItem.Header = entry.Metadata.Header; + menuItem.Command = entry.Value; + if (!string.IsNullOrEmpty(entry.Metadata.Icon)) { + menuItem.Icon = new Image { + Width = 16, + Height = 16, + Source = Images.LoadImage(entry.Value, entry.Metadata.Icon) + }; + } + topLevelMenuItem.Items.Add(menuItem); + } + } + } + } + #endregion void MainWindow_Loaded(object sender, RoutedEventArgs e) { @@ -375,18 +424,6 @@ namespace ICSharpCode.ILSpy } #endregion - #region Exit/About - void ExitClick(object sender, RoutedEventArgs e) - { - Close(); - } - - void AboutClick(object sender, RoutedEventArgs e) - { - AboutPage.Display(decompilerTextView); - } - #endregion - #region Decompile / Save void TreeView_SelectionChanged(object sender, SelectionChangedEventArgs e) { diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index c4c951173..928f11348 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel.Composition; using System.Diagnostics; using System.Globalization; using System.IO; @@ -49,12 +50,12 @@ namespace ICSharpCode.ILSpy.TextView /// Manages the TextEditor showing the decompiled code. /// Contains all the threading logic that makes the decompiler work in the background. /// + [Export, PartCreationPolicy(CreationPolicy.Shared)] public sealed partial class DecompilerTextView : UserControl { readonly ReferenceElementGenerator referenceElementGenerator; readonly UIElementGenerator uiElementGenerator; FoldingManager foldingManager; - internal MainWindow mainWindow; DefinitionLookup definitionLookup; CancellationTokenSource currentCancellationTokenSource; @@ -477,7 +478,7 @@ namespace ICSharpCode.ILSpy.TextView return; } } - mainWindow.JumpToReference(reference); + MainWindow.Instance.JumpToReference(reference); } ///