DiffTreeView.cs 15 KB

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