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}