IncomingChangesTreeView.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  1. using System.Collections.Generic;
  2. using System.IO;
  3. using UnityEditor.IMGUI.Controls;
  4. using UnityEngine;
  5. using Codice.Client.BaseCommands.Merge;
  6. using Codice.Client.Common;
  7. using Codice.CM.Common;
  8. using PlasticGui.WorkspaceWindow.Merge;
  9. using Unity.PlasticSCM.Editor.UI;
  10. using Unity.PlasticSCM.Editor.UI.Tree;
  11. namespace Unity.PlasticSCM.Editor.Views.IncomingChanges.Developer
  12. {
  13. internal class IncomingChangesTreeView : TreeView
  14. {
  15. internal IncomingChangesTreeView(
  16. WorkspaceInfo wkInfo,
  17. IncomingChangesTreeHeaderState headerState,
  18. List<string> columnNames,
  19. IncomingChangesViewMenu menu)
  20. : base(new TreeViewState())
  21. {
  22. mWkInfo = wkInfo;
  23. mColumnNames = columnNames;
  24. mMenu = menu;
  25. multiColumnHeader = new MultiColumnHeader(headerState);
  26. multiColumnHeader.canSort = true;
  27. multiColumnHeader.sortingChanged += SortingChanged;
  28. customFoldoutYOffset = UnityConstants.TREEVIEW_FOLDOUT_Y_OFFSET;
  29. rowHeight = UnityConstants.TREEVIEW_ROW_HEIGHT;
  30. showAlternatingRowBackgrounds = false;
  31. }
  32. public override IList<TreeViewItem> GetRows()
  33. {
  34. return mRows;
  35. }
  36. protected override bool CanChangeExpandedState(TreeViewItem item)
  37. {
  38. return item is ChangeCategoryTreeViewItem;
  39. }
  40. protected override TreeViewItem BuildRoot()
  41. {
  42. return new TreeViewItem(0, -1, string.Empty);
  43. }
  44. protected override IList<TreeViewItem> BuildRows(TreeViewItem rootItem)
  45. {
  46. try
  47. {
  48. RegenerateRows(
  49. mIncomingChangesTree,
  50. mTreeViewItemIds,
  51. this,
  52. rootItem,
  53. mRows,
  54. mExpandCategories);
  55. }
  56. finally
  57. {
  58. mExpandCategories = false;
  59. }
  60. return mRows;
  61. }
  62. protected override void CommandEventHandling()
  63. {
  64. // NOTE - empty override to prevent crash when pressing ctrl-a in the treeview
  65. }
  66. protected override void ContextClickedItem(int id)
  67. {
  68. mMenu.Popup();
  69. Repaint();
  70. }
  71. protected override void RowGUI(RowGUIArgs args)
  72. {
  73. DrawTreeViewItem.InitializeStyles();
  74. if (args.item is ChangeCategoryTreeViewItem)
  75. {
  76. ChangeCategoryTreeViewItem categoryItem =
  77. (ChangeCategoryTreeViewItem)args.item;
  78. CategoryTreeViewItemGUI(
  79. args.rowRect, rowHeight,
  80. categoryItem,
  81. GetSolvedChildrenCount(categoryItem.Category, mSolvedFileConflicts),
  82. args.selected,
  83. args.focused);
  84. return;
  85. }
  86. if (args.item is ChangeTreeViewItem)
  87. {
  88. ChangeTreeViewItem changeTreeViewItem =
  89. (ChangeTreeViewItem)args.item;
  90. MergeChangeInfo changeInfo =
  91. changeTreeViewItem.ChangeInfo;
  92. bool isCurrentConflict = IsCurrent.Conflict(
  93. changeInfo,
  94. mIncomingChangesTree.GetMetaChange(changeInfo),
  95. mSolvedFileConflicts);
  96. bool isSolvedConflict = IsSolved.Conflict(
  97. changeInfo,
  98. mIncomingChangesTree.GetMetaChange(changeInfo),
  99. mSolvedFileConflicts);
  100. IncomingChangeTreeViewItemGUI(
  101. mWkInfo.ClientPath,
  102. mIncomingChangesTree,
  103. this,
  104. changeTreeViewItem,
  105. args,
  106. isCurrentConflict,
  107. isSolvedConflict);
  108. return;
  109. }
  110. base.RowGUI(args);
  111. }
  112. internal void SelectFirstUnsolvedDirectoryConflict()
  113. {
  114. foreach (MergeChangesCategory category in mIncomingChangesTree.GetNodes())
  115. {
  116. if (category.CategoryType != MergeChangesCategory.Type.DirectoryConflicts)
  117. continue;
  118. foreach (MergeChangeInfo changeInfo in category.GetChanges())
  119. {
  120. if (changeInfo.DirectoryConflict.IsResolved())
  121. continue;
  122. int itemId = -1;
  123. if (mTreeViewItemIds.TryGetInfoItemId(changeInfo, out itemId))
  124. {
  125. SetSelection(new List<int>() { itemId });
  126. return;
  127. }
  128. }
  129. }
  130. }
  131. internal void BuildModel(UnityIncomingChangesTree tree)
  132. {
  133. mTreeViewItemIds.Clear();
  134. mIncomingChangesTree = tree;
  135. mSolvedFileConflicts = null;
  136. mExpandCategories = true;
  137. }
  138. internal void Sort()
  139. {
  140. int sortedColumnIdx = multiColumnHeader.state.sortedColumnIndex;
  141. bool sortAscending = multiColumnHeader.IsSortedAscending(sortedColumnIdx);
  142. mIncomingChangesTree.Sort(mColumnNames[sortedColumnIdx], sortAscending);
  143. }
  144. internal void UpdateSolvedFileConflicts(
  145. MergeSolvedFileConflicts solvedFileConflicts)
  146. {
  147. mSolvedFileConflicts = solvedFileConflicts;
  148. }
  149. internal MergeChangeInfo GetMetaChange(MergeChangeInfo change)
  150. {
  151. if (change == null)
  152. return null;
  153. return mIncomingChangesTree.GetMetaChange(change);
  154. }
  155. internal void FillWithMeta(List<MergeChangeInfo> changes)
  156. {
  157. mIncomingChangesTree.FillWithMeta(changes);
  158. }
  159. internal bool SelectionHasMeta()
  160. {
  161. MergeChangeInfo selectedChangeInfo = GetSelectedIncomingChange();
  162. if (selectedChangeInfo == null)
  163. return false;
  164. return mIncomingChangesTree.HasMeta(selectedChangeInfo);
  165. }
  166. internal MergeChangeInfo GetSelectedIncomingChange()
  167. {
  168. IList<int> selectedIds = GetSelection();
  169. if (selectedIds.Count != 1)
  170. return null;
  171. int selectedId = selectedIds[0];
  172. foreach (KeyValuePair<MergeChangeInfo, int> item
  173. in mTreeViewItemIds.GetInfoItems())
  174. {
  175. if (selectedId == item.Value)
  176. return item.Key;
  177. }
  178. return null;
  179. }
  180. internal List<MergeChangeInfo> GetSelectedIncomingChanges()
  181. {
  182. List<MergeChangeInfo> result = new List<MergeChangeInfo>();
  183. IList<int> selectedIds = GetSelection();
  184. if (selectedIds.Count == 0)
  185. return result;
  186. foreach (KeyValuePair<MergeChangeInfo, int> item
  187. in mTreeViewItemIds.GetInfoItems())
  188. {
  189. if (!selectedIds.Contains(item.Value))
  190. continue;
  191. result.Add(item.Key);
  192. }
  193. return result;
  194. }
  195. internal List<MergeChangeInfo> GetSelectedFileConflicts()
  196. {
  197. List<MergeChangeInfo> result = new List<MergeChangeInfo>();
  198. IList<int> selectedIds = GetSelection();
  199. if (selectedIds.Count == 0)
  200. return result;
  201. foreach (KeyValuePair<MergeChangeInfo, int> item
  202. in mTreeViewItemIds.GetInfoItems())
  203. {
  204. if (!selectedIds.Contains(item.Value))
  205. continue;
  206. if (item.Key.CategoryType !=
  207. MergeChangesCategory.Type.FileConflicts)
  208. continue;
  209. result.Add(item.Key);
  210. }
  211. return result;
  212. }
  213. void SortingChanged(MultiColumnHeader multiColumnHeader)
  214. {
  215. Sort();
  216. Reload();
  217. }
  218. static void RegenerateRows(
  219. UnityIncomingChangesTree incomingChangesTree,
  220. TreeViewItemIds<MergeChangesCategory, MergeChangeInfo> treeViewItemIds,
  221. IncomingChangesTreeView treeView,
  222. TreeViewItem rootItem,
  223. List<TreeViewItem> rows,
  224. bool expandCategories)
  225. {
  226. if (incomingChangesTree == null)
  227. return;
  228. ClearRows(rootItem, rows);
  229. List<MergeChangesCategory> categories = incomingChangesTree.GetNodes();
  230. if (categories == null)
  231. return;
  232. List<int> categoriesToExpand = new List<int>();
  233. foreach (MergeChangesCategory category in categories)
  234. {
  235. int categoryId;
  236. if (!treeViewItemIds.TryGetCategoryItemId(category, out categoryId))
  237. categoryId = treeViewItemIds.AddCategoryItem(category);
  238. ChangeCategoryTreeViewItem categoryTreeViewItem =
  239. new ChangeCategoryTreeViewItem(categoryId, category);
  240. rootItem.AddChild(categoryTreeViewItem);
  241. rows.Add(categoryTreeViewItem);
  242. if (!ShouldExpandCategory(
  243. treeView,
  244. categoryTreeViewItem,
  245. expandCategories,
  246. categories.Count))
  247. continue;
  248. categoriesToExpand.Add(categoryTreeViewItem.id);
  249. foreach (MergeChangeInfo changeInfo in category.GetChanges())
  250. {
  251. int differenceId;
  252. if (!treeViewItemIds.TryGetInfoItemId(changeInfo, out differenceId))
  253. differenceId = treeViewItemIds.AddInfoItem(changeInfo);
  254. TreeViewItem changeTreeViewItem =
  255. new ChangeTreeViewItem(differenceId, changeInfo);
  256. categoryTreeViewItem.AddChild(changeTreeViewItem);
  257. rows.Add(changeTreeViewItem);
  258. }
  259. }
  260. treeView.state.expandedIDs = categoriesToExpand;
  261. }
  262. static void ClearRows(
  263. TreeViewItem rootItem,
  264. List<TreeViewItem> rows)
  265. {
  266. if (rootItem.hasChildren)
  267. rootItem.children.Clear();
  268. rows.Clear();
  269. }
  270. static void CategoryTreeViewItemGUI(
  271. Rect rowRect,
  272. float rowHeight,
  273. ChangeCategoryTreeViewItem item,
  274. int solvedChildrenCount,
  275. bool isSelected,
  276. bool isFocused)
  277. {
  278. Texture icon = GetCategoryIcon(item.Category.CategoryType);
  279. string label = item.Category.GetHeaderText();
  280. DefaultStyles.label = GetCategoryStyle(
  281. item.Category,
  282. solvedChildrenCount,
  283. isSelected);
  284. DrawTreeViewItem.ForCategoryItem(
  285. rowRect,
  286. rowHeight,
  287. item.depth,
  288. icon,
  289. label,
  290. isSelected,
  291. isFocused);
  292. DefaultStyles.label = UnityStyles.Tree.Label;
  293. }
  294. static void IncomingChangeTreeViewItemGUI(
  295. string wkPath,
  296. UnityIncomingChangesTree incomingChangesTree,
  297. IncomingChangesTreeView treeView,
  298. ChangeTreeViewItem item,
  299. RowGUIArgs args,
  300. bool isCurrentConflict,
  301. bool isSolvedConflict)
  302. {
  303. for (int visibleColumnIdx = 0; visibleColumnIdx < args.GetNumVisibleColumns(); visibleColumnIdx++)
  304. {
  305. Rect cellRect = args.GetCellRect(visibleColumnIdx);
  306. IncomingChangesTreeColumn column =
  307. (IncomingChangesTreeColumn)args.GetColumn(visibleColumnIdx);
  308. IncomingChangeTreeViewItemCellGUI(
  309. wkPath,
  310. cellRect,
  311. treeView.rowHeight,
  312. incomingChangesTree,
  313. treeView,
  314. item,
  315. column,
  316. args.selected,
  317. args.focused,
  318. isCurrentConflict,
  319. isSolvedConflict);
  320. }
  321. }
  322. static void IncomingChangeTreeViewItemCellGUI(
  323. string wkPath,
  324. Rect rect,
  325. float rowHeight,
  326. UnityIncomingChangesTree incomingChangesTree,
  327. IncomingChangesTreeView treeView,
  328. ChangeTreeViewItem item,
  329. IncomingChangesTreeColumn column,
  330. bool isSelected,
  331. bool isFocused,
  332. bool isCurrentConflict,
  333. bool isSolvedConflict)
  334. {
  335. MergeChangeInfo incomingChange = item.ChangeInfo;
  336. string label = incomingChange.GetColumnText(
  337. IncomingChangesTreeHeaderState.GetColumnName(column));
  338. if (column == IncomingChangesTreeColumn.Path)
  339. {
  340. if (incomingChangesTree.HasMeta(item.ChangeInfo))
  341. label = string.Concat(label, UnityConstants.TREEVIEW_META_LABEL);
  342. Texture icon = GetIcon(wkPath, incomingChange);
  343. GetChangesOverlayIcon.Data overlayIconData =
  344. GetChangesOverlayIcon.ForPlasticIncomingChange(
  345. incomingChange, isSolvedConflict);
  346. DrawTreeViewItem.ForItemCell(
  347. rect,
  348. rowHeight,
  349. item.depth,
  350. icon,
  351. overlayIconData,
  352. label,
  353. isSelected,
  354. isFocused,
  355. isCurrentConflict,
  356. false);
  357. return;
  358. }
  359. if (column == IncomingChangesTreeColumn.Size)
  360. {
  361. DrawTreeViewItem.ForSecondaryLabelRightAligned(
  362. rect, label, isSelected, isFocused, isCurrentConflict);
  363. return;
  364. }
  365. DrawTreeViewItem.ForSecondaryLabel(
  366. rect, label, isSelected, isFocused, isCurrentConflict);
  367. }
  368. static Texture GetCategoryIcon(
  369. MergeChangesCategory.Type categoryType)
  370. {
  371. switch (categoryType)
  372. {
  373. case MergeChangesCategory.Type.DirectoryConflicts:
  374. case MergeChangesCategory.Type.FileConflicts:
  375. return Images.GetImage(Images.Name.IconMergeConflict);
  376. case MergeChangesCategory.Type.Changed:
  377. return Images.GetImage(Images.Name.IconChanged);
  378. case MergeChangesCategory.Type.Moved:
  379. return Images.GetImage(Images.Name.IconMoved);
  380. case MergeChangesCategory.Type.Deleted:
  381. return Images.GetImage(Images.Name.IconDeleted);
  382. case MergeChangesCategory.Type.Added:
  383. return Images.GetImage(Images.Name.IconAdded);
  384. default:
  385. return null;
  386. }
  387. }
  388. static Texture GetIcon(
  389. string wkPath,
  390. MergeChangeInfo incomingChange)
  391. {
  392. RevisionInfo revInfo = incomingChange.GetRevision();
  393. bool isDirectory = revInfo.
  394. Type == EnumRevisionType.enDirectory;
  395. if (isDirectory || incomingChange.IsXLink())
  396. return Images.GetDirectoryIcon();
  397. string fullPath = WorkspacePath.GetWorkspacePathFromCmPath(
  398. wkPath,
  399. incomingChange.GetPath(),
  400. Path.DirectorySeparatorChar);
  401. return Images.GetFileIcon(fullPath);
  402. }
  403. static GUIStyle GetCategoryStyle(
  404. MergeChangesCategory category,
  405. int solvedChildrenCount,
  406. bool isSelected)
  407. {
  408. if (isSelected)
  409. return UnityStyles.Tree.Label;
  410. if (category.CategoryType == MergeChangesCategory.Type.FileConflicts ||
  411. category.CategoryType == MergeChangesCategory.Type.DirectoryConflicts)
  412. {
  413. return category.GetChildrenCount() > solvedChildrenCount ?
  414. UnityStyles.Tree.RedLabel : UnityStyles.Tree.GreenLabel;
  415. }
  416. return UnityStyles.Tree.Label;
  417. }
  418. static bool ShouldExpandCategory(
  419. IncomingChangesTreeView treeView,
  420. ChangeCategoryTreeViewItem categoryTreeViewItem,
  421. bool expandCategories,
  422. int categoriesCount)
  423. {
  424. if (expandCategories)
  425. {
  426. if (categoriesCount == 1)
  427. return true;
  428. if (categoryTreeViewItem.Category.CategoryType ==
  429. MergeChangesCategory.Type.FileConflicts)
  430. return true;
  431. if (categoryTreeViewItem.Category.GetChildrenCount() >
  432. NODES_TO_EXPAND_CATEGORY)
  433. return false;
  434. return true;
  435. }
  436. return treeView.IsExpanded(categoryTreeViewItem.id);
  437. }
  438. static int GetSolvedChildrenCount(
  439. MergeChangesCategory category,
  440. MergeSolvedFileConflicts solvedFileConflicts)
  441. {
  442. int solvedDirConflicts = 0;
  443. if (category.CategoryType == MergeChangesCategory.Type.DirectoryConflicts)
  444. {
  445. foreach (MergeChangeInfo change in category.GetChanges())
  446. {
  447. if (change.DirectoryConflict.IsResolved())
  448. solvedDirConflicts++;
  449. }
  450. return solvedDirConflicts;
  451. }
  452. return (solvedFileConflicts == null) ? 0 :
  453. solvedFileConflicts.GetCount();
  454. }
  455. bool mExpandCategories;
  456. TreeViewItemIds<MergeChangesCategory, MergeChangeInfo> mTreeViewItemIds =
  457. new TreeViewItemIds<MergeChangesCategory, MergeChangeInfo>();
  458. List<TreeViewItem> mRows = new List<TreeViewItem>();
  459. MergeSolvedFileConflicts mSolvedFileConflicts;
  460. UnityIncomingChangesTree mIncomingChangesTree;
  461. readonly IncomingChangesViewMenu mMenu;
  462. readonly List<string> mColumnNames;
  463. readonly WorkspaceInfo mWkInfo;
  464. const int NODES_TO_EXPAND_CATEGORY = 10;
  465. }
  466. }