A game about forced loneliness, made by TACStudios
1using System; 2using System.Collections.Generic; 3using System.IO; 4 5using UnityEditor.IMGUI.Controls; 6using UnityEngine; 7 8using Codice.Client.BaseCommands.Merge; 9using Codice.Client.Common; 10using Codice.CM.Common; 11using PlasticGui; 12using PlasticGui.WorkspaceWindow.Merge; 13using Unity.PlasticSCM.Editor.UI; 14using Unity.PlasticSCM.Editor.UI.Avatar; 15using Unity.PlasticSCM.Editor.UI.Tree; 16using UnityEditor; 17 18namespace Unity.PlasticSCM.Editor.Views.Merge.Developer 19{ 20 internal class MergeTreeView : PlasticTreeView 21 { 22 internal GenericMenu Menu { get { return mMenu.Menu; } } 23 24 internal MergeTreeView( 25 WorkspaceInfo wkInfo, 26 MergeTreeHeaderState headerState, 27 List<string> columnNames, 28 MergeViewMenu menu) 29 { 30 mWkInfo = wkInfo; 31 mColumnNames = columnNames; 32 mMenu = menu; 33 34 multiColumnHeader = new MultiColumnHeader(headerState); 35 multiColumnHeader.canSort = true; 36 multiColumnHeader.sortingChanged += SortingChanged; 37 38 customFoldoutYOffset = UnityConstants.TREEVIEW_FOLDOUT_Y_OFFSET; 39 40 mCooldownFilterAction = new CooldownWindowDelayer( 41 DelayedSearchChanged, UnityConstants.SEARCH_DELAYED_INPUT_ACTION_INTERVAL); 42 } 43 44 protected override bool CanChangeExpandedState(TreeViewItem item) 45 { 46 return item is ChangeCategoryTreeViewItem; 47 } 48 49 protected override IList<TreeViewItem> BuildRows(TreeViewItem rootItem) 50 { 51 try 52 { 53 RegenerateRows( 54 mMergeTree, 55 mTreeViewItemIds, 56 this, 57 rootItem, 58 mRows, 59 mExpandCategories); 60 } 61 finally 62 { 63 mExpandCategories = false; 64 } 65 66 return mRows; 67 } 68 69 protected override void CommandEventHandling() 70 { 71 // NOTE - empty override to prevent crash when pressing ctrl-a in the treeview 72 } 73 74 protected override void SearchChanged(string newSearch) 75 { 76 mCooldownFilterAction.Ping(); 77 } 78 79 protected override void ContextClickedItem(int id) 80 { 81 mMenu.Popup(); 82 Repaint(); 83 } 84 85 public override void OnGUI(Rect rect) 86 { 87 base.OnGUI(rect); 88 89 Event e = Event.current; 90 91 if (e.type != EventType.KeyDown) 92 return; 93 94 bool isProcessed = mMenu.ProcessKeyActionIfNeeded(e); 95 96 if (isProcessed) 97 e.Use(); 98 } 99 100 protected override void RowGUI(RowGUIArgs args) 101 { 102 if (args.item is ChangeCategoryTreeViewItem) 103 { 104 ChangeCategoryTreeViewItem categoryItem = 105 (ChangeCategoryTreeViewItem)args.item; 106 107 CategoryTreeViewItemGUI( 108 args.rowRect, 109 categoryItem, 110 GetSolvedChildrenCount(categoryItem.Category, mSolvedFileConflicts), 111 args.selected, 112 args.focused); 113 return; 114 } 115 116 if (args.item is ChangeTreeViewItem) 117 { 118 ChangeTreeViewItem changeTreeViewItem = 119 (ChangeTreeViewItem)args.item; 120 121 MergeChangeInfo changeInfo = 122 changeTreeViewItem.ChangeInfo; 123 124 bool isCurrentConflict = IsCurrent.Conflict( 125 changeInfo, 126 mMergeTree.GetMetaChange(changeInfo), 127 mSolvedFileConflicts); 128 129 bool isSolvedConflict = IsSolved.Conflict( 130 changeInfo, 131 mMergeTree.GetMetaChange(changeInfo), 132 mSolvedFileConflicts); 133 134 MergeTreeViewItemGUI( 135 mWkInfo.ClientPath, 136 mMergeTree, 137 this, 138 changeTreeViewItem, 139 args, 140 isCurrentConflict, 141 isSolvedConflict, 142 Repaint); 143 return; 144 } 145 146 base.RowGUI(args); 147 } 148 149 internal void SelectFirstUnsolvedDirectoryConflict() 150 { 151 foreach (MergeChangesCategory category in mMergeTree.GetNodes()) 152 { 153 if (category.CategoryType != MergeChangesCategory.Type.DirectoryConflicts) 154 continue; 155 156 foreach (MergeChangeInfo changeInfo in category.GetChanges()) 157 { 158 if (changeInfo.DirectoryConflict.IsResolved()) 159 continue; 160 161 int itemId = -1; 162 if (mTreeViewItemIds.TryGetInfoItemId(changeInfo, out itemId)) 163 { 164 SetSelection(new List<int>() { itemId }); 165 return; 166 } 167 } 168 } 169 } 170 171 internal void BuildModel(UnityMergeTree tree) 172 { 173 mTreeViewItemIds.Clear(); 174 175 mMergeTree = tree; 176 mSolvedFileConflicts = null; 177 178 mExpandCategories = true; 179 } 180 181 internal void Refilter() 182 { 183 Filter filter = new Filter(searchString); 184 mMergeTree.Filter(filter, mColumnNames); 185 186 mExpandCategories = true; 187 } 188 189 internal void Sort() 190 { 191 if (mMergeTree == null) 192 return; 193 194 int sortedColumnIdx = multiColumnHeader.state.sortedColumnIndex; 195 bool sortAscending = multiColumnHeader.IsSortedAscending(sortedColumnIdx); 196 197 mMergeTree.Sort(mColumnNames[sortedColumnIdx], sortAscending); 198 } 199 200 internal void UpdateSolvedFileConflicts( 201 MergeSolvedFileConflicts solvedFileConflicts) 202 { 203 mSolvedFileConflicts = solvedFileConflicts; 204 } 205 206 internal MergeChangeInfo GetMetaChange(MergeChangeInfo change) 207 { 208 if (change == null) 209 return null; 210 211 return mMergeTree.GetMetaChange(change); 212 } 213 214 internal void FillWithMeta(List<MergeChangeInfo> changes) 215 { 216 mMergeTree.FillWithMeta(changes); 217 } 218 219 internal bool SelectionHasMeta() 220 { 221 MergeChangeInfo selectedChangeInfo = GetSelectedMergeChange(); 222 223 if (selectedChangeInfo == null) 224 return false; 225 226 return mMergeTree.HasMeta(selectedChangeInfo); 227 } 228 229 internal MergeChangeInfo GetSelectedMergeChange() 230 { 231 IList<int> selectedIds = GetSelection(); 232 233 if (selectedIds.Count != 1) 234 return null; 235 236 int selectedId = selectedIds[0]; 237 238 foreach (KeyValuePair<MergeChangeInfo, int> item 239 in mTreeViewItemIds.GetInfoItems()) 240 { 241 if (selectedId == item.Value) 242 return item.Key; 243 } 244 245 return null; 246 } 247 248 internal List<MergeChangeInfo> GetSelectedMergeChanges() 249 { 250 List<MergeChangeInfo> result = new List<MergeChangeInfo>(); 251 252 IList<int> selectedIds = GetSelection(); 253 254 if (selectedIds.Count == 0) 255 return result; 256 257 foreach (KeyValuePair<MergeChangeInfo, int> item 258 in mTreeViewItemIds.GetInfoItems()) 259 { 260 if (!selectedIds.Contains(item.Value)) 261 continue; 262 263 result.Add(item.Key); 264 } 265 266 return result; 267 } 268 269 internal List<MergeChangeInfo> GetSelectedFileConflicts() 270 { 271 List<MergeChangeInfo> result = new List<MergeChangeInfo>(); 272 273 IList<int> selectedIds = GetSelection(); 274 275 if (selectedIds.Count == 0) 276 return result; 277 278 foreach (KeyValuePair<MergeChangeInfo, int> item 279 in mTreeViewItemIds.GetInfoItems()) 280 { 281 if (!selectedIds.Contains(item.Value)) 282 continue; 283 284 if (item.Key.CategoryType != 285 MergeChangesCategory.Type.FileConflicts) 286 continue; 287 288 result.Add(item.Key); 289 } 290 291 return result; 292 } 293 294 void DelayedSearchChanged() 295 { 296 Refilter(); 297 298 Sort(); 299 300 Reload(); 301 302 TableViewOperations.ScrollToSelection(this); 303 } 304 305 void SortingChanged(MultiColumnHeader multiColumnHeader) 306 { 307 Sort(); 308 309 Reload(); 310 } 311 312 internal int GetTotalItemCount() 313 { 314 if (mMergeTree == null) 315 return 0; 316 317 List<MergeChangesCategory> categories = mMergeTree.GetNodes(); 318 319 if (categories == null) 320 return 0; 321 322 int totalCount = 0; 323 foreach (MergeChangesCategory category in categories) 324 { 325 totalCount += category.GetChildrenCount(); 326 } 327 return totalCount; 328 } 329 330 static void RegenerateRows( 331 UnityMergeTree mergeTree, 332 TreeViewItemIds<MergeChangesCategory, MergeChangeInfo> treeViewItemIds, 333 MergeTreeView treeView, 334 TreeViewItem rootItem, 335 List<TreeViewItem> rows, 336 bool expandCategories) 337 { 338 if (mergeTree == null) 339 return; 340 341 ClearRows(rootItem, rows); 342 343 List<MergeChangesCategory> categories = mergeTree.GetNodes(); 344 345 if (categories == null) 346 return; 347 348 List<int> categoriesToExpand = new List<int>(); 349 350 foreach (MergeChangesCategory category in categories) 351 { 352 int categoryId; 353 if (!treeViewItemIds.TryGetCategoryItemId(category, out categoryId)) 354 categoryId = treeViewItemIds.AddCategoryItem(category); 355 356 ChangeCategoryTreeViewItem categoryTreeViewItem = 357 new ChangeCategoryTreeViewItem(categoryId, category); 358 359 rootItem.AddChild(categoryTreeViewItem); 360 rows.Add(categoryTreeViewItem); 361 362 if (!ShouldExpandCategory( 363 treeView, 364 categoryTreeViewItem, 365 expandCategories, 366 categories.Count)) 367 continue; 368 369 categoriesToExpand.Add(categoryTreeViewItem.id); 370 371 foreach (MergeChangeInfo changeInfo in category.GetChanges()) 372 { 373 int differenceId; 374 if (!treeViewItemIds.TryGetInfoItemId(changeInfo, out differenceId)) 375 differenceId = treeViewItemIds.AddInfoItem(changeInfo); 376 377 TreeViewItem changeTreeViewItem = 378 new ChangeTreeViewItem(differenceId, changeInfo); 379 380 categoryTreeViewItem.AddChild(changeTreeViewItem); 381 rows.Add(changeTreeViewItem); 382 } 383 } 384 385 treeView.state.expandedIDs = categoriesToExpand; 386 } 387 388 static void ClearRows( 389 TreeViewItem rootItem, 390 List<TreeViewItem> rows) 391 { 392 if (rootItem.hasChildren) 393 rootItem.children.Clear(); 394 395 rows.Clear(); 396 } 397 398 static void CategoryTreeViewItemGUI( 399 Rect rowRect, 400 ChangeCategoryTreeViewItem item, 401 int solvedChildrenCount, 402 bool isSelected, 403 bool isFocused) 404 { 405 string label = item.Category.GetCategoryName(); 406 string infoLabel = item.Category.GetChildrenCountText(); 407 408 DefaultStyles.label = GetCategoryStyle( 409 item.Category, 410 solvedChildrenCount, 411 isSelected); 412 413 DrawTreeViewItem.ForCategoryItem( 414 rowRect, 415 item.depth, 416 label, 417 infoLabel, 418 isSelected, 419 isFocused); 420 421 DefaultStyles.label = UnityStyles.Tree.Label; 422 } 423 424 static void MergeTreeViewItemGUI( 425 string wkPath, 426 UnityMergeTree mergeTree, 427 MergeTreeView treeView, 428 ChangeTreeViewItem item, 429 RowGUIArgs args, 430 bool isCurrentConflict, 431 bool isSolvedConflict, 432 Action avatarLoadedAction) 433 { 434 for (int visibleColumnIdx = 0; visibleColumnIdx < args.GetNumVisibleColumns(); visibleColumnIdx++) 435 { 436 Rect cellRect = args.GetCellRect(visibleColumnIdx); 437 438 MergeTreeColumn column = 439 (MergeTreeColumn)args.GetColumn(visibleColumnIdx); 440 441 MergeTreeViewItemCellGUI( 442 wkPath, 443 cellRect, 444 treeView.rowHeight, 445 mergeTree, 446 treeView, 447 item, 448 column, 449 avatarLoadedAction, 450 args.selected, 451 args.focused, 452 isCurrentConflict, 453 isSolvedConflict); 454 } 455 } 456 457 static void MergeTreeViewItemCellGUI( 458 string wkPath, 459 Rect rect, 460 float rowHeight, 461 UnityMergeTree mergeTree, 462 MergeTreeView treeView, 463 ChangeTreeViewItem item, 464 MergeTreeColumn column, 465 Action avatarLoadedAction, 466 bool isSelected, 467 bool isFocused, 468 bool isCurrentConflict, 469 bool isSolvedConflict) 470 { 471 MergeChangeInfo mergeChange = item.ChangeInfo; 472 473 string label = mergeChange.GetColumnText( 474 MergeTreeHeaderState.GetColumnName(column)); 475 476 if (column == MergeTreeColumn.Path) 477 { 478 if (mergeTree.HasMeta(item.ChangeInfo)) 479 label = string.Concat(label, UnityConstants.TREEVIEW_META_LABEL); 480 481 Texture icon = GetIcon(wkPath, mergeChange); 482 483 Texture overlayIcon = 484 GetChangesOverlayIcon.ForPlasticMergeChange( 485 mergeChange, isSolvedConflict); 486 487 DrawTreeViewItem.ForItemCell( 488 rect, 489 rowHeight, 490 item.depth, 491 icon, 492 overlayIcon, 493 label, 494 isSelected, 495 isFocused, 496 isCurrentConflict, 497 false); 498 499 return; 500 } 501 502 if (column == MergeTreeColumn.Author) 503 { 504 DrawTreeViewItem.ForItemCell( 505 rect, 506 rowHeight, 507 -1, 508 GetAvatar.ForEmail(label, avatarLoadedAction), 509 null, 510 label, 511 isSelected, 512 isFocused, 513 isCurrentConflict, 514 false); 515 return; 516 } 517 518 if (column == MergeTreeColumn.Size) 519 { 520 // If there is a meta file, add the meta file to the file size so that it is consistent 521 // with the Merge overview 522 if (mergeTree.HasMeta(item.ChangeInfo)) 523 { 524 MergeChangeInfo metaFileInfo = mergeTree.GetMetaChange(mergeChange); 525 long metaFileSize = metaFileInfo.GetSize(); 526 long fileSize = mergeChange.GetSize(); 527 528 label = SizeConverter.ConvertToSizeString(fileSize + metaFileSize); 529 } 530 531 DrawTreeViewItem.ForSecondaryLabelRightAligned( 532 rect, label, isSelected, isFocused, isCurrentConflict); 533 return; 534 } 535 536 DrawTreeViewItem.ForSecondaryLabel( 537 rect, label, isSelected, isFocused, isCurrentConflict); 538 } 539 540 static Texture GetIcon( 541 string wkPath, 542 MergeChangeInfo mergeChange) 543 { 544 RevisionInfo revInfo = mergeChange.GetRevision(); 545 bool isDirectory = revInfo. 546 Type == EnumRevisionType.enDirectory; 547 548 if (isDirectory || mergeChange.IsXLink()) 549 return Images.GetDirectoryIcon(); 550 551 string fullPath = WorkspacePath.GetWorkspacePathFromCmPath( 552 wkPath, 553 mergeChange.GetPath(), 554 Path.DirectorySeparatorChar); 555 556 return Images.GetFileIcon(fullPath); 557 } 558 559 static GUIStyle GetCategoryStyle( 560 MergeChangesCategory category, 561 int solvedChildrenCount, 562 bool isSelected) 563 { 564 if (isSelected) 565 return UnityStyles.Tree.Label; 566 567 if (category.CategoryType == MergeChangesCategory.Type.FileConflicts || 568 category.CategoryType == MergeChangesCategory.Type.DirectoryConflicts) 569 { 570 return category.GetChildrenCount() > solvedChildrenCount ? 571 UnityStyles.Tree.RedLabel : UnityStyles.Tree.GreenLabel; 572 } 573 574 return UnityStyles.Tree.Label; 575 } 576 577 static bool ShouldExpandCategory( 578 MergeTreeView treeView, 579 ChangeCategoryTreeViewItem categoryTreeViewItem, 580 bool expandCategories, 581 int categoriesCount) 582 { 583 if (expandCategories) 584 { 585 if (categoriesCount == 1) 586 return true; 587 588 if (categoryTreeViewItem.Category.CategoryType == 589 MergeChangesCategory.Type.FileConflicts) 590 return true; 591 592 if (categoryTreeViewItem.Category.GetChildrenCount() > 593 NODES_TO_EXPAND_CATEGORY) 594 return false; 595 596 return true; 597 } 598 599 return treeView.IsExpanded(categoryTreeViewItem.id); 600 } 601 602 static int GetSolvedChildrenCount( 603 MergeChangesCategory category, 604 MergeSolvedFileConflicts solvedFileConflicts) 605 { 606 int solvedDirConflicts = 0; 607 if (category.CategoryType == MergeChangesCategory.Type.DirectoryConflicts) 608 { 609 foreach (MergeChangeInfo change in category.GetChanges()) 610 { 611 if (change.DirectoryConflict.IsResolved()) 612 solvedDirConflicts++; 613 } 614 615 return solvedDirConflicts; 616 } 617 618 return (solvedFileConflicts == null) ? 0 : 619 solvedFileConflicts.GetCount(); 620 } 621 622 bool mExpandCategories; 623 624 TreeViewItemIds<MergeChangesCategory, MergeChangeInfo> mTreeViewItemIds = 625 new TreeViewItemIds<MergeChangesCategory, MergeChangeInfo>(); 626 627 MergeSolvedFileConflicts mSolvedFileConflicts; 628 UnityMergeTree mMergeTree; 629 CooldownWindowDelayer mCooldownFilterAction; 630 631 readonly MergeViewMenu mMenu; 632 readonly List<string> mColumnNames; 633 readonly WorkspaceInfo mWkInfo; 634 635 const int NODES_TO_EXPAND_CATEGORY = 10; 636 } 637}