DiffTreeView.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. using System.Collections.Generic;
  2. using UnityEditor.IMGUI.Controls;
  3. using UnityEngine;
  4. using Codice.Client.Commands;
  5. using Codice.Client.Common;
  6. using Codice.Utils;
  7. using PlasticGui;
  8. using PlasticGui.WorkspaceWindow.Diff;
  9. using Unity.PlasticSCM.Editor.UI;
  10. using Unity.PlasticSCM.Editor.UI.Tree;
  11. namespace Unity.PlasticSCM.Editor.Views.Diff
  12. {
  13. internal class DiffTreeView : TreeView
  14. {
  15. internal DiffTreeView(DiffTreeViewMenu menu)
  16. : base(new TreeViewState())
  17. {
  18. mMenu = menu;
  19. customFoldoutYOffset = UnityConstants.TREEVIEW_FOLDOUT_Y_OFFSET;
  20. rowHeight = UnityConstants.TREEVIEW_ROW_HEIGHT;
  21. showAlternatingRowBackgrounds = false;
  22. mCooldownFilterAction = new CooldownWindowDelayer(
  23. DelayedSearchChanged, UnityConstants.SEARCH_DELAYED_INPUT_ACTION_INTERVAL);
  24. EnableHorizontalScrollbar();
  25. }
  26. public override IList<TreeViewItem> GetRows()
  27. {
  28. return mRows;
  29. }
  30. public override void OnGUI(Rect rect)
  31. {
  32. base.OnGUI(rect);
  33. Event e = Event.current;
  34. if (e.type != EventType.KeyDown)
  35. return;
  36. bool isProcessed = mMenu.ProcessKeyActionIfNeeded(e);
  37. if (isProcessed)
  38. e.Use();
  39. }
  40. protected override bool CanChangeExpandedState(TreeViewItem item)
  41. {
  42. return item is ChangeCategoryTreeViewItem
  43. || item is MergeCategoryTreeViewItem;
  44. }
  45. protected override TreeViewItem BuildRoot()
  46. {
  47. return new TreeViewItem(0, -1, string.Empty);
  48. }
  49. protected override IList<TreeViewItem> BuildRows(TreeViewItem rootItem)
  50. {
  51. try
  52. {
  53. RegenerateRows(
  54. mDiffTree,
  55. mTreeViewItemIds,
  56. this,
  57. rootItem,
  58. mRows,
  59. mExpandCategories);
  60. }
  61. finally
  62. {
  63. mExpandCategories = false;
  64. }
  65. return mRows;
  66. }
  67. protected override void CommandEventHandling()
  68. {
  69. // NOTE - empty override to prevent crash when pressing ctrl-a in the treeview
  70. }
  71. protected override void SearchChanged(string newSearch)
  72. {
  73. mCooldownFilterAction.Ping();
  74. }
  75. protected override void ContextClickedItem(int id)
  76. {
  77. mMenu.Popup();
  78. Repaint();
  79. }
  80. protected override void BeforeRowsGUI()
  81. {
  82. int firstRowVisible;
  83. int lastRowVisible;
  84. GetFirstAndLastVisibleRows(out firstRowVisible, out lastRowVisible);
  85. GUI.DrawTexture(new Rect(0,
  86. firstRowVisible * rowHeight,
  87. GetRowRect(0).width + 500,
  88. (lastRowVisible * rowHeight) + 1500),
  89. Images.GetTreeviewBackgroundTexture());
  90. DrawTreeViewItem.InitializeStyles();
  91. mLargestRowWidth = 0;
  92. base.BeforeRowsGUI();
  93. }
  94. protected override void RowGUI(RowGUIArgs args)
  95. {
  96. if (args.item is MergeCategoryTreeViewItem)
  97. {
  98. MergeCategoryTreeViewItemGUI(
  99. args.rowRect,
  100. (MergeCategoryTreeViewItem)args.item,
  101. args.selected,
  102. args.focused);
  103. return;
  104. }
  105. if (args.item is ChangeCategoryTreeViewItem)
  106. {
  107. ChangeCategoryTreeViewItemGUI(
  108. args.rowRect,
  109. (ChangeCategoryTreeViewItem)args.item,
  110. args.selected,
  111. args.focused);
  112. return;
  113. }
  114. if (args.item is ClientDiffTreeViewItem)
  115. {
  116. float itemWidth;
  117. ClientDiffTreeViewItemGUI(
  118. args.rowRect,
  119. rowHeight,
  120. mDiffTree,
  121. (ClientDiffTreeViewItem)args.item,
  122. args.selected,
  123. args.focused,
  124. out itemWidth);
  125. float rowWidth = baseIndent + args.item.depth * depthIndentWidth +
  126. itemWidth + UnityConstants.TREEVIEW_ROW_WIDTH_OFFSET;
  127. if (rowWidth > mLargestRowWidth)
  128. mLargestRowWidth = rowWidth;
  129. return;
  130. }
  131. base.RowGUI(args);
  132. }
  133. protected override void AfterRowsGUI()
  134. {
  135. if (mHorizontalColumn != null)
  136. mHorizontalColumn.width = mLargestRowWidth;
  137. base.AfterRowsGUI();
  138. }
  139. internal void ClearModel()
  140. {
  141. mTreeViewItemIds.Clear();
  142. mDiffTree = new UnityDiffTree();
  143. }
  144. internal void BuildModel(
  145. List<ClientDiff> diffs,
  146. BranchResolver brResolver,
  147. bool skipMergeTracking)
  148. {
  149. mTreeViewItemIds.Clear();
  150. mDiffTree.BuildCategories(diffs, brResolver, skipMergeTracking);
  151. }
  152. internal void Refilter()
  153. {
  154. Filter filter = new Filter(searchString);
  155. mDiffTree.Filter(filter, ColumnsNames);
  156. mExpandCategories = true;
  157. }
  158. internal void Sort()
  159. {
  160. mDiffTree.Sort(
  161. PlasticLocalization.GetString(PlasticLocalization.Name.PathColumn),
  162. sortAscending: true);
  163. }
  164. internal ClientDiffInfo GetMetaDiff(ClientDiffInfo diff)
  165. {
  166. return mDiffTree.GetMetaDiff(diff);
  167. }
  168. internal bool SelectionHasMeta()
  169. {
  170. if (!HasSelection())
  171. return false;
  172. ClientDiffInfo selectedDiff = GetSelectedDiffs(false)[0];
  173. if (selectedDiff == null)
  174. return false;
  175. return mDiffTree.HasMeta(selectedDiff);
  176. }
  177. internal List<ClientDiffInfo> GetSelectedDiffs(bool includeMetaFiles)
  178. {
  179. List<ClientDiffInfo> result = new List<ClientDiffInfo>();
  180. IList<int> selectedIds = GetSelection();
  181. if (selectedIds.Count == 0)
  182. return result;
  183. foreach (KeyValuePair<ITreeViewNode, int> item
  184. in mTreeViewItemIds.GetInfoItems())
  185. {
  186. if (!selectedIds.Contains(item.Value))
  187. continue;
  188. if (!(item.Key is ClientDiffInfo))
  189. continue;
  190. result.Add((ClientDiffInfo)item.Key);
  191. }
  192. if (includeMetaFiles)
  193. mDiffTree.FillWithMeta(result);
  194. return result;
  195. }
  196. void DelayedSearchChanged()
  197. {
  198. Refilter();
  199. Sort();
  200. Reload();
  201. TableViewOperations.ScrollToSelection(this);
  202. }
  203. void EnableHorizontalScrollbar()
  204. {
  205. mHorizontalColumn = new MultiColumnHeaderState.Column();
  206. mHorizontalColumn.autoResize = false;
  207. MultiColumnHeaderState.Column[] cols = { mHorizontalColumn };
  208. MultiColumnHeaderState headerState = new MultiColumnHeaderState(cols);
  209. multiColumnHeader = new MultiColumnHeader(headerState);
  210. multiColumnHeader.height = 0f;
  211. }
  212. static void RegenerateRows(
  213. UnityDiffTree diffTree,
  214. TreeViewItemIds<IDiffCategory, ITreeViewNode> treeViewItemIds,
  215. TreeView treeView,
  216. TreeViewItem rootItem,
  217. List<TreeViewItem> rows,
  218. bool expandCategories)
  219. {
  220. ClearRows(rootItem, rows);
  221. List<IDiffCategory> categories = diffTree.GetNodes();
  222. if (categories == null)
  223. return;
  224. foreach (IDiffCategory category in categories)
  225. {
  226. if (category is MergeCategory)
  227. {
  228. AddMergeCategory(
  229. rootItem,
  230. category,
  231. rows,
  232. treeViewItemIds,
  233. treeView,
  234. expandCategories);
  235. }
  236. if (category is ChangeCategory)
  237. {
  238. AddChangeCategory(
  239. rootItem,
  240. category,
  241. rows,
  242. treeViewItemIds,
  243. treeView,
  244. expandCategories);
  245. }
  246. }
  247. if (!expandCategories)
  248. return;
  249. treeView.state.expandedIDs = treeViewItemIds.GetCategoryIds();
  250. }
  251. static void ClearRows(
  252. TreeViewItem rootItem,
  253. List<TreeViewItem> rows)
  254. {
  255. if (rootItem.hasChildren)
  256. rootItem.children.Clear();
  257. rows.Clear();
  258. }
  259. static void AddMergeCategory(
  260. TreeViewItem rootItem,
  261. IDiffCategory category,
  262. List<TreeViewItem> rows,
  263. TreeViewItemIds<IDiffCategory, ITreeViewNode> treeViewItemIds,
  264. TreeView treeView,
  265. bool expandCategories)
  266. {
  267. int categoryId;
  268. if (!treeViewItemIds.TryGetCategoryItemId(category, out categoryId))
  269. categoryId = treeViewItemIds.AddCategoryItem(category);
  270. MergeCategoryTreeViewItem mergeCategoryTreeViewItem =
  271. new MergeCategoryTreeViewItem(
  272. categoryId,
  273. rootItem.depth + 1,
  274. (MergeCategory)category);
  275. rootItem.AddChild(mergeCategoryTreeViewItem);
  276. rows.Add(mergeCategoryTreeViewItem);
  277. if (!expandCategories &&
  278. !treeView.IsExpanded(mergeCategoryTreeViewItem.id))
  279. return;
  280. for (int i = 0; i < category.GetChildrenCount(); i++)
  281. {
  282. IDiffCategory child = (IDiffCategory)((ITreeViewNode)category)
  283. .GetChild(i);
  284. AddChangeCategory(
  285. mergeCategoryTreeViewItem,
  286. child,
  287. rows,
  288. treeViewItemIds,
  289. treeView,
  290. expandCategories);
  291. }
  292. }
  293. static void AddChangeCategory(
  294. TreeViewItem parentItem,
  295. IDiffCategory category,
  296. List<TreeViewItem> rows,
  297. TreeViewItemIds<IDiffCategory, ITreeViewNode> treeViewItemIds,
  298. TreeView treeView,
  299. bool expandCategories)
  300. {
  301. int categoryId;
  302. if (!treeViewItemIds.TryGetCategoryItemId(category, out categoryId))
  303. categoryId = treeViewItemIds.AddCategoryItem(category);
  304. ChangeCategoryTreeViewItem changeCategoryTreeViewItem =
  305. new ChangeCategoryTreeViewItem(
  306. categoryId,
  307. parentItem.depth + 1,
  308. (ChangeCategory)category);
  309. parentItem.AddChild(changeCategoryTreeViewItem);
  310. rows.Add(changeCategoryTreeViewItem);
  311. if (!expandCategories &&
  312. !treeView.IsExpanded(changeCategoryTreeViewItem.id))
  313. return;
  314. AddClientDiffs(
  315. changeCategoryTreeViewItem,
  316. (ITreeViewNode)category,
  317. rows,
  318. treeViewItemIds);
  319. }
  320. static void AddClientDiffs(
  321. TreeViewItem parentItem,
  322. ITreeViewNode parentNode,
  323. List<TreeViewItem> rows,
  324. TreeViewItemIds<IDiffCategory, ITreeViewNode> treeViewItemIds)
  325. {
  326. for (int i = 0; i < parentNode.GetChildrenCount(); i++)
  327. {
  328. ITreeViewNode child = parentNode.GetChild(i);
  329. int nodeId;
  330. if (!treeViewItemIds.TryGetInfoItemId(child, out nodeId))
  331. nodeId = treeViewItemIds.AddInfoItem(child);
  332. TreeViewItem changeTreeViewItem =
  333. new ClientDiffTreeViewItem(
  334. nodeId,
  335. parentItem.depth + 1,
  336. (ClientDiffInfo)child);
  337. parentItem.AddChild(changeTreeViewItem);
  338. rows.Add(changeTreeViewItem);
  339. }
  340. }
  341. static void MergeCategoryTreeViewItemGUI(
  342. Rect rowRect,
  343. MergeCategoryTreeViewItem item,
  344. bool isSelected,
  345. bool isFocused)
  346. {
  347. string label = item.Category.CategoryName;
  348. string infoLabel = item.Category.GetChildrenCountText();
  349. DrawTreeViewItem.ForCategoryItem(
  350. rowRect,
  351. item.depth,
  352. label,
  353. infoLabel,
  354. isSelected,
  355. isFocused);
  356. }
  357. static void ChangeCategoryTreeViewItemGUI(
  358. Rect rowRect,
  359. ChangeCategoryTreeViewItem item,
  360. bool isSelected,
  361. bool isFocused)
  362. {
  363. string label = item.Category.CategoryName;
  364. string infoLabel = item.Category.GetChildrenCountText();
  365. DrawTreeViewItem.ForCategoryItem(
  366. rowRect,
  367. item.depth,
  368. label,
  369. infoLabel,
  370. isSelected,
  371. isFocused);
  372. }
  373. static void ClientDiffTreeViewItemGUI(
  374. Rect rowRect,
  375. float rowHeight,
  376. UnityDiffTree diffTree,
  377. ClientDiffTreeViewItem item,
  378. bool isSelected,
  379. bool isFocused,
  380. out float itemWidth)
  381. {
  382. string label = ClientDiffView.GetColumnText(
  383. item.Difference.DiffWithMount.Mount.RepSpec,
  384. item.Difference.DiffWithMount.Difference,
  385. PlasticLocalization.GetString(PlasticLocalization.Name.PathColumn));
  386. if (diffTree.HasMeta(item.Difference))
  387. label = string.Concat(label, UnityConstants.TREEVIEW_META_LABEL);
  388. Texture icon = GetClientDiffIcon(
  389. item.Difference.DiffWithMount.Difference.IsDirectory,
  390. label);
  391. itemWidth = CalculateItemWidth(label, icon, rowHeight);
  392. DrawTreeViewItem.ForItemCell(
  393. rowRect,
  394. rowHeight,
  395. item.depth,
  396. icon,
  397. null,
  398. label,
  399. isSelected,
  400. isFocused,
  401. false,
  402. false);
  403. }
  404. static float CalculateItemWidth(
  405. string label,
  406. Texture icon,
  407. float rowHeight)
  408. {
  409. GUIContent content = new GUIContent(label);
  410. Vector2 contentSize = TreeView.DefaultStyles.label.CalcSize(content);
  411. float iconWidth = rowHeight * ((float)icon.width / icon.height);
  412. return iconWidth + contentSize.x;
  413. }
  414. static Texture GetClientDiffIcon(bool isDirectory, string path)
  415. {
  416. if (isDirectory)
  417. return Images.GetDirectoryIcon();
  418. return Images.GetFileIconFromCmPath(path);
  419. }
  420. bool mExpandCategories;
  421. TreeViewItemIds<IDiffCategory, ITreeViewNode> mTreeViewItemIds =
  422. new TreeViewItemIds<IDiffCategory, ITreeViewNode>();
  423. List<TreeViewItem> mRows = new List<TreeViewItem>();
  424. UnityDiffTree mDiffTree = new UnityDiffTree();
  425. MultiColumnHeaderState.Column mHorizontalColumn;
  426. float mLargestRowWidth;
  427. readonly CooldownWindowDelayer mCooldownFilterAction;
  428. static readonly List<string> ColumnsNames = new List<string> {
  429. PlasticLocalization.GetString(PlasticLocalization.Name.PathColumn)};
  430. readonly DiffTreeViewMenu mMenu;
  431. }
  432. }