PageRenderTime 58ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/GitUI/DvcsGraph.cs

https://github.com/eisnerd/gitextensions
C# | 1222 lines | 1024 code | 132 blank | 66 comment | 196 complexity | f1a2d329617a111c1ea04253fb5f6584 MD5 | raw file
Possible License(s): GPL-3.0, GPL-2.0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Drawing;
  5. using System.Drawing.Drawing2D;
  6. using System.Drawing.Imaging;
  7. using System.Linq;
  8. using System.Threading;
  9. using System.Windows.Forms;
  10. using GitCommands;
  11. namespace GitUI
  12. {
  13. public sealed partial class DvcsGraph : DataGridView
  14. {
  15. #region Delegates
  16. public delegate void LoadingEventHandler(bool isLoading);
  17. #endregion
  18. #region DataType enum
  19. [Flags]
  20. public enum DataType
  21. {
  22. Normal = 0,
  23. Active = 1,
  24. Special = 2,
  25. Filtered = 4,
  26. }
  27. #endregion
  28. #region FilterType enum
  29. public enum FilterType
  30. {
  31. None,
  32. Highlight,
  33. Hide,
  34. }
  35. #endregion
  36. private int NODE_DIMENSION = 8;
  37. private int LANE_WIDTH = 13;
  38. private int LANE_LINE_WIDTH = 2;
  39. private const int MAX_LANES = 30;
  40. private Brush selectionBrush;
  41. private readonly AutoResetEvent backgroundEvent = new AutoResetEvent(false);
  42. private readonly Graph graphData;
  43. private readonly Dictionary<Junction, int> junctionColors = new Dictionary<Junction, int>();
  44. private readonly Color nonRelativeColor = Color.LightGray;
  45. private readonly Color[] possibleColors =
  46. {
  47. Color.Red,
  48. Color.MistyRose,
  49. Color.Magenta,
  50. Color.Violet,
  51. Color.Blue,
  52. Color.Azure,
  53. Color.Cyan,
  54. Color.SpringGreen,
  55. Color.Green,
  56. Color.Chartreuse,
  57. Color.Gold,
  58. Color.Orange
  59. };
  60. private readonly SynchronizationContext syncContext;
  61. private readonly List<IComparable> toBeSelected = new List<IComparable>();
  62. private int backgroundScrollTo;
  63. private Thread backgroundThread;
  64. private volatile bool shouldRun = true;
  65. private int cacheCount; // Number of elements in the cache.
  66. private int cacheCountMax; // Number of elements allowed in the cache. Is based on control height.
  67. private int cacheHead = -1; // The 'slot' that is the head of the circular bitmap
  68. private int cacheHeadRow; // The node row that is in the head slot
  69. private FilterType filterMode = FilterType.None;
  70. private Bitmap graphBitmap;
  71. private int graphDataCount;
  72. private Graphics graphWorkArea;
  73. private int rowHeight; // Height of elements in the cache. Is equal to the control's row height.
  74. private int visibleBottom;
  75. private int visibleTop;
  76. public void SetDimensions(int node_dimension, int lane_width, int lane_line_width, int row_height, Brush selectionBrush)
  77. {
  78. RowTemplate.Height = row_height;
  79. NODE_DIMENSION = node_dimension;
  80. LANE_WIDTH = lane_width;
  81. LANE_LINE_WIDTH = lane_line_width;
  82. this.selectionBrush = selectionBrush;
  83. dataGrid_Resize(null, null);
  84. }
  85. public DvcsGraph()
  86. {
  87. syncContext = SynchronizationContext.Current;
  88. graphData = new Graph();
  89. backgroundThread = new Thread(BackgroundThreadEntry)
  90. {
  91. IsBackground = true,
  92. Priority = ThreadPriority.BelowNormal,
  93. Name = "DvcsGraph.backgroundThread"
  94. };
  95. backgroundThread.Start();
  96. InitializeComponent();
  97. ColumnHeadersDefaultCellStyle.Font = SystemFonts.DefaultFont;
  98. Font = SystemFonts.DefaultFont;
  99. DefaultCellStyle.Font = SystemFonts.DefaultFont;
  100. AlternatingRowsDefaultCellStyle.Font = SystemFonts.DefaultFont;
  101. RowsDefaultCellStyle.Font = SystemFonts.DefaultFont;
  102. RowHeadersDefaultCellStyle.Font = SystemFonts.DefaultFont;
  103. RowTemplate.DefaultCellStyle.Font = SystemFonts.DefaultFont;
  104. dataGridColumnGraph.DefaultCellStyle.Font = SystemFonts.DefaultFont;
  105. SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
  106. CellPainting += dataGrid_CellPainting;
  107. ColumnWidthChanged += dataGrid_ColumnWidthChanged;
  108. Scroll += dataGrid_Scroll;
  109. graphData.Updated += graphData_Updated;
  110. VirtualMode = true;
  111. Clear();
  112. }
  113. public void ShowAuthor(bool show)
  114. {
  115. this.Columns[2].Visible = show;
  116. this.Columns[3].Visible = show;
  117. }
  118. [DefaultValue(FilterType.None)]
  119. [Category("Behavior")]
  120. public FilterType FilterMode
  121. {
  122. get { return filterMode; }
  123. set
  124. {
  125. // TODO: We only need to rebuild the graph if switching to or from hide
  126. if (filterMode == value)
  127. {
  128. return;
  129. }
  130. syncContext.Send(o =>
  131. {
  132. lock (backgroundEvent) // Make sure the background thread isn't running
  133. {
  134. lock (backgroundThread)
  135. {
  136. backgroundScrollTo = 0;
  137. graphDataCount = 0;
  138. }
  139. lock (graphData)
  140. {
  141. filterMode = value;
  142. graphData.IsFilter = (filterMode & FilterType.Hide) == FilterType.Hide;
  143. RebuildGraph();
  144. }
  145. }
  146. }, this);
  147. }
  148. }
  149. [Browsable(false)]
  150. public object[] SelectedData
  151. {
  152. get
  153. {
  154. if (SelectedRows.Count == 0)
  155. {
  156. return null;
  157. }
  158. var data = new object[SelectedRows.Count];
  159. for (int i = 0; i < SelectedRows.Count; i++)
  160. {
  161. data[i] = graphData[i].Node.Data;
  162. }
  163. return data;
  164. }
  165. }
  166. [Browsable(false)]
  167. public IComparable[] SelectedIds
  168. {
  169. get
  170. {
  171. if (SelectedRows.Count == 0)
  172. {
  173. return null;
  174. }
  175. var data = new IComparable[SelectedRows.Count];
  176. for (int i = 0; i < SelectedRows.Count; i++)
  177. {
  178. if (graphData[SelectedRows[i].Index] != null)
  179. data[i] = graphData[SelectedRows[i].Index].Node.Id;
  180. }
  181. return data;
  182. }
  183. set
  184. {
  185. lock (graphData)
  186. {
  187. ClearSelection();
  188. CurrentCell = null;
  189. toBeSelected.Clear();
  190. if (value == null)
  191. {
  192. return;
  193. }
  194. foreach (IComparable rowItem in value)
  195. {
  196. int row = FindRow(rowItem);
  197. if (row >= 0 && Rows.Count > row)
  198. {
  199. Rows[row].Selected = true;
  200. if (CurrentCell == null)
  201. {
  202. // Set the current cell to the first item. We use cell
  203. // 1 because cell 0 could be hidden if they've chosen to
  204. // not see the graph
  205. CurrentCell = Rows[row].Cells[1];
  206. }
  207. }
  208. else
  209. {
  210. // Remember this node, and if we see it again, select it.
  211. toBeSelected.Add(rowItem);
  212. }
  213. }
  214. }
  215. }
  216. }
  217. protected override void Dispose(bool disposing)
  218. {
  219. shouldRun = false;
  220. if (disposing)
  221. {
  222. if (graphBitmap != null)
  223. {
  224. graphBitmap.Dispose();
  225. graphBitmap = null;
  226. }
  227. }
  228. base.Dispose(disposing);
  229. }
  230. [Description("Loading Handler. NOTE: This will often happen on a background thread so UI operations may not be safe!")]
  231. [Category("Behavior")]
  232. public event LoadingEventHandler Loading;
  233. public void ShowRevisionGraph()
  234. {
  235. Columns[0].Visible = true;
  236. //updateData();
  237. backgroundEvent.Set();
  238. }
  239. public void HideRevisionGraph()
  240. {
  241. Columns[0].Visible = false;
  242. //updateData();
  243. backgroundEvent.Set();
  244. }
  245. [Browsable(false)]
  246. public bool RevisionGraphVisible
  247. {
  248. get
  249. {
  250. return Columns[0].Visible;
  251. }
  252. }
  253. public void Add(IComparable aId, IComparable[] aParentIds, DataType aType, GitRevision aData)
  254. {
  255. lock (graphData)
  256. {
  257. graphData.Add(aId, aParentIds, aType, aData);
  258. }
  259. UpdateData();
  260. }
  261. public void Clear()
  262. {
  263. lock (backgroundThread)
  264. {
  265. backgroundScrollTo = 0;
  266. }
  267. lock (graphData)
  268. {
  269. SetRowCount(0);
  270. junctionColors.Clear();
  271. graphData.Clear();
  272. graphDataCount = 0;
  273. RebuildGraph();
  274. }
  275. filterMode = FilterType.None;
  276. }
  277. public void FilterClear()
  278. {
  279. lock (graphData)
  280. {
  281. foreach (Node n in graphData.Nodes.Values)
  282. {
  283. n.IsFiltered = false;
  284. }
  285. graphData.IsFilter = false;
  286. }
  287. }
  288. public void Filter(IComparable aId)
  289. {
  290. lock (graphData)
  291. {
  292. graphData.Filter(aId);
  293. }
  294. }
  295. public bool RowIsRelative(int aRow)
  296. {
  297. lock (graphData)
  298. {
  299. Graph.ILaneRow row = graphData[aRow];
  300. if (row == null)
  301. {
  302. return false;
  303. }
  304. if (row.Node.Ancestors.Count > 0)
  305. return row.Node.Ancestors[0].IsRelative;
  306. return true;
  307. }
  308. }
  309. public GitRevision GetRowData(int aRow)
  310. {
  311. lock (graphData)
  312. {
  313. Graph.ILaneRow row = graphData[aRow];
  314. return row == null ? null : row.Node.Data;
  315. }
  316. }
  317. public IComparable GetRowId(int aRow)
  318. {
  319. lock (graphData)
  320. {
  321. Graph.ILaneRow row = graphData[aRow];
  322. if (row == null)
  323. {
  324. return null;
  325. }
  326. return row.Node.Id;
  327. }
  328. }
  329. public int FindRow(IComparable aId)
  330. {
  331. lock (graphData)
  332. {
  333. int i;
  334. for (i = 0; i < graphData.CachedCount; i++)
  335. {
  336. if (graphData[i] != null && graphData[i].Node.Id.CompareTo(aId) == 0)
  337. {
  338. break;
  339. }
  340. }
  341. return i == graphData.Count ? -1 : i;
  342. }
  343. }
  344. public void Prune()
  345. {
  346. int count;
  347. lock (graphData)
  348. {
  349. graphData.Prune();
  350. count = graphData.Count;
  351. }
  352. SetRowCount(count);
  353. }
  354. private void RebuildGraph()
  355. {
  356. // Redraw
  357. cacheHead = -1;
  358. cacheHeadRow = 0;
  359. ClearDrawCache();
  360. UpdateData();
  361. Invalidate(true);
  362. }
  363. private void SetRowCount(int count)
  364. {
  365. if (InvokeRequired)
  366. {
  367. // DO NOT INVOKE! The RowCount is fixed at other strategic points in time.
  368. // -Doing this in synch can lock up the application
  369. // -Doing this asynch causes the scrollbar to flicker and eats performance
  370. // -At first I was concerned that returning might lead to some cases where
  371. // we have more items in the list than we're showing, but I'm pretty sure
  372. // when we're done processing we'll update with the final count, so the
  373. // problem will only be temporary, and not able to distinguish it from
  374. // just git giving us data slowly.
  375. //Invoke(new MethodInvoker(delegate { setRowCount(count); }));
  376. return;
  377. }
  378. lock (backgroundThread)
  379. {
  380. if (CurrentCell == null)
  381. {
  382. RowCount = count;
  383. CurrentCell = null;
  384. }
  385. else
  386. {
  387. RowCount = count;
  388. }
  389. }
  390. }
  391. private void graphData_Updated(object graph)
  392. {
  393. // We have to post this since the thread owns a lock on GraphData that we'll
  394. // need in order to re-draw the graph.
  395. syncContext.Post(o =>
  396. {
  397. ClearDrawCache();
  398. Invalidate();
  399. }, this);
  400. }
  401. private void dataGrid_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
  402. {
  403. if (e.RowIndex < 0)
  404. return;
  405. if (Rows[e.RowIndex].Height != RowTemplate.Height)
  406. {
  407. Rows[e.RowIndex].Height = RowTemplate.Height;
  408. dataGrid_Scroll(null, null);
  409. }
  410. lock (graphData)
  411. {
  412. Graph.ILaneRow row = graphData[e.RowIndex];
  413. if (row == null || (e.State & DataGridViewElementStates.Visible) == 0)
  414. return;
  415. if (e.ColumnIndex != 0)
  416. return;
  417. var brush = (e.State & DataGridViewElementStates.Selected) == DataGridViewElementStates.Selected
  418. ? selectionBrush : new SolidBrush(Color.White);
  419. e.Graphics.FillRectangle(brush, e.CellBounds);
  420. Rectangle srcRect = DrawGraph(e.RowIndex);
  421. if (!srcRect.IsEmpty)
  422. {
  423. e.Graphics.DrawImage
  424. (
  425. graphBitmap,
  426. e.CellBounds,
  427. srcRect,
  428. GraphicsUnit.Pixel
  429. );
  430. }
  431. e.Handled = true;
  432. }
  433. }
  434. private void dataGrid_ColumnWidthChanged(object sender, DataGridViewColumnEventArgs e)
  435. {
  436. ClearDrawCache();
  437. }
  438. private void dataGrid_Scroll(object sender, ScrollEventArgs e)
  439. {
  440. UpdateData();
  441. UpdateColumnWidth();
  442. }
  443. private void BackgroundThreadEntry()
  444. {
  445. while (shouldRun && backgroundEvent.WaitOne())
  446. {
  447. lock (backgroundEvent)
  448. {
  449. int scrollTo;
  450. lock (backgroundThread)
  451. {
  452. scrollTo = backgroundScrollTo;
  453. }
  454. int curCount;
  455. lock (graphData)
  456. {
  457. curCount = graphDataCount;
  458. graphDataCount = graphData.CachedCount;
  459. }
  460. if (RevisionGraphVisible)
  461. UpdateGraph(curCount, scrollTo);
  462. else
  463. {
  464. //do nothing... do not cache, the graph is invisible
  465. syncContext.Post(o => UpdateRow((int) o), curCount);
  466. Thread.Sleep(100);
  467. }
  468. }
  469. }
  470. }
  471. private void UpdateGraph(int curCount, int scrollTo)
  472. {
  473. while (curCount < scrollTo)
  474. {
  475. lock (graphData)
  476. {
  477. // Cache the next item
  478. if (!graphData.CacheTo(curCount))
  479. {
  480. Console.WriteLine("Cached item FAILED {0}", curCount.ToString());
  481. lock (backgroundThread)
  482. {
  483. backgroundScrollTo = curCount;
  484. }
  485. break;
  486. }
  487. // Update the row (if needed)
  488. if (curCount < visibleBottom || toBeSelected.Count > 0)
  489. {
  490. syncContext.Post(o => UpdateRow((int)o), curCount);
  491. }
  492. int count = 0;
  493. if (FirstDisplayedCell != null)
  494. count = FirstDisplayedCell.RowIndex + DisplayedRowCount(true);
  495. if (curCount == count)
  496. syncContext.Post(state1 => UpdateColumnWidth(), null);
  497. curCount = graphData.CachedCount;
  498. graphDataCount = curCount;
  499. }
  500. }
  501. }
  502. private void UpdateData()
  503. {
  504. lock (backgroundThread)
  505. {
  506. visibleTop = FirstDisplayedCell == null ? 0 : FirstDisplayedCell.RowIndex;
  507. visibleBottom = rowHeight > 0 ? visibleTop + (Height / rowHeight) : visibleTop;
  508. //Subtract 2 for safe merge (1 for rounding and 1 for whitespace)....
  509. if (visibleBottom - 2 > graphData.Count)
  510. {
  511. //Currently we are doing some important work; we are recieving
  512. //rows that the user is viewing
  513. SetBackgroundThreadToNormalPriority();
  514. if (Loading != null && graphData.Count > RowCount)// && graphData.Count != RowCount)
  515. {
  516. Loading(true);
  517. }
  518. }
  519. else
  520. {
  521. //All rows that the user is viewing are loaded. We now can hide the loading
  522. //animation that is shown. (the event Loading(bool) triggers this!)
  523. //Since the graph is not drawn for the visible graph yet, keep the
  524. //priority on Normal. Lower it when the graph is visible.
  525. if (Loading != null)
  526. {
  527. Loading(false);
  528. }
  529. }
  530. if (visibleBottom > graphData.Count)
  531. {
  532. visibleBottom = graphData.Count;
  533. }
  534. int targetBottom = visibleBottom + 2000;
  535. targetBottom = Math.Min(targetBottom, graphData.Count);
  536. if (backgroundScrollTo < targetBottom)
  537. {
  538. backgroundScrollTo = targetBottom;
  539. backgroundEvent.Set();
  540. }
  541. }
  542. }
  543. private void SetBackgroundThreadToNormalPriority()
  544. {
  545. backgroundThread.Priority = ThreadPriority.Normal;
  546. }
  547. private void SetBackgroundThreadToLowPriority()
  548. {
  549. backgroundThread.Priority = ThreadPriority.BelowNormal;
  550. }
  551. private void UpdateRow(int row)
  552. {
  553. lock (graphData)
  554. {
  555. if (RowCount < graphData.Count)
  556. {
  557. SetRowCount(graphData.Count);
  558. }
  559. // Check to see if the newly added item should be selected
  560. if (graphData.Count > row)
  561. {
  562. IComparable id = graphData[row].Node.Id;
  563. if (toBeSelected.Contains(id))
  564. {
  565. toBeSelected.Remove(id);
  566. Rows[row].Selected = true;
  567. if (CurrentCell == null)
  568. {
  569. // Set the current cell to the first item. We use cell
  570. // 1 because cell 0 could be hidden if they've chosen to
  571. // not see the graph
  572. CurrentCell = Rows[row].Cells[1];
  573. }
  574. }
  575. }
  576. if (visibleBottom < graphDataCount)
  577. {
  578. //All data for the current view is loaded! Lower the thread priority.
  579. SetBackgroundThreadToLowPriority();
  580. }
  581. else
  582. {
  583. //We need to draw the graph for the visible part of the grid. Higher the priority.
  584. SetBackgroundThreadToNormalPriority();
  585. }
  586. try
  587. {
  588. InvalidateRow(row);
  589. }
  590. catch (ArgumentOutOfRangeException)
  591. {
  592. // Ignore. It is possible that RowCount gets changed before
  593. // this is processed and the row is larger than RowCount.
  594. }
  595. }
  596. }
  597. private void UpdateColumnWidth()
  598. {
  599. lock (graphData)
  600. {
  601. // Auto scale width on scroll
  602. if (dataGridColumnGraph.Visible)
  603. {
  604. int laneCount = 2;
  605. if (graphData != null)
  606. {
  607. int width = 1;
  608. int start = VerticalScrollBar.Value / rowHeight;
  609. int stop = start + DisplayedRowCount(true);
  610. for (int i = start; i < stop && graphData[i] != null; i++)
  611. {
  612. width = Math.Max(graphData[i].Count, width);
  613. }
  614. laneCount = Math.Min(Math.Max(laneCount, width), MAX_LANES);
  615. }
  616. if (dataGridColumnGraph.Width != LANE_WIDTH * laneCount && LANE_WIDTH * laneCount > dataGridColumnGraph.MinimumWidth)
  617. dataGridColumnGraph.Width = LANE_WIDTH * laneCount;
  618. }
  619. }
  620. }
  621. //Color of non-relative branches.
  622. private List<Color> GetJunctionColors(IEnumerable<Junction> aJunction)
  623. {
  624. List<Color> colors = new List<Color>();
  625. foreach (Junction j in aJunction)
  626. {
  627. colors.Add(GetJunctionColor(j));
  628. }
  629. if (colors.Count == 0)
  630. {
  631. colors.Add(Color.Black);
  632. }
  633. return colors;
  634. }
  635. // http://en.wikipedia.org/wiki/File:RBG_color_wheel.svg
  636. private Color GetJunctionColor(Junction aJunction)
  637. {
  638. //Draw non-relative branches gray
  639. if (!aJunction.IsRelative && Settings.RevisionGraphDrawNonRelativesGray)
  640. return nonRelativeColor;
  641. if (!Settings.MulticolorBranches)
  642. return Settings.GraphColor;
  643. // This is the order to grab the colors in.
  644. int[] preferedColors = { 4, 8, 6, 10, 2, 5, 7, 3, 9, 1, 11 };
  645. int colorIndex;
  646. if (junctionColors.TryGetValue(aJunction, out colorIndex))
  647. {
  648. return possibleColors[colorIndex];
  649. }
  650. // Get adjacent junctions
  651. var adjacentJunctions = new List<Junction>();
  652. var adjacentColors = new List<int>();
  653. adjacentJunctions.AddRange(aJunction.Child.Ancestors);
  654. adjacentJunctions.AddRange(aJunction.Child.Descendants);
  655. adjacentJunctions.AddRange(aJunction.Parent.Ancestors);
  656. adjacentJunctions.AddRange(aJunction.Parent.Descendants);
  657. foreach (Junction peer in adjacentJunctions)
  658. {
  659. if (junctionColors.TryGetValue(peer, out colorIndex))
  660. {
  661. adjacentColors.Add(colorIndex);
  662. }
  663. else
  664. {
  665. colorIndex = -1;
  666. }
  667. }
  668. if (adjacentColors.Count == 0) //This is an end-point. We need to 'pick' a new color
  669. {
  670. colorIndex = 0;
  671. }
  672. else //This is a parent branch, calculate new color based on parent branch
  673. {
  674. int start = adjacentColors[0];
  675. int i;
  676. for (i = 0; i < preferedColors.Length; i++)
  677. {
  678. colorIndex = (start + preferedColors[i]) % possibleColors.Length;
  679. if (!adjacentColors.Contains(colorIndex))
  680. {
  681. break;
  682. }
  683. }
  684. if (i == preferedColors.Length)
  685. {
  686. var r = new Random();
  687. colorIndex = r.Next(preferedColors.Length);
  688. }
  689. }
  690. junctionColors[aJunction] = colorIndex;
  691. return possibleColors[colorIndex];
  692. }
  693. public override void Refresh()
  694. {
  695. ClearDrawCache();
  696. base.Refresh();
  697. }
  698. private void ClearDrawCache()
  699. {
  700. cacheHead = 0;
  701. cacheCount = 0;
  702. }
  703. private Rectangle DrawGraph(int aNeededRow)
  704. {
  705. lock (graphData)
  706. {
  707. if (aNeededRow < 0 || graphData.Count == 0 || graphData.Count <= aNeededRow)
  708. {
  709. return Rectangle.Empty;
  710. }
  711. #region Make sure the graph cache bitmap is setup
  712. int height = cacheCountMax * rowHeight;
  713. int width = dataGridColumnGraph.Width;
  714. if (graphBitmap == null ||
  715. //Resize the bitmap when the with or height is changed. The height won't change very often.
  716. //The with changes more often, when branches become visible/invisible.
  717. //Try to be 'smart' and not resize the bitmap for each little change. Enlarge when needed
  718. //but never shrink the bitmap since the huge performance hit is worse than the little extra memory.
  719. graphBitmap.Width < width || graphBitmap.Height != height)
  720. {
  721. if (graphBitmap != null)
  722. {
  723. graphBitmap.Dispose();
  724. graphBitmap = null;
  725. }
  726. if (width > 0 && height > 0)
  727. {
  728. graphBitmap = new Bitmap(Math.Max(width, LANE_WIDTH * 3), height, PixelFormat.Format32bppPArgb);
  729. graphWorkArea = Graphics.FromImage(graphBitmap);
  730. graphWorkArea.SmoothingMode = SmoothingMode.AntiAlias;
  731. cacheHead = 0;
  732. cacheCount = 0;
  733. }
  734. else
  735. {
  736. return Rectangle.Empty;
  737. }
  738. }
  739. #endregion
  740. // Compute how much the head needs to move to show the requested item.
  741. int neededHeadAdjustment = aNeededRow - cacheHead;
  742. if (neededHeadAdjustment > 0)
  743. {
  744. neededHeadAdjustment -= cacheCountMax - 1;
  745. if (neededHeadAdjustment < 0)
  746. {
  747. neededHeadAdjustment = 0;
  748. }
  749. }
  750. int newRows = 0;
  751. if (cacheCount < cacheCountMax)
  752. {
  753. newRows = (aNeededRow - cacheCount) + 1;
  754. }
  755. // Adjust the head of the cache
  756. cacheHead = cacheHead + neededHeadAdjustment;
  757. cacheHeadRow = (cacheHeadRow + neededHeadAdjustment) % cacheCountMax;
  758. if (cacheHeadRow < 0)
  759. {
  760. cacheHeadRow = cacheCountMax + cacheHeadRow;
  761. }
  762. int start;
  763. int end;
  764. if (newRows > 0)
  765. {
  766. start = cacheHead + cacheCount;
  767. cacheCount = Math.Min(cacheCount + newRows, cacheCountMax);
  768. end = cacheHead + cacheCount;
  769. }
  770. else if (neededHeadAdjustment > 0)
  771. {
  772. end = cacheHead + cacheCount;
  773. start = Math.Max(cacheHead, end - neededHeadAdjustment);
  774. }
  775. else if (neededHeadAdjustment < 0)
  776. {
  777. start = cacheHead;
  778. end = start + Math.Min(cacheCountMax, -neededHeadAdjustment);
  779. }
  780. else
  781. {
  782. // Item already in the cache
  783. return CreateRectangle(aNeededRow, width);
  784. }
  785. if (RevisionGraphVisible)
  786. {
  787. if (!DrawVisibleGraph(start, end))
  788. return Rectangle.Empty;
  789. }
  790. return CreateRectangle(aNeededRow, width);
  791. } // end lock
  792. }
  793. private bool DrawVisibleGraph(int start, int end)
  794. {
  795. for (int rowIndex = start; rowIndex < end; rowIndex++)
  796. {
  797. Graph.ILaneRow row = graphData[rowIndex];
  798. if (row == null)
  799. {
  800. // This shouldn't be happening...If it does, clear the cache so we
  801. // eventually pick it up.
  802. Console.WriteLine("Draw lane {0} NO DATA", rowIndex.ToString());
  803. ClearDrawCache();
  804. return false;
  805. }
  806. Region oldClip = graphWorkArea.Clip;
  807. // Get the x,y value of the current item's upper left in the cache
  808. int curCacheRow = (cacheHeadRow + rowIndex - cacheHead) % cacheCountMax;
  809. int x = 0;
  810. int y = curCacheRow * rowHeight;
  811. var laneRect = new Rectangle(0, y, Width, rowHeight);
  812. if (rowIndex == start || curCacheRow == 0)
  813. {
  814. // Draw previous row first. Clip top to row. We also need to clear the area
  815. // before we draw since nothing else would clear the top 1/2 of the item to draw.
  816. graphWorkArea.RenderingOrigin = new Point(x, y - rowHeight);
  817. var newClip = new Region(laneRect);
  818. graphWorkArea.Clip = newClip;
  819. graphWorkArea.Clear(Color.Transparent);
  820. DrawItem(graphWorkArea, graphData[rowIndex - 1]);
  821. graphWorkArea.Clip = oldClip;
  822. }
  823. bool isLast = (rowIndex == end - 1);
  824. if (isLast)
  825. {
  826. var newClip = new Region(laneRect);
  827. graphWorkArea.Clip = newClip;
  828. }
  829. graphWorkArea.RenderingOrigin = new Point(x, y);
  830. bool success = DrawItem(graphWorkArea, row);
  831. graphWorkArea.Clip = oldClip;
  832. if (!success)
  833. {
  834. ClearDrawCache();
  835. return false;
  836. }
  837. }
  838. return true;
  839. }
  840. private Rectangle CreateRectangle(int aNeededRow, int width)
  841. {
  842. return new Rectangle
  843. (
  844. 0,
  845. (cacheHeadRow + aNeededRow - cacheHead) % cacheCountMax * RowTemplate.Height,
  846. width,
  847. rowHeight
  848. );
  849. }
  850. // end drawGraph
  851. private bool DrawItem(Graphics wa, Graph.ILaneRow row)
  852. {
  853. if (row == null || row.NodeLane == -1)
  854. {
  855. return false;
  856. }
  857. // Clip to the area we're drawing in, but draw 1 pixel past so
  858. // that the top/bottom of the line segment's anti-aliasing isn't
  859. // visible in the final rendering.
  860. int top = wa.RenderingOrigin.Y + rowHeight / 2;
  861. var laneRect = new Rectangle(0, top, Width, rowHeight);
  862. Region oldClip = wa.Clip;
  863. var newClip = new Region(laneRect);
  864. newClip.Intersect(oldClip);
  865. wa.Clip = newClip;
  866. wa.Clear(Color.Transparent);
  867. for (int r = 0; r < 2; r++)
  868. for (int lane = 0; lane < row.Count; lane++)
  869. {
  870. int mid = wa.RenderingOrigin.X + (int)((lane + 0.5) * LANE_WIDTH);
  871. for (int item = 0; item < row.LaneInfoCount(lane); item++)
  872. {
  873. Graph.LaneInfo laneInfo = row[lane, item];
  874. //Draw all non-relative items first, them draw
  875. //all relative items on top
  876. if (laneInfo.Junctions.FirstOrDefault() != null)
  877. if (laneInfo.Junctions.First().IsRelative == (r == 0))
  878. continue;
  879. List<Color> curColors = GetJunctionColors(laneInfo.Junctions);
  880. // Create the brush for drawing the line
  881. Brush brushLineColor;
  882. bool drawBorder = Settings.BranchBorders; //hide border for "non-relatives"
  883. if (curColors.Count == 1 || !Settings.StripedBranchChange)
  884. {
  885. if (curColors[0] != nonRelativeColor)
  886. {
  887. brushLineColor = new SolidBrush(curColors[0]);
  888. }
  889. else if (curColors.Count > 1 && curColors[1] != nonRelativeColor)
  890. {
  891. brushLineColor = new SolidBrush(curColors[1]);
  892. }
  893. else
  894. {
  895. drawBorder = false;
  896. brushLineColor = new SolidBrush(nonRelativeColor);
  897. }
  898. }
  899. else
  900. {
  901. brushLineColor = new HatchBrush(HatchStyle.DarkDownwardDiagonal, curColors[0], curColors[1]);
  902. if (curColors[0] == nonRelativeColor && curColors[1] == nonRelativeColor) drawBorder = false;
  903. }
  904. for (int i = drawBorder ? 0 : 2; i < 3; i++)
  905. {
  906. Pen penLine;
  907. if (i == 0)
  908. {
  909. penLine = new Pen(new SolidBrush(Color.White), LANE_LINE_WIDTH + 2);
  910. }
  911. else if (i == 1)
  912. {
  913. penLine = new Pen(new SolidBrush(Color.Black), LANE_LINE_WIDTH + 1);
  914. }
  915. else
  916. {
  917. penLine = new Pen(brushLineColor, LANE_LINE_WIDTH);
  918. }
  919. if (laneInfo.ConnectLane == lane)
  920. {
  921. wa.DrawLine
  922. (
  923. penLine,
  924. new Point(mid, top - 1),
  925. new Point(mid, top + rowHeight + 2)
  926. );
  927. }
  928. else
  929. {
  930. wa.DrawBezier
  931. (
  932. penLine,
  933. new Point(mid, top - 1),
  934. new Point(mid, top + rowHeight + 2),
  935. new Point(mid + (laneInfo.ConnectLane - lane) * LANE_WIDTH, top - 1),
  936. new Point(mid + (laneInfo.ConnectLane - lane) * LANE_WIDTH, top + rowHeight + 2)
  937. );
  938. }
  939. }
  940. }
  941. }
  942. // Reset the clip region
  943. wa.Clip = oldClip;
  944. {
  945. // Draw node
  946. var nodeRect = new Rectangle
  947. (
  948. wa.RenderingOrigin.X + (LANE_WIDTH - NODE_DIMENSION) / 2 + row.NodeLane * LANE_WIDTH,
  949. wa.RenderingOrigin.Y + (rowHeight - NODE_DIMENSION) / 2,
  950. NODE_DIMENSION,
  951. NODE_DIMENSION
  952. );
  953. Brush nodeBrush;
  954. bool drawBorder = Settings.BranchBorders;
  955. List<Color> nodeColors = GetJunctionColors(row.Node.Ancestors);
  956. if (nodeColors.Count == 1)
  957. {
  958. nodeBrush = new SolidBrush(nodeColors[0]);
  959. if (nodeColors[0] == nonRelativeColor) drawBorder = false;
  960. }
  961. else
  962. {
  963. nodeBrush = new LinearGradientBrush(nodeRect, nodeColors[0], nodeColors[1],
  964. LinearGradientMode.Horizontal);
  965. if (nodeColors[0] == nonRelativeColor && nodeColors[1] == Color.LightGray) drawBorder = false;
  966. }
  967. if (filterMode == FilterType.Highlight && row.Node.IsFiltered)
  968. {
  969. Rectangle highlightRect = nodeRect;
  970. highlightRect.Inflate(2, 3);
  971. wa.FillRectangle(Brushes.Yellow, highlightRect);
  972. wa.DrawRectangle(new Pen(Brushes.Black), highlightRect);
  973. }
  974. if (row.Node.Data == null)
  975. {
  976. wa.FillEllipse(Brushes.White, nodeRect);
  977. wa.DrawEllipse(new Pen(Color.Red, 2), nodeRect);
  978. }
  979. else if (row.Node.IsActive)
  980. {
  981. wa.FillRectangle(nodeBrush, nodeRect);
  982. nodeRect.Inflate(1, 1);
  983. wa.DrawRectangle(new Pen(Color.Black, 3), nodeRect);
  984. }
  985. else if (row.Node.IsSpecial)
  986. {
  987. wa.FillRectangle(nodeBrush, nodeRect);
  988. if (drawBorder)
  989. {
  990. wa.DrawRectangle(new Pen(Color.Black, 1), nodeRect);
  991. }
  992. }
  993. else
  994. {
  995. wa.FillEllipse(nodeBrush, nodeRect);
  996. if (drawBorder)
  997. {
  998. wa.DrawEllipse(new Pen(Color.Black, 1), nodeRect);
  999. }
  1000. }
  1001. }
  1002. return true;
  1003. }
  1004. private void dataGrid_Resize(object sender, EventArgs e)
  1005. {
  1006. rowHeight = RowTemplate.Height;
  1007. // Keep an extra page in the cache
  1008. cacheCountMax = Height * 2 / rowHeight + 1;
  1009. ClearDrawCache();
  1010. dataGrid_Scroll(null, null);
  1011. }
  1012. protected override void OnKeyDown(KeyEventArgs e)
  1013. {
  1014. if (e.KeyData == Keys.Home)
  1015. {
  1016. if (RowCount != 0)
  1017. {
  1018. ClearSelection();
  1019. Rows[0].Selected = true;
  1020. CurrentCell = Rows[0].Cells[1];
  1021. }
  1022. return;
  1023. }
  1024. else if (e.KeyData == Keys.End)
  1025. {
  1026. if (RowCount != 0)
  1027. {
  1028. ClearSelection();
  1029. Rows[RowCount - 1].Selected = true;
  1030. CurrentCell = Rows[RowCount - 1].Cells[1];
  1031. }
  1032. return;
  1033. }
  1034. base.OnKeyDown(e);
  1035. }
  1036. #region Nested type: Node
  1037. private sealed class Node
  1038. {
  1039. public readonly List<Junction> Ancestors = new List<Junction>();
  1040. public readonly List<Junction> Descendants = new List<Junction>();
  1041. public readonly IComparable Id;
  1042. public GitRevision Data;
  1043. public DataType DataType;
  1044. public int InLane = int.MaxValue;
  1045. public int Index = int.MaxValue;
  1046. public Node(IComparable aId)
  1047. {
  1048. Id = aId;
  1049. }
  1050. public bool IsActive
  1051. {
  1052. get { return (DataType & DataType.Active) == DataType.Active; }
  1053. }
  1054. public bool IsFiltered
  1055. {
  1056. get { return (DataType & DataType.Filtered) == DataType.Filtered; }
  1057. set
  1058. {
  1059. if (value)
  1060. {
  1061. DataType |= DataType.Filtered;
  1062. }
  1063. else
  1064. {
  1065. DataType &= ~DataType.Filtered;
  1066. }
  1067. }
  1068. }
  1069. public bool IsSpecial
  1070. {
  1071. get { return (DataType & DataType.Special) == DataType.Special; }
  1072. }
  1073. public override string ToString()
  1074. {
  1075. if (Data == null)
  1076. {
  1077. string name = Id.ToString();
  1078. if (name.Length > 8)
  1079. {
  1080. name = name.Substring(0, 4) + ".." + name.Substring(name.Length - 4, 4);
  1081. }
  1082. return string.Format("{0} ({1})", name, Index);
  1083. }
  1084. return Data.ToString();
  1085. }
  1086. }
  1087. #endregion
  1088. }
  1089. // end of class DvcsGraph
  1090. }