PageRenderTime 34ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/GitUI/RevisionGrid.cs

https://github.com/eisnerd/gitextensions
C# | 2372 lines | 1952 code | 370 blank | 50 comment | 366 complexity | c320cf16e8d9c7602f49af41767b31c6 MD5 | raw file
Possible License(s): GPL-3.0, GPL-2.0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Drawing;
  5. using System.Drawing.Drawing2D;
  6. using System.IO;
  7. using System.Linq;
  8. using System.Text.RegularExpressions;
  9. using System.Threading;
  10. using System.Windows.Forms;
  11. using GitCommands;
  12. using GitCommands.Git;
  13. using GitUI.Hotkey;
  14. using GitUI.Script;
  15. using GitUI.Tag;
  16. using Gravatar;
  17. using ResourceManager.Translation;
  18. using System.DirectoryServices;
  19. namespace GitUI
  20. {
  21. public enum RevisionGridLayout
  22. {
  23. FilledBranchesSmall = 1,
  24. FilledBranchesSmallWithGraph = 2,
  25. Small = 3,
  26. SmallWithGraph = 4,
  27. Card = 5,
  28. CardWithGraph = 6,
  29. LargeCard = 7,
  30. LargeCardWithGraph = 8
  31. }
  32. [DefaultEvent("DoubleClick")]
  33. public sealed partial class RevisionGrid : GitExtensionsControl
  34. {
  35. private readonly IndexWatcher _indexWatcher = new IndexWatcher();
  36. private readonly TranslationString _currentWorkingDirChanges = new TranslationString("Current uncommitted changes");
  37. private readonly TranslationString _currentIndex = new TranslationString("Commit index");
  38. private readonly TranslationString _areYouSureYouWantCheckout = new TranslationString("Are you sure to checkout the selected revision?");
  39. private readonly TranslationString _areYouSureYouWantCheckoutCaption = new TranslationString("Checkout revision");
  40. private readonly TranslationString _droppingFilesBlocked = new TranslationString("For you own protection dropping more than 10 patch files at once is blocked!");
  41. private const int NODE_DIMENSION = 8;
  42. private const int LANE_WIDTH = 13;
  43. private const int LANE_LINE_WIDTH = 2;
  44. private Brush selectedItemBrush;
  45. private readonly FormRevisionFilter _revisionFilter = new FormRevisionFilter();
  46. private readonly SynchronizationContext _syncContext;
  47. public string LogParam = "HEAD --all --boundary";
  48. private bool _initialLoad = true;
  49. private string _initialSelectedRevision;
  50. private string _lastQuickSearchString = string.Empty;
  51. private Label _quickSearchLabel;
  52. private string _quickSearchString;
  53. private RevisionGraph _revisionGraphCommand;
  54. private RevisionGridLayout layout;
  55. private int rowHeigth;
  56. public RevisionGrid()
  57. {
  58. _syncContext = SynchronizationContext.Current;
  59. InitLayout();
  60. InitializeComponent();
  61. this.Loading.Image = global::GitUI.Properties.Resources.loadingpanel;
  62. Translate();
  63. NormalFont = SystemFonts.DefaultFont;
  64. Loading.Paint += Loading_Paint;
  65. Revisions.CellPainting += RevisionsCellPainting;
  66. Revisions.CellFormatting += RevisionsCellFormatting;
  67. Revisions.KeyDown += RevisionsKeyDown;
  68. showAuthorDateToolStripMenuItem.Checked = Settings.ShowAuthorDate;
  69. orderRevisionsByDateToolStripMenuItem.Checked = Settings.OrderRevisionByDate;
  70. showRelativeDateToolStripMenuItem.Checked = Settings.RelativeDate;
  71. drawNonrelativesGrayToolStripMenuItem.Checked = Settings.RevisionGraphDrawNonRelativesGray;
  72. showGitNotesToolStripMenuItem.Checked = Settings.ShowGitNotes;
  73. BranchFilter = String.Empty;
  74. SetShowBranches();
  75. Filter = "";
  76. FixedFilter = "";
  77. InMemFilterIgnoreCase = false;
  78. InMemAuthorFilter = "";
  79. InMemCommitterFilter = "";
  80. InMemMessageFilter = "";
  81. AllowGraphWithFilter = false;
  82. _quickSearchString = "";
  83. quickSearchTimer.Tick += QuickSearchTimerTick;
  84. Revisions.Loading += RevisionsLoading;
  85. //Allow to drop patch file on revisiongrid
  86. Revisions.DragEnter += Revisions_DragEnter;
  87. Revisions.DragDrop += Revisions_DragDrop;
  88. Revisions.AllowDrop = true;
  89. Revisions.ColumnHeadersVisible = false;
  90. this.HotkeysEnabled = true;
  91. try
  92. {
  93. SetRevisionsLayout((RevisionGridLayout)Settings.RevisionGraphLayout);
  94. }
  95. catch
  96. {
  97. SetRevisionsLayout(RevisionGridLayout.SmallWithGraph);
  98. }
  99. }
  100. void Loading_Paint(object sender, PaintEventArgs e)
  101. {
  102. // If our loading state has changed since the last paint, update it.
  103. if (Loading != null)
  104. {
  105. if (Loading.Visible != _isLoading)
  106. {
  107. Loading.Visible = _isLoading;
  108. }
  109. }
  110. }
  111. [Browsable(false)]
  112. public Font HeadFont { get; private set; }
  113. [Browsable(false)]
  114. public Font SuperprojectFont { get; private set; }
  115. [Browsable(false)]
  116. public int LastScrollPos { get; private set; }
  117. [Browsable(false)]
  118. public IComparable[] LastSelectedRows { get; private set; }
  119. [Browsable(false)]
  120. public Font RefsFont { get; private set; }
  121. private Font _normalFont;
  122. [Category("Appearance")]
  123. public Font NormalFont
  124. {
  125. get { return _normalFont; }
  126. set
  127. {
  128. _normalFont = value;
  129. Message.DefaultCellStyle.Font = _normalFont;
  130. Date.DefaultCellStyle.Font = _normalFont;
  131. RefsFont = IsFilledBranchesLayout() ? _normalFont : new Font(_normalFont, FontStyle.Bold);
  132. HeadFont = new Font(_normalFont, FontStyle.Bold);
  133. SuperprojectFont = new Font(_normalFont, FontStyle.Underline);
  134. }
  135. }
  136. [Category("Filter")]
  137. public string Filter { get; set; }
  138. [Category("Filter")]
  139. public string FixedFilter { get; set; }
  140. [Category("Filter")]
  141. [DefaultValue(false)]
  142. public bool InMemFilterIgnoreCase { get; set; }
  143. [Category("Filter")]
  144. public string InMemAuthorFilter { get; set; }
  145. [Category("Filter")]
  146. public string InMemCommitterFilter { get; set; }
  147. [Category("Filter")]
  148. public string InMemMessageFilter { get; set; }
  149. [Category("Filter")]
  150. public string BranchFilter { get; set; }
  151. [Category("Filter")]
  152. [DefaultValue(false)]
  153. public bool AllowGraphWithFilter { get; set; }
  154. [Browsable(false)]
  155. public string CurrentCheckout { get; set; }
  156. [Browsable(false)]
  157. public string SuperprojectCurrentCheckout { get; set; }
  158. [Browsable(false)]
  159. public int LastRow { get; set; }
  160. [Description("Indicates whether the user is allowed to select more than one commit at a time.")]
  161. [Category("Behavior")]
  162. [DefaultValue(true)]
  163. [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
  164. public bool MultiSelect
  165. {
  166. get { return Revisions.MultiSelect; }
  167. set { Revisions.MultiSelect = value; }
  168. }
  169. public void SetInitialRevision(GitRevision initialSelectedRevision)
  170. {
  171. _initialSelectedRevision = initialSelectedRevision != null ? initialSelectedRevision.Guid : null;
  172. }
  173. public event EventHandler ActionOnRepositoryPerformed;
  174. private void OnActionOnRepositoryPerformed()
  175. {
  176. if (ActionOnRepositoryPerformed != null)
  177. ActionOnRepositoryPerformed(this, null);
  178. }
  179. private bool _isLoading;
  180. private void RevisionsLoading(bool isLoading)
  181. {
  182. // Since this can happen on a background thread, we'll just set a
  183. // flag and deal with it next time we paint (a bit of a hack, but
  184. // it works)
  185. _isLoading = isLoading;
  186. }
  187. private void ShowQuickSearchString()
  188. {
  189. if (_quickSearchLabel == null)
  190. {
  191. _quickSearchLabel
  192. = new Label
  193. {
  194. Location = new Point(10, 10),
  195. BorderStyle = BorderStyle.FixedSingle,
  196. ForeColor = SystemColors.InfoText,
  197. BackColor = SystemColors.Info
  198. };
  199. Controls.Add(_quickSearchLabel);
  200. }
  201. _quickSearchLabel.Visible = true;
  202. _quickSearchLabel.BringToFront();
  203. _quickSearchLabel.Text = _quickSearchString;
  204. _quickSearchLabel.AutoSize = true;
  205. }
  206. private void HideQuickSearchString()
  207. {
  208. if (_quickSearchLabel != null)
  209. _quickSearchLabel.Visible = false;
  210. }
  211. private void QuickSearchTimerTick(object sender, EventArgs e)
  212. {
  213. quickSearchTimer.Stop();
  214. _quickSearchString = "";
  215. HideQuickSearchString();
  216. }
  217. private void RestartQuickSearchTimer()
  218. {
  219. quickSearchTimer.Stop();
  220. quickSearchTimer.Interval = Settings.RevisionGridQuickSearchTimeout;
  221. quickSearchTimer.Start();
  222. }
  223. private void RevisionsKeyDown(object sender, KeyEventArgs e)
  224. {
  225. var curIndex = -1;
  226. if (Revisions.SelectedRows.Count > 0)
  227. curIndex = Revisions.SelectedRows[0].Index;
  228. if (e.Alt && (e.KeyCode == Keys.Up || e.KeyCode == Keys.Down))
  229. {
  230. RestartQuickSearchTimer();
  231. bool reverse = e.KeyCode == Keys.Up;
  232. var nextIndex = 0;
  233. if (curIndex >= 0)
  234. nextIndex = reverse ? curIndex - 1 : curIndex + 1;
  235. _quickSearchString = _lastQuickSearchString;
  236. FindNextMatch(nextIndex, _quickSearchString, reverse);
  237. ShowQuickSearchString();
  238. e.Handled = true;
  239. return;
  240. }
  241. curIndex = curIndex >= 0 ? curIndex : 0;
  242. int key = e.KeyValue;
  243. if (!e.Alt && !e.Control && key == 8 && _quickSearchString.Length > 1) //backspace
  244. {
  245. RestartQuickSearchTimer();
  246. _quickSearchString = _quickSearchString.Substring(0, _quickSearchString.Length - 1);
  247. FindNextMatch(curIndex, _quickSearchString, false);
  248. _lastQuickSearchString = _quickSearchString;
  249. e.Handled = true;
  250. ShowQuickSearchString();
  251. }
  252. else if (!e.Alt && !e.Control && (char.IsLetterOrDigit((char)key) || char.IsNumber((char)key) || char.IsSeparator((char)key) || key == 191))
  253. {
  254. RestartQuickSearchTimer();
  255. //The code below is meant to fix the weird keyvalues when pressing keys e.g. ".".
  256. switch (key)
  257. {
  258. case 51:
  259. _quickSearchString = e.Shift ? string.Concat(_quickSearchString, "#").ToLower() : string.Concat(_quickSearchString, "3").ToLower();
  260. break;
  261. case 188:
  262. _quickSearchString = string.Concat(_quickSearchString, ",").ToLower();
  263. break;
  264. case 189:
  265. _quickSearchString = e.Shift ? string.Concat(_quickSearchString, "_").ToLower() : string.Concat(_quickSearchString, "-").ToLower();
  266. break;
  267. case 190:
  268. _quickSearchString = string.Concat(_quickSearchString, ".").ToLower();
  269. break;
  270. case 191:
  271. _quickSearchString = string.Concat(_quickSearchString, "/").ToLower();
  272. break;
  273. default:
  274. _quickSearchString = string.Concat(_quickSearchString, (char)e.KeyValue).ToLower();
  275. break;
  276. }
  277. FindNextMatch(curIndex, _quickSearchString, false);
  278. _lastQuickSearchString = _quickSearchString;
  279. e.Handled = true;
  280. ShowQuickSearchString();
  281. }
  282. else
  283. {
  284. _quickSearchString = "";
  285. HideQuickSearchString();
  286. e.Handled = false;
  287. }
  288. }
  289. private void FindNextMatch(int startIndex, string searchString, bool reverse)
  290. {
  291. if (Revisions.RowCount == 0)
  292. return;
  293. var searchResult =
  294. reverse
  295. ? SearchInReverseOrder(startIndex, searchString)
  296. : SearchForward(startIndex, searchString);
  297. if (!searchResult.HasValue)
  298. return;
  299. Revisions.ClearSelection();
  300. Revisions.Rows[searchResult.Value].Selected = true;
  301. Revisions.CurrentCell = Revisions.Rows[searchResult.Value].Cells[1];
  302. }
  303. private int? SearchForward(int startIndex, string searchString)
  304. {
  305. // Check for out of bounds roll over if required
  306. int index;
  307. if (startIndex < 0 || startIndex >= Revisions.RowCount)
  308. startIndex = 0;
  309. for (index = startIndex; index < Revisions.RowCount; ++index)
  310. {
  311. if (GetRevision(index).MatchesSearchString(searchString))
  312. return index;
  313. }
  314. // We didn't find it so start searching from the top
  315. for (index = 0; index < startIndex; ++index)
  316. {
  317. if (GetRevision(index).MatchesSearchString(searchString))
  318. return index;
  319. }
  320. return null;
  321. }
  322. private int? SearchInReverseOrder(int startIndex, string searchString)
  323. {
  324. // Check for out of bounds roll over if required
  325. int index;
  326. if (startIndex < 0 || startIndex >= Revisions.RowCount)
  327. startIndex = Revisions.RowCount - 1;
  328. for (index = startIndex; index >= 0; --index)
  329. {
  330. if (GetRevision(index).MatchesSearchString(searchString))
  331. return index;
  332. }
  333. // We didn't find it so start searching from the bottom
  334. for (index = Revisions.RowCount - 1; index > startIndex; --index)
  335. {
  336. if (GetRevision(index).MatchesSearchString(searchString))
  337. return index;
  338. }
  339. return null;
  340. }
  341. public void DisableContextMenu()
  342. {
  343. Revisions.ContextMenuStrip = null;
  344. }
  345. public void FormatQuickFilter(string filter,
  346. bool[] parameters,
  347. out string revListArgs,
  348. out string inMemMessageFilter,
  349. out string inMemCommitterFilter,
  350. out string inMemAuthorFilter)
  351. {
  352. revListArgs = string.Empty;
  353. inMemMessageFilter = string.Empty;
  354. inMemCommitterFilter = string.Empty;
  355. inMemAuthorFilter = string.Empty;
  356. if (!string.IsNullOrEmpty(filter))
  357. {
  358. // hash filtering only possible in memory
  359. var cmdLineSafe = GitCommandHelpers.VersionInUse.IsRegExStringCmdPassable(filter);
  360. revListArgs = " --regexp-ignore-case ";
  361. if (parameters[0])
  362. if (cmdLineSafe)
  363. revListArgs += "--grep=\"" + filter + "\" ";
  364. else
  365. inMemMessageFilter = filter;
  366. if (parameters[1])
  367. if (cmdLineSafe)
  368. revListArgs += "--committer=\"" + filter + "\" ";
  369. else
  370. inMemCommitterFilter = filter;
  371. if (parameters[2])
  372. if (cmdLineSafe)
  373. revListArgs += "--author=\"" + filter + "\" ";
  374. else
  375. inMemAuthorFilter = filter;
  376. if (parameters[3])
  377. if (cmdLineSafe)
  378. revListArgs += "\"-S" + filter + "\" ";
  379. else
  380. throw new InvalidOperationException("Filter text not valid for \"Diff contains\" filter.");
  381. }
  382. }
  383. public bool SetAndApplyBranchFilter(string filter)
  384. {
  385. if (filter.Equals(_revisionFilter.GetBranchFilter()))
  386. return false;
  387. if (filter.Equals(""))
  388. {
  389. Settings.BranchFilterEnabled = false;
  390. Settings.ShowCurrentBranchOnly = true;
  391. }
  392. else
  393. {
  394. Settings.BranchFilterEnabled = true;
  395. Settings.ShowCurrentBranchOnly = false;
  396. _revisionFilter.SetBranchFilter(filter);
  397. }
  398. SetShowBranches();
  399. return true;
  400. }
  401. public void SetLimit(int limit)
  402. {
  403. _revisionFilter.SetLimit(limit);
  404. }
  405. public override void Refresh()
  406. {
  407. SetRevisionsLayout();
  408. base.Refresh();
  409. Revisions.Refresh();
  410. }
  411. protected override void OnCreateControl()
  412. {
  413. base.OnCreateControl();
  414. _isLoading = true;
  415. Error.Visible = false;
  416. NoCommits.Visible = false;
  417. NoGit.Visible = false;
  418. Revisions.Visible = false;
  419. Loading.Visible = true;
  420. Loading.BringToFront();
  421. }
  422. public new void Load()
  423. {
  424. if (!DesignMode)
  425. ReloadHotkeys();
  426. ForceRefreshRevisions();
  427. }
  428. public event EventHandler SelectionChanged;
  429. public void SetSelectedIndex(int index)
  430. {
  431. if (Revisions.Rows[index].Selected)
  432. return;
  433. Revisions.ClearSelection();
  434. Revisions.Rows[index].Selected = true;
  435. Revisions.CurrentCell = Revisions.Rows[index].Cells[1];
  436. Revisions.Select();
  437. }
  438. public void SetSelectedRevision(GitRevision revision)
  439. {
  440. if (revision != null)
  441. {
  442. for (var i = 0; i < Revisions.RowCount; i++)
  443. {
  444. if (GetRevision(i).Guid == revision.Guid)
  445. {
  446. SetSelectedIndex(i);
  447. return;
  448. }
  449. }
  450. }
  451. Revisions.ClearSelection();
  452. Revisions.Select();
  453. }
  454. private void RevisionsSelectionChanged(object sender, EventArgs e)
  455. {
  456. if (Revisions.SelectedRows.Count > 0)
  457. LastRow = Revisions.SelectedRows[0].Index;
  458. SelectionTimer.Enabled = false;
  459. SelectionTimer.Stop();
  460. SelectionTimer.Enabled = true;
  461. SelectionTimer.Start();
  462. }
  463. public List<GitRevision> GetSelectedRevisions()
  464. {
  465. return GetSelectedRevisions(null);
  466. }
  467. public List<GitRevision> GetSelectedRevisions(SortDirection? direction)
  468. {
  469. var rows = Revisions
  470. .SelectedRows
  471. .Cast<DataGridViewRow>()
  472. .Where(row => Revisions.RowCount > row.Index);
  473. if (direction.HasValue)
  474. {
  475. int d = direction.Value == SortDirection.Ascending ? 1 : -1;
  476. rows = rows.OrderBy((row) => row.Index, (r1, r2) => d * (r1 - r2));
  477. }
  478. return rows
  479. .Select(row => GetRevision(row.Index))
  480. .ToList();
  481. }
  482. public GitRevision GetRevision(int aRow)
  483. {
  484. return Revisions.GetRowData(aRow);
  485. }
  486. public GitRevision GetCurrentRevision()
  487. {
  488. const string formatString =
  489. /* Tree */ "%T%n" +
  490. /* Author Name */ "%aN%n" +
  491. /* Author Date */ "%ai%n" +
  492. /* Committer Name */ "%cN%n" +
  493. /* Committer Date */ "%ci%n" +
  494. /* Commit Message */ "%s";
  495. string cmd = "log -n 1 --pretty=format:" + formatString + " " + CurrentCheckout;
  496. var RevInfo = Settings.Module.RunGitCmd(cmd);
  497. string[] Infos = RevInfo.Split('\n');
  498. var Revision = new GitRevision(CurrentCheckout)
  499. {
  500. TreeGuid = Infos[0],
  501. Author = Infos[1],
  502. Committer = Infos[3],
  503. Message = Infos[5]
  504. };
  505. DateTime date;
  506. DateTime.TryParse(Infos[2], out date);
  507. Revision.AuthorDate = date;
  508. DateTime.TryParse(Infos[4], out date);
  509. Revision.CommitDate = date;
  510. List<GitHead> heads = Settings.Module.GetHeads(true, true);
  511. foreach (GitHead head in heads)
  512. {
  513. if (head.Guid.Equals(Revision.Guid))
  514. Revision.Heads.Add(head);
  515. }
  516. return Revision;
  517. }
  518. public void RefreshRevisions()
  519. {
  520. if (IndexWatcher.IndexChanged)
  521. ForceRefreshRevisions();
  522. }
  523. private class RevisionGraphInMemFilterOr : RevisionGraphInMemFilter
  524. {
  525. private RevisionGraphInMemFilter fFilter1;
  526. private RevisionGraphInMemFilter fFilter2;
  527. public RevisionGraphInMemFilterOr(RevisionGraphInMemFilter aFilter1,
  528. RevisionGraphInMemFilter aFilter2)
  529. {
  530. fFilter1 = aFilter1;
  531. fFilter2 = aFilter2;
  532. }
  533. public override bool PassThru(GitRevision rev)
  534. {
  535. return fFilter1.PassThru(rev) || fFilter2.PassThru(rev);
  536. }
  537. }
  538. private class RevisionGridInMemFilter : RevisionGraphInMemFilter
  539. {
  540. private readonly bool _IgnoreCase;
  541. private readonly string _AuthorFilter;
  542. private readonly Regex _AuthorFilterRegex;
  543. private readonly string _CommitterFilter;
  544. private readonly Regex _CommitterFilterRegex;
  545. private readonly string _MessageFilter;
  546. private readonly Regex _MessageFilterRegex;
  547. public RevisionGridInMemFilter(string authorFilter, string committerFilter, string messageFilter, bool ignoreCase)
  548. {
  549. _IgnoreCase = ignoreCase;
  550. SetUpVars(authorFilter, ref _AuthorFilter, ref _AuthorFilterRegex);
  551. SetUpVars(committerFilter, ref _CommitterFilter, ref _CommitterFilterRegex);
  552. SetUpVars(messageFilter, ref _MessageFilter, ref _MessageFilterRegex);
  553. }
  554. private void SetUpVars(string filterValue,
  555. ref string filterStr,
  556. ref Regex filterRegEx)
  557. {
  558. RegexOptions opts = RegexOptions.None;
  559. if (_IgnoreCase) opts = opts | RegexOptions.IgnoreCase;
  560. filterStr = filterValue != null ? filterValue.Trim() : string.Empty;
  561. try
  562. {
  563. filterRegEx = new Regex(filterStr, opts);
  564. }
  565. catch (ArgumentException)
  566. {
  567. filterRegEx = null;
  568. }
  569. }
  570. private static bool CheckCondition(string filter, Regex regex, string value)
  571. {
  572. return string.IsNullOrEmpty(filter) ||
  573. ((regex != null) && regex.Match(value).Success);
  574. }
  575. public override bool PassThru(GitRevision rev)
  576. {
  577. return CheckCondition(_AuthorFilter, _AuthorFilterRegex, rev.Author) &&
  578. CheckCondition(_CommitterFilter, _CommitterFilterRegex, rev.Committer) &&
  579. CheckCondition(_MessageFilter, _MessageFilterRegex, rev.Message);
  580. }
  581. public static RevisionGridInMemFilter CreateIfNeeded(string authorFilter,
  582. string committerFilter,
  583. string messageFilter,
  584. bool ignoreCase)
  585. {
  586. if (!(string.IsNullOrEmpty(authorFilter) &&
  587. string.IsNullOrEmpty(committerFilter) &&
  588. string.IsNullOrEmpty(messageFilter)))
  589. return new RevisionGridInMemFilter(authorFilter,
  590. committerFilter,
  591. messageFilter,
  592. ignoreCase);
  593. else
  594. return null;
  595. }
  596. }
  597. public void ReloadHotkeys()
  598. {
  599. this.Hotkeys = HotkeySettingsManager.LoadHotkeys(HotkeySettingsName);
  600. }
  601. public void ReloadTranslation()
  602. {
  603. Translate();
  604. }
  605. public void ForceRefreshRevisions()
  606. {
  607. try
  608. {
  609. ApplyFilterFromRevisionFilterDialog();
  610. _initialLoad = true;
  611. LastScrollPos = Revisions.FirstDisplayedScrollingRowIndex;
  612. DisposeRevisionGraphCommand();
  613. var newCurrentCheckout = Settings.Module.GetCurrentCheckout();
  614. var newSuperprojectCurrentCheckout = Settings.Module.GetSuperprojectCurrentCheckout();
  615. // If the current checkout changed, don't get the currently selected rows, select the
  616. // new current checkout instead.
  617. if (newCurrentCheckout == CurrentCheckout)
  618. {
  619. LastSelectedRows = Revisions.SelectedIds;
  620. }
  621. else
  622. {
  623. // This is a new checkout, so ensure the variable is cleared out.
  624. LastSelectedRows = null;
  625. }
  626. Revisions.ClearSelection();
  627. CurrentCheckout = newCurrentCheckout;
  628. SuperprojectCurrentCheckout = newSuperprojectCurrentCheckout;
  629. Revisions.Clear();
  630. Error.Visible = false;
  631. if (!Settings.Module.ValidWorkingDir())
  632. {
  633. Revisions.Visible = false;
  634. NoCommits.Visible = true;
  635. Loading.Visible = false;
  636. NoGit.Visible = true;
  637. string dir = Settings.Module.WorkingDir;
  638. if (String.IsNullOrEmpty(dir) || !Directory.Exists(dir) ||
  639. Directory.GetDirectories(dir).Length == 0 &&
  640. Directory.GetFiles(dir).Length == 0)
  641. CloneRepository.Show();
  642. else
  643. CloneRepository.Hide();
  644. NoGit.BringToFront();
  645. return;
  646. }
  647. NoCommits.Visible = false;
  648. NoGit.Visible = false;
  649. Revisions.Visible = true;
  650. Revisions.BringToFront();
  651. Revisions.Enabled = false;
  652. Loading.Visible = true;
  653. Loading.BringToFront();
  654. _isLoading = true;
  655. base.Refresh();
  656. IndexWatcher.Reset();
  657. if (!Settings.ShowGitNotes && !LogParam.Contains(" --not --glob=notes --not"))
  658. LogParam = LogParam + " --not --glob=notes --not";
  659. if (Settings.ShowGitNotes && LogParam.Contains(" --not --glob=notes --not"))
  660. LogParam = LogParam.Replace(" --not --glob=notes --not", string.Empty);
  661. RevisionGridInMemFilter revisionFilterIMF = RevisionGridInMemFilter.CreateIfNeeded(_revisionFilter.GetInMemAuthorFilter(),
  662. _revisionFilter.GetInMemCommitterFilter(),
  663. _revisionFilter.GetInMemMessageFilter(),
  664. _revisionFilter.GetIgnoreCase());
  665. RevisionGridInMemFilter filterBarIMF = RevisionGridInMemFilter.CreateIfNeeded(InMemAuthorFilter,
  666. InMemCommitterFilter,
  667. InMemMessageFilter,
  668. InMemFilterIgnoreCase);
  669. RevisionGraphInMemFilter revGraphIMF;
  670. if (revisionFilterIMF != null && filterBarIMF != null)
  671. revGraphIMF = new RevisionGraphInMemFilterOr(revisionFilterIMF, filterBarIMF);
  672. else if (revisionFilterIMF != null)
  673. revGraphIMF = revisionFilterIMF;
  674. else
  675. revGraphIMF = filterBarIMF;
  676. _revisionGraphCommand = new RevisionGraph { BranchFilter = BranchFilter, LogParam = LogParam + _revisionFilter.GetFilter() + Filter + FixedFilter };
  677. _revisionGraphCommand.Updated += GitGetCommitsCommandUpdated;
  678. _revisionGraphCommand.Exited += GitGetCommitsCommandExited;
  679. _revisionGraphCommand.Error += _revisionGraphCommand_Error;
  680. _revisionGraphCommand.InMemFilter = revGraphIMF;
  681. //_revisionGraphCommand.BeginUpdate += ((s, e) => Revisions.Invoke((Action) (() => Revisions.Clear())));
  682. _revisionGraphCommand.Execute();
  683. LoadRevisions();
  684. SetRevisionsLayout();
  685. }
  686. catch (Exception exception)
  687. {
  688. Error.Visible = true;
  689. Error.BringToFront();
  690. MessageBox.Show(this, exception.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
  691. }
  692. }
  693. private void _revisionGraphCommand_Error(object sender, EventArgs e)
  694. {
  695. // This has to happen on the UI thread
  696. _syncContext.Send(o =>
  697. {
  698. Error.Visible = true;
  699. //Error.BringToFront();
  700. NoGit.Visible = false;
  701. NoCommits.Visible = false;
  702. Revisions.Visible = false;
  703. Loading.Visible = false;
  704. }, this);
  705. }
  706. private void GitGetCommitsCommandUpdated(object sender, EventArgs e)
  707. {
  708. var updatedEvent = (RevisionGraph.RevisionGraphUpdatedEventArgs)e;
  709. UpdateGraph(updatedEvent.Revision);
  710. }
  711. private bool FilterIsApplied(bool inclBranchFilter)
  712. {
  713. return (inclBranchFilter && !string.IsNullOrEmpty(BranchFilter)) ||
  714. !(string.IsNullOrEmpty(Filter) &&
  715. !_revisionFilter.FilterEnabled() &&
  716. string.IsNullOrEmpty(InMemAuthorFilter) &&
  717. string.IsNullOrEmpty(InMemCommitterFilter) &&
  718. string.IsNullOrEmpty(InMemMessageFilter));
  719. }
  720. private bool ShouldHideGraph(bool inclBranchFilter)
  721. {
  722. return (inclBranchFilter && !string.IsNullOrEmpty(BranchFilter)) ||
  723. !(!_revisionFilter.ShouldHideGraph() &&
  724. string.IsNullOrEmpty(InMemAuthorFilter) &&
  725. string.IsNullOrEmpty(InMemCommitterFilter) &&
  726. string.IsNullOrEmpty(InMemMessageFilter));
  727. }
  728. private void DisposeRevisionGraphCommand()
  729. {
  730. if (_revisionGraphCommand != null)
  731. {
  732. //Dispose command, it is not needed anymore
  733. _revisionGraphCommand.Updated -= GitGetCommitsCommandUpdated;
  734. _revisionGraphCommand.Exited -= GitGetCommitsCommandExited;
  735. _revisionGraphCommand.Error -= _revisionGraphCommand_Error;
  736. _revisionGraphCommand.Dispose();
  737. _revisionGraphCommand = null;
  738. }
  739. }
  740. private void GitGetCommitsCommandExited(object sender, EventArgs e)
  741. {
  742. _isLoading = false;
  743. if (_revisionGraphCommand.RevisionCount == 0 &&
  744. !FilterIsApplied(true))
  745. {
  746. // This has to happen on the UI thread
  747. _syncContext.Send(o =>
  748. {
  749. NoGit.Visible = false;
  750. NoCommits.Visible = true;
  751. //NoCommits.BringToFront();
  752. Revisions.Visible = false;
  753. Loading.Visible = false;
  754. }, this);
  755. }
  756. else
  757. {
  758. // This has to happen on the UI thread
  759. _syncContext.Send(o =>
  760. {
  761. UpdateGraph(null);
  762. Loading.Visible = false;
  763. SelectInitialRevision();
  764. _isLoading = false;
  765. }, this);
  766. }
  767. }
  768. private void SelectInitialRevision()
  769. {
  770. if (string.IsNullOrEmpty(_initialSelectedRevision) || Revisions.SelectedRows.Count != 0)
  771. return;
  772. for (var i = 0; i < Revisions.RowCount; i++)
  773. {
  774. if (GetRevision(i).Guid == _initialSelectedRevision)
  775. SetSelectedIndex(i);
  776. }
  777. }
  778. private static string GetDateHeaderText()
  779. {
  780. return Settings.ShowAuthorDate ? Strings.GetAuthorDateText() : Strings.GetCommitDateText();
  781. }
  782. private void LoadRevisions()
  783. {
  784. if (_revisionGraphCommand == null)
  785. {
  786. return;
  787. }
  788. Revisions.SuspendLayout();
  789. Revisions.Columns[1].HeaderText = Strings.GetMessageText();
  790. Revisions.Columns[2].HeaderText = Strings.GetAuthorText();
  791. Revisions.Columns[3].HeaderText = GetDateHeaderText();
  792. Revisions.SelectionChanged -= RevisionsSelectionChanged;
  793. if (LastSelectedRows != null)
  794. {
  795. Revisions.SelectedIds = LastSelectedRows;
  796. LastSelectedRows = null;
  797. }
  798. else if (_initialSelectedRevision == null)
  799. {
  800. Revisions.SelectedIds = new IComparable[] { CurrentCheckout };
  801. }
  802. if (LastScrollPos > 0 && Revisions.RowCount > LastScrollPos)
  803. {
  804. Revisions.FirstDisplayedScrollingRowIndex = LastScrollPos;
  805. LastScrollPos = -1;
  806. }
  807. Revisions.Enabled = true;
  808. Revisions.Focus();
  809. Revisions.SelectionChanged += RevisionsSelectionChanged;
  810. Revisions.ResumeLayout();
  811. if (!_initialLoad)
  812. return;
  813. _initialLoad = false;
  814. SelectionTimer.Enabled = false;
  815. SelectionTimer.Stop();
  816. SelectionTimer.Enabled = true;
  817. SelectionTimer.Start();
  818. }
  819. private void RevisionsCellPainting(object sender, DataGridViewCellPaintingEventArgs e)
  820. {
  821. // If our loading state has changed since the last paint, update it.
  822. if (Loading != null)
  823. {
  824. if (Loading.Visible != _isLoading)
  825. {
  826. Loading.Visible = _isLoading;
  827. }
  828. }
  829. // The graph column is handled by the DvcsGraph
  830. if (e.ColumnIndex == 0)
  831. {
  832. return;
  833. }
  834. var column = e.ColumnIndex;
  835. if (e.RowIndex < 0 || (e.State & DataGridViewElementStates.Visible) == 0)
  836. return;
  837. if (Revisions.RowCount <= e.RowIndex)
  838. return;
  839. var revision = GetRevision(e.RowIndex);
  840. if (revision == null)
  841. return;
  842. e.Handled = true;
  843. bool isRowSelected = ((e.State & DataGridViewElementStates.Selected) == DataGridViewElementStates.Selected);
  844. if (isRowSelected /*&& !showRevisionCards*/)
  845. e.Graphics.FillRectangle(selectedItemBrush, e.CellBounds);
  846. else
  847. e.Graphics.FillRectangle(new SolidBrush(Color.White), e.CellBounds);
  848. Color foreColor;
  849. if (!Settings.RevisionGraphDrawNonRelativesGray || !Settings.RevisionGraphDrawNonRelativesTextGray || Revisions.RowIsRelative(e.RowIndex))
  850. {
  851. foreColor = isRowSelected && IsFilledBranchesLayout()
  852. ? SystemColors.HighlightText
  853. : e.CellStyle.ForeColor;
  854. }
  855. else
  856. {
  857. foreColor = Color.LightGray;
  858. }
  859. Brush foreBrush = new SolidBrush(foreColor);
  860. var rowFont = NormalFont;
  861. if (revision.Guid == CurrentCheckout /*&& !showRevisionCards*/)
  862. rowFont = HeadFont;
  863. else if (revision.Guid == SuperprojectCurrentCheckout)
  864. rowFont = SuperprojectFont;
  865. switch (column)
  866. {
  867. case 1: //Description!!
  868. {
  869. int baseOffset = 0;
  870. if (IsCardLayout())
  871. {
  872. baseOffset = 5;
  873. Rectangle cellRectangle = new Rectangle(e.CellBounds.Left + baseOffset, e.CellBounds.Top + 1, e.CellBounds.Width - (baseOffset * 2), e.CellBounds.Height - 4);
  874. if (!Settings.RevisionGraphDrawNonRelativesGray || Revisions.RowIsRelative(e.RowIndex))
  875. {
  876. e.Graphics.FillRectangle(
  877. new LinearGradientBrush(cellRectangle,
  878. Color.FromArgb(255, 220, 220, 231),
  879. Color.FromArgb(255, 240, 240, 250), 90, false), cellRectangle);
  880. e.Graphics.DrawRectangle(new Pen(Color.FromArgb(255, 200, 200, 200), 1), cellRectangle);
  881. }
  882. else
  883. {
  884. e.Graphics.FillRectangle(
  885. new LinearGradientBrush(cellRectangle,
  886. Color.FromArgb(255, 240, 240, 240),
  887. Color.FromArgb(255, 250, 250, 250), 90, false), cellRectangle);
  888. }
  889. if ((e.State & DataGridViewElementStates.Selected) == DataGridViewElementStates.Selected)
  890. e.Graphics.DrawRectangle(new Pen(Revisions.RowTemplate.DefaultCellStyle.SelectionBackColor, 1), cellRectangle);
  891. }
  892. float offset = baseOffset;
  893. var heads = revision.Heads;
  894. if (heads.Count > 0)
  895. {
  896. heads.Sort((left, right) =>
  897. {
  898. if (left.IsTag != right.IsTag)
  899. return right.IsTag.CompareTo(left.IsTag);
  900. if (left.IsRemote != right.IsRemote)
  901. return left.IsRemote.CompareTo(right.IsRemote);
  902. return left.Name.CompareTo(right.Name);
  903. });
  904. foreach (var head in heads)
  905. {
  906. if ((head.IsRemote && !ShowRemoteBranches.Checked))
  907. continue;
  908. Font refsFont;
  909. if (IsFilledBranchesLayout())
  910. {
  911. //refsFont = head.Selected ? rowFont : new Font(rowFont, FontStyle.Regular);
  912. refsFont = rowFont;
  913. //refsFont = head.Selected
  914. // ? new Font(rowFont, rowFont.Style | FontStyle.Italic)
  915. // : rowFont;
  916. }
  917. else
  918. {
  919. refsFont = RefsFont;
  920. }
  921. Color headColor = GetHeadColor(head);
  922. Brush textBrush = new SolidBrush(headColor);
  923. string headName;
  924. PointF location;
  925. if (IsCardLayout())
  926. {
  927. headName = head.Name;
  928. offset += e.Graphics.MeasureString(headName, refsFont).Width + 6;
  929. location = new PointF(e.CellBounds.Right - offset, e.CellBounds.Top + 4);
  930. var size = new SizeF(e.Graphics.MeasureString(headName, refsFont).Width,
  931. e.Graphics.MeasureString(headName, RefsFont).Height);
  932. e.Graphics.FillRectangle(new SolidBrush(SystemColors.Info), location.X - 1,
  933. location.Y - 1, size.Width + 3, size.Height + 2);
  934. e.Graphics.DrawRectangle(new Pen(SystemColors.InfoText), location.X - 1,
  935. location.Y - 1, size.Width + 3, size.Height + 2);
  936. e.Graphics.DrawString(headName, refsFont, textBrush, location);
  937. }
  938. else
  939. {
  940. headName = IsFilledBranchesLayout()
  941. ? head.Name
  942. : string.Concat("[", head.Name, "] ");
  943. var headBounds = AdjustCellBounds(e.CellBounds, offset);
  944. SizeF textSize = e.Graphics.MeasureString(headName, refsFont);
  945. offset += textSize.Width;
  946. if (IsFilledBranchesLayout())
  947. {
  948. offset += 9;
  949. float extraOffset = DrawHeadBackground(isRowSelected, e.Graphics,
  950. headColor, headBounds.X,
  951. headBounds.Y,
  952. RoundToEven(textSize.Width + 3),
  953. RoundToEven(textSize.Height), 3,
  954. head.Selected,
  955. head.SelectedHeadMergeSource);
  956. offset += extraOffset;
  957. headBounds.Offset((int)(extraOffset + 1), 0);
  958. }
  959. DrawColumnText(e.Graphics, headName, refsFont, headColor, headBounds);
  960. }
  961. }
  962. }
  963. if (IsCardLayout())
  964. offset = baseOffset;
  965. var text = (string)e.FormattedValue;
  966. var bounds = AdjustCellBounds(e.CellBounds, offset);
  967. DrawColumnText(e.Graphics, text, rowFont, foreColor, bounds);
  968. if (IsCardLayout())
  969. {
  970. int textHeight = (int)e.Graphics.MeasureString(text, rowFont).Height;
  971. int gravatarSize = rowHeigth - textHeight - 12;
  972. int gravatarTop = e.CellBounds.Top + textHeight + 6;
  973. int gravatarLeft = e.CellBounds.Left + baseOffset + 2;
  974. Image gravatar = Gravatar.GravatarService.GetImageFromCache(revision.AuthorEmail + gravatarSize.ToString() + ".png", revision.AuthorEmail, Settings.AuthorImageCacheDays, gravatarSize, Settings.GravatarCachePath, FallBackService.MonsterId);
  975. if (gravatar == null && !string.IsNullOrEmpty(revision.AuthorEmail))
  976. {
  977. ThreadPool.QueueUserWorkItem(o =>
  978. Gravatar.GravatarService.LoadCachedImage(revision.AuthorEmail + gravatarSize.ToString() + ".png", revision.AuthorEmail, null, Settings.AuthorImageCacheDays, gravatarSize, Settings.GravatarCachePath, RefreshGravatar, FallBackService.MonsterId));
  979. }
  980. if (gravatar != null)
  981. e.Graphics.DrawImage(gravatar, gravatarLeft + 1, gravatarTop + 1, gravatarSize, gravatarSize);
  982. e.Graphics.DrawRectangle(Pens.Black, gravatarLeft, gravatarTop, gravatarSize + 1, gravatarSize + 1);
  983. string authorText;
  984. string timeText;
  985. if (rowHeigth >= 60)
  986. {
  987. authorText = revision.Author;
  988. timeText = TimeToString(Settings.ShowAuthorDate ? revision.AuthorDate : revision.CommitDate);
  989. }
  990. else
  991. {
  992. timeText = string.Concat(revision.Author, " (", TimeToString(Settings.ShowAuthorDate ? revision.AuthorDate : revision.CommitDate), ")");
  993. authorText = string.Empty;
  994. }
  995. e.Graphics.DrawString(authorText, rowFont, foreBrush,
  996. new PointF(gravatarLeft + gravatarSize + 5, gravatarTop + 6));
  997. e.Graphics.DrawString(timeText, rowFont, foreBrush,
  998. new PointF(gravatarLeft + gravatarSize + 5, e.CellBounds.Bottom - textHeight - 4));
  999. }
  1000. }
  1001. break;
  1002. case 2:
  1003. {
  1004. var text = (string)e.FormattedValue;
  1005. e.Graphics.DrawString(text, rowFont, foreBrush,
  1006. new PointF(e.CellBounds.Left, e.CellBounds.Top + 4));
  1007. }
  1008. break;
  1009. case 3:
  1010. {
  1011. var time = Settings.ShowAuthorDate ? revision.AuthorDate : revision.CommitDate;
  1012. var text = TimeToString(time);
  1013. e.Graphics.DrawString(text, rowFont, foreBrush,
  1014. new PointF(e.CellBounds.Left, e.CellBounds.Top + 4));
  1015. }
  1016. break;
  1017. }
  1018. }
  1019. private void RevisionsCellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
  1020. {
  1021. var column = e.ColumnIndex;
  1022. if (e.RowIndex < 0)
  1023. return;
  1024. if (Revisions.RowCount <= e.RowIndex)
  1025. return;
  1026. var revision = GetRevision(e.RowIndex);
  1027. if (revision == null)
  1028. return;
  1029. e.FormattingApplied = true;
  1030. switch (column)
  1031. {
  1032. case 0:
  1033. e.Value = revision.Guid;
  1034. break;
  1035. case 1:
  1036. e.Value = revision.Message;
  1037. break;
  1038. case 2:
  1039. e.Value = revision.Author ?? "";
  1040. break;
  1041. case 3:
  1042. {
  1043. var time = Settings.ShowAuthorDate ? revision.AuthorDate : revision.CommitDate;
  1044. if (time == DateTime.MinValue || time == DateTime.MaxValue)
  1045. e.Value = "";
  1046. else
  1047. e.Value = string.Format("{0} {1}", time.ToShortDateString(), time.ToLongTimeString());
  1048. }
  1049. break;
  1050. }
  1051. }
  1052. private void DrawColumnText(IDeviceContext dc, string text, Font font, Color color, Rectangle bounds)
  1053. {
  1054. TextRenderer.DrawText(dc, text, font, bounds, color, TextFormatFlags.EndEllipsis | TextFormatFlags.NoPrefix);
  1055. }
  1056. private static Rectangle AdjustCellBounds(Rectangle cellBounds, float offset)
  1057. {
  1058. return new Rectangle((int)(cellBounds.Left + offset), cellBounds.Top + 4,
  1059. cellBounds.Width - (int)offset, cellBounds.Height);
  1060. }
  1061. private static Color GetHeadColor(GitHead head)
  1062. {
  1063. return head.IsTag
  1064. ? Settings.TagColor
  1065. : head.IsHead
  1066. ? Settings.BranchColor
  1067. : head.IsRemote
  1068. ? Settings.RemoteBranchColor
  1069. : Settings.OtherTagColor;
  1070. }
  1071. private float RoundToEven(float value)
  1072. {
  1073. int result = ((int)value / 2) * 2;
  1074. return result < value ? result + 2 : result;
  1075. }
  1076. private float DrawHeadBackground(bool isSelected, Graphics graphics, Color color,
  1077. float x, float y, float width, float height, float radius, bool isCurrentBranch,
  1078. bool isCurentBranchMergeSource)
  1079. {
  1080. float additionalOffset = isCurrentBranch || isCurentBranchMergeSource ? GetArrowSize(height) : 0;
  1081. width += additionalOffset;
  1082. var oldMode = graphics.SmoothingMode;
  1083. graphics.SmoothingMode = SmoothingMode.AntiAlias;
  1084. try
  1085. {
  1086. // shade
  1087. using (var shadePath = CreateRoundRectPath(x + 1, y + 1, width, height, radius))
  1088. {
  1089. Color shadeColor = isSelected ? Color.Black : Color.Gray;
  1090. graphics.FillPath(new SolidBrush(shadeColor), shadePath);
  1091. }
  1092. using (var forePath = CreateRoundRectPath(x, y, width, height, radius))
  1093. {
  1094. Color fillColor = Lerp(color, Color.White, 0.92F);
  1095. var fillBrush = new LinearGradientBrush(new RectangleF(x, y, width, height), fillColor,
  1096. Lerp(fillColor, Color.White, 0.9F), 90);
  1097. // fore rectangle
  1098. graphics.FillPath(fillBrush, forePath);
  1099. // frame
  1100. graphics.DrawPath(new Pen(Lerp(color, Color.White, 0.83F)), forePath);
  1101. // arrow if the head is the current branch
  1102. if (isCurrentBranch)
  1103. DrawArrow(graphics, x, y, height, color, true);
  1104. else if (isCurentBranchMergeSource)
  1105. DrawArrow(graphics, x, y, height, color, false);
  1106. }
  1107. }
  1108. finally
  1109. {
  1110. graphics.SmoothingMode = oldMode;
  1111. }
  1112. return additionalOffset;
  1113. }
  1114. private float GetArrowSize(float rowHeight)
  1115. {
  1116. return rowHeight - 6;
  1117. }
  1118. private void DrawArrow(Graphics graphics, float x, float y, float rowHeight, Color color, bool filled)
  1119. {
  1120. const float horShift = 4;
  1121. const float verShift = 3;
  1122. float height = rowHeight - verShift * 2;
  1123. float width = height / 2;
  1124. var points = new[]
  1125. {
  1126. new PointF(x + horShift, y + verShift),
  1127. new PointF(x + horShift + width, y + verShift + height/2),
  1128. new PointF(x + horShift, y + verShift + height),
  1129. new PointF(x + horShift, y + verShift)
  1130. };
  1131. if (filled)
  1132. graphics.FillPolygon(new SolidBrush(color), points);
  1133. else
  1134. graphics.DrawPolygon(new Pen(color), points);
  1135. }
  1136. private static GraphicsPath CreateRoundRectPath(float x, float y, float width, float height, float radius)
  1137. {
  1138. var path = new GraphicsPath();
  1139. path.AddLine(x + radius, y, x + width - (radius * 2), y);
  1140. path.AddArc(x + width - (radius * 2), y, radius * 2, radius * 2, 270, 90);
  1141. path.AddLine(x + width, y + radius, x + width, y + height - (radius * 2));
  1142. path.AddArc(x + width - (radius * 2), y + height - (radius * 2), radius * 2, radius * 2, 0, 90);
  1143. path.AddLine(x + width - (radius * 2), y + height, x + radius, y + height);
  1144. path.AddArc(x, y + height - (radius * 2), radius * 2, radius * 2, 90, 90);
  1145. path.AddLine(x, y + height - (radius * 2), x, y + radius);
  1146. path.AddArc(x, y, radius * 2, radius * 2, 180, 90);
  1147. path.CloseFigure();
  1148. return path;
  1149. }
  1150. private static float Lerp(float start, float end, float amount)
  1151. {
  1152. float difference = end - start;
  1153. float adjusted = difference * amount;
  1154. return start + adjusted;
  1155. }
  1156. private static Color Lerp(Color colour, Color to, float amount)
  1157. {
  1158. // start colours as lerp-able floats
  1159. float sr = colour.R, sg = colour.G, sb = colour.B;
  1160. // end colours as lerp-able floats
  1161. float er = to.R, eg = to.G, eb = to.B;
  1162. // lerp the colours to get the difference
  1163. byte r = (byte)Lerp(sr, er, amount),
  1164. g = (byte)Lerp(sg, eg, amount),
  1165. b = (byte)Lerp(sb, eb, amount);
  1166. // return the new colour
  1167. return Color.FromArgb(r, g, b);
  1168. }
  1169. private void RefreshGravatar(Image image)
  1170. {
  1171. _syncContext.Post(state => Revisions.Refresh(), null);
  1172. }
  1173. private void RevisionsDoubleClick(object sender, EventArgs e)
  1174. {
  1175. ViewSelectedRevisions();
  1176. }
  1177. public void ViewSelectedRevisions()
  1178. {
  1179. var selectedRevisions = GetSelectedRevisions();
  1180. if (selectedRevisions.Count > 0)
  1181. {
  1182. var form = new FormDiffSmall(selectedRevisions[0]);
  1183. form.ShowDialog(this);
  1184. }
  1185. else
  1186. GitUICommands.Instance.StartCompareRevisionsDialog(this);
  1187. }
  1188. private void SelectionTimerTick(object sender, EventArgs e)
  1189. {
  1190. SelectionTimer.Enabled = false;
  1191. SelectionTimer.Stop();
  1192. if (SelectionChanged != null)
  1193. SelectionChanged(this, e);
  1194. }
  1195. private void CreateTagToolStripMenuItemClick(object sender, EventArgs e)
  1196. {
  1197. if (Revisions.RowCount <= LastRow || LastRow < 0)
  1198. return;
  1199. using (var frm = new FormTagSmall(GetRevision(LastRow)))
  1200. {
  1201. frm.ShowDialog(this);
  1202. }
  1203. RefreshRevisions();
  1204. }
  1205. private void ResetCurrentBranchToHereToolStripMenuItemClick(object sender, EventArgs e)
  1206. {
  1207. if (Revisions.RowCount <= LastRow || LastRow < 0)
  1208. return;
  1209. var frm = new FormResetCurrentBranch(GetRevision(LastRow));
  1210. frm.ShowDialog(this);
  1211. RefreshRevisions();
  1212. OnActionOnRepositoryPerformed();
  1213. }
  1214. private void CreateNewBranchToolStripMenuItemClick(object sender, EventArgs e)
  1215. {
  1216. if (Revisions.RowCount <= LastRow || LastRow < 0)
  1217. return;
  1218. var frm = new FormBranchSmall { Revision = GetRevision(LastRow) };
  1219. if (frm.ShowDialog(this) == DialogResult.OK)
  1220. {
  1221. RefreshRevisions();
  1222. OnActionOnRepositoryPerformed();
  1223. }
  1224. }
  1225. private void RevisionsMouseClick(object sender, MouseEventArgs e)
  1226. {
  1227. var pt = Revisions.PointToClient(Cursor.Position);
  1228. var hti = Revisions.HitTest(pt.X, pt.Y);
  1229. LastRow = hti.RowIndex;
  1230. }
  1231. private void RevisionsCellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
  1232. {
  1233. if (e.Button != MouseButtons.Right)
  1234. return;
  1235. var pt = Revisions.PointToClient(Cursor.Position);
  1236. var hti = Revisions.HitTest(pt.X, pt.Y);
  1237. if (LastRow == hti.RowIndex)
  1238. return;
  1239. LastRow = hti.RowIndex;
  1240. Revisions.ClearSelection();
  1241. if (LastRow >= 0 && Revisions.Rows.Count > LastRow)
  1242. Revisions.Rows[LastRow].Selected = true;
  1243. }
  1244. private void CommitClick(object sender, EventArgs e)
  1245. {
  1246. GitUICommands.Instance.StartCommitDialog(this);
  1247. OnActionOnRepositoryPerformed();
  1248. RefreshRevisions();
  1249. }
  1250. private void GitIgnoreClick(object sender, EventArgs e)
  1251. {
  1252. GitUICommands.Instance.StartEditGitIgnoreDialog(this);
  1253. }
  1254. private void ShowRemoteBranchesClick(object sender, EventArgs e)
  1255. {
  1256. ShowRemoteBranches.Checked = !ShowRemoteBranches.Checked;
  1257. Revisions.Invalidate();
  1258. }
  1259. private void ShowCurrentBranchOnlyToolStripMenuItemClick(object sender, EventArgs e)
  1260. {
  1261. if (showCurrentBranchOnlyToolStripMenuItem.Checked)
  1262. return;
  1263. Settings.BranchFilterEnabled = true;
  1264. Settings.ShowCurrentBranchOnly = true;
  1265. SetShowBranches();
  1266. ForceRefreshRevisions();
  1267. }
  1268. private void ShowAllBranchesToolStripMenuItemClick(object sender, EventArgs e)
  1269. {
  1270. if (showAllBranchesToolStripMenuItem.Checked)
  1271. return;
  1272. Settings.BranchFilterEnabled = false;
  1273. SetShowBranches();
  1274. ForceRefreshRevisions();
  1275. }
  1276. private void ShowFilteredBranchesToolStripMenuItemClick(object sender, EventArgs e)
  1277. {
  1278. if (showFilteredBranchesToolStripMenuItem.Checked)
  1279. return;
  1280. Settings.BranchFilterEnabled = true;
  1281. Settings.ShowCurrentBranchOnly = false;
  1282. SetShowBranches();
  1283. ForceRefreshRevisions();
  1284. }
  1285. private void SetShowBranches()
  1286. {
  1287. showAllBranchesToolStripMenuItem.Checked = !Settings.BranchFilterEnabled;
  1288. showCurrentBranchOnlyToolStripMenuItem.Checked =
  1289. Settings.BranchFilterEnabled && Settings.ShowCurrentBranchOnly;
  1290. showFilteredBranchesToolStripMenuItem.Checked =
  1291. Settings.BranchFilterEnabled && !Settings.ShowCurrentBranchOnly;
  1292. BranchFilter = _revisionFilter.GetBranchFilter();
  1293. if (!Settings.BranchFilterEnabled)
  1294. LogParam = "HEAD --all --boundary";
  1295. else if (Settings.ShowCurrentBranchOnly)
  1296. LogParam = "HEAD";
  1297. else
  1298. LogParam = BranchFilter.Length > 0
  1299. ? String.Empty
  1300. : "HEAD --all --boundary";
  1301. }
  1302. private void RevertCommitToolStripMenuItemClick(object sender, EventArgs e)
  1303. {
  1304. if (Revisions.RowCount <= LastRow || LastRow < 0)
  1305. return;
  1306. var frm = new FormRevertCommitSmall(GetRevision(LastRow));
  1307. frm.ShowDialog(this);
  1308. RefreshRevisions();
  1309. }
  1310. private void FilterToolStripMenuItemClick(object sender, EventArgs e)
  1311. {
  1312. _revisionFilter.ShowDialog(this);
  1313. ForceRefreshRevisions();
  1314. }
  1315. private void ApplyFilterFromRevisionFilterDialog()
  1316. {
  1317. BranchFilter = _revisionFilter.GetBranchFilter();
  1318. SetShowBranches();
  1319. }
  1320. List<ToolStripItem> mergeLoose = new List<ToolStripItem>(), checkoutLoose = new List<ToolStripItem>(), rebaseLoose = new List<ToolStripItem>();
  1321. private void CreateTagOpening(object sender, CancelEventArgs e)
  1322. {
  1323. if (Revisions.RowCount < LastRow || LastRow < 0 || Revisions.RowCount == 0)
  1324. return;
  1325. var inTheMiddleOfBisect = Settings.Module.InTheMiddleOfBisect();
  1326. markRevisionAsBadToolStripMenuItem.Visible = inTheMiddleOfBisect;
  1327. markRevisionAsGoodToolStripMenuItem.Visible = inTheMiddleOfBisect;
  1328. bisectSkipRevisionToolStripMenuItem.Visible = inTheMiddleOfBisect;
  1329. stopBisectToolStripMenuItem.Visible = inTheMiddleOfBisect;
  1330. bisectSeparator.Visible = inTheMiddleOfBisect;
  1331. mergeLoose.ForEach(CreateTag.Items.Remove);
  1332. checkoutLoose.ForEach(CreateTag.Items.Remove);
  1333. rebaseLoose.ForEach(CreateTag.Items.Remove);
  1334. var revision = GetRevision(LastRow);
  1335. var current = Settings.Module.GetCurrentCheckout();
  1336. {
  1337. var topItem = revision.Guid == current? revertCommitToolStripMenuItem: cherryPickCommitToolStripMenuItem;
  1338. manipulateCommitToolStripMenuItem.DropDownItems.Remove(topItem);
  1339. manipulateCommitToolStripMenuItem.DropDownItems.Insert(0, topItem);
  1340. }
  1341. var tagDropDown = new ContextMenuStrip();
  1342. var deleteBranchDropDown = new ContextMenuStrip();
  1343. var checkoutBranchDropDown = new ContextMenuStrip();
  1344. var mergeBranchDropDown = new ContextMenuStrip();
  1345. var rebaseDropDown = new ContextMenuStrip();
  1346. var renameDropDown = new ContextMenuStrip();
  1347. var tagNameCopy = new ContextMenuStrip();
  1348. var branchNameCopy = new ContextMenuStrip();
  1349. foreach (var head in revision.Heads.Where(h => h.IsTag))
  1350. {
  1351. ToolStripItem toolStripItem = new ToolStripMenuItem(head.Name);
  1352. ToolStripItem tagName = new ToolStripMenuItem(head.Name);
  1353. toolStripItem.Click += ToolStripItemClick;
  1354. tagDropDown.Items.Add(toolStripItem);
  1355. tagName.Click += copyToClipBoard;
  1356. tagNameCopy.Items.Add(tagName);
  1357. }
  1358. //For now there is no action that could be done on currentBranch
  1359. string currentBranch = Settings.Module.GetSelectedBranch();
  1360. var allBranches = revision.Heads.Where(h => !h.IsTag && (h.IsHead || h.IsRemote));
  1361. var localBranches = allBranches.Where(b => !b.IsRemote);
  1362. var branchesWithNoIdenticalRemotes = allBranches.Where(
  1363. b => b.IsRemote?b.LocalName != "HEAD" || !allBranches.Any(rb => b.Remote == rb.Remote) : !localBranches.Any(lb => lb.TrackingRemote == b.Remote && lb.MergeWith == b.LocalName));
  1364. bool currentBranchPointsToRevision = false;
  1365. foreach (var head in branchesWithNoIdenticalRemotes)
  1366. {
  1367. if (head.Name.Equals(currentBranch))
  1368. currentBranchPointsToRevision = true;
  1369. else if (current != head.Guid)
  1370. {
  1371. ToolStripItem toolStripItem = new ToolStripMenuItem(head.Name);
  1372. toolStripItem.Click += ToolStripItemClickMergeBranch;
  1373. toolStripItem.Tag = head;
  1374. mergeBranchDropDown.Items.Add(toolStripItem);
  1375. toolStripItem = new ToolStripMenuItem("Merge with " + head.Name);
  1376. toolStripItem.Click += ToolStripItemClickMergeBranch;
  1377. toolStripItem.Tag = head;
  1378. CreateTag.Items.Insert(CreateTag.Items.IndexOf(mergeBranchToolStripMenuItem), toolStripItem);
  1379. mergeLoose.Add(toolStripItem);
  1380. toolStripItem = new ToolStripMenuItem(head.Name);
  1381. toolStripItem.Click += ToolStripItemClickRebaseBranch;
  1382. toolStripItem.Tag = head;
  1383. rebaseDropDown.Items.Add(toolStripItem);
  1384. toolStripItem = new ToolStripMenuItem("Rebase on " + head.Name);
  1385. toolStripItem.Click += ToolStripItemClickRebaseBranch;
  1386. toolStripItem.Tag = head;
  1387. CreateTag.Items.Insert(CreateTag.Items.IndexOf(rebaseOnToolStripMenuItem), toolStripItem);
  1388. rebaseLoose.Add(toolStripItem);
  1389. }
  1390. }
  1391. //if there is no branch to merge, then let user to merge selected commit into current branch
  1392. if (mergeBranchDropDown.Items.Count == 0 && !currentBranchPointsToRevision)
  1393. {
  1394. ToolStripItem toolStripItem = new ToolStripMenuItem("Merge with revision");
  1395. toolStripItem.Click += ToolStripItemClickMergeBranch;
  1396. toolStripItem.Tag = revision;
  1397. CreateTag.Items.Insert(CreateTag.Items.IndexOf(checkoutRevisionToolStripMenuItem), toolStripItem);
  1398. mergeLoose.Add(toolStripItem);
  1399. }
  1400. //if there is no branch to rebase on, then allow user to rebase on selected commit
  1401. if (rebaseDropDown.Items.Count == 0 && !currentBranchPointsToRevision)
  1402. {
  1403. ToolStripItem toolStripItem = new ToolStripMenuItem("Rebase on revision");
  1404. toolStripItem.Tag = revision;
  1405. toolStripItem.Click += ToolStripItemClickRebaseBranch;
  1406. CreateTag.Items.Insert(CreateTag.Items.IndexOf(checkoutRevisionToolStripMenuItem), toolStripItem);
  1407. rebaseLoose.Add(toolStripItem);
  1408. }
  1409. foreach (var head in allBranches)
  1410. {
  1411. ToolStripItem toolStripItem = new ToolStripMenuItem(head.Name);
  1412. ToolStripItem branchName = new ToolStripMenuItem(head.Name);
  1413. branchName.Click += copyToClipBoard;
  1414. branchNameCopy.Items.Add(branchName);
  1415. //skip remote branches - they can not be deleted this way
  1416. if (!head.IsRemote)
  1417. {
  1418. if (!head.Name.Equals(currentBranch))
  1419. {
  1420. toolStripItem = new ToolStripMenuItem(head.Name);
  1421. toolStripItem.Click += ToolStripItemClickBranch;
  1422. deleteBranchDropDown.Items.Add(toolStripItem); //Add to delete branch
  1423. }
  1424. toolStripItem = new ToolStripMenuItem(head.Name);
  1425. toolStripItem.Click += ToolStripItemClickRenameBranch;
  1426. renameDropDown.Items.Add(toolStripItem); //Add to rename branch
  1427. }
  1428. if (!head.Name.Equals(currentBranch))
  1429. if (!head.IsRemote || !allBranches.Any(h => head.IsRemote ? head.LocalName == "HEAD" && head.Remote == h.Remote : h != head && h.LocalName == head.LocalName))
  1430. {
  1431. toolStripItem = new ToolStripMenuItem(head.Name);
  1432. toolStripItem.Click += ToolStripItemClickCheckoutBranch;
  1433. toolStripItem.Tag = head;
  1434. checkoutBranchDropDown.Items.Add(toolStripItem);
  1435. toolStripItem = new ToolStripMenuItem("Checkout " + head.Name);
  1436. toolStripItem.Click += ToolStripItemClickCheckoutBranch;
  1437. toolStripItem.Tag = head;
  1438. CreateTag.Items.Insert(CreateTag.Items.IndexOf(checkoutBranchToolStripMenuItem), toolStripItem);
  1439. checkoutLoose.Add(toolStripItem);
  1440. }
  1441. }
  1442. deleteTagToolStripMenuItem.DropDown = tagDropDown;
  1443. deleteTagToolStripMenuItem.Visible = tagDropDown.Items.Count > 0;
  1444. deleteBranchToolStripMenuItem.DropDown = deleteBranchDropDown;
  1445. deleteBranchToolStripMenuItem.Visible = deleteBranchDropDown.Items.Count > 0;
  1446. checkoutBranchToolStripMenuItem.DropDown = checkoutBranchDropDown;
  1447. {
  1448. bool inline = checkoutBranchDropDown.Items.Count < 3;
  1449. checkoutBranchToolStripMenuItem.Visible = !inline;
  1450. foreach (var t in checkoutLoose) t.Visible = inline;
  1451. }
  1452. checkoutBranchToolStripMenuItem.Text = "Checkout " + (checkoutBranchDropDown.Items.Count == 1 ? checkoutBranchDropDown.Items[0].Text : "branch");
  1453. mergeBranchToolStripMenuItem.DropDown = mergeBranchDropDown;
  1454. {
  1455. bool inline = mergeBranchDropDown.Items.Count < 2;
  1456. mergeBranchToolStripMenuItem.Visible = !inline;
  1457. foreach (var t in mergeLoose) t.Visible = inline;
  1458. }
  1459. mergeBranchToolStripMenuItem.Text = mergeBranchDropDown.Items.Count == 1 ? "Merge with " + mergeBranchDropDown.Items[0].Text : "Merge into current branch";
  1460. rebaseOnToolStripMenuItem.DropDown = rebaseDropDown;
  1461. {
  1462. bool inline = rebaseDropDown.Items.Count < 2;
  1463. rebaseOnToolStripMenuItem.Visible = !inline;
  1464. foreach (var t in rebaseLoose) t.Visible = inline;
  1465. }
  1466. rebaseOnToolStripMenuItem.Text = rebaseDropDown.Items.Count == 1 ? "Rebase on " + rebaseDropDown.Items[0].Text : "Rebase current branch on";
  1467. renameBranchToolStripMenuItem.DropDown = renameDropDown;
  1468. renameBranchToolStripMenuItem.Visible = renameDropDown.Items.Count > 0;
  1469. branchNameToolStripMenuItem.DropDown = branchNameCopy;
  1470. branchNameToolStripMenuItem.Visible = branchNameCopy.Items.Count > 0;
  1471. tagToolStripMenuItem.DropDown = tagNameCopy;
  1472. tagToolStripMenuItem.Visible = tagNameCopy.Items.Count > 0;
  1473. toolStripSeparator6.Visible = tagNameCopy.Items.Count > 0 || branchNameCopy.Items.Count > 0;
  1474. RefreshOwnScripts();
  1475. goToParentToolStripMenuItem.Visible = revision.HasParent();
  1476. }
  1477. private void ToolStripItemClick(object sender, EventArgs e)
  1478. {
  1479. var toolStripItem = sender as ToolStripItem;
  1480. if (toolStripItem == null)
  1481. return;
  1482. FormProcess.ShowDialog(this, GitCommandHelpers.DeleteTagCmd(toolStripItem.Text));
  1483. ForceRefreshRevisions();
  1484. }
  1485. private void ToolStripItemClickBranch(object sender, EventArgs e)
  1486. {
  1487. var toolStripItem = sender as ToolStripItem;
  1488. if (toolStripItem == null)
  1489. return;
  1490. GitUICommands.Instance.StartDeleteBranchDialog(this, toolStripItem.Text);
  1491. ForceRefreshRevisions();
  1492. }
  1493. private void CheckoutBranch(string branch)
  1494. {
  1495. var command = GitCommandHelpers.CheckoutCmd(branch);
  1496. FormProcess.ShowDialog(this, command);
  1497. ForceRefreshRevisions();
  1498. OnActionOnRepositoryPerformed();
  1499. }
  1500. private void ToolStripItemClickCheckoutBranch(object sender, EventArgs e)
  1501. {
  1502. var toolStripItem = sender as ToolStripItem;
  1503. if (toolStripItem == null)
  1504. return;
  1505. var head = toolStripItem.Tag as GitHead;
  1506. if (head.IsRemote)
  1507. {
  1508. GitUICommands.Instance.StartCheckoutRemoteBranchDialog(this, head.CompleteName);
  1509. ForceRefreshRevisions();
  1510. OnActionOnRepositoryPerformed();
  1511. }
  1512. else
  1513. CheckoutBranch(head.CompleteName);
  1514. }
  1515. private void ToolStripItemClickMergeBranch(object sender, EventArgs e)
  1516. {
  1517. var toolStripItem = sender as ToolStripItem;
  1518. if (toolStripItem == null)
  1519. return;
  1520. var head = toolStripItem.Tag as GitHead;
  1521. GitUICommands.Instance.StartMergeBranchDialog(this, head.Name);
  1522. ForceRefreshRevisions();
  1523. OnActionOnRepositoryPerformed();
  1524. }
  1525. private void ToolStripItemClickRebaseBranch(object sender, EventArgs e)
  1526. {
  1527. var toolStripItem = sender as ToolStripItem;
  1528. if (toolStripItem == null)
  1529. return;
  1530. var head = toolStripItem.Tag as GitHead;
  1531. GitUICommands.Instance.StartRebaseDialog(this, head.Name);
  1532. ForceRefreshRevisions();
  1533. OnActionOnRepositoryPerformed();
  1534. }
  1535. private void ToolStripItemClickRenameBranch(object sender, EventArgs e)
  1536. {
  1537. var toolStripItem = sender as ToolStripItem;
  1538. if (toolStripItem == null)
  1539. return;
  1540. var renameExecuted = GitUICommands.Instance.StartRenameDialog(this, toolStripItem.Text);
  1541. if (!renameExecuted)
  1542. return;
  1543. ForceRefreshRevisions();
  1544. OnActionOnRepositoryPerformed();
  1545. }
  1546. private void CheckoutRevisionToolStripMenuItemClick(object sender, EventArgs e)
  1547. {
  1548. if (Revisions.RowCount <= LastRow || LastRow < 0)
  1549. return;
  1550. if (MessageBox.Show(this, _areYouSureYouWantCheckout.Text, _areYouSureYouWantCheckoutCaption.Text,
  1551. MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
  1552. return;
  1553. CheckoutBranch(GetRevision(LastRow).Guid);
  1554. }
  1555. private void ShowAuthorDateToolStripMenuItemClick(object sender, EventArgs e)
  1556. {
  1557. Settings.ShowAuthorDate = !showAuthorDateToolStripMenuItem.Checked;
  1558. showAuthorDateToolStripMenuItem.Checked = Settings.ShowAuthorDate;
  1559. ForceRefreshRevisions();
  1560. }
  1561. private void OrderRevisionsByDateToolStripMenuItemClick(object sender, EventArgs e)
  1562. {
  1563. Settings.OrderRevisionByDate = !orderRevisionsByDateToolStripMenuItem.Checked;
  1564. orderRevisionsByDateToolStripMenuItem.Checked = Settings.OrderRevisionByDate;
  1565. ForceRefreshRevisions();
  1566. }
  1567. private void CherryPickCommitToolStripMenuItemClick(object sender, EventArgs e)
  1568. {
  1569. var revisions = GetSelectedRevisions(SortDirection.Descending);
  1570. FormCherryPickCommitSmall prevForm = null;
  1571. try
  1572. {
  1573. foreach (var r in revisions)
  1574. if (Settings.CherryPickSilently && !Settings.Module.IsMerge(r.Guid))
  1575. {
  1576. FormProcess.ShowDialog(this, GitCommandHelpers.CherryPickCmd(r.Guid, true, Settings.CherryPickAddsReference ? "-x" : ""));
  1577. MergeConflictHandler.HandleMergeConflicts(this, true);
  1578. }
  1579. else
  1580. {
  1581. var frm = new FormCherryPickCommitSmall(r);
  1582. if (prevForm != null)
  1583. {
  1584. frm.CopyOptions(prevForm);
  1585. prevForm.Dispose();
  1586. }
  1587. prevForm = frm;
  1588. if (frm.ShowDialog(this) != DialogResult.OK)
  1589. break;
  1590. }
  1591. }
  1592. finally
  1593. {
  1594. if (prevForm != null)
  1595. prevForm.Dispose();
  1596. }
  1597. ForceRefreshRevisions();
  1598. OnActionOnRepositoryPerformed();
  1599. }
  1600. private void FixupCommitToolStripMenuItemClick(object sender, EventArgs e)
  1601. {
  1602. PrepareCorrectionCommit(CommitKind.Fixup);
  1603. }
  1604. private void SquashCommitToolStripMenuItemClick(object sender, EventArgs e)
  1605. {
  1606. PrepareCorrectionCommit(CommitKind.Squash);
  1607. }
  1608. private void PrepareCorrectionCommit(CommitKind commitKind)
  1609. {
  1610. if (Revisions.RowCount <= LastRow || LastRow < 0)
  1611. return;
  1612. var frm = new FormCommit(commitKind, GetRevision(LastRow));
  1613. frm.ShowDialog(this);
  1614. ForceRefreshRevisions();
  1615. OnActionOnRepositoryPerformed();
  1616. }
  1617. private void ShowRelativeDateToolStripMenuItemClick(object sender, EventArgs e)
  1618. {
  1619. Settings.RelativeDate = !showRelativeDateToolStripMenuItem.Checked;
  1620. showRelativeDateToolStripMenuItem.Checked = Settings.RelativeDate;
  1621. ForceRefreshRevisions();
  1622. }
  1623. private string TimeToString(DateTime time)
  1624. {
  1625. if (time == DateTime.MinValue || time == DateTime.MaxValue)
  1626. return "";
  1627. if (!Settings.RelativeDate)
  1628. return string.Format("{0} {1}", time.ToShortDateString(), time.ToLongTimeString());
  1629. return GitCommandHelpers.GetRelativeDateString(DateTime.Now, time, false);
  1630. }
  1631. private void UpdateGraph(GitRevision rev)
  1632. {
  1633. if (rev == null)
  1634. {
  1635. // Prune the graph and make sure the row count matches reality
  1636. Revisions.Prune();
  1637. if (Revisions.RowCount == 0 && Settings.RevisionGraphShowWorkingDirChanges)
  1638. {
  1639. bool uncommittedChanges = false;
  1640. bool stagedChanges = false;
  1641. //Only check for tracked files. This usually makes more sense and it performs a lot
  1642. //better then checking for untracked files.
  1643. if (Settings.Module.GetTrackedChangedFiles().Count > 0)
  1644. uncommittedChanges = true;
  1645. if (Settings.Module.GetStagedFiles().Count > 0)
  1646. stagedChanges = true;
  1647. if (uncommittedChanges)
  1648. {
  1649. //Add working dir as virtual commit
  1650. var workingDir = new GitRevision(GitRevision.UncommittedWorkingDirGuid)
  1651. {
  1652. Message = _currentWorkingDirChanges.Text,
  1653. ParentGuids =
  1654. stagedChanges
  1655. ? new[] { GitRevision.IndexGuid }
  1656. : new[] { CurrentCheckout }
  1657. };
  1658. Revisions.Add(workingDir.Guid, workingDir.ParentGuids, DvcsGraph.DataType.Normal, workingDir);
  1659. }
  1660. if (stagedChanges)
  1661. {
  1662. //Add index as virtual commit
  1663. var index = new GitRevision(GitRevision.IndexGuid)
  1664. {
  1665. Message = _currentIndex.Text,
  1666. ParentGuids = new string[] { CurrentCheckout }
  1667. };
  1668. Revisions.Add(index.Guid, index.ParentGuids, DvcsGraph.DataType.Normal, index);
  1669. }
  1670. }
  1671. return;
  1672. }
  1673. var dataType = DvcsGraph.DataType.Normal;
  1674. if (rev.Guid == CurrentCheckout)
  1675. dataType = DvcsGraph.DataType.Active;
  1676. else if (rev.Heads.Count > 0)
  1677. dataType = DvcsGraph.DataType.Special;
  1678. Revisions.Add(rev.Guid, rev.ParentGuids, dataType, rev);
  1679. }
  1680. private void drawNonrelativesGrayToolStripMenuItem_Click(object sender, EventArgs e)
  1681. {
  1682. Settings.RevisionGraphDrawNonRelativesGray = !Settings.RevisionGraphDrawNonRelativesGray;
  1683. drawNonrelativesGrayToolStripMenuItem.Checked = Settings.RevisionGraphDrawNonRelativesGray;
  1684. Revisions.Refresh();
  1685. }
  1686. private void messageToolStripMenuItem_Click(object sender, EventArgs e)
  1687. {
  1688. Clipboard.SetText(GetRevision(LastRow).Message);
  1689. }
  1690. private void authorToolStripMenuItem_Click(object sender, EventArgs e)
  1691. {
  1692. Clipboard.SetText(GetRevision(LastRow).Author);
  1693. }
  1694. private void dateToolStripMenuItem_Click(object sender, EventArgs e)
  1695. {
  1696. Clipboard.SetText(GetRevision(LastRow).CommitDate.ToString());
  1697. }
  1698. private void hashToolStripMenuItem_Click(object sender, EventArgs e)
  1699. {
  1700. Clipboard.SetText(GetRevision(LastRow).Guid);
  1701. }
  1702. private static void copyToClipBoard(object sender, EventArgs e)
  1703. {
  1704. Clipboard.SetText(sender.ToString());
  1705. }
  1706. private void markRevisionAsBadToolStripMenuItem_Click(object sender, EventArgs e)
  1707. {
  1708. ContinueBisect(GitBisectOption.Bad);
  1709. }
  1710. private void markRevisionAsGoodToolStripMenuItem_Click(object sender, EventArgs e)
  1711. {
  1712. ContinueBisect(GitBisectOption.Good);
  1713. }
  1714. private void bisectSkipRevisionToolStripMenuItem_Click(object sender, EventArgs e)
  1715. {
  1716. ContinueBisect(GitBisectOption.Skip);
  1717. }
  1718. private void ContinueBisect(GitBisectOption bisectOption)
  1719. {
  1720. if (Revisions.RowCount <= LastRow || LastRow < 0)
  1721. return;
  1722. FormProcess.ShowDialog(this, GitCommandHelpers.ContinueBisectCmd(bisectOption, GetRevision(LastRow).Guid), false);
  1723. RefreshRevisions();
  1724. }
  1725. private void stopBisectToolStripMenuItem_Click(object sender, EventArgs e)
  1726. {
  1727. FormProcess.ShowDialog(this, GitCommandHelpers.StopBisectCmd());
  1728. RefreshRevisions();
  1729. }
  1730. private void RefreshOwnScripts()
  1731. {
  1732. RemoveOwnScripts();
  1733. AddOwnScripts();
  1734. }
  1735. private void AddOwnScripts()
  1736. {
  1737. IList<ScriptInfo> scripts = ScriptManager.GetScripts();
  1738. int addedScripts = 0;
  1739. if (scripts != null)
  1740. {
  1741. foreach (ScriptInfo scriptInfo in scripts)
  1742. {
  1743. if (scriptInfo.Enabled)
  1744. {
  1745. addedScripts++;
  1746. ToolStripItem item = new ToolStripMenuItem(scriptInfo.Name);
  1747. item.Name = item.Text + "_ownScript";
  1748. item.Click += runScript;
  1749. if (scriptInfo.AddToRevisionGridContextMenu)
  1750. CreateTag.Items.Add(item);
  1751. else
  1752. runScriptToolStripMenuItem.DropDown.Items.Add(item);
  1753. }
  1754. }
  1755. toolStripSeparator7.Visible = addedScripts > 1;
  1756. runScriptToolStripMenuItem.Visible = runScriptToolStripMenuItem.DropDown.Items.Count > 0;
  1757. }
  1758. }
  1759. private void RemoveOwnScripts()
  1760. {
  1761. runScriptToolStripMenuItem.DropDown.Items.Clear();
  1762. List<ToolStripItem> list = new List<ToolStripItem>();
  1763. foreach (ToolStripItem item in CreateTag.Items)
  1764. list.Add(item);
  1765. foreach (ToolStripItem item in list)
  1766. if (item.Name.Contains("_ownScript"))
  1767. CreateTag.Items.RemoveByKey(item.Name);
  1768. }
  1769. private bool settingsLoaded;
  1770. private void runScript(object sender, EventArgs e)
  1771. {
  1772. if (settingsLoaded == false)
  1773. {
  1774. new FormSettings().LoadSettings();
  1775. settingsLoaded = true;
  1776. }
  1777. ScriptRunner.RunScript(sender.ToString(), this);
  1778. RefreshRevisions();
  1779. }
  1780. #region Drag/drop patch files on revision grid
  1781. void Revisions_DragDrop(object sender, DragEventArgs e)
  1782. {
  1783. var fileNameArray = e.Data.GetData(DataFormats.FileDrop) as Array;
  1784. if (fileNameArray != null)
  1785. {
  1786. if (fileNameArray.Length > 10)
  1787. {
  1788. //Some users need to be protected against themselves!
  1789. MessageBox.Show(this, _droppingFilesBlocked.Text);
  1790. return;
  1791. }
  1792. foreach (object fileNameObject in fileNameArray)
  1793. {
  1794. var fileName = fileNameObject as string;
  1795. if (!string.IsNullOrEmpty(fileName) && fileName.EndsWith(".patch", StringComparison.InvariantCultureIgnoreCase))
  1796. {
  1797. //Start apply patch dialog for each dropped patch file...
  1798. GitUICommands.Instance.StartApplyPatchDialog(this, fileName);
  1799. }
  1800. }
  1801. }
  1802. }
  1803. static void Revisions_DragEnter(object sender, DragEventArgs e)
  1804. {
  1805. var fileNameArray = e.Data.GetData(DataFormats.FileDrop) as Array;
  1806. if (fileNameArray != null)
  1807. {
  1808. foreach (object fileNameObject in fileNameArray)
  1809. {
  1810. var fileName = fileNameObject as string;
  1811. if (!string.IsNullOrEmpty(fileName) && fileName.EndsWith(".patch", StringComparison.InvariantCultureIgnoreCase))
  1812. {
  1813. //Allow drop (copy, not move) patch files
  1814. e.Effect = DragDropEffects.Copy;
  1815. }
  1816. else
  1817. {
  1818. //When a non-patch file is dragged, do not allow it
  1819. e.Effect = DragDropEffects.None;
  1820. return;
  1821. }
  1822. }
  1823. }
  1824. }
  1825. #endregion
  1826. private void ShowGitNotesToolStripMenuItem_Click(object sender, EventArgs e)
  1827. {
  1828. Settings.ShowGitNotes = !showGitNotesToolStripMenuItem.Checked;
  1829. showGitNotesToolStripMenuItem.Checked = Settings.ShowGitNotes;
  1830. ForceRefreshRevisions();
  1831. }
  1832. private void InitRepository_Click(object sender, EventArgs e)
  1833. {
  1834. if (GitUICommands.Instance.StartInitializeDialog(this, Settings.WorkingDir))
  1835. ForceRefreshRevisions();
  1836. }
  1837. private void CloneRepository_Click(object sender, EventArgs e)
  1838. {
  1839. if (GitUICommands.Instance.StartCloneDialog(this))
  1840. ForceRefreshRevisions();
  1841. }
  1842. private void ShowRevisionGraphToolStripMenuItemClick(object sender, EventArgs e)
  1843. {
  1844. if (Settings.RevisionGraphLayout == (int)RevisionGridLayout.Small) Settings.RevisionGraphLayout = (int)RevisionGridLayout.SmallWithGraph;
  1845. else if (Settings.RevisionGraphLayout == (int)RevisionGridLayout.Card) Settings.RevisionGraphLayout = (int)RevisionGridLayout.CardWithGraph;
  1846. else if (Settings.RevisionGraphLayout == (int)RevisionGridLayout.LargeCard) Settings.RevisionGraphLayout = (int)RevisionGridLayout.LargeCardWithGraph;
  1847. else if (Settings.RevisionGraphLayout == (int)RevisionGridLayout.SmallWithGraph) Settings.RevisionGraphLayout = (int)RevisionGridLayout.Small;
  1848. else if (Settings.RevisionGraphLayout == (int)RevisionGridLayout.CardWithGraph) Settings.RevisionGraphLayout = (int)RevisionGridLayout.Card;
  1849. else if (Settings.RevisionGraphLayout == (int)RevisionGridLayout.LargeCardWithGraph) Settings.RevisionGraphLayout = (int)RevisionGridLayout.LargeCard;
  1850. else if (Settings.RevisionGraphLayout == (int)RevisionGridLayout.FilledBranchesSmall) Settings.RevisionGraphLayout = (int)RevisionGridLayout.FilledBranchesSmallWithGraph;
  1851. else if (Settings.RevisionGraphLayout == (int)RevisionGridLayout.FilledBranchesSmallWithGraph) Settings.RevisionGraphLayout = (int)RevisionGridLayout.FilledBranchesSmall;
  1852. SetRevisionsLayout();
  1853. Refresh();
  1854. }
  1855. public void ToggleRevisionCardLayout()
  1856. {
  1857. var layouts = new List<RevisionGridLayout>((RevisionGridLayout[])Enum.GetValues(typeof(RevisionGridLayout)));
  1858. layouts.Sort();
  1859. var maxLayout = (int)layouts[layouts.Count - 1];
  1860. int nextLayout = Settings.RevisionGraphLayout + 1;
  1861. if (nextLayout > maxLayout)
  1862. nextLayout = 1;
  1863. SetRevisionsLayout((RevisionGridLayout)nextLayout);
  1864. }
  1865. public void SetRevisionsLayout(RevisionGridLayout revisionGridLayout)
  1866. {
  1867. Settings.RevisionGraphLayout = (int)revisionGridLayout;
  1868. SetRevisionsLayout();
  1869. }
  1870. private void SetRevisionsLayout()
  1871. {
  1872. layout = Enum.IsDefined(typeof(RevisionGridLayout), Settings.RevisionGraphLayout)
  1873. ? (RevisionGridLayout)Settings.RevisionGraphLayout
  1874. : RevisionGridLayout.SmallWithGraph;
  1875. showRevisionGraphToolStripMenuItem.Checked = IsGraphLayout();
  1876. IsCardLayout();
  1877. if (IsFilledBranchesLayout())
  1878. {
  1879. NormalFont = new Font("Tahoma", 8.75F); // SystemFonts.DefaultFont.FontFamily, SystemFonts.DefaultFont.Size + 2);
  1880. }
  1881. else
  1882. {
  1883. NormalFont = new Font("Tahoma", 8.75F);
  1884. }
  1885. if (IsCardLayout())
  1886. {
  1887. if (Settings.RevisionGraphLayout == (int)RevisionGridLayout.Card
  1888. || Settings.RevisionGraphLayout == (int)RevisionGridLayout.CardWithGraph)
  1889. {
  1890. rowHeigth = 45;
  1891. }
  1892. else
  1893. {
  1894. rowHeigth = 70;
  1895. }
  1896. selectedItemBrush = new LinearGradientBrush(new Rectangle(0, 0, rowHeigth, rowHeigth),
  1897. Revisions.RowTemplate.DefaultCellStyle.SelectionBackColor,
  1898. Color.LightBlue, 90, false);
  1899. Revisions.ShowAuthor(!IsCardLayout());
  1900. Revisions.SetDimensions(NODE_DIMENSION, LANE_WIDTH, LANE_LINE_WIDTH, rowHeigth, selectedItemBrush);
  1901. }
  1902. else
  1903. {
  1904. if (IsFilledBranchesLayout())
  1905. {
  1906. using (var graphics = Graphics.FromHwnd(Handle))
  1907. {
  1908. rowHeigth = (int)graphics.MeasureString("By", NormalFont).Height + 9;
  1909. }
  1910. selectedItemBrush = SystemBrushes.Highlight;
  1911. }
  1912. else
  1913. {
  1914. rowHeigth = 25;
  1915. selectedItemBrush = new LinearGradientBrush(new Rectangle(0, 0, rowHeigth, rowHeigth),
  1916. Revisions.RowTemplate.DefaultCellStyle.SelectionBackColor,
  1917. Color.LightBlue, 90, false);
  1918. }
  1919. Revisions.ShowAuthor(!IsCardLayout());
  1920. Revisions.SetDimensions(NODE_DIMENSION, LANE_WIDTH, LANE_LINE_WIDTH, rowHeigth, selectedItemBrush);
  1921. }
  1922. //Hide graph column when there it is disabled OR when a filter is active
  1923. //allowing for special case when history of a single file is being displayed
  1924. if (!IsGraphLayout() || (ShouldHideGraph(false) && !AllowGraphWithFilter))
  1925. {
  1926. Revisions.HideRevisionGraph();
  1927. }
  1928. else
  1929. {
  1930. Revisions.ShowRevisionGraph();
  1931. }
  1932. }
  1933. private bool IsFilledBranchesLayout()
  1934. {
  1935. return layout == RevisionGridLayout.FilledBranchesSmall || layout == RevisionGridLayout.FilledBranchesSmallWithGraph;
  1936. }
  1937. private bool IsCardLayout()
  1938. {
  1939. return layout == RevisionGridLayout.Card
  1940. || layout == RevisionGridLayout.CardWithGraph
  1941. || layout == RevisionGridLayout.LargeCard
  1942. || layout == RevisionGridLayout.LargeCardWithGraph;
  1943. }
  1944. private bool IsGraphLayout()
  1945. {
  1946. return layout == RevisionGridLayout.SmallWithGraph
  1947. || layout == RevisionGridLayout.CardWithGraph
  1948. || layout == RevisionGridLayout.LargeCardWithGraph
  1949. || layout == RevisionGridLayout.FilledBranchesSmallWithGraph;
  1950. }
  1951. #region Hotkey commands
  1952. public const string HotkeySettingsName = "RevisionGrid";
  1953. internal enum Commands
  1954. {
  1955. ToggleRevisionGraph,
  1956. RevisionFilter,
  1957. ToggleAuthorDateCommitDate,
  1958. ToggleOrderRevisionsByDate,
  1959. ToggleShowRelativeDate,
  1960. ToggleDrawNonRelativesGray,
  1961. ToggleShowGitNotes,
  1962. ToggleRevisionCardLayout,
  1963. ShowAllBranches,
  1964. ShowCurrentBranchOnly
  1965. }
  1966. protected override bool ExecuteCommand(int cmd)
  1967. {
  1968. Commands command = (Commands)cmd;
  1969. switch (command)
  1970. {
  1971. case Commands.ToggleRevisionGraph: ShowRevisionGraphToolStripMenuItemClick(null, null); break;
  1972. case Commands.RevisionFilter: FilterToolStripMenuItemClick(null, null); break;
  1973. case Commands.ToggleAuthorDateCommitDate: ShowAuthorDateToolStripMenuItemClick(null, null); break;
  1974. case Commands.ToggleOrderRevisionsByDate: OrderRevisionsByDateToolStripMenuItemClick(null, null); break;
  1975. case Commands.ToggleShowRelativeDate: ShowRelativeDateToolStripMenuItemClick(null, null); break;
  1976. case Commands.ToggleDrawNonRelativesGray: drawNonrelativesGrayToolStripMenuItem_Click(null, null); break;
  1977. case Commands.ToggleShowGitNotes: ShowGitNotesToolStripMenuItem_Click(null, null); break;
  1978. case Commands.ToggleRevisionCardLayout: ToggleRevisionCardLayout(); break;
  1979. case Commands.ShowAllBranches: ShowAllBranchesToolStripMenuItemClick(null, null); break;
  1980. case Commands.ShowCurrentBranchOnly: ShowCurrentBranchOnlyToolStripMenuItemClick(null, null); break;
  1981. default: ExecuteScriptCommand(cmd, Keys.None); break;
  1982. }
  1983. return true;
  1984. }
  1985. #endregion
  1986. private void toolStripMenuWithOneItem_Click(object sender, EventArgs e)
  1987. {
  1988. if (sender is ToolStripMenuItem)
  1989. {
  1990. ToolStripMenuItem item = sender as ToolStripMenuItem;
  1991. if (item.DropDown != null && item.DropDown.Items.Count > 0)
  1992. item.DropDown.Items[0].PerformClick();
  1993. }
  1994. }
  1995. private void goToParentToolStripMenuItem_Click(object sender, EventArgs e)
  1996. {
  1997. var r = GetRevision(LastRow);
  1998. if (r.HasParent())
  1999. SetSelectedRevision(new GitRevision(r.ParentGuids[0]));
  2000. }
  2001. }
  2002. }