PageRenderTime 56ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/GitUI/GitExtensionsForm.cs

https://github.com/vbjay/gitextensions
C# | 448 lines | 339 code | 59 blank | 50 comment | 71 complexity | bc548008214fd121864e4c31a8be0936 MD5 | raw file
Possible License(s): GPL-3.0, GPL-2.0
  1. using System;
  2. using System.ComponentModel;
  3. using System.Configuration;
  4. using System.Diagnostics;
  5. using System.Drawing;
  6. using System.Linq;
  7. using System.Windows.Forms;
  8. using GitUI.Properties;
  9. #if !__MonoCS__
  10. using Microsoft.WindowsAPICodePack.Taskbar;
  11. #endif
  12. using ResourceManager;
  13. using Settings = GitCommands.AppSettings;
  14. using System.Collections.Generic;
  15. namespace GitUI
  16. {
  17. /// <summary>Base class for a Git Extensions <see cref="Form"/>.
  18. /// <remarks>
  19. /// Includes support for font, hotkey, icon, translation, and position restore.
  20. /// </remarks></summary>
  21. public class GitExtensionsForm : Form, ITranslate
  22. {
  23. internal static Icon ApplicationIcon = GetApplicationIcon(Settings.IconStyle, Settings.IconColor);
  24. /// <summary>indicates whether the <see cref="Form"/> has been translated</summary>
  25. bool _translated;
  26. /// <summary>indicates whether the <see cref="Form"/>'s position will be restored</summary>
  27. bool _enablePositionRestore;
  28. /// <summary>Creates a new <see cref="GitExtensionsForm"/> without position restore.</summary>
  29. public GitExtensionsForm()
  30. : this(false) { }
  31. /// <summary>Creates a new <see cref="GitExtensionsForm"/> indicating position restore.</summary>
  32. /// <param name="enablePositionRestore">Indicates whether the <see cref="Form"/>'s position
  33. /// will be restored upon being re-opened.</param>
  34. public GitExtensionsForm(bool enablePositionRestore)
  35. {
  36. _enablePositionRestore = enablePositionRestore;
  37. Icon = ApplicationIcon;
  38. SetFont();
  39. ShowInTaskbar = Application.OpenForms.Count <= 0 || (Application.OpenForms.Count == 1 && Application.OpenForms[0] is FormSplash);
  40. var cancelButton = new Button();
  41. cancelButton.Click += CancelButtonClick;
  42. CancelButton = cancelButton;
  43. Load += GitExtensionsFormLoad;
  44. FormClosing += GitExtensionsForm_FormClosing;
  45. }
  46. void GitExtensionsForm_FormClosing(object sender, FormClosingEventArgs e)
  47. {
  48. if (_enablePositionRestore)
  49. SavePosition(GetType().Name);
  50. #if !__MonoCS__
  51. if (GitCommands.Utils.EnvUtils.RunningOnWindows() && TaskbarManager.IsPlatformSupported)
  52. {
  53. try
  54. {
  55. TaskbarManager.Instance.SetProgressState(TaskbarProgressBarState.NoProgress);
  56. }
  57. catch (InvalidOperationException) { }
  58. }
  59. #endif
  60. }
  61. #region Hotkeys
  62. /// <summary>Gets or sets a value that specifies if the hotkeys are used</summary>
  63. protected bool HotkeysEnabled { get; set; }
  64. /// <summary>Gets or sets the hotkeys</summary>
  65. protected IEnumerable<Hotkey.HotkeyCommand> Hotkeys { get; set; }
  66. /// <summary>Overridden: Checks if a hotkey wants to handle the key before letting the message propagate</summary>
  67. protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
  68. {
  69. if (HotkeysEnabled && Hotkeys != null)
  70. foreach (var hotkey in Hotkeys)
  71. {
  72. if (hotkey != null && hotkey.KeyData == keyData)
  73. {
  74. return ExecuteCommand(hotkey.CommandCode);
  75. }
  76. }
  77. return base.ProcessCmdKey(ref msg, keyData);
  78. }
  79. protected Keys GetShortcutKeys(int commandCode)
  80. {
  81. var hotkey = GetHotkeyCommand(commandCode);
  82. return hotkey == null ? Keys.None : hotkey.KeyData;
  83. }
  84. protected Hotkey.HotkeyCommand GetHotkeyCommand(int commandCode)
  85. {
  86. if (Hotkeys == null)
  87. return null;
  88. return Hotkeys.FirstOrDefault(h => h.CommandCode == commandCode);
  89. }
  90. /// <summary>Override this method to handle form-specific Hotkey commands.</summary>
  91. protected virtual bool ExecuteCommand(int command)
  92. {
  93. return false;
  94. }
  95. #endregion
  96. private void SetFont()
  97. {
  98. Font = Settings.Font;
  99. }
  100. #region icon
  101. protected void RotateApplicationIcon()
  102. {
  103. ApplicationIcon = GetApplicationIcon(Settings.IconStyle, Settings.IconColor);
  104. Icon = ApplicationIcon;
  105. }
  106. /// <summary>Specifies a Git Extensions' color index.</summary>
  107. protected enum ColorIndex
  108. {
  109. Default,
  110. Blue,
  111. Green,
  112. LightBlue,
  113. Purple,
  114. Red,
  115. Yellow,
  116. Unknown = -1
  117. }
  118. protected static ColorIndex GetColorIndexByName(string color)
  119. {
  120. switch (color)
  121. {
  122. case "default":
  123. return ColorIndex.Default;
  124. case "blue":
  125. return ColorIndex.Blue;
  126. case "green":
  127. return ColorIndex.Green;
  128. case "lightblue":
  129. return ColorIndex.LightBlue;
  130. case "purple":
  131. return ColorIndex.Purple;
  132. case "red":
  133. return ColorIndex.Red;
  134. case "yellow":
  135. return ColorIndex.Yellow;
  136. case "random":
  137. return (ColorIndex)new Random(DateTime.Now.Millisecond).Next(7);
  138. }
  139. return ColorIndex.Unknown;
  140. }
  141. public static Icon GetApplicationIcon(string iconStyle, string iconColor)
  142. {
  143. var colorIndex = (int)GetColorIndexByName(iconColor);
  144. if (colorIndex == (int) ColorIndex.Unknown)
  145. colorIndex = 0;
  146. Icon appIcon;
  147. if (iconStyle.Equals("small", StringComparison.OrdinalIgnoreCase))
  148. {
  149. Icon[] icons = {
  150. Resources.x_with_arrow,
  151. Resources.x_with_arrow_blue,
  152. Resources.x_with_arrow_green,
  153. Resources.x_with_arrow_lightblue,
  154. Resources.x_with_arrow_purple,
  155. Resources.x_with_arrow_red,
  156. Resources.x_with_arrow_yellow
  157. };
  158. Debug.Assert(icons.Length == 7);
  159. appIcon = icons[colorIndex];
  160. }
  161. else if (iconStyle.Equals("large", StringComparison.OrdinalIgnoreCase))
  162. {
  163. Icon[] icons = {
  164. Resources.git_extensions_logo_final,
  165. Resources.git_extensions_logo_final_blue,
  166. Resources.git_extensions_logo_final_green,
  167. Resources.git_extensions_logo_final_lightblue,
  168. Resources.git_extensions_logo_final_purple,
  169. Resources.git_extensions_logo_final_red,
  170. Resources.git_extensions_logo_final_yellow
  171. };
  172. Debug.Assert(icons.Length == 7);
  173. appIcon = icons[colorIndex];
  174. }
  175. else if (iconStyle.Equals("cow", StringComparison.OrdinalIgnoreCase))
  176. {
  177. Icon[] icons = {
  178. Resources.cow_head,
  179. Resources.cow_head_blue,
  180. Resources.cow_head_green,
  181. Resources.cow_head_blue,
  182. Resources.cow_head_purple,
  183. Resources.cow_head_red,
  184. Resources.cow_head_yellow
  185. };
  186. Debug.Assert(icons.Length == 7);
  187. appIcon = icons[colorIndex];
  188. }
  189. else
  190. {
  191. Icon[] icons = {
  192. Resources.git_extensions_logo_final_mixed,
  193. Resources.git_extensions_logo_final_mixed_blue,
  194. Resources.git_extensions_logo_final_mixed_green,
  195. Resources.git_extensions_logo_final_mixed_lightblue,
  196. Resources.git_extensions_logo_final_mixed_purple,
  197. Resources.git_extensions_logo_final_mixed_red,
  198. Resources.git_extensions_logo_final_mixed_yellow
  199. };
  200. Debug.Assert(icons.Length == 7);
  201. appIcon = icons[colorIndex];
  202. }
  203. Debug.Assert(appIcon != null);
  204. return appIcon;
  205. }
  206. #endregion icon
  207. /// <summary>Indicates whether this is a valid <see cref="IComponent"/> running in design mode.</summary>
  208. static bool CheckComponent(object value)
  209. {
  210. var component = value as IComponent;
  211. if (component == null)
  212. return false;
  213. var site = component.Site;
  214. return (site != null) && site.DesignMode;
  215. }
  216. /// <summary>Invoked at runtime during the <see cref="OnLoad"/> method.</summary>
  217. protected virtual void OnRuntimeLoad(EventArgs e)
  218. {
  219. }
  220. /// <summary>Sets <see cref="AutoScaleMode"/>,
  221. /// restores position, raises the <see cref="Form.Load"/> event,
  222. /// and .
  223. /// </summary>
  224. protected override void OnLoad(EventArgs e)
  225. {
  226. AutoScaleMode = Settings.EnableAutoScale
  227. ? AutoScaleMode.Dpi
  228. : AutoScaleMode.None;
  229. if (_enablePositionRestore)
  230. RestorePosition(GetType().Name);
  231. base.OnLoad(e);
  232. if (!CheckComponent(this))
  233. OnRuntimeLoad(e);
  234. }
  235. private void GitExtensionsFormLoad(object sender, EventArgs e)
  236. {
  237. // find out if the value is a component and is currently in design mode
  238. var isComponentInDesignMode = CheckComponent(this);
  239. if (!_translated && !isComponentInDesignMode)
  240. throw new Exception("The control " + GetType().Name +
  241. " is not translated in the constructor. You need to call Translate() right after InitializeComponent().");
  242. }
  243. /// <summary>Translates the <see cref="Form"/>'s fields and properties, including child controls.</summary>
  244. protected void Translate()
  245. {
  246. Translator.Translate(this, Settings.CurrentTranslation);
  247. _translated = true;
  248. }
  249. public virtual void CancelButtonClick(object sender, EventArgs e)
  250. {
  251. Close();
  252. }
  253. private bool _windowCentred;
  254. /// <summary>
  255. /// Restores the position of a form from the user settings. Does
  256. /// nothing if there is no entry for the form in the settings, or the
  257. /// setting would be invisible on the current display configuration.
  258. /// </summary>
  259. /// <param name = "name">The name to use when looking up the position in
  260. /// the settings</param>
  261. private void RestorePosition(String name)
  262. {
  263. if (!Visible ||
  264. WindowState == FormWindowState.Minimized)
  265. return;
  266. _windowCentred = (StartPosition == FormStartPosition.CenterParent);
  267. var position = LookupWindowPosition(name);
  268. if (position == null)
  269. return;
  270. StartPosition = FormStartPosition.Manual;
  271. if (FormBorderStyle == FormBorderStyle.Sizable ||
  272. FormBorderStyle == FormBorderStyle.SizableToolWindow)
  273. Size = position.Rect.Size;
  274. if (Owner == null || !_windowCentred)
  275. {
  276. Point location = position.Rect.Location;
  277. Rectangle? rect = FindWindowScreen(location);
  278. if (rect != null)
  279. location.Y = rect.Value.Y;
  280. DesktopLocation = location;
  281. }
  282. else
  283. {
  284. // Calculate location for modal form with parent
  285. Location = new Point(Owner.Left + Owner.Width / 2 - Width / 2,
  286. Math.Max(0, Owner.Top + Owner.Height / 2 - Height / 2));
  287. }
  288. WindowState = position.State;
  289. }
  290. static Rectangle? FindWindowScreen(Point location)
  291. {
  292. SortedDictionary<float, Rectangle> distance = new SortedDictionary<float, Rectangle>();
  293. foreach (var rect in (from screen in Screen.AllScreens
  294. select screen.WorkingArea))
  295. {
  296. if (rect.Contains(location) && !distance.ContainsKey(0.0f))
  297. return null; // title in screen
  298. int midPointX = (rect.X + rect.Width / 2);
  299. int midPointY = (rect.Y + rect.Height / 2);
  300. float d = (float)Math.Sqrt((location.X - midPointX) * (location.X - midPointX) +
  301. (location.Y - midPointY) * (location.Y - midPointY));
  302. distance.Add(d, rect);
  303. }
  304. if (distance.Count > 0)
  305. {
  306. return distance.First().Value;
  307. }
  308. else
  309. {
  310. return null;
  311. }
  312. }
  313. /// <summary>
  314. /// Save the position of a form to the user settings. Hides the window
  315. /// as a side-effect.
  316. /// </summary>
  317. /// <param name = "name">The name to use when writing the position to the
  318. /// settings</param>
  319. private void SavePosition(String name)
  320. {
  321. try
  322. {
  323. var rectangle =
  324. WindowState == FormWindowState.Normal
  325. ? DesktopBounds
  326. : RestoreBounds;
  327. var formWindowState =
  328. WindowState == FormWindowState.Maximized
  329. ? FormWindowState.Maximized
  330. : FormWindowState.Normal;
  331. // Write to the user settings:
  332. if (Properties.Settings.Default.WindowPositions == null)
  333. Properties.Settings.Default.WindowPositions = new WindowPositionList();
  334. WindowPosition windowPosition = (WindowPosition)Properties.Settings.Default.WindowPositions[name];
  335. // Don't save location when we center modal form
  336. if (windowPosition != null && Owner != null && _windowCentred)
  337. {
  338. if (rectangle.Width <= windowPosition.Rect.Width && rectangle.Height <= windowPosition.Rect.Height)
  339. rectangle.Location = windowPosition.Rect.Location;
  340. }
  341. var position = new WindowPosition(rectangle, formWindowState);
  342. Properties.Settings.Default.WindowPositions[name] = position;
  343. Properties.Settings.Default.Save();
  344. }
  345. catch (ConfigurationException)
  346. {
  347. //TODO: howto restore a corrupted config? Properties.Settings.Default.Reset() doesn't work.
  348. }
  349. }
  350. /// <summary>
  351. /// Looks up a window in the user settings and returns its saved position.
  352. /// </summary>
  353. /// <param name = "name">The name.</param>
  354. /// <returns>
  355. /// The saved window position if it exists. Null if the entry
  356. /// doesn't exist, or would not be visible on any screen in the user's
  357. /// current display setup.
  358. /// </returns>
  359. private static WindowPosition LookupWindowPosition(String name)
  360. {
  361. try
  362. {
  363. var list = Properties.Settings.Default.WindowPositions;
  364. if (list == null)
  365. return null;
  366. var position = (WindowPosition)list[name];
  367. if (position == null || position.Rect.IsEmpty)
  368. return null;
  369. if (Screen.AllScreens.Any(screen => screen.WorkingArea.IntersectsWith(position.Rect)))
  370. {
  371. return position;
  372. }
  373. }
  374. catch (ConfigurationException)
  375. {
  376. //TODO: howto restore a corrupted config? Properties.Settings.Default.Reset() doesn't work.
  377. }
  378. return null;
  379. }
  380. public virtual void AddTranslationItems(ITranslation translation)
  381. {
  382. TranslationUtils.AddTranslationItemsFromFields(Name, this, translation);
  383. }
  384. public virtual void TranslateItems(ITranslation translation)
  385. {
  386. TranslationUtils.TranslateItemsFromFields(Name, this, translation);
  387. }
  388. }
  389. }