549 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			549 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|  | using System; | |||
|  | using System.Collections.Generic; | |||
|  | 
 | |||
|  | using UnityEditor.IMGUI.Controls; | |||
|  | using UnityEngine; | |||
|  | 
 | |||
|  | using Codice.Client.Commands; | |||
|  | using Codice.Client.Common; | |||
|  | using Codice.CM.Common; | |||
|  | using Codice.Utils; | |||
|  | using PlasticGui; | |||
|  | using PlasticGui.WorkspaceWindow.Diff; | |||
|  | using Unity.PlasticSCM.Editor.UI; | |||
|  | using Unity.PlasticSCM.Editor.UI.Tree; | |||
|  | #if UNITY_6000_2_OR_NEWER | |||
|  | using TreeView = UnityEditor.IMGUI.Controls.TreeView<int>; | |||
|  | using TreeViewItem = UnityEditor.IMGUI.Controls.TreeViewItem<int>; | |||
|  | #endif | |||
|  | 
 | |||
|  | namespace Unity.PlasticSCM.Editor.Views.Diff | |||
|  | { | |||
|  |     internal class DiffTreeView : PlasticTreeView | |||
|  |     { | |||
|  |         internal DiffTreeView( | |||
|  |             DiffTreeViewMenu menu, | |||
|  |             Action doubleClickAction) | |||
|  |         { | |||
|  |             mMenu = menu; | |||
|  |             mDoubleClickAction = doubleClickAction; | |||
|  | 
 | |||
|  |             customFoldoutYOffset = UnityConstants.TREEVIEW_FOLDOUT_Y_OFFSET; | |||
|  | 
 | |||
|  |             mDelayedFilterAction = new DelayedActionBySecondsRunner( | |||
|  |                 DelayedSearchChanged, UnityConstants.SEARCH_DELAYED_INPUT_ACTION_INTERVAL); | |||
|  | 
 | |||
|  |             EnableHorizontalScrollbar(); | |||
|  |         } | |||
|  | 
 | |||
|  |         public override void OnGUI(Rect rect) | |||
|  |         { | |||
|  |             base.OnGUI(rect); | |||
|  | 
 | |||
|  |             Event e = Event.current; | |||
|  | 
 | |||
|  |             if (e.type != EventType.KeyDown) | |||
|  |                 return; | |||
|  | 
 | |||
|  |             bool isProcessed = mMenu.ProcessKeyActionIfNeeded(e); | |||
|  | 
 | |||
|  |             if (isProcessed) | |||
|  |                 e.Use(); | |||
|  |         } | |||
|  | 
 | |||
|  |         protected override bool CanChangeExpandedState(TreeViewItem item) | |||
|  |         { | |||
|  |             return item is ChangeCategoryTreeViewItem | |||
|  |                 || item is MergeCategoryTreeViewItem; | |||
|  |         } | |||
|  | 
 | |||
|  |         protected override IList<TreeViewItem> BuildRows(TreeViewItem rootItem) | |||
|  |         { | |||
|  |             try | |||
|  |             { | |||
|  |                 RegenerateRows( | |||
|  |                     mDiffTree, | |||
|  |                     mTreeViewItemIds, | |||
|  |                     this, | |||
|  |                     rootItem, | |||
|  |                     mRows, | |||
|  |                     mExpandCategories); | |||
|  |             } | |||
|  |             finally | |||
|  |             { | |||
|  |                 mExpandCategories = false; | |||
|  |             } | |||
|  | 
 | |||
|  |             return mRows; | |||
|  |         } | |||
|  | 
 | |||
|  |         protected override void CommandEventHandling() | |||
|  |         { | |||
|  |             // NOTE - empty override to prevent crash when pressing ctrl-a in the treeview | |||
|  |         } | |||
|  | 
 | |||
|  |         protected override void SearchChanged(string newSearch) | |||
|  |         { | |||
|  |             mDelayedFilterAction.Run(); | |||
|  |         } | |||
|  | 
 | |||
|  |         protected override void ContextClickedItem(int id) | |||
|  |         { | |||
|  |             mMenu.Popup(); | |||
|  |             Repaint(); | |||
|  |         } | |||
|  | 
 | |||
|  |         protected override void RowGUI(RowGUIArgs args) | |||
|  |         { | |||
|  |             float itemWidth; | |||
|  |             TreeViewItemGUI( | |||
|  |                 args.item, args.rowRect, rowHeight, mDiffTree, args.selected, args.focused, out itemWidth); | |||
|  | 
 | |||
|  |             float rowWidth = baseIndent + args.item.depth * depthIndentWidth + | |||
|  |                 itemWidth + UnityConstants.TREEVIEW_ROW_WIDTH_OFFSET; | |||
|  | 
 | |||
|  |             if (rowWidth > mLargestRowWidth) | |||
|  |                 mLargestRowWidth = rowWidth; | |||
|  |         } | |||
|  | 
 | |||
|  |         protected override void AfterRowsGUI() | |||
|  |         { | |||
|  |             if (mHorizontalColumn != null) | |||
|  |                 mHorizontalColumn.width = mLargestRowWidth; | |||
|  | 
 | |||
|  |             base.AfterRowsGUI(); | |||
|  |         } | |||
|  | 
 | |||
|  |         protected override void DoubleClickedItem(int id) | |||
|  |         { | |||
|  |             mDoubleClickAction(); | |||
|  |         } | |||
|  | 
 | |||
|  |         internal void ClearModel() | |||
|  |         { | |||
|  |             mTreeViewItemIds.Clear(); | |||
|  | 
 | |||
|  |             mDiffTree = new UnityDiffTree(); | |||
|  |         } | |||
|  | 
 | |||
|  |         internal void BuildModel( | |||
|  |             WorkspaceInfo wkInfo, | |||
|  |             List<ClientDiff> diffs, | |||
|  |             BranchResolver brResolver, | |||
|  |             bool skipMergeTracking) | |||
|  |         { | |||
|  |             mTreeViewItemIds.Clear(); | |||
|  | 
 | |||
|  |             mDiffTree.BuildCategories(wkInfo, diffs, brResolver, skipMergeTracking); | |||
|  |         } | |||
|  | 
 | |||
|  |         internal void Refilter() | |||
|  |         { | |||
|  |             Filter filter = new Filter(searchString); | |||
|  |             mDiffTree.Filter(filter, ColumnsNames); | |||
|  | 
 | |||
|  |             mExpandCategories = true; | |||
|  |         } | |||
|  | 
 | |||
|  |         internal void Sort() | |||
|  |         { | |||
|  |             mDiffTree.Sort( | |||
|  |                 PlasticLocalization.GetString(PlasticLocalization.Name.PathColumn), | |||
|  |                 sortAscending: true); | |||
|  |         } | |||
|  | 
 | |||
|  |         internal ClientDiffInfo GetMetaDiff(ClientDiffInfo diff) | |||
|  |         { | |||
|  |             return mDiffTree.GetMetaDiff(diff); | |||
|  |         } | |||
|  | 
 | |||
|  |         internal bool SelectionHasMeta() | |||
|  |         { | |||
|  |             if (!HasSelection()) | |||
|  |                 return false; | |||
|  | 
 | |||
|  |             ClientDiffInfo selectedDiff = GetSelectedDiffs(false)[0]; | |||
|  | 
 | |||
|  |             if (selectedDiff == null) | |||
|  |                 return false; | |||
|  | 
 | |||
|  |             return mDiffTree.HasMeta(selectedDiff); | |||
|  |         } | |||
|  | 
 | |||
|  |         internal List<ClientDiffInfo> GetSelectedDiffs(bool includeMetaFiles) | |||
|  |         { | |||
|  |             List<ClientDiffInfo> result = new List<ClientDiffInfo>(); | |||
|  | 
 | |||
|  |             IList<int> selectedIds = GetSelection(); | |||
|  | 
 | |||
|  |             if (selectedIds.Count == 0) | |||
|  |                 return result; | |||
|  | 
 | |||
|  |             foreach (KeyValuePair<ITreeViewNode, int> item | |||
|  |                 in mTreeViewItemIds.GetInfoItems()) | |||
|  |             { | |||
|  |                 if (!selectedIds.Contains(item.Value)) | |||
|  |                     continue; | |||
|  | 
 | |||
|  |                 if (!(item.Key is ClientDiffInfo)) | |||
|  |                     continue; | |||
|  | 
 | |||
|  |                 result.Add((ClientDiffInfo)item.Key); | |||
|  |             } | |||
|  | 
 | |||
|  |             if (includeMetaFiles) | |||
|  |                 mDiffTree.FillWithMeta(result); | |||
|  | 
 | |||
|  |             return result; | |||
|  |         } | |||
|  | 
 | |||
|  |         void DelayedSearchChanged() | |||
|  |         { | |||
|  |             Refilter(); | |||
|  | 
 | |||
|  |             Sort(); | |||
|  | 
 | |||
|  |             Reload(); | |||
|  | 
 | |||
|  |             TableViewOperations.ScrollToSelection(this); | |||
|  |         } | |||
|  | 
 | |||
|  |         void EnableHorizontalScrollbar() | |||
|  |         { | |||
|  |             mHorizontalColumn = new MultiColumnHeaderState.Column(); | |||
|  |             mHorizontalColumn.autoResize = false; | |||
|  | 
 | |||
|  |             MultiColumnHeaderState.Column[] cols = { mHorizontalColumn }; | |||
|  |             MultiColumnHeaderState headerState = new MultiColumnHeaderState(cols); | |||
|  | 
 | |||
|  |             multiColumnHeader = new MultiColumnHeader(headerState); | |||
|  |             multiColumnHeader.height = 0f; | |||
|  |         } | |||
|  | 
 | |||
|  |         static void RegenerateRows( | |||
|  |             UnityDiffTree diffTree, | |||
|  |             TreeViewItemIds<IDiffCategory, ITreeViewNode> treeViewItemIds, | |||
|  |             TreeView treeView, | |||
|  |             TreeViewItem rootItem, | |||
|  |             List<TreeViewItem> rows, | |||
|  |             bool expandCategories) | |||
|  |         { | |||
|  |             ClearRows(rootItem, rows); | |||
|  | 
 | |||
|  |             List<IDiffCategory> categories = diffTree.GetNodes(); | |||
|  | 
 | |||
|  |             if (categories == null) | |||
|  |                 return; | |||
|  | 
 | |||
|  |             foreach (IDiffCategory category in categories) | |||
|  |             { | |||
|  |                 if (category is CategoryGroup && | |||
|  |                     ((CategoryGroup)category).CategoryType == CategoryGroup.Type.MergeCategory) | |||
|  |                 { | |||
|  |                     AddMergeCategory( | |||
|  |                         rootItem, | |||
|  |                         category, | |||
|  |                         rows, | |||
|  |                         treeViewItemIds, | |||
|  |                         treeView, | |||
|  |                         expandCategories); | |||
|  |                 } | |||
|  | 
 | |||
|  |                 if (category is ChangeCategory) | |||
|  |                 { | |||
|  |                     AddChangeCategory( | |||
|  |                         rootItem, | |||
|  |                         category, | |||
|  |                         rows, | |||
|  |                         treeViewItemIds, | |||
|  |                         treeView, | |||
|  |                         expandCategories); | |||
|  |                 } | |||
|  |             } | |||
|  | 
 | |||
|  |             if (!expandCategories) | |||
|  |                 return; | |||
|  | 
 | |||
|  |             treeView.state.expandedIDs = treeViewItemIds.GetCategoryIds(); | |||
|  |         } | |||
|  | 
 | |||
|  |         static void ClearRows( | |||
|  |             TreeViewItem rootItem, | |||
|  |             List<TreeViewItem> rows) | |||
|  |         { | |||
|  |             if (rootItem.hasChildren) | |||
|  |                 rootItem.children.Clear(); | |||
|  | 
 | |||
|  |             rows.Clear(); | |||
|  |         } | |||
|  | 
 | |||
|  |         static void AddMergeCategory( | |||
|  |             TreeViewItem rootItem, | |||
|  |             IDiffCategory category, | |||
|  |             List<TreeViewItem> rows, | |||
|  |             TreeViewItemIds<IDiffCategory, ITreeViewNode> treeViewItemIds, | |||
|  |             TreeView treeView, | |||
|  |             bool expandCategories) | |||
|  |         { | |||
|  |             int categoryId; | |||
|  |             if (!treeViewItemIds.TryGetCategoryItemId(category, out categoryId)) | |||
|  |                 categoryId = treeViewItemIds.AddCategoryItem(category); | |||
|  | 
 | |||
|  |             MergeCategoryTreeViewItem mergeCategoryTreeViewItem = | |||
|  |                 new MergeCategoryTreeViewItem( | |||
|  |                     categoryId, | |||
|  |                     rootItem.depth + 1, | |||
|  |                     (CategoryGroup)category); | |||
|  | 
 | |||
|  |             rootItem.AddChild(mergeCategoryTreeViewItem); | |||
|  |             rows.Add(mergeCategoryTreeViewItem); | |||
|  | 
 | |||
|  |             if (!expandCategories && | |||
|  |                 !treeView.IsExpanded(mergeCategoryTreeViewItem.id)) | |||
|  |                 return; | |||
|  | 
 | |||
|  |             for (int i = 0; i < category.GetChildrenCount(); i++) | |||
|  |             { | |||
|  |                 IDiffCategory child = (IDiffCategory)((ITreeViewNode)category) | |||
|  |                     .GetChild(i); | |||
|  | 
 | |||
|  |                 AddChangeCategory( | |||
|  |                     mergeCategoryTreeViewItem, | |||
|  |                     child, | |||
|  |                     rows, | |||
|  |                     treeViewItemIds, | |||
|  |                     treeView, | |||
|  |                     expandCategories); | |||
|  |             } | |||
|  |         } | |||
|  | 
 | |||
|  |         static void AddChangeCategory( | |||
|  |             TreeViewItem parentItem, | |||
|  |             IDiffCategory category, | |||
|  |             List<TreeViewItem> rows, | |||
|  |             TreeViewItemIds<IDiffCategory, ITreeViewNode> treeViewItemIds, | |||
|  |             TreeView treeView, | |||
|  |             bool expandCategories) | |||
|  |         { | |||
|  |             int categoryId; | |||
|  |             if (!treeViewItemIds.TryGetCategoryItemId(category, out categoryId)) | |||
|  |                 categoryId = treeViewItemIds.AddCategoryItem(category); | |||
|  | 
 | |||
|  |             ChangeCategoryTreeViewItem changeCategoryTreeViewItem = | |||
|  |                 new ChangeCategoryTreeViewItem( | |||
|  |                     categoryId, | |||
|  |                     parentItem.depth + 1, | |||
|  |                     (ChangeCategory)category); | |||
|  | 
 | |||
|  |             parentItem.AddChild(changeCategoryTreeViewItem); | |||
|  |             rows.Add(changeCategoryTreeViewItem); | |||
|  | 
 | |||
|  |             if (!expandCategories && | |||
|  |                 !treeView.IsExpanded(changeCategoryTreeViewItem.id)) | |||
|  |                 return; | |||
|  | 
 | |||
|  |             AddClientDiffs( | |||
|  |                 changeCategoryTreeViewItem, | |||
|  |                 (ITreeViewNode)category, | |||
|  |                 rows, | |||
|  |                 treeViewItemIds); | |||
|  |         } | |||
|  | 
 | |||
|  |         static void AddClientDiffs( | |||
|  |             TreeViewItem parentItem, | |||
|  |             ITreeViewNode parentNode, | |||
|  |             List<TreeViewItem> rows, | |||
|  |             TreeViewItemIds<IDiffCategory, ITreeViewNode> treeViewItemIds) | |||
|  |         { | |||
|  |             for (int i = 0; i < parentNode.GetChildrenCount(); i++) | |||
|  |             { | |||
|  |                 ITreeViewNode child = parentNode.GetChild(i); | |||
|  | 
 | |||
|  |                 int nodeId; | |||
|  |                 if (!treeViewItemIds.TryGetInfoItemId(child, out nodeId)) | |||
|  |                     nodeId = treeViewItemIds.AddInfoItem(child); | |||
|  | 
 | |||
|  |                 TreeViewItem changeTreeViewItem = | |||
|  |                     new ClientDiffTreeViewItem( | |||
|  |                         nodeId, | |||
|  |                         parentItem.depth + 1, | |||
|  |                         (ClientDiffInfo)child); | |||
|  | 
 | |||
|  |                 parentItem.AddChild(changeTreeViewItem); | |||
|  |                 rows.Add(changeTreeViewItem); | |||
|  |             } | |||
|  |         } | |||
|  | 
 | |||
|  |         static void TreeViewItemGUI( | |||
|  |             TreeViewItem item, | |||
|  |             Rect rowRect, | |||
|  |             float rowHeight, | |||
|  |             UnityDiffTree diffTree, | |||
|  |             bool isSelected, | |||
|  |             bool isFocused, | |||
|  |             out float itemWidth) | |||
|  |         { | |||
|  |             if (item is MergeCategoryTreeViewItem) | |||
|  |             { | |||
|  |                 MergeCategoryTreeViewItemGUI( | |||
|  |                     rowRect, | |||
|  |                     (MergeCategoryTreeViewItem)item, | |||
|  |                     isSelected, | |||
|  |                     isFocused, | |||
|  |                     out itemWidth); | |||
|  |                 return; | |||
|  |             } | |||
|  | 
 | |||
|  |             if (item is ChangeCategoryTreeViewItem) | |||
|  |             { | |||
|  |                 ChangeCategoryTreeViewItemGUI( | |||
|  |                     rowRect, | |||
|  |                     (ChangeCategoryTreeViewItem)item, | |||
|  |                     isSelected, | |||
|  |                     isFocused, | |||
|  |                     out itemWidth); | |||
|  |                 return; | |||
|  |             } | |||
|  | 
 | |||
|  |             if (item is ClientDiffTreeViewItem) | |||
|  |             { | |||
|  |                 ClientDiffTreeViewItemGUI( | |||
|  |                     rowRect, | |||
|  |                     rowHeight, | |||
|  |                     diffTree, | |||
|  |                     (ClientDiffTreeViewItem)item, | |||
|  |                     isSelected, | |||
|  |                     isFocused, | |||
|  |                     out itemWidth); | |||
|  |                 return; | |||
|  |             } | |||
|  | 
 | |||
|  |             itemWidth = 0; | |||
|  |         } | |||
|  | 
 | |||
|  |         static void MergeCategoryTreeViewItemGUI( | |||
|  |             Rect rowRect, | |||
|  |             MergeCategoryTreeViewItem item, | |||
|  |             bool isSelected, | |||
|  |             bool isFocused, | |||
|  |             out float itemWidth) | |||
|  |         { | |||
|  |             string label = item.Category.CategoryName; | |||
|  |             string infoLabel = PlasticLocalization.Name.ItemsCount.GetString( | |||
|  |                 item.Category.GetChildrenCount()); | |||
|  | 
 | |||
|  |             itemWidth = CalculateLabelWidth(label); | |||
|  | 
 | |||
|  |             DrawTreeViewItem.ForIndentedItem( | |||
|  |                 rowRect, | |||
|  |                 item.depth, | |||
|  |                 label, | |||
|  |                 infoLabel, | |||
|  |                 isSelected, | |||
|  |                 isFocused); | |||
|  |         } | |||
|  | 
 | |||
|  |         static void ChangeCategoryTreeViewItemGUI( | |||
|  |             Rect rowRect, | |||
|  |             ChangeCategoryTreeViewItem item, | |||
|  |             bool isSelected, | |||
|  |             bool isFocused, | |||
|  |             out float itemWidth) | |||
|  |         { | |||
|  |             string label = item.Category.CategoryName; | |||
|  |             string infoLabel = PlasticLocalization.Name.ItemsCount.GetString( | |||
|  |                 item.Category.GetChildrenCount()); | |||
|  | 
 | |||
|  |             itemWidth = CalculateLabelWidth(label); | |||
|  | 
 | |||
|  |             DrawTreeViewItem.ForIndentedItem( | |||
|  |                 rowRect, | |||
|  |                 item.depth, | |||
|  |                 label, | |||
|  |                 infoLabel, | |||
|  |                 isSelected, | |||
|  |                 isFocused); | |||
|  |         } | |||
|  | 
 | |||
|  |         static void ClientDiffTreeViewItemGUI( | |||
|  |             Rect rowRect, | |||
|  |             float rowHeight, | |||
|  |             UnityDiffTree diffTree, | |||
|  |             ClientDiffTreeViewItem item, | |||
|  |             bool isSelected, | |||
|  |             bool isFocused, | |||
|  |             out float itemWidth) | |||
|  |         { | |||
|  |             string label = ClientDiffView.GetColumnText( | |||
|  |                 item.Difference.DiffWithMount.Mount.RepSpec, | |||
|  |                 item.Difference.DiffWithMount.Difference, | |||
|  |                 PlasticLocalization.GetString(PlasticLocalization.Name.PathColumn)); | |||
|  | 
 | |||
|  |             if (diffTree.HasMeta(item.Difference)) | |||
|  |                 label = string.Concat(label, UnityConstants.TREEVIEW_META_LABEL); | |||
|  | 
 | |||
|  |             Texture icon = GetClientDiffIcon( | |||
|  |                 item.Difference.DiffWithMount.Difference.IsDirectory, | |||
|  |                 label); | |||
|  | 
 | |||
|  |             itemWidth = CalculateItemWidth(label, icon, rowHeight); | |||
|  | 
 | |||
|  |             DrawTreeViewItem.ForItemCell( | |||
|  |                 rowRect, | |||
|  |                 rowHeight, | |||
|  |                 item.depth, | |||
|  |                 icon, | |||
|  |                 null, | |||
|  |                 label, | |||
|  |                 isSelected, | |||
|  |                 isFocused, | |||
|  |                 false, | |||
|  |                 false); | |||
|  |         } | |||
|  | 
 | |||
|  |         static float CalculateItemWidth( | |||
|  |             string label, | |||
|  |             Texture icon, | |||
|  |             float rowHeight) | |||
|  |         { | |||
|  |             float labelWidth = CalculateLabelWidth(label); | |||
|  |             float iconWidth = rowHeight * ((float)icon.width / icon.height); | |||
|  | 
 | |||
|  |             return labelWidth + iconWidth; | |||
|  |         } | |||
|  | 
 | |||
|  |         static float CalculateLabelWidth(string label) | |||
|  |         { | |||
|  |             GUIContent content = new GUIContent(label); | |||
|  |             Vector2 contentSize = DefaultStyles.label.CalcSize(content); | |||
|  | 
 | |||
|  |             return contentSize.x; | |||
|  |         } | |||
|  | 
 | |||
|  |         static Texture GetClientDiffIcon(bool isDirectory, string path) | |||
|  |         { | |||
|  |             if (isDirectory) | |||
|  |                 return Images.GetFolderIcon(); | |||
|  | 
 | |||
|  |             return Images.GetFileIconFromCmPath(path); | |||
|  |         } | |||
|  | 
 | |||
|  |         bool mExpandCategories; | |||
|  | 
 | |||
|  |         TreeViewItemIds<IDiffCategory, ITreeViewNode> mTreeViewItemIds = | |||
|  |             new TreeViewItemIds<IDiffCategory, ITreeViewNode>(); | |||
|  | 
 | |||
|  |         UnityDiffTree mDiffTree = new UnityDiffTree(); | |||
|  | 
 | |||
|  |         MultiColumnHeaderState.Column mHorizontalColumn; | |||
|  |         float mLargestRowWidth; | |||
|  | 
 | |||
|  |         readonly DelayedActionBySecondsRunner mDelayedFilterAction; | |||
|  | 
 | |||
|  |         static readonly List<string> ColumnsNames = new List<string> { | |||
|  |             PlasticLocalization.GetString(PlasticLocalization.Name.PathColumn)}; | |||
|  |         readonly DiffTreeViewMenu mMenu; | |||
|  |         readonly Action mDoubleClickAction; | |||
|  |     } | |||
|  | } |