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; namespace Unity.PlasticSCM.Editor.Views.Diff { internal class DiffTreeView : TreeView { internal DiffTreeView(DiffTreeViewMenu menu) : base(new TreeViewState()) { mMenu = menu; customFoldoutYOffset = UnityConstants.TREEVIEW_FOLDOUT_Y_OFFSET; rowHeight = UnityConstants.TREEVIEW_ROW_HEIGHT; showAlternatingRowBackgrounds = false; mCooldownFilterAction = new CooldownWindowDelayer( DelayedSearchChanged, UnityConstants.SEARCH_DELAYED_INPUT_ACTION_INTERVAL); EnableHorizontalScrollbar(); } public override IList GetRows() { return mRows; } 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 TreeViewItem BuildRoot() { return new TreeViewItem(0, -1, string.Empty); } protected override IList 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) { mCooldownFilterAction.Ping(); } protected override void ContextClickedItem(int id) { mMenu.Popup(); Repaint(); } protected override void BeforeRowsGUI() { int firstRowVisible; int lastRowVisible; GetFirstAndLastVisibleRows(out firstRowVisible, out lastRowVisible); GUI.DrawTexture(new Rect(0, firstRowVisible * rowHeight, GetRowRect(0).width + 500, (lastRowVisible * rowHeight) + 1500), Images.GetTreeviewBackgroundTexture()); DrawTreeViewItem.InitializeStyles(); mLargestRowWidth = 0; base.BeforeRowsGUI(); } 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(); } internal void ClearModel() { mTreeViewItemIds.Clear(); mDiffTree = new UnityDiffTree(); } internal void BuildModel( WorkspaceInfo wkInfo, List 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 GetSelectedDiffs(bool includeMetaFiles) { List result = new List(); IList selectedIds = GetSelection(); if (selectedIds.Count == 0) return result; foreach (KeyValuePair 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 treeViewItemIds, TreeView treeView, TreeViewItem rootItem, List rows, bool expandCategories) { ClearRows(rootItem, rows); List 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 rows) { if (rootItem.hasChildren) rootItem.children.Clear(); rows.Clear(); } static void AddMergeCategory( TreeViewItem rootItem, IDiffCategory category, List rows, TreeViewItemIds 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 rows, TreeViewItemIds 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 rows, TreeViewItemIds 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.ForCategoryItem( 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.ForCategoryItem( 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.GetDirectoryIcon(); return Images.GetFileIconFromCmPath(path); } bool mExpandCategories; TreeViewItemIds mTreeViewItemIds = new TreeViewItemIds(); List mRows = new List(); UnityDiffTree mDiffTree = new UnityDiffTree(); MultiColumnHeaderState.Column mHorizontalColumn; float mLargestRowWidth; readonly CooldownWindowDelayer mCooldownFilterAction; static readonly List ColumnsNames = new List { PlasticLocalization.GetString(PlasticLocalization.Name.PathColumn)}; readonly DiffTreeViewMenu mMenu; } }