You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

277 lines
8.5 KiB

  1. // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy of this
  4. // software and associated documentation files (the "Software"), to deal in the Software
  5. // without restriction, including without limitation the rights to use, copy, modify, merge,
  6. // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
  7. // to whom the Software is furnished to do so, subject to the following conditions:
  8. //
  9. // The above copyright notice and this permission notice shall be included in all copies or
  10. // substantial portions of the Software.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  13. // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  14. // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
  15. // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  16. // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  17. // DEALINGS IN THE SOFTWARE.
  18. using System;
  19. using System.Collections.Generic;
  20. using System.ComponentModel;
  21. using System.Composition;
  22. using System.Diagnostics;
  23. using System.Drawing;
  24. using System.Linq;
  25. using System.Threading.Tasks;
  26. using System.Windows;
  27. using System.Windows.Input;
  28. using System.Windows.Media;
  29. using System.Windows.Threading;
  30. using AvalonDock.Layout.Serialization;
  31. using ICSharpCode.ILSpy.AssemblyTree;
  32. using ICSharpCode.ILSpy.TreeNodes;
  33. using ICSharpCode.ILSpy.Updates;
  34. using ICSharpCode.ILSpyX.FileLoaders;
  35. using ICSharpCode.ILSpyX.Settings;
  36. using ICSharpCode.ILSpyX.TreeView;
  37. using Screen = System.Windows.Forms.Screen;
  38. namespace ICSharpCode.ILSpy
  39. {
  40. /// <summary>
  41. /// The main window of the application.
  42. /// </summary>
  43. [Export]
  44. [Shared]
  45. #pragma warning disable MEF003 // Main window is a singleton
  46. partial class MainWindow
  47. {
  48. private readonly AssemblyTreeModel assemblyTreeModel;
  49. private readonly IEnumerable<IFileLoader> fileLoaders;
  50. private readonly MenuService menuService;
  51. private readonly SettingsService settingsService;
  52. public MainWindow(MainWindowViewModel mainWindowViewModel, AssemblyTreeModel assemblyTreeModel, IEnumerable<IFileLoader> fileLoaders, MenuService menuService, SettingsService settingsService)
  53. {
  54. this.assemblyTreeModel = assemblyTreeModel;
  55. this.fileLoaders = fileLoaders;
  56. this.menuService = menuService;
  57. this.settingsService = settingsService;
  58. // Make sure Images are initialized on the UI thread.
  59. this.Icon = Images.ILSpyIcon;
  60. this.DataContext = mainWindowViewModel;
  61. InitializeComponent();
  62. InitFileLoaders();
  63. Dispatcher.BeginInvoke(DispatcherPriority.Background, () => {
  64. mainWindowViewModel.Workspace.InitializeLayout();
  65. menuService.Init(mainMenu, toolBar, InputBindings);
  66. Dispatcher.BeginInvoke(DispatcherPriority.Background, () => {
  67. assemblyTreeModel.Initialize();
  68. assemblyTreeModel.Show();
  69. });
  70. });
  71. }
  72. void SetWindowBounds(Rect bounds)
  73. {
  74. this.Left = bounds.Left;
  75. this.Top = bounds.Top;
  76. this.Width = bounds.Width;
  77. this.Height = bounds.Height;
  78. }
  79. #region File Loader extensibility
  80. void InitFileLoaders()
  81. {
  82. // TODO
  83. foreach (var loader in fileLoaders)
  84. {
  85. }
  86. }
  87. #endregion
  88. #region Message Hook
  89. protected override void OnSourceInitialized(EventArgs e)
  90. {
  91. base.OnSourceInitialized(e);
  92. var source = PresentationSource.FromVisual(this);
  93. var sessionSettings = settingsService.SessionSettings;
  94. // Validate and Set Window Bounds
  95. var windowBounds = Rect.Transform(sessionSettings.WindowBounds, source?.CompositionTarget?.TransformToDevice ?? Matrix.Identity);
  96. var boundsRect = new Rectangle((int)windowBounds.Left, (int)windowBounds.Top, (int)windowBounds.Width, (int)windowBounds.Height);
  97. bool areBoundsValid = Screen.AllScreens.Any(screen => Rectangle.Intersect(boundsRect, screen.WorkingArea) is { Width: > 10, Height: > 10 });
  98. SetWindowBounds(areBoundsValid ? sessionSettings.WindowBounds : SessionSettings.DefaultWindowBounds);
  99. this.WindowState = sessionSettings.WindowState;
  100. }
  101. #endregion
  102. protected override void OnKeyDown(KeyEventArgs e)
  103. {
  104. base.OnKeyDown(e);
  105. if (!e.Handled && e.KeyboardDevice.Modifiers == ModifierKeys.Alt && e.Key == Key.System)
  106. {
  107. switch (e.SystemKey)
  108. {
  109. case Key.A:
  110. assemblyListComboBox.Focus();
  111. e.Handled = true;
  112. break;
  113. case Key.L:
  114. languageComboBox.Focus();
  115. e.Handled = true;
  116. break;
  117. case Key.E: // Alt+V was already taken by _View menu
  118. languageVersionComboBox.Focus();
  119. e.Handled = true;
  120. break;
  121. }
  122. }
  123. }
  124. #region Update Check
  125. string updateAvailableDownloadUrl;
  126. public async Task ShowMessageIfUpdatesAvailableAsync(UpdateSettings settings, bool forceCheck = false)
  127. {
  128. string downloadUrl;
  129. if (forceCheck)
  130. {
  131. downloadUrl = await NotifyOfUpdatesStrategy.CheckForUpdatesAsync(settings);
  132. }
  133. else
  134. {
  135. downloadUrl = await NotifyOfUpdatesStrategy.CheckForUpdatesIfEnabledAsync(settings);
  136. }
  137. // The Update Panel is only available for NotifyOfUpdatesStrategy, AutoUpdate will have differing UI requirements
  138. AdjustUpdateUIAfterCheck(downloadUrl, forceCheck);
  139. }
  140. void UpdatePanelCloseButtonClick(object sender, RoutedEventArgs e)
  141. {
  142. updatePanel.Visibility = Visibility.Collapsed;
  143. }
  144. async void DownloadOrCheckUpdateButtonClick(object sender, RoutedEventArgs e)
  145. {
  146. if (updateAvailableDownloadUrl != null)
  147. {
  148. OpenLink(updateAvailableDownloadUrl);
  149. }
  150. else
  151. {
  152. updatePanel.Visibility = Visibility.Collapsed;
  153. string downloadUrl = await NotifyOfUpdatesStrategy.CheckForUpdatesAsync(settingsService.GetSettings<UpdateSettings>());
  154. AdjustUpdateUIAfterCheck(downloadUrl, true);
  155. }
  156. }
  157. void AdjustUpdateUIAfterCheck(string downloadUrl, bool displayMessage)
  158. {
  159. updateAvailableDownloadUrl = downloadUrl;
  160. updatePanel.Visibility = displayMessage ? Visibility.Visible : Visibility.Collapsed;
  161. if (downloadUrl != null)
  162. {
  163. updatePanelMessage.Text = Properties.Resources.ILSpyVersionAvailable;
  164. downloadOrCheckUpdateButton.Content = Properties.Resources.Download;
  165. }
  166. else
  167. {
  168. updatePanelMessage.Text = Properties.Resources.UpdateILSpyFound;
  169. downloadOrCheckUpdateButton.Content = Properties.Resources.CheckAgain;
  170. }
  171. }
  172. #endregion
  173. public static void OpenLink(string link)
  174. {
  175. try
  176. {
  177. Process.Start(new ProcessStartInfo { FileName = link, UseShellExecute = true });
  178. #pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
  179. }
  180. catch (Exception)
  181. {
  182. #pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
  183. // Process.Start can throw several errors (not all of them documented),
  184. // just ignore all of them.
  185. }
  186. }
  187. public static void ExecuteCommand(string fileName, string arguments)
  188. {
  189. try
  190. {
  191. Process.Start(fileName, arguments);
  192. #pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
  193. }
  194. catch (Exception)
  195. {
  196. #pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
  197. // Process.Start can throw several errors (not all of them documented),
  198. // just ignore all of them.
  199. }
  200. }
  201. protected override void OnStateChanged(EventArgs e)
  202. {
  203. base.OnStateChanged(e);
  204. // store window state in settings only if it's not minimized
  205. if (this.WindowState != WindowState.Minimized)
  206. settingsService.SessionSettings.WindowState = this.WindowState;
  207. }
  208. protected override void OnClosing(CancelEventArgs e)
  209. {
  210. base.OnClosing(e);
  211. var snapshot = settingsService.CreateSnapshot();
  212. var sessionSettings = snapshot.GetSettings<SessionSettings>();
  213. sessionSettings.ActiveAssemblyList = assemblyTreeModel.AssemblyList.ListName;
  214. sessionSettings.ActiveTreeViewPath = assemblyTreeModel.SelectedPath;
  215. sessionSettings.ActiveAutoLoadedAssembly = GetAutoLoadedAssemblyNode(assemblyTreeModel.SelectedItem);
  216. sessionSettings.WindowBounds = this.RestoreBounds;
  217. sessionSettings.DockLayout.Serialize(new XmlLayoutSerializer(DockManager));
  218. snapshot.Save();
  219. }
  220. private static string GetAutoLoadedAssemblyNode(SharpTreeNode node)
  221. {
  222. var assemblyTreeNode = node?
  223. .AncestorsAndSelf()
  224. .OfType<AssemblyTreeNode>()
  225. .FirstOrDefault();
  226. var loadedAssembly = assemblyTreeNode?.LoadedAssembly;
  227. return loadedAssembly is not { IsLoaded: true, IsAutoLoaded: true }
  228. ? null
  229. : loadedAssembly.FileName;
  230. }
  231. }
  232. }