PageRenderTime 62ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/GitUI/UserControls/RevisionGrid.cs

https://github.com/vbjay/gitextensions
C# | 2860 lines | 2333 code | 451 blank | 76 comment | 459 complexity | 955b6e15475ae64482907136f7e395ca 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.DirectoryServices;
  5. using System.Drawing;
  6. using System.Drawing.Drawing2D;
  7. using System.IO;
  8. using System.Linq;
  9. using System.Text.RegularExpressions;
  10. using System.Threading;
  11. using System.Threading.Tasks;
  12. using System.Windows.Forms;
  13. using GitCommands;
  14. using GitCommands.Git;
  15. using GitUI.BuildServerIntegration;
  16. using GitUI.CommandsDialogs;
  17. using GitUI.HelperDialogs;
  18. using GitUI.Hotkey;
  19. using GitUI.RevisionGridClasses;
  20. using GitUI.Script;
  21. using Gravatar;
  22. using ResourceManager;
  23. using GitUI.UserControls.RevisionGridClasses;
  24. using GitUI.CommandsDialogs.BrowseDialog;
  25. namespace GitUI
  26. {
  27. public enum RevisionGridLayout
  28. {
  29. FilledBranchesSmall = 1,
  30. FilledBranchesSmallWithGraph = 2,
  31. Small = 3,
  32. SmallWithGraph = 4,
  33. Card = 5,
  34. CardWithGraph = 6,
  35. LargeCard = 7,
  36. LargeCardWithGraph = 8
  37. }
  38. public enum RevisionGraphDrawStyleEnum
  39. {
  40. Normal,
  41. DrawNonRelativesGray,
  42. HighlightSelected
  43. }
  44. [DefaultEvent("DoubleClick")]
  45. public sealed partial class RevisionGrid : GitModuleControl
  46. {
  47. private readonly TranslationString _droppingFilesBlocked = new TranslationString("For you own protection dropping more than 10 patch files at once is blocked!");
  48. private readonly TranslationString _cannotHighlightSelectedBranch = new TranslationString("Cannot highlight selected branch when revision graph is loading.");
  49. private readonly TranslationString _noRevisionFoundError = new TranslationString("No revision found.");
  50. private const int NodeDimension = 8;
  51. private const int LaneWidth = 13;
  52. private const int LaneLineWidth = 2;
  53. private Brush _selectedItemBrush;
  54. private Brush _filledItemBrush; // disposable brush
  55. private readonly FormRevisionFilter _revisionFilter = new FormRevisionFilter();
  56. private RefsFiltringOptions _refsOptions = RefsFiltringOptions.All | RefsFiltringOptions.Boundary;
  57. private bool _initialLoad = true;
  58. private string _initialSelectedRevision;
  59. private string _lastQuickSearchString = string.Empty;
  60. private Label _quickSearchLabel;
  61. private string _quickSearchString;
  62. private RevisionGraph _revisionGraphCommand;
  63. public BuildServerWatcher BuildServerWatcher { get; private set; }
  64. private RevisionGridLayout _layout;
  65. private int _rowHeigth;
  66. public event GitModuleChangedEventHandler GitModuleChanged;
  67. public event EventHandler<DoubleClickRevisionEventArgs> DoubleClickRevision;
  68. private readonly RevisionGridMenuCommands _revisionGridMenuCommands;
  69. bool showCurrentBranchOnlyToolStripMenuItemChecked; // refactoring
  70. bool showAllBranchesToolStripMenuItemChecked; // refactoring
  71. bool showFilteredBranchesToolStripMenuItemChecked; // refactoring
  72. public RevisionGrid()
  73. {
  74. InitLayout();
  75. InitializeComponent();
  76. this.Loading.Image = global::GitUI.Properties.Resources.loadingpanel;
  77. Translate();
  78. _revisionGridMenuCommands = new RevisionGridMenuCommands(this);
  79. _revisionGridMenuCommands.CreateOrUpdateMenuCommands();
  80. // fill View context menu from MenuCommands
  81. var viewMenuCommands = _revisionGridMenuCommands.GetViewMenuCommands();
  82. FillMenuFromMenuCommands(viewMenuCommands, viewToolStripMenuItem);
  83. // fill Navigate context menu from MenuCommands
  84. var navigateMenuCommands = _revisionGridMenuCommands.GetNavigateMenuCommands();
  85. FillMenuFromMenuCommands(navigateMenuCommands, navigateToolStripMenuItem);
  86. NormalFont = AppSettings.Font;
  87. Loading.Paint += Loading_Paint;
  88. Revisions.CellPainting += RevisionsCellPainting;
  89. Revisions.CellFormatting += RevisionsCellFormatting;
  90. Revisions.KeyPress += RevisionsKeyPress;
  91. showMergeCommitsToolStripMenuItem.Checked = AppSettings.ShowMergeCommits;
  92. BranchFilter = String.Empty;
  93. SetShowBranches();
  94. Filter = "";
  95. FixedFilter = "";
  96. InMemFilterIgnoreCase = false;
  97. InMemAuthorFilter = "";
  98. InMemCommitterFilter = "";
  99. InMemMessageFilter = "";
  100. AllowGraphWithFilter = false;
  101. _quickSearchString = "";
  102. quickSearchTimer.Tick += QuickSearchTimerTick;
  103. Revisions.Loading += RevisionsLoading;
  104. //Allow to drop patch file on revisiongrid
  105. Revisions.DragEnter += Revisions_DragEnter;
  106. Revisions.DragDrop += Revisions_DragDrop;
  107. Revisions.AllowDrop = true;
  108. Revisions.ColumnHeadersVisible = false;
  109. IsMessageMultilineDataGridViewColumn.Width = 25;
  110. IsMessageMultilineDataGridViewColumn.DisplayIndex = 2;
  111. this.HotkeysEnabled = true;
  112. try
  113. {
  114. SetRevisionsLayout((RevisionGridLayout)AppSettings.RevisionGraphLayout);
  115. }
  116. catch
  117. {
  118. SetRevisionsLayout(RevisionGridLayout.SmallWithGraph);
  119. }
  120. }
  121. private void FillMenuFromMenuCommands(IEnumerable<MenuCommand> menuCommands, ToolStripMenuItem targetMenuItem)
  122. {
  123. foreach (var menuCommand in menuCommands)
  124. {
  125. var toolStripItem = (ToolStripItem)MenuCommand.CreateToolStripItem(menuCommand);
  126. var toolStripMenuItem = toolStripItem as ToolStripMenuItem;
  127. if (toolStripMenuItem != null)
  128. {
  129. menuCommand.RegisterMenuItem(toolStripMenuItem);
  130. }
  131. targetMenuItem.DropDownItems.Add(toolStripItem);
  132. }
  133. }
  134. void Loading_Paint(object sender, PaintEventArgs e)
  135. {
  136. // If our loading state has changed since the last paint, update it.
  137. if (Loading != null)
  138. {
  139. if (Loading.Visible != _isLoading)
  140. {
  141. Loading.Visible = _isLoading;
  142. }
  143. }
  144. }
  145. [Browsable(false)]
  146. public Font HeadFont { get; private set; }
  147. [Browsable(false)]
  148. public Font SuperprojectFont { get; private set; }
  149. [Browsable(false)]
  150. public int LastScrollPos { get; private set; }
  151. [Browsable(false)]
  152. public string[] LastSelectedRows { get; private set; }
  153. [Browsable(false)]
  154. public Font RefsFont { get; private set; }
  155. private Font _normalFont;
  156. [Browsable(false)]
  157. [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  158. public Font NormalFont
  159. {
  160. get { return _normalFont; }
  161. set
  162. {
  163. _normalFont = value;
  164. MessageDataGridViewColumn.DefaultCellStyle.Font = _normalFont;
  165. DateDataGridViewColumn.DefaultCellStyle.Font = _normalFont;
  166. IsMessageMultilineDataGridViewColumn.DefaultCellStyle.Font = _normalFont;
  167. RefsFont = IsFilledBranchesLayout() ? _normalFont : new Font(_normalFont, FontStyle.Bold);
  168. HeadFont = new Font(_normalFont, FontStyle.Bold);
  169. SuperprojectFont = new Font(_normalFont, FontStyle.Underline);
  170. }
  171. }
  172. [Category("Filter")]
  173. [DefaultValue("")]
  174. public string Filter { get; set; }
  175. [Category("Filter")]
  176. [DefaultValue("")]
  177. public string FixedFilter { get; set; }
  178. [Category("Filter")]
  179. [DefaultValue(false)]
  180. public bool InMemFilterIgnoreCase { get; set; }
  181. [Category("Filter")]
  182. [DefaultValue("")]
  183. public string InMemAuthorFilter { get; set; }
  184. [Category("Filter")]
  185. [DefaultValue("")]
  186. public string InMemCommitterFilter { get; set; }
  187. [Category("Filter")]
  188. [DefaultValue("")]
  189. public string InMemMessageFilter { get; set; }
  190. [Category("Filter")]
  191. [DefaultValue("")]
  192. public string BranchFilter { get; set; }
  193. [Category("Filter")]
  194. [DefaultValue(false)]
  195. public bool AllowGraphWithFilter { get; set; }
  196. [Browsable(false)]
  197. public string CurrentCheckout { get; private set; }
  198. [Browsable(false)]
  199. [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  200. public string FiltredFileName { get; set; }
  201. [Browsable(false)]
  202. private string FiltredCurrentCheckout { get; set; }
  203. [Browsable(false)]
  204. public Task<SuperProjectInfo> SuperprojectCurrentCheckout { get; private set; }
  205. [Browsable(false)]
  206. public int LastRow { get; private set; }
  207. [Description("Indicates whether the user is allowed to select more than one commit at a time.")]
  208. [Category("Behavior")]
  209. [DefaultValue(true)]
  210. public bool MultiSelect
  211. {
  212. get { return Revisions.MultiSelect; }
  213. set { Revisions.MultiSelect = value; }
  214. }
  215. [Description("Show uncommited changes in revision grid if enabled in settings.")]
  216. [Category("Behavior")]
  217. [DefaultValue(false)]
  218. public bool ShowUncommitedChangesIfPossible
  219. {
  220. get;
  221. set;
  222. }
  223. [Description("Show build server information in revision grid if enabled in settings.")]
  224. [Category("Behavior")]
  225. [DefaultValue(false)]
  226. public bool ShowBuildServerInfo
  227. {
  228. get;
  229. set;
  230. }
  231. [Description("Do not open the commit info dialog on double click. This is used if the double click event is handled elseswhere.")]
  232. [Category("Behavior")]
  233. [DefaultValue(false)]
  234. public bool DoubleClickDoesNotOpenCommitInfo
  235. {
  236. get;
  237. set;
  238. }
  239. private IndexWatcher _indexWatcher;
  240. [Browsable(false)]
  241. public IndexWatcher IndexWatcher
  242. {
  243. get
  244. {
  245. if (_indexWatcher == null)
  246. _indexWatcher = new IndexWatcher(UICommandsSource);
  247. return _indexWatcher;
  248. }
  249. }
  250. public void SetInitialRevision(GitRevision initialSelectedRevision)
  251. {
  252. _initialSelectedRevision = initialSelectedRevision != null ? initialSelectedRevision.Guid : null;
  253. }
  254. private bool _isLoading;
  255. private void RevisionsLoading(bool isLoading)
  256. {
  257. // Since this can happen on a background thread, we'll just set a
  258. // flag and deal with it next time we paint (a bit of a hack, but
  259. // it works)
  260. _isLoading = isLoading;
  261. }
  262. private void ShowQuickSearchString()
  263. {
  264. if (_quickSearchLabel == null)
  265. {
  266. _quickSearchLabel
  267. = new Label
  268. {
  269. Location = new Point(10, 10),
  270. BorderStyle = BorderStyle.FixedSingle,
  271. ForeColor = SystemColors.InfoText,
  272. BackColor = SystemColors.Info
  273. };
  274. Controls.Add(_quickSearchLabel);
  275. }
  276. _quickSearchLabel.Visible = true;
  277. _quickSearchLabel.BringToFront();
  278. _quickSearchLabel.Text = _quickSearchString;
  279. _quickSearchLabel.AutoSize = true;
  280. }
  281. private void HideQuickSearchString()
  282. {
  283. if (_quickSearchLabel != null)
  284. _quickSearchLabel.Visible = false;
  285. }
  286. private void QuickSearchTimerTick(object sender, EventArgs e)
  287. {
  288. quickSearchTimer.Stop();
  289. _quickSearchString = "";
  290. HideQuickSearchString();
  291. }
  292. private void RestartQuickSearchTimer()
  293. {
  294. quickSearchTimer.Stop();
  295. quickSearchTimer.Interval = AppSettings.RevisionGridQuickSearchTimeout;
  296. quickSearchTimer.Start();
  297. }
  298. private void RevisionsKeyPress(object sender, KeyPressEventArgs e)
  299. {
  300. var curIndex = -1;
  301. if (Revisions.SelectedRows.Count > 0)
  302. curIndex = Revisions.SelectedRows[0].Index;
  303. curIndex = curIndex >= 0 ? curIndex : 0;
  304. if (e.KeyChar == 8 && _quickSearchString.Length > 1) //backspace
  305. {
  306. RestartQuickSearchTimer();
  307. _quickSearchString = _quickSearchString.Substring(0, _quickSearchString.Length - 1);
  308. FindNextMatch(curIndex, _quickSearchString, false);
  309. _lastQuickSearchString = _quickSearchString;
  310. e.Handled = true;
  311. ShowQuickSearchString();
  312. }
  313. else if (!char.IsControl(e.KeyChar))
  314. {
  315. RestartQuickSearchTimer();
  316. //The code below is meant to fix the weird keyvalues when pressing keys e.g. ".".
  317. _quickSearchString = string.Concat(_quickSearchString, char.ToLower(e.KeyChar));
  318. FindNextMatch(curIndex, _quickSearchString, false);
  319. _lastQuickSearchString = _quickSearchString;
  320. e.Handled = true;
  321. ShowQuickSearchString();
  322. }
  323. else
  324. {
  325. _quickSearchString = "";
  326. HideQuickSearchString();
  327. e.Handled = false;
  328. }
  329. }
  330. private void FindNextMatch(int startIndex, string searchString, bool reverse)
  331. {
  332. if (Revisions.RowCount == 0)
  333. return;
  334. int? searchResult;
  335. if (reverse)
  336. searchResult = SearchInReverseOrder(startIndex, searchString);
  337. else
  338. searchResult = SearchForward(startIndex, searchString);
  339. if (!searchResult.HasValue)
  340. return;
  341. Revisions.ClearSelection();
  342. Revisions.Rows[searchResult.Value].Selected = true;
  343. Revisions.CurrentCell = Revisions.Rows[searchResult.Value].Cells[1];
  344. }
  345. private int? SearchForward(int startIndex, string searchString)
  346. {
  347. // Check for out of bounds roll over if required
  348. int index;
  349. if (startIndex < 0 || startIndex >= Revisions.RowCount)
  350. startIndex = 0;
  351. for (index = startIndex; index < Revisions.RowCount; ++index)
  352. {
  353. if (GetRevision(index).MatchesSearchString(searchString))
  354. return index;
  355. }
  356. // We didn't find it so start searching from the top
  357. for (index = 0; index < startIndex; ++index)
  358. {
  359. if (GetRevision(index).MatchesSearchString(searchString))
  360. return index;
  361. }
  362. return null;
  363. }
  364. private int? SearchInReverseOrder(int startIndex, string searchString)
  365. {
  366. // Check for out of bounds roll over if required
  367. int index;
  368. if (startIndex < 0 || startIndex >= Revisions.RowCount)
  369. startIndex = Revisions.RowCount - 1;
  370. for (index = startIndex; index >= 0; --index)
  371. {
  372. if (GetRevision(index).MatchesSearchString(searchString))
  373. return index;
  374. }
  375. // We didn't find it so start searching from the bottom
  376. for (index = Revisions.RowCount - 1; index > startIndex; --index)
  377. {
  378. if (GetRevision(index).MatchesSearchString(searchString))
  379. return index;
  380. }
  381. return null;
  382. }
  383. public void DisableContextMenu()
  384. {
  385. Revisions.ContextMenuStrip = null;
  386. }
  387. public void FormatQuickFilter(string filter,
  388. bool[] parameters,
  389. out string revListArgs,
  390. out string inMemMessageFilter,
  391. out string inMemCommitterFilter,
  392. out string inMemAuthorFilter)
  393. {
  394. revListArgs = string.Empty;
  395. inMemMessageFilter = string.Empty;
  396. inMemCommitterFilter = string.Empty;
  397. inMemAuthorFilter = string.Empty;
  398. if (!string.IsNullOrEmpty(filter))
  399. {
  400. // hash filtering only possible in memory
  401. var cmdLineSafe = GitCommandHelpers.VersionInUse.IsRegExStringCmdPassable(filter);
  402. revListArgs = " --regexp-ignore-case ";
  403. if (parameters[0])
  404. if (cmdLineSafe && !MessageFilterCouldBeSHA(filter))
  405. revListArgs += "--grep=\"" + filter + "\" ";
  406. else
  407. inMemMessageFilter = filter;
  408. if (parameters[1])
  409. if (cmdLineSafe)
  410. revListArgs += "--committer=\"" + filter + "\" ";
  411. else
  412. inMemCommitterFilter = filter;
  413. if (parameters[2])
  414. if (cmdLineSafe)
  415. revListArgs += "--author=\"" + filter + "\" ";
  416. else
  417. inMemAuthorFilter = filter;
  418. if (parameters[3])
  419. if (cmdLineSafe)
  420. revListArgs += "\"-S" + filter + "\" ";
  421. else
  422. throw new InvalidOperationException("Filter text not valid for \"Diff contains\" filter.");
  423. }
  424. }
  425. public bool SetAndApplyBranchFilter(string filter)
  426. {
  427. if (filter.Equals(_revisionFilter.GetBranchFilter()))
  428. return false;
  429. if (filter.Equals(""))
  430. {
  431. AppSettings.BranchFilterEnabled = false;
  432. AppSettings.ShowCurrentBranchOnly = true;
  433. }
  434. else
  435. {
  436. AppSettings.BranchFilterEnabled = true;
  437. AppSettings.ShowCurrentBranchOnly = false;
  438. _revisionFilter.SetBranchFilter(filter);
  439. }
  440. SetShowBranches();
  441. return true;
  442. }
  443. public void SetLimit(int limit)
  444. {
  445. _revisionFilter.SetLimit(limit);
  446. }
  447. public override void Refresh()
  448. {
  449. SetRevisionsLayout();
  450. base.Refresh();
  451. Revisions.Refresh();
  452. }
  453. protected override void OnCreateControl()
  454. {
  455. base.OnCreateControl();
  456. _isLoading = true;
  457. Error.Visible = false;
  458. NoCommits.Visible = false;
  459. NoGit.Visible = false;
  460. Revisions.Visible = false;
  461. Loading.Visible = true;
  462. Loading.BringToFront();
  463. BuildServerWatcher = new BuildServerWatcher(this, Revisions);
  464. }
  465. public new void Load()
  466. {
  467. if (!DesignMode)
  468. ReloadHotkeys();
  469. ForceRefreshRevisions();
  470. }
  471. public event EventHandler SelectionChanged;
  472. public void SetSelectedIndex(int index)
  473. {
  474. if (Revisions.Rows[index].Selected)
  475. return;
  476. Revisions.ClearSelection();
  477. Revisions.Rows[index].Selected = true;
  478. Revisions.CurrentCell = Revisions.Rows[index].Cells[1];
  479. Revisions.Select();
  480. }
  481. public void SetSelectedRevision(string revision)
  482. {
  483. if (revision != null)
  484. {
  485. for (var i = 0; i < Revisions.RowCount; i++)
  486. {
  487. if (GetRevision(i).Guid != revision)
  488. continue;
  489. SetSelectedIndex(i);
  490. return;
  491. }
  492. }
  493. Revisions.ClearSelection();
  494. Revisions.Select();
  495. }
  496. public GitRevision GetRevision(string guid)
  497. {
  498. return Revisions.GetRevision(guid);
  499. }
  500. public void SetSelectedRevision(GitRevision revision)
  501. {
  502. SetSelectedRevision(revision != null ? revision.Guid : null);
  503. }
  504. public void HighlightBranch(string aId)
  505. {
  506. RevisionGraphDrawStyle = RevisionGraphDrawStyleEnum.HighlightSelected;
  507. Revisions.HighlightBranch(aId);
  508. }
  509. private void RevisionsSelectionChanged(object sender, EventArgs e)
  510. {
  511. if (Revisions.SelectedRows.Count > 0)
  512. LastRow = Revisions.SelectedRows[0].Index;
  513. SelectionTimer.Enabled = false;
  514. SelectionTimer.Stop();
  515. SelectionTimer.Enabled = true;
  516. SelectionTimer.Start();
  517. }
  518. public RevisionGraphDrawStyleEnum RevisionGraphDrawStyle
  519. {
  520. get
  521. {
  522. return Revisions.RevisionGraphDrawStyle;
  523. }
  524. set
  525. {
  526. Revisions.RevisionGraphDrawStyle = value;
  527. }
  528. }
  529. public List<GitRevision> GetSelectedRevisions()
  530. {
  531. return GetSelectedRevisions(null);
  532. }
  533. public List<GitRevision> GetSelectedRevisions(SortDirection? direction)
  534. {
  535. var rows = Revisions
  536. .SelectedRows
  537. .Cast<DataGridViewRow>()
  538. .Where(row => Revisions.RowCount > row.Index);
  539. if (direction.HasValue)
  540. {
  541. int d = direction.Value == SortDirection.Ascending ? 1 : -1;
  542. rows = rows.OrderBy((row) => row.Index, (r1, r2) => d * (r1 - r2));
  543. }
  544. return rows
  545. .Select(row => GetRevision(row.Index))
  546. .ToList();
  547. }
  548. public List<string> GetRevisionChildren(string revision)
  549. {
  550. return Revisions.GetRevisionChildren(revision);
  551. }
  552. public GitRevision GetRevision(int aRow)
  553. {
  554. return Revisions.GetRowData(aRow);
  555. }
  556. public GitRevision GetCurrentRevision()
  557. {
  558. var revision = Module.GetRevision(CurrentCheckout, true);
  559. var refs = Module.GetRefs(true, true);
  560. foreach (var gitRef in refs)
  561. {
  562. if (gitRef.Guid.Equals(revision.Guid))
  563. {
  564. revision.Refs.Add(gitRef);
  565. }
  566. }
  567. return revision;
  568. }
  569. public void RefreshRevisions()
  570. {
  571. if (IndexWatcher.IndexChanged)
  572. ForceRefreshRevisions();
  573. }
  574. private class RevisionGraphInMemFilterOr : RevisionGraphInMemFilter
  575. {
  576. private RevisionGraphInMemFilter fFilter1;
  577. private RevisionGraphInMemFilter fFilter2;
  578. public RevisionGraphInMemFilterOr(RevisionGraphInMemFilter aFilter1,
  579. RevisionGraphInMemFilter aFilter2)
  580. {
  581. fFilter1 = aFilter1;
  582. fFilter2 = aFilter2;
  583. }
  584. public override bool PassThru(GitRevision rev)
  585. {
  586. return fFilter1.PassThru(rev) || fFilter2.PassThru(rev);
  587. }
  588. }
  589. private class RevisionGridInMemFilter : RevisionGraphInMemFilter
  590. {
  591. private readonly string _AuthorFilter;
  592. private readonly Regex _AuthorFilterRegex;
  593. private readonly string _CommitterFilter;
  594. private readonly Regex _CommitterFilterRegex;
  595. private readonly string _MessageFilter;
  596. private readonly Regex _MessageFilterRegex;
  597. private readonly string _ShaFilter;
  598. private readonly Regex _ShaFilterRegex;
  599. public RevisionGridInMemFilter(string authorFilter, string committerFilter, string messageFilter, bool ignoreCase)
  600. {
  601. SetUpVars(authorFilter, ref _AuthorFilter, ref _AuthorFilterRegex, ignoreCase);
  602. SetUpVars(committerFilter, ref _CommitterFilter, ref _CommitterFilterRegex, ignoreCase);
  603. SetUpVars(messageFilter, ref _MessageFilter, ref _MessageFilterRegex, ignoreCase);
  604. if (!string.IsNullOrEmpty(_MessageFilter) && MessageFilterCouldBeSHA(_MessageFilter))
  605. {
  606. SetUpVars(messageFilter, ref _ShaFilter, ref _ShaFilterRegex, false);
  607. }
  608. }
  609. private static void SetUpVars(string filterValue,
  610. ref string filterStr,
  611. ref Regex filterRegEx,
  612. bool ignoreCase)
  613. {
  614. RegexOptions opts = RegexOptions.None;
  615. if (ignoreCase) opts = opts | RegexOptions.IgnoreCase;
  616. filterStr = filterValue != null ? filterValue.Trim() : string.Empty;
  617. try
  618. {
  619. filterRegEx = new Regex(filterStr, opts);
  620. }
  621. catch (ArgumentException)
  622. {
  623. filterRegEx = null;
  624. }
  625. }
  626. private static bool CheckCondition(string filter, Regex regex, string value)
  627. {
  628. return string.IsNullOrEmpty(filter) ||
  629. ((regex != null) && regex.Match(value).Success);
  630. }
  631. public override bool PassThru(GitRevision rev)
  632. {
  633. return CheckCondition(_AuthorFilter, _AuthorFilterRegex, rev.Author) &&
  634. CheckCondition(_CommitterFilter, _CommitterFilterRegex, rev.Committer) &&
  635. (CheckCondition(_MessageFilter, _MessageFilterRegex, rev.Message) ||
  636. CheckCondition(_ShaFilter, _ShaFilterRegex, rev.Guid));
  637. }
  638. public static RevisionGridInMemFilter CreateIfNeeded(string authorFilter,
  639. string committerFilter,
  640. string messageFilter,
  641. bool ignoreCase)
  642. {
  643. if (!(string.IsNullOrEmpty(authorFilter) &&
  644. string.IsNullOrEmpty(committerFilter) &&
  645. string.IsNullOrEmpty(messageFilter) &&
  646. !MessageFilterCouldBeSHA(messageFilter)))
  647. return new RevisionGridInMemFilter(authorFilter,
  648. committerFilter,
  649. messageFilter,
  650. ignoreCase);
  651. else
  652. return null;
  653. }
  654. }
  655. public void ReloadHotkeys()
  656. {
  657. this.Hotkeys = HotkeySettingsManager.LoadHotkeys(HotkeySettingsName);
  658. _revisionGridMenuCommands.CreateOrUpdateMenuCommands();
  659. }
  660. public void ReloadTranslation()
  661. {
  662. Translate();
  663. }
  664. public bool ShowRemoteRef(GitRef r)
  665. {
  666. if (r.IsTag)
  667. return AppSettings.ShowSuperprojectTags;
  668. if (r.IsHead)
  669. return AppSettings.ShowSuperprojectBranches;
  670. if (r.IsRemote)
  671. return AppSettings.ShowSuperprojectRemoteBranches;
  672. return false;
  673. }
  674. public void ForceRefreshRevisions()
  675. {
  676. try
  677. {
  678. RevisionGraphDrawStyle = RevisionGraphDrawStyleEnum.DrawNonRelativesGray;
  679. IsMessageMultilineDataGridViewColumn.Visible = AppSettings.ShowIndicatorForMultilineMessage;
  680. ApplyFilterFromRevisionFilterDialog();
  681. _initialLoad = true;
  682. BuildServerWatcher.CancelBuildStatusFetchOperation();
  683. DisposeRevisionGraphCommand();
  684. var newCurrentCheckout = Module.GetCurrentCheckout();
  685. Task<SuperProjectInfo> newSuperPrjectInfo =
  686. Task.Factory.StartNew(() => GetSuperprojectCheckout(ShowRemoteRef));
  687. newSuperPrjectInfo.ContinueWith((task) => Refresh(),
  688. TaskScheduler.FromCurrentSynchronizationContext());
  689. // If the current checkout changed, don't get the currently selected rows, select the
  690. // new current checkout instead.
  691. if (newCurrentCheckout == CurrentCheckout)
  692. {
  693. LastSelectedRows = Revisions.SelectedIds;
  694. }
  695. else
  696. {
  697. // This is a new checkout, so ensure the variable is cleared out.
  698. LastSelectedRows = null;
  699. }
  700. Revisions.ClearSelection();
  701. CurrentCheckout = newCurrentCheckout;
  702. FiltredCurrentCheckout = CurrentCheckout;
  703. SuperprojectCurrentCheckout = newSuperPrjectInfo;
  704. Revisions.Clear();
  705. Error.Visible = false;
  706. if (!Module.IsValidGitWorkingDir())
  707. {
  708. Revisions.Visible = false;
  709. NoCommits.Visible = true;
  710. Loading.Visible = false;
  711. NoGit.Visible = true;
  712. string dir = Module.WorkingDir;
  713. if (String.IsNullOrEmpty(dir) || !Directory.Exists(dir) ||
  714. Directory.GetDirectories(dir).Length == 0 &&
  715. Directory.GetFiles(dir).Length == 0)
  716. CloneRepository.Show();
  717. else
  718. CloneRepository.Hide();
  719. NoGit.BringToFront();
  720. return;
  721. }
  722. NoCommits.Visible = false;
  723. NoGit.Visible = false;
  724. Revisions.Visible = true;
  725. Revisions.BringToFront();
  726. Revisions.Enabled = false;
  727. Loading.Visible = true;
  728. Loading.BringToFront();
  729. _isLoading = true;
  730. base.Refresh();
  731. IndexWatcher.Reset();
  732. if (!AppSettings.ShowGitNotes && (_refsOptions & (RefsFiltringOptions.All | RefsFiltringOptions.Boundary)) == (RefsFiltringOptions.All | RefsFiltringOptions.Boundary))
  733. _refsOptions |= RefsFiltringOptions.ShowGitNotes;
  734. if (AppSettings.ShowGitNotes)
  735. _refsOptions &= ~RefsFiltringOptions.ShowGitNotes;
  736. if (!AppSettings.ShowMergeCommits)
  737. _refsOptions |= RefsFiltringOptions.NoMerges;
  738. RevisionGridInMemFilter revisionFilterIMF = RevisionGridInMemFilter.CreateIfNeeded(_revisionFilter.GetInMemAuthorFilter(),
  739. _revisionFilter.GetInMemCommitterFilter(),
  740. _revisionFilter.GetInMemMessageFilter(),
  741. _revisionFilter.GetIgnoreCase());
  742. RevisionGridInMemFilter filterBarIMF = RevisionGridInMemFilter.CreateIfNeeded(InMemAuthorFilter,
  743. InMemCommitterFilter,
  744. InMemMessageFilter,
  745. InMemFilterIgnoreCase);
  746. RevisionGraphInMemFilter revGraphIMF;
  747. if (revisionFilterIMF != null && filterBarIMF != null)
  748. revGraphIMF = new RevisionGraphInMemFilterOr(revisionFilterIMF, filterBarIMF);
  749. else if (revisionFilterIMF != null)
  750. revGraphIMF = revisionFilterIMF;
  751. else
  752. revGraphIMF = filterBarIMF;
  753. _revisionGraphCommand = new RevisionGraph(Module) { BranchFilter = BranchFilter, RefsOptions = _refsOptions, Filter = _revisionFilter.GetFilter() + Filter + FixedFilter };
  754. _revisionGraphCommand.Updated += GitGetCommitsCommandUpdated;
  755. _revisionGraphCommand.Exited += GitGetCommitsCommandExited;
  756. _revisionGraphCommand.Error += _revisionGraphCommand_Error;
  757. _revisionGraphCommand.InMemFilter = revGraphIMF;
  758. _revisionGraphCommand.Execute();
  759. LoadRevisions();
  760. SetRevisionsLayout();
  761. }
  762. catch (Exception)
  763. {
  764. Error.Visible = true;
  765. Error.BringToFront();
  766. throw;
  767. }
  768. }
  769. public class SuperProjectInfo
  770. {
  771. public string CurrentBranch;
  772. public string Conflict_Base;
  773. public string Conflict_Remote;
  774. public string Conflict_Local;
  775. public Dictionary<string, List<GitRef>> Refs;
  776. }
  777. private SuperProjectInfo GetSuperprojectCheckout(Func<GitRef, bool> showRemoteRef)
  778. {
  779. if (Module.SuperprojectModule == null)
  780. return null;
  781. SuperProjectInfo spi = new SuperProjectInfo();
  782. var currentCheckout = Module.GetSuperprojectCurrentCheckout();
  783. if (currentCheckout.Key == 'U')
  784. {
  785. // return local and remote hashes
  786. var array = Module.SuperprojectModule.GetConflictedSubmoduleHashes(Module.SubmodulePath);
  787. spi.Conflict_Base = array[0];
  788. spi.Conflict_Local = array[1];
  789. spi.Conflict_Remote = array[2];
  790. }
  791. else
  792. {
  793. spi.CurrentBranch = currentCheckout.Value;
  794. }
  795. var refs = Module.SuperprojectModule.GetSubmoduleItemsForEachRef(Module.SubmodulePath, showRemoteRef);
  796. if (refs != null)
  797. {
  798. spi.Refs = refs.Where(a => a.Value != null).GroupBy(a => a.Value.Guid).ToDictionary(gr => gr.Key, gr => gr.Select(a => a.Key).ToList());
  799. }
  800. return spi;
  801. }
  802. private static readonly Regex PotentialShaPattern = new Regex(@"^[a-f0-9]{5,}", RegexOptions.Compiled);
  803. public static bool MessageFilterCouldBeSHA(string filter)
  804. {
  805. bool result = PotentialShaPattern.IsMatch(filter);
  806. return result;
  807. }
  808. private void _revisionGraphCommand_Error(object sender, AsyncErrorEventArgs e)
  809. {
  810. // This has to happen on the UI thread
  811. this.InvokeSync(o =>
  812. {
  813. Error.Visible = true;
  814. //Error.BringToFront();
  815. NoGit.Visible = false;
  816. NoCommits.Visible = false;
  817. Revisions.Visible = false;
  818. Loading.Visible = false;
  819. }, this);
  820. DisposeRevisionGraphCommand();
  821. this.InvokeAsync(() =>
  822. {
  823. throw e.Exception;
  824. }
  825. );
  826. e.Handled = true;
  827. }
  828. private void GitGetCommitsCommandUpdated(object sender, EventArgs e)
  829. {
  830. var updatedEvent = (RevisionGraph.RevisionGraphUpdatedEventArgs)e;
  831. UpdateGraph(updatedEvent.Revision);
  832. }
  833. internal bool FilterIsApplied(bool inclBranchFilter)
  834. {
  835. return (inclBranchFilter && !string.IsNullOrEmpty(BranchFilter)) ||
  836. !(string.IsNullOrEmpty(Filter) &&
  837. !_revisionFilter.FilterEnabled() &&
  838. string.IsNullOrEmpty(InMemAuthorFilter) &&
  839. string.IsNullOrEmpty(InMemCommitterFilter) &&
  840. string.IsNullOrEmpty(InMemMessageFilter));
  841. }
  842. private bool ShouldHideGraph(bool inclBranchFilter)
  843. {
  844. return (inclBranchFilter && !string.IsNullOrEmpty(BranchFilter)) ||
  845. !(!_revisionFilter.ShouldHideGraph() &&
  846. string.IsNullOrEmpty(InMemAuthorFilter) &&
  847. string.IsNullOrEmpty(InMemCommitterFilter) &&
  848. string.IsNullOrEmpty(InMemMessageFilter));
  849. }
  850. private void DisposeRevisionGraphCommand()
  851. {
  852. if (_revisionGraphCommand != null)
  853. {
  854. //Dispose command, it is not needed anymore
  855. _revisionGraphCommand.Updated -= GitGetCommitsCommandUpdated;
  856. _revisionGraphCommand.Exited -= GitGetCommitsCommandExited;
  857. _revisionGraphCommand.Error -= _revisionGraphCommand_Error;
  858. _revisionGraphCommand.Dispose();
  859. _revisionGraphCommand = null;
  860. }
  861. }
  862. private void GitGetCommitsCommandExited(object sender, EventArgs e)
  863. {
  864. _isLoading = false;
  865. if (_revisionGraphCommand.RevisionCount == 0 &&
  866. !FilterIsApplied(true))
  867. {
  868. // This has to happen on the UI thread
  869. this.InvokeSync(o =>
  870. {
  871. NoGit.Visible = false;
  872. NoCommits.Visible = true;
  873. //NoCommits.BringToFront();
  874. Revisions.Visible = false;
  875. Loading.Visible = false;
  876. }, this);
  877. }
  878. else
  879. {
  880. // This has to happen on the UI thread
  881. this.InvokeSync(o =>
  882. {
  883. UpdateGraph(null);
  884. Loading.Visible = false;
  885. SelectInitialRevision();
  886. _isLoading = false;
  887. if (ShowBuildServerInfo)
  888. BuildServerWatcher.LaunchBuildServerInfoFetchOperation();
  889. }, this);
  890. }
  891. DisposeRevisionGraphCommand();
  892. }
  893. private void SelectInitialRevision()
  894. {
  895. string filtredCurrentCheckout;
  896. if (SearchRevision(CurrentCheckout, out filtredCurrentCheckout) >= 0)
  897. FiltredCurrentCheckout = filtredCurrentCheckout;
  898. else
  899. FiltredCurrentCheckout = CurrentCheckout;
  900. if (LastSelectedRows == null)
  901. {
  902. if (!string.IsNullOrEmpty(_initialSelectedRevision))
  903. {
  904. string revision;
  905. int index = SearchRevision(_initialSelectedRevision, out revision);
  906. if (index >= 0)
  907. SetSelectedIndex(index);
  908. }
  909. else
  910. {
  911. SetSelectedRevision(FiltredCurrentCheckout);
  912. }
  913. }
  914. LastSelectedRows = null;
  915. if (!Revisions.IsRevisionRelative(FiltredCurrentCheckout))
  916. {
  917. HighlightBranch(FiltredCurrentCheckout);
  918. }
  919. }
  920. internal int TrySearchRevision(string initRevision, out string graphRevision)
  921. {
  922. var rows = Revisions
  923. .Rows
  924. .Cast<DataGridViewRow>();
  925. var revisions = rows
  926. .Select(row => new { row.Index, GetRevision(row.Index).Guid });
  927. var idx = revisions.FirstOrDefault(rev => rev.Guid == initRevision);
  928. if (idx != null)
  929. {
  930. graphRevision = idx.Guid;
  931. return idx.Index;
  932. }
  933. graphRevision = null;
  934. return -1;
  935. }
  936. private int SearchRevision(string initRevision, out string graphRevision)
  937. {
  938. int index = TrySearchRevision(initRevision, out graphRevision);
  939. if (index >= 0)
  940. return index;
  941. var rows = Revisions
  942. .Rows
  943. .Cast<DataGridViewRow>();
  944. var dict = rows
  945. .ToDictionary(row => GetRevision(row.Index).Guid, row => row.Index);
  946. var revListParams = "rev-list ";
  947. if (AppSettings.OrderRevisionByDate)
  948. revListParams += "--date-order ";
  949. else
  950. revListParams += "--topo-order ";
  951. if (AppSettings.MaxRevisionGraphCommits > 0)
  952. revListParams += string.Format("--max-count=\"{0}\" ", (int)AppSettings.MaxRevisionGraphCommits);
  953. var allrevisions = Module.ReadGitOutputLines(revListParams + initRevision);
  954. graphRevision = allrevisions.FirstOrDefault(rev => dict.TryGetValue(rev, out index));
  955. if (graphRevision != null)
  956. return index;
  957. return -1;
  958. }
  959. private static string GetDateHeaderText()
  960. {
  961. return AppSettings.ShowAuthorDate ? Strings.GetAuthorDateText() : Strings.GetCommitDateText();
  962. }
  963. private void LoadRevisions()
  964. {
  965. if (_revisionGraphCommand == null)
  966. {
  967. return;
  968. }
  969. Revisions.SuspendLayout();
  970. Revisions.MessageColumn.HeaderText = Strings.GetMessageText();
  971. Revisions.AuthorColumn.HeaderText = Strings.GetAuthorText();
  972. Revisions.DateColumn.HeaderText = GetDateHeaderText();
  973. Revisions.SelectionChanged -= RevisionsSelectionChanged;
  974. if (LastSelectedRows != null)
  975. Revisions.SelectedIds = LastSelectedRows;
  976. Revisions.Enabled = true;
  977. Revisions.Focus();
  978. Revisions.SelectionChanged += RevisionsSelectionChanged;
  979. Revisions.ResumeLayout();
  980. if (!_initialLoad)
  981. return;
  982. _initialLoad = false;
  983. SelectionTimer.Enabled = false;
  984. SelectionTimer.Stop();
  985. SelectionTimer.Enabled = true;
  986. SelectionTimer.Start();
  987. }
  988. public struct DrawRefArgs
  989. {
  990. public Graphics Graphics;
  991. public Rectangle CellBounds;
  992. public bool IsRowSelected;
  993. public Font RefsFont;
  994. }
  995. private void RevisionsCellPainting(object sender, DataGridViewCellPaintingEventArgs e)
  996. {
  997. // If our loading state has changed since the last paint, update it.
  998. if (Loading != null)
  999. {
  1000. if (Loading.Visible != _isLoading)
  1001. {
  1002. Loading.Visible = _isLoading;
  1003. }
  1004. }
  1005. var columnIndex = e.ColumnIndex;
  1006. int graphColIndex = GraphDataGridViewColumn.Index;
  1007. int messageColIndex = MessageDataGridViewColumn.Index;
  1008. int authorColIndex = AuthorDataGridViewColumn.Index;
  1009. int dateColIndex = DateDataGridViewColumn.Index;
  1010. int isMsgMultilineColIndex = IsMessageMultilineDataGridViewColumn.Index;
  1011. // The graph column is handled by the DvcsGraph
  1012. if (e.ColumnIndex == graphColIndex)
  1013. {
  1014. return;
  1015. }
  1016. if (e.RowIndex < 0 || (e.State & DataGridViewElementStates.Visible) == 0)
  1017. return;
  1018. if (Revisions.RowCount <= e.RowIndex)
  1019. return;
  1020. var revision = GetRevision(e.RowIndex);
  1021. if (revision == null)
  1022. return;
  1023. var spi = SuperprojectCurrentCheckout.IsCompleted ? SuperprojectCurrentCheckout.Result : null;
  1024. var superprojectRefs = new List<GitRef>();
  1025. if (spi != null && spi.Refs != null && spi.Refs.ContainsKey(revision.Guid))
  1026. {
  1027. superprojectRefs.AddRange(spi.Refs[revision.Guid].Where(ShowRemoteRef));
  1028. }
  1029. e.Handled = true;
  1030. var drawRefArgs = new DrawRefArgs();
  1031. drawRefArgs.Graphics = e.Graphics;
  1032. drawRefArgs.CellBounds = e.CellBounds;
  1033. drawRefArgs.IsRowSelected = ((e.State & DataGridViewElementStates.Selected) == DataGridViewElementStates.Selected);
  1034. if (drawRefArgs.IsRowSelected /*&& !showRevisionCards*/)
  1035. e.Graphics.FillRectangle(_selectedItemBrush, e.CellBounds);
  1036. else
  1037. e.Graphics.FillRectangle(Brushes.White, e.CellBounds);
  1038. Color foreColor;
  1039. if (!AppSettings.RevisionGraphDrawNonRelativesTextGray || Revisions.RowIsRelative(e.RowIndex))
  1040. {
  1041. foreColor = drawRefArgs.IsRowSelected && IsFilledBranchesLayout()
  1042. ? SystemColors.HighlightText
  1043. : e.CellStyle.ForeColor;
  1044. }
  1045. else
  1046. {
  1047. foreColor = drawRefArgs.IsRowSelected ? SystemColors.HighlightText : Color.Gray;
  1048. }
  1049. using (Brush foreBrush = new SolidBrush(foreColor))
  1050. {
  1051. var rowFont = NormalFont;
  1052. if (revision.Guid == CurrentCheckout /*&& !showRevisionCards*/)
  1053. rowFont = HeadFont;
  1054. else if (spi != null && spi.CurrentBranch == revision.Guid)
  1055. rowFont = SuperprojectFont;
  1056. if (columnIndex == messageColIndex)
  1057. {
  1058. int baseOffset = 0;
  1059. if (IsCardLayout())
  1060. {
  1061. baseOffset = 5;
  1062. Rectangle cellRectangle = new Rectangle(e.CellBounds.Left + baseOffset, e.CellBounds.Top + 1, e.CellBounds.Width - (baseOffset * 2), e.CellBounds.Height - 4);
  1063. if (!AppSettings.RevisionGraphDrawNonRelativesGray || Revisions.RowIsRelative(e.RowIndex))
  1064. {
  1065. e.Graphics.FillRectangle(
  1066. new LinearGradientBrush(cellRectangle,
  1067. Color.FromArgb(255, 220, 220, 231),
  1068. Color.FromArgb(255, 240, 240, 250), 90, false), cellRectangle);
  1069. using (var pen = new Pen(Color.FromArgb(255, 200, 200, 200), 1))
  1070. {
  1071. e.Graphics.DrawRectangle(pen, cellRectangle);
  1072. }
  1073. }
  1074. else
  1075. {
  1076. e.Graphics.FillRectangle(
  1077. new LinearGradientBrush(cellRectangle,
  1078. Color.FromArgb(255, 240, 240, 240),
  1079. Color.FromArgb(255, 250, 250, 250), 90, false), cellRectangle);
  1080. }
  1081. if ((e.State & DataGridViewElementStates.Selected) == DataGridViewElementStates.Selected)
  1082. {
  1083. using (var penSelectionBackColor = new Pen(Revisions.RowTemplate.DefaultCellStyle.SelectionBackColor, 1))
  1084. e.Graphics.DrawRectangle(penSelectionBackColor, cellRectangle);
  1085. }
  1086. }
  1087. float offset = baseOffset;
  1088. var gitRefs = revision.Refs;
  1089. drawRefArgs.RefsFont = IsFilledBranchesLayout() ? rowFont : RefsFont;
  1090. if (spi != null)
  1091. {
  1092. if (spi.Conflict_Base == revision.Guid)
  1093. offset = DrawRef(drawRefArgs, offset, "Base", Color.OrangeRed, ArrowType.NotFilled);
  1094. if (spi.Conflict_Local == revision.Guid)
  1095. offset = DrawRef(drawRefArgs, offset, "Local", Color.OrangeRed, ArrowType.NotFilled);
  1096. if (spi.Conflict_Remote == revision.Guid)
  1097. offset = DrawRef(drawRefArgs, offset, "Remote", Color.OrangeRed, ArrowType.NotFilled);
  1098. }
  1099. if (gitRefs.Any())
  1100. {
  1101. gitRefs.Sort((left, right) =>
  1102. {
  1103. if (left.IsTag != right.IsTag)
  1104. return right.IsTag.CompareTo(left.IsTag);
  1105. if (left.IsRemote != right.IsRemote)
  1106. return left.IsRemote.CompareTo(right.IsRemote);
  1107. return left.Name.CompareTo(right.Name);
  1108. });
  1109. foreach (var gitRef in gitRefs.Where(head => (!head.IsRemote || AppSettings.ShowRemoteBranches)))
  1110. {
  1111. if (gitRef.IsTag)
  1112. {
  1113. if (!AppSettings.ShowTags)
  1114. {
  1115. continue;
  1116. }
  1117. }
  1118. Color headColor = GetHeadColor(gitRef);
  1119. ArrowType arrowType = gitRef.Selected ? ArrowType.Filled :
  1120. gitRef.SelectedHeadMergeSource ? ArrowType.NotFilled : ArrowType.None;
  1121. var superprojectRef = superprojectRefs.FirstOrDefault(spGitRef => gitRef.CompleteName == spGitRef.CompleteName);
  1122. if (superprojectRef != null)
  1123. superprojectRefs.Remove(superprojectRef);
  1124. offset = DrawRef(drawRefArgs, offset, gitRef.Name, headColor, arrowType, superprojectRef != null, true);
  1125. }
  1126. }
  1127. foreach (var gitRef in superprojectRefs)
  1128. {
  1129. Color headColor = GetHeadColor(gitRef);
  1130. ArrowType arrowType = gitRef.Selected ? ArrowType.Filled :
  1131. gitRef.SelectedHeadMergeSource ? ArrowType.NotFilled : ArrowType.None;
  1132. offset = DrawRef(drawRefArgs, offset, gitRef.Name, headColor, arrowType, true, false);
  1133. }
  1134. if (IsCardLayout())
  1135. offset = baseOffset;
  1136. var text = (string)e.FormattedValue;
  1137. var bounds = AdjustCellBounds(e.CellBounds, offset);
  1138. DrawColumnText(e.Graphics, text, rowFont, foreColor, bounds);
  1139. if (IsCardLayout())
  1140. {
  1141. int textHeight = (int)e.Graphics.MeasureString(text, rowFont).Height;
  1142. int gravatarSize = _rowHeigth - textHeight - 12;
  1143. int gravatarTop = e.CellBounds.Top + textHeight + 6;
  1144. int gravatarLeft = e.CellBounds.Left + baseOffset + 2;
  1145. Image gravatar = Gravatar.GravatarService.GetImageFromCache(revision.AuthorEmail + gravatarSize.ToString() + ".png", revision.AuthorEmail, AppSettings.AuthorImageCacheDays, gravatarSize, AppSettings.GravatarCachePath, FallBackService.MonsterId);
  1146. if (gravatar == null && !string.IsNullOrEmpty(revision.AuthorEmail))
  1147. {
  1148. ThreadPool.QueueUserWorkItem(o =>
  1149. Gravatar.GravatarService.LoadCachedImage(revision.AuthorEmail + gravatarSize.ToString() + ".png", revision.AuthorEmail, null, AppSettings.AuthorImageCacheDays, gravatarSize, AppSettings.GravatarCachePath, RefreshGravatar, FallBackService.MonsterId));
  1150. }
  1151. if (gravatar != null)
  1152. e.Graphics.DrawImage(gravatar, gravatarLeft + 1, gravatarTop + 1, gravatarSize, gravatarSize);
  1153. e.Graphics.DrawRectangle(Pens.Black, gravatarLeft, gravatarTop, gravatarSize + 1, gravatarSize + 1);
  1154. string authorText;
  1155. string timeText;
  1156. if (_rowHeigth >= 60)
  1157. {
  1158. authorText = revision.Author;
  1159. timeText = TimeToString(AppSettings.ShowAuthorDate ? revision.AuthorDate : revision.CommitDate);
  1160. }
  1161. else
  1162. {
  1163. timeText = string.Concat(revision.Author, " (", TimeToString(AppSettings.ShowAuthorDate ? revision.AuthorDate : revision.CommitDate), ")");
  1164. authorText = string.Empty;
  1165. }
  1166. e.Graphics.DrawString(authorText, rowFont, foreBrush,
  1167. new PointF(gravatarLeft + gravatarSize + 5, gravatarTop + 6));
  1168. e.Graphics.DrawString(timeText, rowFont, foreBrush,
  1169. new PointF(gravatarLeft + gravatarSize + 5, e.CellBounds.Bottom - textHeight - 4));
  1170. }
  1171. }
  1172. else if (columnIndex == authorColIndex)
  1173. {
  1174. var text = (string)e.FormattedValue;
  1175. e.Graphics.DrawString(text, rowFont, foreBrush,
  1176. new PointF(e.CellBounds.Left, e.CellBounds.Top + 4));
  1177. }
  1178. else if (columnIndex == dateColIndex)
  1179. {
  1180. var time = AppSettings.ShowAuthorDate ? revision.AuthorDate : revision.CommitDate;
  1181. var text = TimeToString(time);
  1182. e.Graphics.DrawString(text, rowFont, foreBrush,
  1183. new PointF(e.CellBounds.Left, e.CellBounds.Top + 4));
  1184. }
  1185. else if (columnIndex == BuildServerWatcher.BuildStatusImageColumnIndex)
  1186. {
  1187. BuildInfoDrawingLogic.BuildStatusImageColumnCellPainting(e, revision, foreBrush, rowFont);
  1188. }
  1189. else if (columnIndex == BuildServerWatcher.BuildStatusMessageColumnIndex)
  1190. {
  1191. BuildInfoDrawingLogic.BuildStatusMessageCellPainting(e, revision, foreBrush, rowFont);
  1192. }
  1193. else if (AppSettings.ShowIndicatorForMultilineMessage && columnIndex == isMsgMultilineColIndex)
  1194. {
  1195. var text = (string)e.FormattedValue;
  1196. e.Graphics.DrawString(text, rowFont, foreBrush,
  1197. new PointF(e.CellBounds.Left, e.CellBounds.Top + 4));
  1198. }
  1199. }
  1200. }
  1201. private float DrawRef(DrawRefArgs drawRefArgs, float offset, string name, Color headColor, ArrowType arrowType, bool dashedLine = false, bool fill = false)
  1202. {
  1203. var textColor = fill ? headColor : Lerp(headColor, Color.White, 0.5f);
  1204. if (IsCardLayout())
  1205. {
  1206. using (Brush textBrush = new SolidBrush(textColor))
  1207. {
  1208. string headName = name;
  1209. offset += drawRefArgs.Graphics.MeasureString(headName, drawRefArgs.RefsFont).Width + 6;
  1210. var location = new PointF(drawRefArgs.CellBounds.Right - offset, drawRefArgs.CellBounds.Top + 4);
  1211. var size = new SizeF(drawRefArgs.Graphics.MeasureString(headName, drawRefArgs.RefsFont).Width,
  1212. drawRefArgs.Graphics.MeasureString(headName, drawRefArgs.RefsFont).Height);
  1213. if (fill)
  1214. drawRefArgs.Graphics.FillRectangle(SystemBrushes.Info, location.X - 1,
  1215. location.Y - 1, size.Width + 3, size.Height + 2);
  1216. drawRefArgs.Graphics.DrawRectangle(SystemPens.InfoText, location.X - 1,
  1217. location.Y - 1, size.Width + 3, size.Height + 2);
  1218. drawRefArgs.Graphics.DrawString(headName, drawRefArgs.RefsFont, textBrush, location);
  1219. }
  1220. }
  1221. else
  1222. {
  1223. string headName = IsFilledBranchesLayout()
  1224. ? name
  1225. : string.Concat("[", name, "] ");
  1226. var headBounds = AdjustCellBounds(drawRefArgs.CellBounds, offset);
  1227. SizeF textSize = drawRefArgs.Graphics.MeasureString(headName, drawRefArgs.RefsFont);
  1228. offset += textSize.Width;
  1229. if (IsFilledBranchesLayout())
  1230. {
  1231. offset += 9;
  1232. float extraOffset = DrawHeadBackground(drawRefArgs.IsRowSelected, drawRefArgs.Graphics,
  1233. headColor, headBounds.X,
  1234. headBounds.Y,
  1235. RoundToEven(textSize.Width + 3),
  1236. RoundToEven(textSize.Height), 3,
  1237. arrowType, dashedLine, fill);
  1238. offset += extraOffset;
  1239. headBounds.Offset((int)(extraOffset + 1), 0);
  1240. }
  1241. DrawColumnText(drawRefArgs.Graphics, headName, drawRefArgs.RefsFont, textColor, headBounds);
  1242. }
  1243. return offset;
  1244. }
  1245. private void RevisionsCellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
  1246. {
  1247. var columnIndex = e.ColumnIndex;
  1248. if (e.RowIndex < 0)
  1249. return;
  1250. if (Revisions.RowCount <= e.RowIndex)
  1251. return;
  1252. var revision = GetRevision(e.RowIndex);
  1253. if (revision == null)
  1254. return;
  1255. e.FormattingApplied = true;
  1256. int graphColIndex = GraphDataGridViewColumn.Index;
  1257. int messageColIndex = MessageDataGridViewColumn.Index;
  1258. int authorColIndex = AuthorDataGridViewColumn.Index;
  1259. int dateColIndex = DateDataGridViewColumn.Index;
  1260. int isMsgMultilineColIndex = IsMessageMultilineDataGridViewColumn.Index;
  1261. if (columnIndex == graphColIndex)
  1262. {
  1263. e.Value = revision.Guid;
  1264. }
  1265. else if (columnIndex == messageColIndex)
  1266. {
  1267. e.Value = revision.Message;
  1268. }
  1269. else if (columnIndex == authorColIndex)
  1270. {
  1271. e.Value = revision.Author ?? "";
  1272. }
  1273. else if (columnIndex == dateColIndex)
  1274. {
  1275. var time = AppSettings.ShowAuthorDate ? revision.AuthorDate : revision.CommitDate;
  1276. if (time == DateTime.MinValue || time == DateTime.MaxValue)
  1277. e.Value = "";
  1278. else
  1279. e.Value = string.Format("{0} {1}", time.ToShortDateString(), time.ToLongTimeString());
  1280. }
  1281. else if (columnIndex == BuildServerWatcher.BuildStatusImageColumnIndex)
  1282. {
  1283. BuildInfoDrawingLogic.BuildStatusImageColumnCellFormatting(e, Revisions, revision);
  1284. }
  1285. else if (columnIndex == BuildServerWatcher.BuildStatusMessageColumnIndex)
  1286. {
  1287. BuildInfoDrawingLogic.BuildStatusMessageCellFormatting(e, revision);
  1288. }
  1289. else if (AppSettings.ShowIndicatorForMultilineMessage && columnIndex == isMsgMultilineColIndex)
  1290. {
  1291. if (revision.Body == null && !revision.IsArtificial())
  1292. {
  1293. ThreadPool.QueueUserWorkItem(o => LoadIsMultilineMessageInfo(revision, columnIndex, e.RowIndex, Revisions.RowCount));
  1294. }
  1295. if (revision.Body != null)
  1296. {
  1297. e.Value = revision.Body.TrimEnd().Contains("\n") ? "[...]" : "";
  1298. }
  1299. else
  1300. {
  1301. e.Value = "";
  1302. }
  1303. }
  1304. else
  1305. {
  1306. e.FormattingApplied = false;
  1307. }
  1308. }
  1309. /// <summary>
  1310. ///
  1311. /// </summary>
  1312. /// <param name="revision"></param>
  1313. /// <param name="totalRowCount">check if grid has changed while thread is queued</param>
  1314. /// <param name="colIndex"></param>
  1315. /// <param name="rowIndex"></param>
  1316. private void LoadIsMultilineMessageInfo(GitRevision revision, int colIndex, int rowIndex, int totalRowCount)
  1317. {
  1318. // code taken from CommitInfo.cs
  1319. CommitData commitData = CommitData.CreateFromRevision(revision);
  1320. string error = "";
  1321. if (revision.Body == null)
  1322. {
  1323. CommitData.UpdateCommitMessage(commitData, Module, revision.Guid, ref error);
  1324. revision.Body = commitData.Body;
  1325. }
  1326. // now that Body is filled (not null anymore) the revision grid can be refreshed to display the new information
  1327. this.InvokeAsync(() =>
  1328. {
  1329. if (Revisions == null || Revisions.RowCount == 0 || Revisions.RowCount <= rowIndex || Revisions.RowCount != totalRowCount)
  1330. {
  1331. return;
  1332. }
  1333. Revisions.InvalidateCell(colIndex, rowIndex);
  1334. });
  1335. }
  1336. private void DrawColumnText(Graphics gc, string text, Font font, Color color, Rectangle bounds)
  1337. {
  1338. TextRenderer.DrawText(gc, text, font, bounds, color, TextFormatFlags.EndEllipsis | TextFormatFlags.NoPrefix);
  1339. }
  1340. private static Rectangle AdjustCellBounds(Rectangle cellBounds, float offset)
  1341. {
  1342. return new Rectangle((int)(cellBounds.Left + offset), cellBounds.Top + 4,
  1343. cellBounds.Width - (int)offset, cellBounds.Height);
  1344. }
  1345. private static Color GetHeadColor(GitRef gitRef)
  1346. {
  1347. if (gitRef.IsTag)
  1348. return AppSettings.TagColor;
  1349. if (gitRef.IsHead)
  1350. return AppSettings.BranchColor;
  1351. if (gitRef.IsRemote)
  1352. return AppSettings.RemoteBranchColor;
  1353. return AppSettings.OtherTagColor;
  1354. }
  1355. private float RoundToEven(float value)
  1356. {
  1357. int result = ((int)value / 2) * 2;
  1358. return result < value ? result + 2 : result;
  1359. }
  1360. enum ArrowType
  1361. {
  1362. None,
  1363. Filled,
  1364. NotFilled
  1365. }
  1366. static readonly float[] dashPattern = new float[] { 4, 4 };
  1367. private float DrawHeadBackground(bool isSelected, Graphics graphics, Color color,
  1368. float x, float y, float width, float height, float radius, ArrowType arrowType, bool dashedLine, bool fill)
  1369. {
  1370. float additionalOffset = arrowType != ArrowType.None ? GetArrowSize(height) : 0;
  1371. width += additionalOffset;
  1372. var oldMode = graphics.SmoothingMode;
  1373. graphics.SmoothingMode = SmoothingMode.AntiAlias;
  1374. try
  1375. {
  1376. // shade
  1377. if (fill)
  1378. using (var shadePath = CreateRoundRectPath(x + 1, y + 1, width, height, radius))
  1379. {
  1380. var shadeBrush = isSelected ? Brushes.Black : Brushes.Gray;
  1381. graphics.FillPath(shadeBrush, shadePath);
  1382. }
  1383. using (var forePath = CreateRoundRectPath(x, y, width, height, radius))
  1384. {
  1385. Color fillColor = Lerp(color, Color.White, 0.92F);
  1386. if (fill)
  1387. using (var fillBrush = new LinearGradientBrush(new RectangleF(x, y, width, height), fillColor, Lerp(fillColor, Color.White, 0.9F), 90))
  1388. graphics.FillPath(fillBrush, forePath);
  1389. else if (isSelected)
  1390. graphics.FillPath(Brushes.White, forePath);
  1391. // frame
  1392. using (var pen = new Pen(Lerp(color, Color.White, 0.83F)))
  1393. {
  1394. if (dashedLine)
  1395. pen.DashPattern = dashPattern;
  1396. graphics.DrawPath(pen, forePath);
  1397. }
  1398. // arrow if the head is the current branch
  1399. if (arrowType != ArrowType.None)
  1400. DrawArrow(graphics, x, y, height, color, arrowType == ArrowType.Filled);
  1401. }
  1402. }
  1403. finally
  1404. {
  1405. graphics.SmoothingMode = oldMode;
  1406. }
  1407. return additionalOffset;
  1408. }
  1409. private float GetArrowSize(float rowHeight)
  1410. {
  1411. return rowHeight - 6;
  1412. }
  1413. private void DrawArrow(Graphics graphics, float x, float y, float rowHeight, Color color, bool filled)
  1414. {
  1415. const float horShift = 4;
  1416. const float verShift = 3;
  1417. float height = rowHeight - verShift * 2;
  1418. float width = height / 2;
  1419. var points = new[]
  1420. {
  1421. new PointF(x + horShift, y + verShift),
  1422. new PointF(x + horShift + width, y + verShift + height/2),
  1423. new PointF(x + horShift, y + verShift + height),
  1424. new PointF(x + horShift, y + verShift)
  1425. };
  1426. if (filled)
  1427. {
  1428. using (var solidBrush = new SolidBrush(color))
  1429. graphics.FillPolygon(solidBrush, points);
  1430. }
  1431. else
  1432. {
  1433. using (var pen = new Pen(color))
  1434. graphics.DrawPolygon(pen, points);
  1435. }
  1436. }
  1437. private static GraphicsPath CreateRoundRectPath(float x, float y, float width, float height, float radius)
  1438. {
  1439. var path = new GraphicsPath();
  1440. path.AddLine(x + radius, y, x + width - (radius * 2), y);
  1441. path.AddArc(x + width - (radius * 2), y, radius * 2, radius * 2, 270, 90);
  1442. path.AddLine(x + width, y + radius, x + width, y + height - (radius * 2));
  1443. path.AddArc(x + width - (radius * 2), y + height - (radius * 2), radius * 2, radius * 2, 0, 90);
  1444. path.AddLine(x + width - (radius * 2), y + height, x + radius, y + height);
  1445. path.AddArc(x, y + height - (radius * 2), radius * 2, radius * 2, 90, 90);
  1446. path.AddLine(x, y + height - (radius * 2), x, y + radius);
  1447. path.AddArc(x, y, radius * 2, radius * 2, 180, 90);
  1448. path.CloseFigure();
  1449. return path;
  1450. }
  1451. private static float Lerp(float start, float end, float amount)
  1452. {
  1453. float difference = end - start;
  1454. float adjusted = difference * amount;
  1455. return start + adjusted;
  1456. }
  1457. private static Color Lerp(Color colour, Color to, float amount)
  1458. {
  1459. // start colours as lerp-able floats
  1460. float sr = colour.R, sg = colour.G, sb = colour.B;
  1461. // end colours as lerp-able floats
  1462. float er = to.R, eg = to.G, eb = to.B;
  1463. // lerp the colours to get the difference
  1464. byte r = (byte)Lerp(sr, er, amount),
  1465. g = (byte)Lerp(sg, eg, amount),
  1466. b = (byte)Lerp(sb, eb, amount);
  1467. // return the new colour
  1468. return Color.FromArgb(r, g, b);
  1469. }
  1470. private void RefreshGravatar(Image image)
  1471. {
  1472. this.InvokeAsync(Revisions.Refresh);
  1473. }
  1474. private void RevisionsDoubleClick(object sender, EventArgs e)
  1475. {
  1476. if (DoubleClickRevision != null)
  1477. {
  1478. var selectedRevisions = GetSelectedRevisions();
  1479. DoubleClickRevision(this, new DoubleClickRevisionEventArgs(selectedRevisions.FirstOrDefault()));
  1480. }
  1481. if (!DoubleClickDoesNotOpenCommitInfo)
  1482. {
  1483. ViewSelectedRevisions();
  1484. }
  1485. }
  1486. public void ViewSelectedRevisions()
  1487. {
  1488. var selectedRevisions = GetSelectedRevisions();
  1489. if (selectedRevisions.Any())
  1490. {
  1491. var form = new FormCommitDiff(UICommands, selectedRevisions[0].Guid);
  1492. form.ShowDialog(this);
  1493. }
  1494. else
  1495. UICommands.StartCompareRevisionsDialog(this);
  1496. }
  1497. private void SelectionTimerTick(object sender, EventArgs e)
  1498. {
  1499. SelectionTimer.Enabled = false;
  1500. SelectionTimer.Stop();
  1501. if (SelectionChanged != null)
  1502. SelectionChanged(this, e);
  1503. }
  1504. private void CreateTagToolStripMenuItemClick(object sender, EventArgs e)
  1505. {
  1506. if (Revisions.RowCount <= LastRow || LastRow < 0)
  1507. return;
  1508. using (var frm = new FormCreateTag(UICommands, GetRevision(LastRow)))
  1509. {
  1510. if (frm.ShowDialog(this) == DialogResult.OK)
  1511. {
  1512. RefreshRevisions();
  1513. }
  1514. }
  1515. }
  1516. private void ResetCurrentBranchToHereToolStripMenuItemClick(object sender, EventArgs e)
  1517. {
  1518. if (Revisions.RowCount <= LastRow || LastRow < 0)
  1519. return;
  1520. var frm = new FormResetCurrentBranch(UICommands, GetRevision(LastRow));
  1521. frm.ShowDialog(this);
  1522. }
  1523. private void CreateNewBranchToolStripMenuItemClick(object sender, EventArgs e)
  1524. {
  1525. if (Revisions.RowCount <= LastRow || LastRow < 0)
  1526. return;
  1527. UICommands.DoActionOnRepo(() =>
  1528. {
  1529. var frm = new FormCreateBranch(UICommands, GetRevision(LastRow));
  1530. return frm.ShowDialog(this) == DialogResult.OK;
  1531. });
  1532. }
  1533. private void RevisionsMouseClick(object sender, MouseEventArgs e)
  1534. {
  1535. var pt = Revisions.PointToClient(Cursor.Position);
  1536. var hti = Revisions.HitTest(pt.X, pt.Y);
  1537. LastRow = hti.RowIndex;
  1538. }
  1539. private void RevisionsCellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
  1540. {
  1541. if (e.Button != MouseButtons.Right)
  1542. return;
  1543. var pt = Revisions.PointToClient(Cursor.Position);
  1544. var hti = Revisions.HitTest(pt.X, pt.Y);
  1545. if (LastRow == hti.RowIndex)
  1546. return;
  1547. LastRow = hti.RowIndex;
  1548. Revisions.ClearSelection();
  1549. if (LastRow >= 0 && Revisions.Rows.Count > LastRow)
  1550. Revisions.Rows[LastRow].Selected = true;
  1551. }
  1552. private void CommitClick(object sender, EventArgs e)
  1553. {
  1554. UICommands.StartCommitDialog(this);
  1555. }
  1556. private void GitIgnoreClick(object sender, EventArgs e)
  1557. {
  1558. UICommands.StartEditGitIgnoreDialog(this);
  1559. }
  1560. internal void InvalidateRevisions()
  1561. {
  1562. Revisions.Invalidate();
  1563. }
  1564. // internal because used by RevisonGridMenuCommands
  1565. internal void ShowCurrentBranchOnly_ToolStripMenuItemClick(object sender, EventArgs e)
  1566. {
  1567. if (showCurrentBranchOnlyToolStripMenuItemChecked)
  1568. return;
  1569. AppSettings.BranchFilterEnabled = true;
  1570. AppSettings.ShowCurrentBranchOnly = true;
  1571. SetShowBranches();
  1572. ForceRefreshRevisions();
  1573. }
  1574. // internal because used by RevisonGridMenuCommands
  1575. internal void ShowAllBranches_ToolStripMenuItemClick(object sender, EventArgs e)
  1576. {
  1577. if (showAllBranchesToolStripMenuItemChecked)
  1578. return;
  1579. AppSettings.BranchFilterEnabled = false;
  1580. SetShowBranches();
  1581. ForceRefreshRevisions();
  1582. }
  1583. // internal because used by RevisonGridMenuCommands
  1584. internal void ShowFilteredBranches_ToolStripMenuItemClick(object sender, EventArgs e)
  1585. {
  1586. if (showFilteredBranchesToolStripMenuItemChecked)
  1587. return;
  1588. AppSettings.BranchFilterEnabled = true;
  1589. AppSettings.ShowCurrentBranchOnly = false;
  1590. SetShowBranches();
  1591. ForceRefreshRevisions();
  1592. }
  1593. internal bool ShowCurrentBranchOnly_ToolStripMenuItemChecked { get { return showCurrentBranchOnlyToolStripMenuItemChecked; } }
  1594. internal bool ShowAllBranches_ToolStripMenuItemChecked { get { return showAllBranchesToolStripMenuItemChecked; } }
  1595. internal bool ShowFilteredBranches_ToolStripMenuItemChecked { get { return showFilteredBranchesToolStripMenuItemChecked; } }
  1596. private void SetShowBranches()
  1597. {
  1598. showAllBranchesToolStripMenuItemChecked = !AppSettings.BranchFilterEnabled;
  1599. showCurrentBranchOnlyToolStripMenuItemChecked =
  1600. AppSettings.BranchFilterEnabled && AppSettings.ShowCurrentBranchOnly;
  1601. showFilteredBranchesToolStripMenuItemChecked =
  1602. AppSettings.BranchFilterEnabled && !AppSettings.ShowCurrentBranchOnly;
  1603. BranchFilter = _revisionFilter.GetBranchFilter();
  1604. if (!AppSettings.BranchFilterEnabled)
  1605. _refsOptions = RefsFiltringOptions.All | RefsFiltringOptions.Boundary;
  1606. else if (AppSettings.ShowCurrentBranchOnly)
  1607. _refsOptions = 0;
  1608. else
  1609. _refsOptions = BranchFilter.Length > 0
  1610. ? 0
  1611. : RefsFiltringOptions.All | RefsFiltringOptions.Boundary;
  1612. _revisionGridMenuCommands.TriggerMenuChanged(); // apply checkboxes changes also to FormBrowse main menu
  1613. }
  1614. private void RevertCommitToolStripMenuItemClick(object sender, EventArgs e)
  1615. {
  1616. if (Revisions.RowCount <= LastRow || LastRow < 0)
  1617. return;
  1618. UICommands.StartRevertCommitDialog(this, GetRevision(LastRow));
  1619. }
  1620. internal void FilterToolStripMenuItemClick(object sender, EventArgs e)
  1621. {
  1622. _revisionFilter.ShowDialog(this);
  1623. ForceRefreshRevisions();
  1624. }
  1625. private void ApplyFilterFromRevisionFilterDialog()
  1626. {
  1627. BranchFilter = _revisionFilter.GetBranchFilter();
  1628. SetShowBranches();
  1629. }
  1630. private void ContextMenuOpening(object sender, CancelEventArgs e)
  1631. {
  1632. if (Revisions.RowCount < LastRow || LastRow < 0 || Revisions.RowCount == 0)
  1633. return;
  1634. var inTheMiddleOfBisect = Module.InTheMiddleOfBisect();
  1635. markRevisionAsBadToolStripMenuItem.Visible = inTheMiddleOfBisect;
  1636. markRevisionAsGoodToolStripMenuItem.Visible = inTheMiddleOfBisect;
  1637. bisectSkipRevisionToolStripMenuItem.Visible = inTheMiddleOfBisect;
  1638. stopBisectToolStripMenuItem.Visible = inTheMiddleOfBisect;
  1639. bisectSeparator.Visible = inTheMiddleOfBisect;
  1640. var revision = GetRevision(LastRow);
  1641. var deleteTagDropDown = new ContextMenuStrip();
  1642. var deleteBranchDropDown = new ContextMenuStrip();
  1643. var checkoutBranchDropDown = new ContextMenuStrip();
  1644. var mergeBranchDropDown = new ContextMenuStrip();
  1645. var rebaseDropDown = new ContextMenuStrip();
  1646. var renameDropDown = new ContextMenuStrip();
  1647. var gitRefListsForRevision = new GitRefListsForRevision(revision);
  1648. foreach (var head in gitRefListsForRevision.AllTags)
  1649. {
  1650. ToolStripItem toolStripItem = new ToolStripMenuItem(head.Name);
  1651. ToolStripItem tagName = new ToolStripMenuItem(head.Name);
  1652. toolStripItem.Click += ToolStripItemClickDeleteTag;
  1653. deleteTagDropDown.Items.Add(toolStripItem);
  1654. }
  1655. //For now there is no action that could be done on currentBranch
  1656. bool bareRepository = Module.IsBareRepository();
  1657. string currentBranch = Module.GetSelectedBranch();
  1658. var allBranches = gitRefListsForRevision.AllBranches;
  1659. var localBranches = gitRefListsForRevision.LocalBranches;
  1660. var branchesWithNoIdenticalRemotes = gitRefListsForRevision.BranchesWithNoIdenticalRemotes;
  1661. bool currentBranchPointsToRevision = false;
  1662. foreach (var head in branchesWithNoIdenticalRemotes)
  1663. {
  1664. if (head.Name.Equals(currentBranch))
  1665. currentBranchPointsToRevision = true;
  1666. else
  1667. {
  1668. ToolStripItem toolStripItem = new ToolStripMenuItem(head.Name);
  1669. toolStripItem.Click += ToolStripItemClickMergeBranch;
  1670. mergeBranchDropDown.Items.Add(toolStripItem);
  1671. toolStripItem = new ToolStripMenuItem(head.Name);
  1672. toolStripItem.Click += ToolStripItemClickRebaseBranch;
  1673. rebaseDropDown.Items.Add(toolStripItem);
  1674. }
  1675. }
  1676. //if there is no branch to rebase on, then allow user to rebase on selected commit
  1677. if (rebaseDropDown.Items.Count == 0 && !currentBranchPointsToRevision)
  1678. {
  1679. ToolStripItem toolStripItem = new ToolStripMenuItem(revision.Guid);
  1680. toolStripItem.Click += ToolStripItemClickRebaseBranch;
  1681. rebaseDropDown.Items.Add(toolStripItem);
  1682. }
  1683. //if there is no branch to merge, then let user to merge selected commit into current branch
  1684. if (mergeBranchDropDown.Items.Count == 0 && !currentBranchPointsToRevision)
  1685. {
  1686. ToolStripItem toolStripItem = new ToolStripMenuItem(revision.Guid);
  1687. toolStripItem.Click += ToolStripItemClickMergeBranch;
  1688. mergeBranchDropDown.Items.Add(toolStripItem);
  1689. }
  1690. // clipboard branch and tag menu handling
  1691. {
  1692. branchNameCopyToolStripMenuItem.Tag = "caption";
  1693. tagNameCopyToolStripMenuItem.Tag = "caption";
  1694. MenuUtil.SetAsCaptionMenuItem(branchNameCopyToolStripMenuItem, mainContextMenu);
  1695. MenuUtil.SetAsCaptionMenuItem(tagNameCopyToolStripMenuItem, mainContextMenu);
  1696. var branchNames = gitRefListsForRevision.GetAllBranchNames();
  1697. CopyToClipboardMenuHelper.SetCopyToClipboardMenuItems(
  1698. copyToClipboardToolStripMenuItem, branchNameCopyToolStripMenuItem, branchNames, "branchNameItem");
  1699. var tagNames = gitRefListsForRevision.GetAllTagNames();
  1700. CopyToClipboardMenuHelper.SetCopyToClipboardMenuItems(
  1701. copyToClipboardToolStripMenuItem, tagNameCopyToolStripMenuItem, tagNames, "tagNameItem");
  1702. toolStripSeparator6.Visible = branchNames.Any() || tagNames.Any();
  1703. }
  1704. foreach (var head in allBranches)
  1705. {
  1706. ToolStripItem toolStripItem = new ToolStripMenuItem(head.Name);
  1707. //skip remote branches - they can not be deleted this way
  1708. if (!head.IsRemote)
  1709. {
  1710. if (!head.Name.Equals(currentBranch))
  1711. {
  1712. toolStripItem = new ToolStripMenuItem(head.Name);
  1713. toolStripItem.Click += ToolStripItemClickDeleteBranch;
  1714. deleteBranchDropDown.Items.Add(toolStripItem); //Add to delete branch
  1715. }
  1716. toolStripItem = new ToolStripMenuItem(head.Name);
  1717. toolStripItem.Click += ToolStripItemClickRenameBranch;
  1718. renameDropDown.Items.Add(toolStripItem); //Add to rename branch
  1719. }
  1720. if (!head.Name.Equals(currentBranch))
  1721. {
  1722. toolStripItem = new ToolStripMenuItem(head.Name);
  1723. if (head.IsRemote)
  1724. toolStripItem.Click += ToolStripItemClickCheckoutRemoteBranch;
  1725. else
  1726. toolStripItem.Click += ToolStripItemClickCheckoutBranch;
  1727. checkoutBranchDropDown.Items.Add(toolStripItem);
  1728. }
  1729. }
  1730. deleteTagToolStripMenuItem.DropDown = deleteTagDropDown;
  1731. deleteTagToolStripMenuItem.Enabled = deleteTagDropDown.Items.Count > 0;
  1732. deleteBranchToolStripMenuItem.DropDown = deleteBranchDropDown;
  1733. deleteBranchToolStripMenuItem.Enabled = deleteBranchDropDown.Items.Count > 0;
  1734. checkoutBranchToolStripMenuItem.DropDown = checkoutBranchDropDown;
  1735. checkoutBranchToolStripMenuItem.Enabled = !bareRepository && checkoutBranchDropDown.Items.Count > 0;
  1736. mergeBranchToolStripMenuItem.DropDown = mergeBranchDropDown;
  1737. mergeBranchToolStripMenuItem.Enabled = !bareRepository && mergeBranchDropDown.Items.Count > 0;
  1738. rebaseOnToolStripMenuItem.DropDown = rebaseDropDown;
  1739. rebaseOnToolStripMenuItem.Enabled = !bareRepository && rebaseDropDown.Items.Count > 0;
  1740. renameBranchToolStripMenuItem.DropDown = renameDropDown;
  1741. renameBranchToolStripMenuItem.Enabled = renameDropDown.Items.Count > 0;
  1742. checkoutRevisionToolStripMenuItem.Enabled = !bareRepository;
  1743. revertCommitToolStripMenuItem.Enabled = !bareRepository;
  1744. cherryPickCommitToolStripMenuItem.Enabled = !bareRepository;
  1745. manipulateCommitToolStripMenuItem.Enabled = !bareRepository;
  1746. toolStripSeparator6.Enabled = branchNameCopyToolStripMenuItem.Enabled || tagNameCopyToolStripMenuItem.Enabled;
  1747. RefreshOwnScripts();
  1748. }
  1749. private void ToolStripItemClickDeleteTag(object sender, EventArgs e)
  1750. {
  1751. var toolStripItem = sender as ToolStripItem;
  1752. if (toolStripItem == null)
  1753. return;
  1754. UICommands.StartDeleteTagDialog(this, toolStripItem.Text);
  1755. }
  1756. private void ToolStripItemClickDeleteBranch(object sender, EventArgs e)
  1757. {
  1758. var toolStripItem = sender as ToolStripItem;
  1759. if (toolStripItem == null)
  1760. return;
  1761. UICommands.StartDeleteBranchDialog(this, toolStripItem.Text);
  1762. }
  1763. private void ToolStripItemClickCheckoutBranch(object sender, EventArgs e)
  1764. {
  1765. var toolStripItem = sender as ToolStripItem;
  1766. if (toolStripItem == null)
  1767. return;
  1768. string branch = toolStripItem.Text;
  1769. UICommands.StartCheckoutBranch(this, branch, false);
  1770. }
  1771. private void ToolStripItemClickCheckoutRemoteBranch(object sender, EventArgs e)
  1772. {
  1773. var toolStripItem = sender as ToolStripItem;
  1774. if (toolStripItem == null)
  1775. return;
  1776. UICommands.StartCheckoutRemoteBranch(this, toolStripItem.Text);
  1777. }
  1778. private void ToolStripItemClickMergeBranch(object sender, EventArgs e)
  1779. {
  1780. var toolStripItem = sender as ToolStripItem;
  1781. if (toolStripItem == null)
  1782. return;
  1783. UICommands.StartMergeBranchDialog(this, toolStripItem.Text);
  1784. }
  1785. private void ToolStripItemClickRebaseBranch(object sender, EventArgs e)
  1786. {
  1787. var toolStripItem = sender as ToolStripItem;
  1788. if (toolStripItem == null)
  1789. return;
  1790. UICommands.StartRebaseDialog(this, toolStripItem.Text);
  1791. }
  1792. private void ToolStripItemClickRenameBranch(object sender, EventArgs e)
  1793. {
  1794. var toolStripItem = sender as ToolStripItem;
  1795. if (toolStripItem == null)
  1796. return;
  1797. UICommands.StartRenameDialog(this, toolStripItem.Text);
  1798. }
  1799. private void CheckoutRevisionToolStripMenuItemClick(object sender, EventArgs e)
  1800. {
  1801. if (Revisions.RowCount <= LastRow || LastRow < 0)
  1802. return;
  1803. string revision = GetRevision(LastRow).Guid;
  1804. UICommands.StartCheckoutRevisionDialog(this, revision);
  1805. }
  1806. private void ArchiveRevisionToolStripMenuItemClick(object sender, EventArgs e)
  1807. {
  1808. var selectedRevisions = GetSelectedRevisions();
  1809. if (selectedRevisions.Count > 2)
  1810. {
  1811. MessageBox.Show(this, "Select only one or two revisions. Abort.", "Archive revision");
  1812. return;
  1813. }
  1814. GitRevision mainRevision = selectedRevisions.First();
  1815. GitRevision diffRevision = null;
  1816. if (selectedRevisions.Count == 2)
  1817. diffRevision = selectedRevisions.Last();
  1818. UICommands.StartArchiveDialog(this, mainRevision, diffRevision);
  1819. }
  1820. internal void ShowAuthorDate_ToolStripMenuItemClick(object sender, EventArgs e)
  1821. {
  1822. AppSettings.ShowAuthorDate = !AppSettings.ShowAuthorDate;
  1823. ForceRefreshRevisions();
  1824. }
  1825. internal void OrderRevisionsByDate_ToolStripMenuItemClick(object sender, EventArgs e)
  1826. {
  1827. AppSettings.OrderRevisionByDate = !AppSettings.OrderRevisionByDate;
  1828. ForceRefreshRevisions();
  1829. }
  1830. internal void ShowRemoteBranches_ToolStripMenuItemClick(object sender, EventArgs e)
  1831. {
  1832. AppSettings.ShowRemoteBranches = !AppSettings.ShowRemoteBranches;
  1833. InvalidateRevisions();
  1834. }
  1835. internal void ShowSuperprojectTags_ToolStripMenuItemClick(object sender, EventArgs e)
  1836. {
  1837. AppSettings.ShowSuperprojectTags = !AppSettings.ShowSuperprojectTags;
  1838. ForceRefreshRevisions();
  1839. }
  1840. internal void ShowSuperprojectBranches_ToolStripMenuItemClick(object sender, EventArgs e)
  1841. {
  1842. AppSettings.ShowSuperprojectBranches = !AppSettings.ShowSuperprojectBranches;
  1843. ForceRefreshRevisions();
  1844. }
  1845. internal void ShowSuperprojectRemoteBranches_ToolStripMenuItemClick(object sender, EventArgs e)
  1846. {
  1847. AppSettings.ShowSuperprojectRemoteBranches = !AppSettings.ShowSuperprojectRemoteBranches;
  1848. ForceRefreshRevisions();
  1849. }
  1850. private void CherryPickCommitToolStripMenuItemClick(object sender, EventArgs e)
  1851. {
  1852. var revisions = GetSelectedRevisions(SortDirection.Descending);
  1853. UICommands.StartCherryPickDialog(this, revisions);
  1854. }
  1855. private void FixupCommitToolStripMenuItemClick(object sender, EventArgs e)
  1856. {
  1857. if (Revisions.RowCount <= LastRow || LastRow < 0)
  1858. return;
  1859. UICommands.StartFixupCommitDialog(this, GetRevision(LastRow));
  1860. }
  1861. private void SquashCommitToolStripMenuItemClick(object sender, EventArgs e)
  1862. {
  1863. if (Revisions.RowCount <= LastRow || LastRow < 0)
  1864. return;
  1865. UICommands.StartSquashCommitDialog(this, GetRevision(LastRow));
  1866. }
  1867. internal void ShowRelativeDate_ToolStripMenuItemClick(object sender, EventArgs e)
  1868. {
  1869. AppSettings.RelativeDate = !AppSettings.RelativeDate;
  1870. ForceRefreshRevisions();
  1871. }
  1872. private string TimeToString(DateTime time)
  1873. {
  1874. if (time == DateTime.MinValue || time == DateTime.MaxValue)
  1875. return "";
  1876. if (!AppSettings.RelativeDate)
  1877. return string.Format("{0} {1}", time.ToShortDateString(), time.ToLongTimeString());
  1878. return GitCommandHelpers.GetRelativeDateString(DateTime.Now, time, false);
  1879. }
  1880. private bool ShowUncommitedChanged()
  1881. {
  1882. return ShowUncommitedChangesIfPossible && AppSettings.RevisionGraphShowWorkingDirChanges;
  1883. }
  1884. private void UpdateGraph(GitRevision rev)
  1885. {
  1886. if (rev == null)
  1887. {
  1888. // Prune the graph and make sure the row count matches reality
  1889. Revisions.Prune();
  1890. if (Revisions.RowCount == 0 && ShowUncommitedChanged())
  1891. CheckUncommitedChanged();
  1892. return;
  1893. }
  1894. var dataType = DvcsGraph.DataType.Normal;
  1895. if (rev.Guid == FiltredCurrentCheckout)
  1896. dataType = DvcsGraph.DataType.Active;
  1897. else if (rev.Refs.Any())
  1898. dataType = DvcsGraph.DataType.Special;
  1899. Revisions.Add(rev.Guid, rev.ParentGuids, dataType, rev);
  1900. }
  1901. private void CheckUncommitedChanged()
  1902. {
  1903. bool unstagedChanges = false;
  1904. bool stagedChanges = false;
  1905. //Only check for tracked files. This usually makes more sense and it performs a lot
  1906. //better then checking for untracked files.
  1907. // TODO: Check FiltredFileName
  1908. if (Module.GetUnstagedFiles().Any())
  1909. unstagedChanges = true;
  1910. if (Module.GetStagedFiles().Any())
  1911. stagedChanges = true;
  1912. // FiltredCurrentCheckout doesn't works here because only calculated after loading all revisions in SelectInitialRevision()
  1913. if (unstagedChanges)
  1914. {
  1915. //Add working directory as virtual commit
  1916. var workingDir = new GitRevision(Module, GitRevision.UnstagedGuid)
  1917. {
  1918. Message = Strings.GetCurrentUnstagedChanges(),
  1919. ParentGuids =
  1920. stagedChanges
  1921. ? new[] { GitRevision.IndexGuid }
  1922. : new[] { FiltredCurrentCheckout }
  1923. };
  1924. Revisions.Add(workingDir.Guid, workingDir.ParentGuids, DvcsGraph.DataType.Normal, workingDir);
  1925. }
  1926. if (stagedChanges)
  1927. {
  1928. //Add index as virtual commit
  1929. var index = new GitRevision(Module, GitRevision.IndexGuid)
  1930. {
  1931. Message = Strings.GetCurrentIndex(),
  1932. ParentGuids = new[] { FiltredCurrentCheckout }
  1933. };
  1934. Revisions.Add(index.Guid, index.ParentGuids, DvcsGraph.DataType.Normal, index);
  1935. }
  1936. }
  1937. internal void DrawNonrelativesGray_ToolStripMenuItemClick(object sender, EventArgs e)
  1938. {
  1939. AppSettings.RevisionGraphDrawNonRelativesGray = !AppSettings.RevisionGraphDrawNonRelativesGray;
  1940. _revisionGridMenuCommands.TriggerMenuChanged();
  1941. Revisions.Refresh();
  1942. }
  1943. private void MessageToolStripMenuItemClick(object sender, EventArgs e)
  1944. {
  1945. Clipboard.SetText(GetRevision(LastRow).Message);
  1946. }
  1947. private void AuthorToolStripMenuItemClick(object sender, EventArgs e)
  1948. {
  1949. Clipboard.SetText(GetRevision(LastRow).Author);
  1950. }
  1951. private void DateToolStripMenuItemClick(object sender, EventArgs e)
  1952. {
  1953. Clipboard.SetText(GetRevision(LastRow).CommitDate.ToString());
  1954. }
  1955. private void HashToolStripMenuItemClick(object sender, EventArgs e)
  1956. {
  1957. Clipboard.SetText(GetRevision(LastRow).Guid);
  1958. }
  1959. private void MarkRevisionAsBadToolStripMenuItemClick(object sender, EventArgs e)
  1960. {
  1961. ContinueBisect(GitBisectOption.Bad);
  1962. }
  1963. private void MarkRevisionAsGoodToolStripMenuItemClick(object sender, EventArgs e)
  1964. {
  1965. ContinueBisect(GitBisectOption.Good);
  1966. }
  1967. private void BisectSkipRevisionToolStripMenuItemClick(object sender, EventArgs e)
  1968. {
  1969. ContinueBisect(GitBisectOption.Skip);
  1970. }
  1971. private void ContinueBisect(GitBisectOption bisectOption)
  1972. {
  1973. if (Revisions.RowCount <= LastRow || LastRow < 0)
  1974. return;
  1975. FormProcess.ShowDialog(this, Module, GitCommandHelpers.ContinueBisectCmd(bisectOption, GetRevision(LastRow).Guid), false);
  1976. RefreshRevisions();
  1977. }
  1978. private void StopBisectToolStripMenuItemClick(object sender, EventArgs e)
  1979. {
  1980. FormProcess.ShowDialog(this, Module, GitCommandHelpers.StopBisectCmd());
  1981. RefreshRevisions();
  1982. }
  1983. private void RefreshOwnScripts()
  1984. {
  1985. RemoveOwnScripts();
  1986. AddOwnScripts();
  1987. }
  1988. private void AddOwnScripts()
  1989. {
  1990. IList<ScriptInfo> scripts = ScriptManager.GetScripts();
  1991. if (scripts == null)
  1992. return;
  1993. int lastIndex = mainContextMenu.Items.Count;
  1994. foreach (ScriptInfo scriptInfo in scripts)
  1995. {
  1996. if (scriptInfo.Enabled)
  1997. {
  1998. ToolStripItem item = new ToolStripMenuItem(scriptInfo.Name);
  1999. item.Name = item.Text + "_ownScript";
  2000. item.Click += RunScript;
  2001. if (scriptInfo.AddToRevisionGridContextMenu)
  2002. mainContextMenu.Items.Add(item);
  2003. else
  2004. runScriptToolStripMenuItem.DropDown.Items.Add(item);
  2005. }
  2006. }
  2007. if (lastIndex != mainContextMenu.Items.Count)
  2008. mainContextMenu.Items.Insert(lastIndex, new ToolStripSeparator());
  2009. bool showScriptsMenu = runScriptToolStripMenuItem.DropDown.Items.Count > 0;
  2010. runScriptToolStripMenuItem.Visible = showScriptsMenu;
  2011. }
  2012. private void RemoveOwnScripts()
  2013. {
  2014. runScriptToolStripMenuItem.DropDown.Items.Clear();
  2015. List<ToolStripItem> list = new List<ToolStripItem>();
  2016. foreach (ToolStripItem item in mainContextMenu.Items)
  2017. list.Add(item);
  2018. foreach (ToolStripItem item in list)
  2019. if (item.Name.Contains("_ownScript"))
  2020. mainContextMenu.Items.RemoveByKey(item.Name);
  2021. if (mainContextMenu.Items[mainContextMenu.Items.Count - 1] is ToolStripSeparator)
  2022. mainContextMenu.Items.RemoveAt(mainContextMenu.Items.Count - 1);
  2023. }
  2024. private bool _settingsLoaded;
  2025. private void RunScript(object sender, EventArgs e)
  2026. {
  2027. if (_settingsLoaded == false)
  2028. {
  2029. new FormSettings(UICommands).LoadSettings();
  2030. _settingsLoaded = true;
  2031. }
  2032. if (ScriptRunner.RunScript(this, Module, sender.ToString(), this))
  2033. RefreshRevisions();
  2034. }
  2035. #region Drag/drop patch files on revision grid
  2036. void Revisions_DragDrop(object sender, DragEventArgs e)
  2037. {
  2038. var fileNameArray = e.Data.GetData(DataFormats.FileDrop) as Array;
  2039. if (fileNameArray != null)
  2040. {
  2041. if (fileNameArray.Length > 10)
  2042. {
  2043. //Some users need to be protected against themselves!
  2044. MessageBox.Show(this, _droppingFilesBlocked.Text);
  2045. return;
  2046. }
  2047. foreach (object fileNameObject in fileNameArray)
  2048. {
  2049. var fileName = fileNameObject as string;
  2050. if (!string.IsNullOrEmpty(fileName) && fileName.EndsWith(".patch", StringComparison.InvariantCultureIgnoreCase))
  2051. {
  2052. //Start apply patch dialog for each dropped patch file...
  2053. UICommands.StartApplyPatchDialog(this, fileName);
  2054. }
  2055. }
  2056. }
  2057. }
  2058. static void Revisions_DragEnter(object sender, DragEventArgs e)
  2059. {
  2060. var fileNameArray = e.Data.GetData(DataFormats.FileDrop) as Array;
  2061. if (fileNameArray != null)
  2062. {
  2063. foreach (object fileNameObject in fileNameArray)
  2064. {
  2065. var fileName = fileNameObject as string;
  2066. if (!string.IsNullOrEmpty(fileName) && fileName.EndsWith(".patch", StringComparison.InvariantCultureIgnoreCase))
  2067. {
  2068. //Allow drop (copy, not move) patch files
  2069. e.Effect = DragDropEffects.Copy;
  2070. }
  2071. else
  2072. {
  2073. //When a non-patch file is dragged, do not allow it
  2074. e.Effect = DragDropEffects.None;
  2075. return;
  2076. }
  2077. }
  2078. }
  2079. }
  2080. #endregion
  2081. internal void ShowGitNotes_ToolStripMenuItemClick(object sender, EventArgs e)
  2082. {
  2083. AppSettings.ShowGitNotes = !AppSettings.ShowGitNotes;
  2084. ForceRefreshRevisions();
  2085. }
  2086. internal void ShowMergeCommits_ToolStripMenuItemClick(object sender, EventArgs e)
  2087. {
  2088. AppSettings.ShowMergeCommits = !showMergeCommitsToolStripMenuItem.Checked;
  2089. showMergeCommitsToolStripMenuItem.Checked = AppSettings.ShowMergeCommits;
  2090. // hide revision graph when hiding merge commits, reasons:
  2091. // 1, revison graph is no longer relevant, as we are not sohwing all commits
  2092. // 2, performance hit when both revision graph and no merge commits are enabled
  2093. if (IsGraphLayout() && !AppSettings.ShowMergeCommits)
  2094. {
  2095. ToggleRevisionGraph();
  2096. SetRevisionsLayout();
  2097. }
  2098. ForceRefreshRevisions();
  2099. }
  2100. public void OnModuleChanged(GitModule aModule)
  2101. {
  2102. if (GitModuleChanged != null)
  2103. GitModuleChanged(aModule);
  2104. }
  2105. private void InitRepository_Click(object sender, EventArgs e)
  2106. {
  2107. UICommands.StartInitializeDialog(this, Module.WorkingDir, OnModuleChanged);
  2108. }
  2109. private void CloneRepository_Click(object sender, EventArgs e)
  2110. {
  2111. if (UICommands.StartCloneDialog(this, null, OnModuleChanged))
  2112. ForceRefreshRevisions();
  2113. }
  2114. internal void ShowRevisionGraph_ToolStripMenuItemClick(object sender, EventArgs e)
  2115. {
  2116. ToggleRevisionGraph();
  2117. SetRevisionsLayout();
  2118. _revisionGridMenuCommands.TriggerMenuChanged();
  2119. // must show MergeCommits when showing revision graph
  2120. if (!AppSettings.ShowMergeCommits && IsGraphLayout())
  2121. {
  2122. AppSettings.ShowMergeCommits = true;
  2123. showMergeCommitsToolStripMenuItem.Checked = true;
  2124. ForceRefreshRevisions();
  2125. }
  2126. else
  2127. Refresh();
  2128. }
  2129. private void ToggleRevisionGraph()
  2130. {
  2131. if (AppSettings.RevisionGraphLayout == (int)RevisionGridLayout.Small)
  2132. AppSettings.RevisionGraphLayout = (int)RevisionGridLayout.SmallWithGraph;
  2133. else if (AppSettings.RevisionGraphLayout == (int)RevisionGridLayout.Card)
  2134. AppSettings.RevisionGraphLayout = (int)RevisionGridLayout.CardWithGraph;
  2135. else if (AppSettings.RevisionGraphLayout == (int)RevisionGridLayout.LargeCard)
  2136. AppSettings.RevisionGraphLayout = (int)RevisionGridLayout.LargeCardWithGraph;
  2137. else if (AppSettings.RevisionGraphLayout == (int)RevisionGridLayout.SmallWithGraph)
  2138. AppSettings.RevisionGraphLayout = (int)RevisionGridLayout.Small;
  2139. else if (AppSettings.RevisionGraphLayout == (int)RevisionGridLayout.CardWithGraph)
  2140. AppSettings.RevisionGraphLayout = (int)RevisionGridLayout.Card;
  2141. else if (AppSettings.RevisionGraphLayout == (int)RevisionGridLayout.LargeCardWithGraph)
  2142. AppSettings.RevisionGraphLayout = (int)RevisionGridLayout.LargeCard;
  2143. else if (AppSettings.RevisionGraphLayout == (int)RevisionGridLayout.FilledBranchesSmall)
  2144. AppSettings.RevisionGraphLayout = (int)RevisionGridLayout.FilledBranchesSmallWithGraph;
  2145. else if (AppSettings.RevisionGraphLayout == (int)RevisionGridLayout.FilledBranchesSmallWithGraph)
  2146. AppSettings.RevisionGraphLayout = (int)RevisionGridLayout.FilledBranchesSmall;
  2147. }
  2148. internal void ShowTags_ToolStripMenuItemClick(object sender, EventArgs e)
  2149. {
  2150. AppSettings.ShowTags = !AppSettings.ShowTags;
  2151. _revisionGridMenuCommands.TriggerMenuChanged();
  2152. Refresh();
  2153. }
  2154. public void ToggleRevisionCardLayout()
  2155. {
  2156. var layouts = new List<RevisionGridLayout>((RevisionGridLayout[])Enum.GetValues(typeof(RevisionGridLayout)));
  2157. layouts.Sort();
  2158. var maxLayout = (int)layouts[layouts.Count - 1];
  2159. int nextLayout = AppSettings.RevisionGraphLayout + 1;
  2160. if (nextLayout > maxLayout)
  2161. nextLayout = 1;
  2162. SetRevisionsLayout((RevisionGridLayout)nextLayout);
  2163. }
  2164. public void SetRevisionsLayout(RevisionGridLayout revisionGridLayout)
  2165. {
  2166. AppSettings.RevisionGraphLayout = (int)revisionGridLayout;
  2167. SetRevisionsLayout();
  2168. }
  2169. private void SetRevisionsLayout()
  2170. {
  2171. _layout = Enum.IsDefined(typeof(RevisionGridLayout), AppSettings.RevisionGraphLayout)
  2172. ? (RevisionGridLayout)AppSettings.RevisionGraphLayout
  2173. : RevisionGridLayout.SmallWithGraph;
  2174. IsCardLayout();
  2175. NormalFont = AppSettings.Font;// new Font(Settings.Font.Name, Settings.Font.Size + 2); // SystemFonts.DefaultFont.FontFamily, SystemFonts.DefaultFont.Size + 2);
  2176. if (IsCardLayout())
  2177. {
  2178. if (AppSettings.RevisionGraphLayout == (int)RevisionGridLayout.Card
  2179. || AppSettings.RevisionGraphLayout == (int)RevisionGridLayout.CardWithGraph)
  2180. {
  2181. _rowHeigth = 45;
  2182. }
  2183. else
  2184. {
  2185. _rowHeigth = 70;
  2186. }
  2187. if (_filledItemBrush == null)
  2188. {
  2189. _filledItemBrush = new LinearGradientBrush(new Rectangle(0, 0, _rowHeigth, _rowHeigth),
  2190. Revisions.RowTemplate.DefaultCellStyle.SelectionBackColor,
  2191. Color.LightBlue, 90, false);
  2192. }
  2193. _selectedItemBrush = _filledItemBrush;
  2194. Revisions.ShowAuthor(!IsCardLayout());
  2195. Revisions.SetDimensions(NodeDimension, LaneWidth, LaneLineWidth, _rowHeigth, _selectedItemBrush);
  2196. }
  2197. else
  2198. {
  2199. if (IsFilledBranchesLayout())
  2200. {
  2201. using (var graphics = Graphics.FromHwnd(Handle))
  2202. {
  2203. _rowHeigth = (int)graphics.MeasureString("By", NormalFont).Height + 9;
  2204. }
  2205. _selectedItemBrush = SystemBrushes.Highlight;
  2206. }
  2207. else
  2208. {
  2209. _rowHeigth = 25;
  2210. if (_filledItemBrush == null)
  2211. {
  2212. _filledItemBrush = new LinearGradientBrush(new Rectangle(0, 0, _rowHeigth, _rowHeigth),
  2213. Revisions.RowTemplate.DefaultCellStyle.SelectionBackColor,
  2214. Color.LightBlue, 90, false);
  2215. }
  2216. _selectedItemBrush = _filledItemBrush;
  2217. }
  2218. Revisions.ShowAuthor(!IsCardLayout());
  2219. Revisions.SetDimensions(NodeDimension, LaneWidth, LaneLineWidth, _rowHeigth, _selectedItemBrush);
  2220. }
  2221. //Hide graph column when there it is disabled OR when a filter is active
  2222. //allowing for special case when history of a single file is being displayed
  2223. if (!IsGraphLayout() || (ShouldHideGraph(false) && !AllowGraphWithFilter))
  2224. {
  2225. Revisions.HideRevisionGraph();
  2226. }
  2227. else
  2228. {
  2229. Revisions.ShowRevisionGraph();
  2230. }
  2231. }
  2232. private bool IsFilledBranchesLayout()
  2233. {
  2234. return _layout == RevisionGridLayout.FilledBranchesSmall || _layout == RevisionGridLayout.FilledBranchesSmallWithGraph;
  2235. }
  2236. private bool IsCardLayout()
  2237. {
  2238. return _layout == RevisionGridLayout.Card
  2239. || _layout == RevisionGridLayout.CardWithGraph
  2240. || _layout == RevisionGridLayout.LargeCard
  2241. || _layout == RevisionGridLayout.LargeCardWithGraph;
  2242. }
  2243. internal bool IsGraphLayout()
  2244. {
  2245. return _layout == RevisionGridLayout.SmallWithGraph
  2246. || _layout == RevisionGridLayout.CardWithGraph
  2247. || _layout == RevisionGridLayout.LargeCardWithGraph
  2248. || _layout == RevisionGridLayout.FilledBranchesSmallWithGraph;
  2249. }
  2250. #region Hotkey commands
  2251. public const string HotkeySettingsName = "RevisionGrid";
  2252. internal enum Commands
  2253. {
  2254. ToggleRevisionGraph,
  2255. RevisionFilter,
  2256. ToggleAuthorDateCommitDate,
  2257. ToggleOrderRevisionsByDate,
  2258. ToggleShowRelativeDate,
  2259. ToggleDrawNonRelativesGray,
  2260. ToggleShowGitNotes,
  2261. ToggleRevisionCardLayout,
  2262. ShowAllBranches,
  2263. ShowCurrentBranchOnly,
  2264. GoToParent,
  2265. GoToChild,
  2266. ToggleHighlightSelectedBranch,
  2267. NextQuickSearch,
  2268. PrevQuickSearch,
  2269. SelectCurrentRevision,
  2270. GoToCommit
  2271. }
  2272. protected override bool ExecuteCommand(int cmd)
  2273. {
  2274. Commands command = (Commands)cmd;
  2275. switch (command)
  2276. {
  2277. case Commands.ToggleRevisionGraph: ShowRevisionGraph_ToolStripMenuItemClick(null, null); break;
  2278. case Commands.RevisionFilter: FilterToolStripMenuItemClick(null, null); break;
  2279. case Commands.ToggleAuthorDateCommitDate: ShowAuthorDate_ToolStripMenuItemClick(null, null); break;
  2280. case Commands.ToggleOrderRevisionsByDate: OrderRevisionsByDate_ToolStripMenuItemClick(null, null); break;
  2281. case Commands.ToggleShowRelativeDate: ShowRelativeDate_ToolStripMenuItemClick(null, null); break;
  2282. case Commands.ToggleDrawNonRelativesGray: DrawNonrelativesGray_ToolStripMenuItemClick(null, null); break;
  2283. case Commands.ToggleShowGitNotes: ShowGitNotes_ToolStripMenuItemClick(null, null); break;
  2284. case Commands.ToggleRevisionCardLayout: ToggleRevisionCardLayout(); break;
  2285. case Commands.ShowAllBranches: ShowAllBranches_ToolStripMenuItemClick(null, null); break;
  2286. case Commands.SelectCurrentRevision: SetSelectedRevision(new GitRevision(Module, CurrentCheckout)); break;
  2287. case Commands.GoToCommit: _revisionGridMenuCommands.GotoCommitExcecute(); break;
  2288. case Commands.ShowCurrentBranchOnly: ShowCurrentBranchOnly_ToolStripMenuItemClick(null, null); break;
  2289. case Commands.GoToParent: goToParentToolStripMenuItem_Click(null, null); break;
  2290. case Commands.GoToChild: goToChildToolStripMenuItem_Click(null, null); break;
  2291. case Commands.ToggleHighlightSelectedBranch: ToggleHighlightSelectedBranch(); break;
  2292. case Commands.NextQuickSearch: NextQuickSearch(true); break;
  2293. case Commands.PrevQuickSearch: NextQuickSearch(false); break;
  2294. default:
  2295. {
  2296. bool result = base.ExecuteCommand(cmd);
  2297. RefreshRevisions();
  2298. return result;
  2299. }
  2300. }
  2301. return true;
  2302. }
  2303. #endregion
  2304. internal bool ExecuteCommand(Commands cmd)
  2305. {
  2306. return ExecuteCommand((int)cmd);
  2307. }
  2308. private void NextQuickSearch(bool down)
  2309. {
  2310. var curIndex = -1;
  2311. if (Revisions.SelectedRows.Count > 0)
  2312. curIndex = Revisions.SelectedRows[0].Index;
  2313. RestartQuickSearchTimer();
  2314. bool reverse = !down;
  2315. var nextIndex = 0;
  2316. if (curIndex >= 0)
  2317. nextIndex = reverse ? curIndex - 1 : curIndex + 1;
  2318. _quickSearchString = _lastQuickSearchString;
  2319. FindNextMatch(nextIndex, _quickSearchString, reverse);
  2320. ShowQuickSearchString();
  2321. }
  2322. private void ToggleHighlightSelectedBranch()
  2323. {
  2324. if (_revisionGraphCommand != null)
  2325. {
  2326. MessageBox.Show(_cannotHighlightSelectedBranch.Text);
  2327. return;
  2328. }
  2329. HighlightSelectedBranch();
  2330. }
  2331. public void HighlightSelectedBranch()
  2332. {
  2333. var revisions = GetSelectedRevisions();
  2334. if (revisions.Count > 0)
  2335. {
  2336. HighlightBranch(revisions[0].Guid);
  2337. Refresh();
  2338. }
  2339. }
  2340. private void deleteBranchTagToolStripMenuItem_Click(object sender, EventArgs e)
  2341. {
  2342. ToolStripMenuItem item = sender as ToolStripMenuItem;
  2343. if (item != null)
  2344. {
  2345. if (item.DropDown != null && item.DropDown.Items.Count == 1)
  2346. item.DropDown.Items[0].PerformClick();
  2347. }
  2348. }
  2349. private void goToParentToolStripMenuItem_Click(object sender, EventArgs e)
  2350. {
  2351. var r = GetRevision(LastRow);
  2352. if (r.HasParent())
  2353. SetSelectedRevision(r.ParentGuids[0]);
  2354. }
  2355. private void goToChildToolStripMenuItem_Click(object sender, EventArgs e)
  2356. {
  2357. var r = GetRevision(LastRow);
  2358. var children = GetRevisionChildren(r.Guid);
  2359. if (children.Any())
  2360. SetSelectedRevision(children[0]);
  2361. }
  2362. private void copyToClipboardToolStripMenuItem_DropDownOpened(object sender, EventArgs e)
  2363. {
  2364. var revision = GetRevision(LastRow);
  2365. CopyToClipboardMenuHelper.AddOrUpdateTextPostfix(hashCopyToolStripMenuItem, CopyToClipboardMenuHelper.StrLimitWithElipses(revision.Guid, 15));
  2366. CopyToClipboardMenuHelper.AddOrUpdateTextPostfix(messageCopyToolStripMenuItem, CopyToClipboardMenuHelper.StrLimitWithElipses(revision.Message, 30));
  2367. CopyToClipboardMenuHelper.AddOrUpdateTextPostfix(authorCopyToolStripMenuItem, revision.Author);
  2368. CopyToClipboardMenuHelper.AddOrUpdateTextPostfix(dateCopyToolStripMenuItem, revision.CommitDate.ToString());
  2369. }
  2370. public void GoToRef(string refName, bool showNoRevisionMsg)
  2371. {
  2372. string revisionGuid = Module.RevParse(refName);
  2373. if (!string.IsNullOrEmpty(revisionGuid))
  2374. {
  2375. SetSelectedRevision(new GitRevision(Module, revisionGuid));
  2376. }
  2377. else if (showNoRevisionMsg)
  2378. {
  2379. MessageBox.Show((ParentForm as IWin32Window) ?? this, _noRevisionFoundError.Text);
  2380. }
  2381. }
  2382. /// <summary>
  2383. /// duplicated from GitExtensionsForm
  2384. /// </summary>
  2385. /// <param name="commandCode"></param>
  2386. /// <returns></returns>
  2387. private Keys GetShortcutKeys(int commandCode)
  2388. {
  2389. var hotkey = GetHotkeyCommand(commandCode);
  2390. return hotkey == null ? Keys.None : hotkey.KeyData;
  2391. }
  2392. internal Keys GetShortcutKeys(Commands cmd)
  2393. {
  2394. return GetShortcutKeys((int)cmd);
  2395. }
  2396. /// <summary>
  2397. /// duplicated from GitExtensionsForm
  2398. /// </summary>
  2399. /// <param name="commandCode"></param>
  2400. /// <returns></returns>
  2401. private Hotkey.HotkeyCommand GetHotkeyCommand(int commandCode)
  2402. {
  2403. if (Hotkeys == null)
  2404. return null;
  2405. return Hotkeys.FirstOrDefault(h => h.CommandCode == commandCode);
  2406. }
  2407. internal RevisionGridMenuCommands MenuCommands { get { return _revisionGridMenuCommands; } }
  2408. }
  2409. }