PageRenderTime 54ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/GitUI/FileStatusList.cs

https://github.com/eisnerd/gitextensions
C# | 466 lines | 381 code | 66 blank | 19 comment | 63 complexity | 3d5c67923d846fd3d461ea9160d76876 MD5 | raw file
Possible License(s): GPL-3.0, GPL-2.0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.Specialized;
  4. using System.ComponentModel;
  5. using System.Drawing;
  6. using System.Linq;
  7. using System.Text.RegularExpressions;
  8. using System.Windows.Forms;
  9. using GitCommands;
  10. using GitUI.Properties;
  11. namespace GitUI
  12. {
  13. public sealed partial class FileStatusList : GitExtensionsControl
  14. {
  15. private const int ImageSize = 16;
  16. public FileStatusList()
  17. {
  18. InitializeComponent(); Translate();
  19. SizeChanged += FileStatusList_SizeChanged;
  20. FileStatusListBox.DrawMode = DrawMode.OwnerDrawVariable;
  21. FileStatusListBox.MeasureItem += FileStatusListBox_MeasureItem;
  22. FileStatusListBox.DrawItem += FileStatusListBox_DrawItem;
  23. FileStatusListBox.SelectedIndexChanged += FileStatusListBox_SelectedIndexChanged;
  24. FileStatusListBox.DataSourceChanged += FileStatusListBox_DataSourceChanged;
  25. FileStatusListBox.DoubleClick += FileStatusListBox_DoubleClick;
  26. FileStatusListBox.Sorted = true;
  27. FileStatusListBox.SelectionMode = SelectionMode.MultiExtended;
  28. #if !__MonoCS__ // TODO Drag'n'Drop doesnt work on Mono/Linux
  29. FileStatusListBox.MouseMove += FileStatusListBox_MouseMove;
  30. FileStatusListBox.MouseDown += FileStatusListBox_MouseDown;
  31. #endif
  32. FileStatusListBox.HorizontalScrollbar = true;
  33. NoFiles.Visible = false;
  34. NoFiles.Font = new Font(SystemFonts.MessageBoxFont, FontStyle.Italic);
  35. }
  36. void FileStatusList_SizeChanged(object sender, EventArgs e)
  37. {
  38. FileStatusListBox.HorizontalExtent = 0;
  39. }
  40. public override bool Focused
  41. {
  42. get
  43. {
  44. return FileStatusListBox.Focused;
  45. }
  46. }
  47. public new void Focus()
  48. {
  49. if (FileStatusListBox.Items.Count > 0)
  50. {
  51. if (FileStatusListBox.SelectedItem == null)
  52. FileStatusListBox.SelectedIndex = 0;
  53. FileStatusListBox.Focus();
  54. }
  55. }
  56. void FileStatusListBox_MeasureItem(object sender, MeasureItemEventArgs e)
  57. {
  58. var gitItemStatus = (GitItemStatus)FileStatusListBox.Items[e.Index];
  59. e.ItemHeight = Math.Max((int)e.Graphics.MeasureString(gitItemStatus.Name, FileStatusListBox.Font).Height, ImageSize);
  60. //Do NOT set e.ItemWidth becauase it will crash in MONO
  61. }
  62. private string GetItemText(Graphics graphics, GitItemStatus gitItemStatus)
  63. {
  64. var pathFormatter = new PathFormatter(graphics, FileStatusListBox.Font);
  65. return pathFormatter.FormatTextForDrawing(FileStatusListBox.ClientSize.Width - ImageSize,
  66. gitItemStatus.Name, gitItemStatus.OldName);
  67. }
  68. public void SetNoFilesText(string text)
  69. {
  70. NoFiles.Text = text;
  71. }
  72. public string GetNoFilesText()
  73. {
  74. return NoFiles.Text;
  75. }
  76. #if !__MonoCS__ // TODO Drag'n'Drop doesnt work on Mono/Linux
  77. void FileStatusListBox_MouseDown(object sender, MouseEventArgs e)
  78. {
  79. //SELECT
  80. if (e.Button == MouseButtons.Right)
  81. {
  82. var hoverIndex = FileStatusListBox.IndexFromPoint(e.Location);
  83. if (hoverIndex >= 0)
  84. {
  85. if (!FileStatusListBox.GetSelected(hoverIndex))
  86. {
  87. while (FileStatusListBox.SelectedIndices.Count > 0)
  88. {
  89. FileStatusListBox.SetSelected(FileStatusListBox.SelectedIndices[0], false);
  90. }
  91. FileStatusListBox.SetSelected(hoverIndex, true);
  92. }
  93. }
  94. }
  95. //DRAG
  96. if (e.Button == MouseButtons.Left)
  97. {
  98. if (SelectedItems.Count > 0)
  99. {
  100. // Remember the point where the mouse down occurred.
  101. // The DragSize indicates the size that the mouse can move
  102. // before a drag event should be started.
  103. Size dragSize = SystemInformation.DragSize;
  104. // Create a rectangle using the DragSize, with the mouse position being
  105. // at the center of the rectangle.
  106. dragBoxFromMouseDown = new Rectangle(new Point(e.X - (dragSize.Width / 2),
  107. e.Y - (dragSize.Height / 2)),
  108. dragSize);
  109. }
  110. else
  111. // Reset the rectangle if the mouse is not over an item in the ListBox.
  112. dragBoxFromMouseDown = Rectangle.Empty;
  113. }
  114. }
  115. #endif
  116. public override ContextMenuStrip ContextMenuStrip
  117. {
  118. get
  119. {
  120. return FileStatusListBox.ContextMenuStrip;
  121. }
  122. set
  123. {
  124. FileStatusListBox.ContextMenuStrip = value;
  125. }
  126. }
  127. public override ContextMenu ContextMenu
  128. {
  129. get
  130. {
  131. return FileStatusListBox.ContextMenu;
  132. }
  133. set
  134. {
  135. FileStatusListBox.ContextMenu = value;
  136. }
  137. }
  138. #if !__MonoCS__ // TODO Drag'n'Drop doesnt work on Mono/Linux
  139. private Rectangle dragBoxFromMouseDown;
  140. void FileStatusListBox_MouseMove(object sender, MouseEventArgs e)
  141. {
  142. ListBox listBox = sender as ListBox;
  143. //DRAG
  144. // If the mouse moves outside the rectangle, start the drag.
  145. if (dragBoxFromMouseDown != Rectangle.Empty &&
  146. !dragBoxFromMouseDown.Contains(e.X, e.Y))
  147. {
  148. if (SelectedItems.Count > 0)
  149. {
  150. StringCollection fileList = new StringCollection();
  151. foreach (GitItemStatus item in SelectedItems)
  152. {
  153. string fileName = GitCommands.Settings.WorkingDir + item.Name;
  154. fileList.Add(fileName.Replace('/', '\\'));
  155. }
  156. DataObject obj = new DataObject();
  157. obj.SetFileDropList(fileList);
  158. // Proceed with the drag and drop, passing in the list item.
  159. DoDragDrop(obj, DragDropEffects.Copy);
  160. dragBoxFromMouseDown = Rectangle.Empty;
  161. }
  162. }
  163. //TOOLTIP
  164. if (listBox != null)
  165. {
  166. Point point = new Point(e.X, e.Y);
  167. int hoverIndex = listBox.IndexFromPoint(point);
  168. if (hoverIndex >= 0 && hoverIndex <= listBox.Items.Count)
  169. {
  170. GitItemStatus gitItemStatus = (GitItemStatus)listBox.Items[hoverIndex];
  171. string text;
  172. if (gitItemStatus.IsRenamed || gitItemStatus.IsCopied)
  173. text = string.Concat(gitItemStatus.Name, " (", gitItemStatus.OldName, ")");
  174. else
  175. text = gitItemStatus.Name;
  176. float fTextWidth = listBox.CreateGraphics().MeasureString(text, listBox.Font).Width + 17;
  177. //Use width-itemheight because the icon drawn in front of the text is the itemheight
  178. if (fTextWidth > (FileStatusListBox.Width - FileStatusListBox.ItemHeight))
  179. {
  180. if (!DiffFilesTooltip.GetToolTip(listBox).Equals(gitItemStatus.ToString()))
  181. DiffFilesTooltip.SetToolTip(listBox, gitItemStatus.ToString());
  182. }
  183. else
  184. DiffFilesTooltip.RemoveAll();
  185. }
  186. else
  187. {
  188. DiffFilesTooltip.RemoveAll();
  189. }
  190. }
  191. }
  192. #endif
  193. [Browsable(false)]
  194. public IList<GitItemStatus> AllItems
  195. {
  196. get
  197. {
  198. IList<GitItemStatus> selectedItems = new List<GitItemStatus>();
  199. foreach (object selectedItem in FileStatusListBox.Items)
  200. selectedItems.Add((GitItemStatus)selectedItem);
  201. return selectedItems;
  202. }
  203. }
  204. [Browsable(false)]
  205. public IList<GitItemStatus> SelectedItems
  206. {
  207. get
  208. {
  209. return FileStatusListBox.SelectedItems.Cast<GitItemStatus>().ToList();
  210. }
  211. }
  212. [Browsable(false)]
  213. public GitItemStatus SelectedItem
  214. {
  215. get
  216. {
  217. return (GitItemStatus)FileStatusListBox.SelectedItem;
  218. }
  219. set
  220. {
  221. FileStatusListBox.ClearSelected();
  222. FileStatusListBox.SelectedItem = value;
  223. }
  224. }
  225. [Browsable(false)]
  226. public int SelectedIndex
  227. {
  228. get
  229. {
  230. return FileStatusListBox.SelectedIndex;
  231. }
  232. set
  233. {
  234. FileStatusListBox.ClearSelected();
  235. FileStatusListBox.SelectedIndex = value;
  236. }
  237. }
  238. private int nextIndexToSelect = -1;
  239. public void StoreNextIndexToSelect()
  240. {
  241. nextIndexToSelect = -1;
  242. foreach (int idx in FileStatusListBox.SelectedIndices)
  243. if (idx > nextIndexToSelect)
  244. nextIndexToSelect = idx;
  245. nextIndexToSelect = nextIndexToSelect - FileStatusListBox.SelectedIndices.Count + 1;
  246. }
  247. public void SelectStoredNextIndex()
  248. {
  249. nextIndexToSelect = Math.Min(nextIndexToSelect, FileStatusListBox.Items.Count - 1);
  250. if (nextIndexToSelect > -1)
  251. SelectedIndex = nextIndexToSelect;
  252. nextIndexToSelect = -1;
  253. }
  254. public event EventHandler SelectedIndexChanged;
  255. public event EventHandler DataSourceChanged;
  256. public new event EventHandler DoubleClick;
  257. public new event KeyEventHandler KeyDown;
  258. void FileStatusListBox_DoubleClick(object sender, EventArgs e)
  259. {
  260. if (DoubleClick == null)
  261. GitUICommands.Instance.StartFileHistoryDialog(this, SelectedItem.Name, Revision);
  262. else
  263. DoubleClick(sender, e);
  264. }
  265. void FileStatusListBox_SelectedIndexChanged(object sender, EventArgs e)
  266. {
  267. if (SelectedIndexChanged != null)
  268. SelectedIndexChanged(this, e);
  269. }
  270. void FileStatusListBox_DataSourceChanged(object sender, EventArgs e)
  271. {
  272. if (DataSourceChanged != null)
  273. DataSourceChanged(sender, e);
  274. }
  275. void FileStatusListBox_DrawItem(object sender, DrawItemEventArgs e)
  276. {
  277. if (e.Bounds.Height <= 0 || e.Bounds.Width <= 0 || e.Index < 0)
  278. return;
  279. e.DrawBackground();
  280. e.DrawFocusRectangle();
  281. GitItemStatus gitItemStatus = (GitItemStatus)FileStatusListBox.Items[e.Index];
  282. e.Graphics.FillRectangle(Brushes.White, e.Bounds.Left, e.Bounds.Top, ImageSize, e.Bounds.Height);
  283. int centeredImageTop = e.Bounds.Top;
  284. if ((e.Bounds.Height - ImageSize) > 1)
  285. centeredImageTop = e.Bounds.Top + ((e.Bounds.Height - ImageSize) / 2);
  286. if (gitItemStatus.IsDeleted)
  287. e.Graphics.DrawImage(Resources.Removed, e.Bounds.Left, centeredImageTop, ImageSize, ImageSize);
  288. else if (gitItemStatus.IsNew || !gitItemStatus.IsTracked)
  289. e.Graphics.DrawImage(Resources.Added, e.Bounds.Left, centeredImageTop, ImageSize, ImageSize);
  290. else if (gitItemStatus.IsChanged)
  291. e.Graphics.DrawImage(Resources.Modified, e.Bounds.Left, centeredImageTop, ImageSize, ImageSize);
  292. else if (gitItemStatus.IsRenamed)
  293. e.Graphics.DrawImage(Resources.Renamed, e.Bounds.Left, centeredImageTop, ImageSize, ImageSize);
  294. else if (gitItemStatus.IsCopied)
  295. e.Graphics.DrawImage(Resources.Copied, e.Bounds.Left, centeredImageTop, ImageSize, ImageSize);
  296. string text = GetItemText(e.Graphics, gitItemStatus);
  297. e.Graphics.DrawString(text, FileStatusListBox.Font,
  298. new SolidBrush(e.ForeColor), e.Bounds.Left + ImageSize, e.Bounds.Top);
  299. int width = (int)e.Graphics.MeasureString(text, e.Font).Width + ImageSize;
  300. ListBox listBox = (ListBox)sender;
  301. if (listBox.HorizontalExtent < width)
  302. listBox.HorizontalExtent = width;
  303. }
  304. [Browsable(false)]
  305. public bool IsEmpty
  306. {
  307. get { return GitItemStatuses == null || GitItemStatuses.Count == 0; }
  308. }
  309. [Browsable(false)]
  310. public IList<GitItemStatus> GitItemStatuses
  311. {
  312. get
  313. {
  314. return FileStatusListBox.DataSource as IList<GitItemStatus>;
  315. }
  316. set
  317. {
  318. if (value == null || value.Count == 0)
  319. NoFiles.Visible = true;
  320. else
  321. NoFiles.Visible = false;
  322. FileStatusListBox.HorizontalExtent = 0;
  323. int prevSelectedIndex = FileStatusListBox.SelectedIndex;
  324. FileStatusListBox.DataSource = value;
  325. if (value != null && value.Count == 0 && prevSelectedIndex >= 0)
  326. {
  327. //bug in the ListBox control where supplying an empty list will not trigger a SelectedIndexChanged event, so we force it to trigger
  328. FileStatusListBox_SelectedIndexChanged(this, EventArgs.Empty);
  329. }
  330. }
  331. }
  332. /// <summary>
  333. /// Gets or sets the revision.
  334. /// </summary>
  335. /// <value>The revision.</value>
  336. [Browsable(false)]
  337. public GitRevision Revision { get; set; }
  338. private void NoFiles_SizeChanged(object sender, EventArgs e)
  339. {
  340. NoFiles.Location = new Point(5, 5);
  341. NoFiles.Size = new Size(Size.Width - 10, Size.Height - 10);
  342. Refresh();
  343. }
  344. private void FileStatusListBox_KeyDown(object sender, KeyEventArgs e)
  345. {
  346. switch (e.KeyCode)
  347. {
  348. case Keys.A:
  349. {
  350. if (e.Control)
  351. {
  352. FileStatusListBox.BeginUpdate();
  353. try
  354. {
  355. for (var i = 0; i < FileStatusListBox.Items.Count; i++)
  356. FileStatusListBox.SetSelected(i, true);
  357. e.Handled = true;
  358. }
  359. finally
  360. {
  361. FileStatusListBox.EndUpdate();
  362. }
  363. }
  364. break;
  365. }
  366. default:
  367. if (KeyDown != null)
  368. KeyDown(sender, e);
  369. break;
  370. }
  371. }
  372. public int SetSelectionFilter(string filter)
  373. {
  374. return FilterFiles(RegexFor(filter));
  375. }
  376. private static Regex RegexFor(string value)
  377. {
  378. return string.IsNullOrEmpty(value)
  379. ? new Regex("^$", RegexOptions.Compiled)
  380. : new Regex(value, RegexOptions.Compiled);
  381. }
  382. private int FilterFiles(Regex filter)
  383. {
  384. try
  385. {
  386. SuspendLayout();
  387. var items = FileStatusListBox.Items.Cast<GitItemStatus>().ToList();
  388. for (var i = 0; i < items.Count; i++)
  389. {
  390. FileStatusListBox.SetSelected(i, filter.IsMatch(items[i].Name));
  391. }
  392. return FileStatusListBox.SelectedIndices.Count;
  393. }
  394. finally
  395. {
  396. ResumeLayout(true);
  397. }
  398. }
  399. }
  400. }