A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using System.IO;
4
5using UnityEditor;
6using UnityEditor.IMGUI.Controls;
7using UnityEngine;
8
9using Codice.Client.Commands;
10using Codice.Client.Common;
11using Codice.Client.Common.EventTracking;
12using Codice.Client.Common.Threading;
13using Codice.CM.Common;
14using Codice.CM.Common.Mount;
15using PlasticGui;
16using PlasticGui.WorkspaceWindow;
17using PlasticGui.WorkspaceWindow.BrowseRepository;
18using PlasticGui.WorkspaceWindow.Diff;
19using Unity.PlasticSCM.Editor.AssetUtils;
20using Unity.PlasticSCM.Editor.Tool;
21using Unity.PlasticSCM.Editor.UI;
22using Unity.PlasticSCM.Editor.UI.Progress;
23using Unity.PlasticSCM.Editor.UI.Tree;
24using Unity.PlasticSCM.Editor.Views.Diff.Dialogs;
25using Unity.PlasticSCM.Editor.Views.History;
26
27namespace Unity.PlasticSCM.Editor.Views.Diff
28{
29 internal class DiffPanel :
30 IDiffTreeViewMenuOperations,
31 DiffTreeViewMenu.IMetaMenuOperations,
32 UndeleteClientDiffsOperation.IGetRestorePathDialog
33 {
34 internal DiffPanel(
35 WorkspaceInfo wkInfo,
36 IWorkspaceWindow workspaceWindow,
37 IRefreshView refreshView,
38 IViewSwitcher viewSwitcher,
39 IHistoryViewLauncher historyViewLauncher,
40 LaunchTool.IShowDownloadPlasticExeWindow showDownloadPlasticExeWindow,
41 EditorWindow parentWindow,
42 bool isGluonMode)
43 {
44 mWkInfo = wkInfo;
45 mWorkspaceWindow = workspaceWindow;
46 mRefreshView = refreshView;
47 mViewSwitcher = viewSwitcher;
48 mHistoryViewLauncher = historyViewLauncher;
49 mShowDownloadPlasticExeWindow = showDownloadPlasticExeWindow;
50 mParentWindow = parentWindow;
51 mGuiMessage = new UnityPlasticGuiMessage();
52 mIsGluonMode = isGluonMode;
53
54 BuildComponents();
55
56 mProgressControls = new ProgressControlsForViews();
57 }
58
59 internal void ClearInfo()
60 {
61 ClearData();
62
63 mParentWindow.Repaint();
64 }
65
66 internal void UpdateInfo(
67 MountPointWithPath mountWithPath,
68 ChangesetInfo csetInfo)
69 {
70 FillData(mountWithPath, csetInfo);
71
72 mParentWindow.Repaint();
73 }
74
75 internal void OnEnable()
76 {
77 mSearchField.downOrUpArrowKeyPressed +=
78 SearchField_OnDownOrUpArrowKeyPressed;
79 }
80
81 internal void OnDisable()
82 {
83 mSearchField.downOrUpArrowKeyPressed -=
84 SearchField_OnDownOrUpArrowKeyPressed;
85 }
86
87 internal void Update()
88 {
89 mProgressControls.UpdateProgress(mParentWindow);
90 }
91
92 internal void OnGUI()
93 {
94 EditorGUILayout.BeginVertical();
95
96 DoActionsToolbar(
97 mDiffs,
98 mDiffsBranchResolver,
99 mProgressControls,
100 mIsSkipMergeTrackingButtonVisible,
101 mIsSkipMergeTrackingButtonChecked,
102 mSearchField,
103 mDiffTreeView);
104
105 DoDiffTreeViewArea(
106 mDiffTreeView,
107 mEmptyStateData,
108 mProgressControls.IsOperationRunning(),
109 mParentWindow.Repaint);
110
111 if (mProgressControls.HasNotification())
112 {
113 DrawProgressForViews.ForNotificationArea(
114 mProgressControls.ProgressData);
115 }
116
117 EditorGUILayout.EndVertical();
118 }
119
120 void IDiffTreeViewMenuOperations.SaveRevisionAs()
121 {
122 TrackFeatureUseEvent.For(
123 PlasticGui.Plastic.API.GetRepositorySpec(mWkInfo),
124 TrackFeatureUseEvent.Features.SaveRevisionFromDiff);
125
126 ClientDiffInfo clientDiffInfo =
127 DiffSelection.GetSelectedDiff(mDiffTreeView);
128 RepositorySpec repSpec = clientDiffInfo.DiffWithMount.Mount.RepSpec;
129 RevisionInfo revision = clientDiffInfo.DiffWithMount.Difference.RevInfo;
130
131 string defaultFileName = DefaultRevisionName.Get(
132 Path.GetFileName(clientDiffInfo.DiffWithMount.Difference.Path), revision.Changeset);
133 string destinationPath = SaveAction.GetDestinationPath(
134 mWkInfo.ClientPath,
135 clientDiffInfo.DiffWithMount.Difference.Path,
136 defaultFileName);
137
138 if (string.IsNullOrEmpty(destinationPath))
139 return;
140
141 SaveRevisionOperation.SaveRevision(
142 repSpec,
143 destinationPath,
144 revision,
145 mProgressControls);
146 }
147
148 SelectedDiffsGroupInfo IDiffTreeViewMenuOperations.GetSelectedDiffsGroupInfo()
149 {
150 return SelectedDiffsGroupInfo.BuildFromSelectedNodes(
151 DiffSelection.GetSelectedDiffsWithoutMeta(mDiffTreeView),
152 mWkInfo != null);
153 }
154
155 void IDiffTreeViewMenuOperations.Diff()
156 {
157 ClientDiffInfo clientDiffInfo =
158 DiffSelection.GetSelectedDiff(mDiffTreeView);
159
160 DiffOperation.DiffClientDiff(
161 mWkInfo,
162 clientDiffInfo.DiffWithMount.Mount.Mount,
163 clientDiffInfo.DiffWithMount.Difference,
164 PlasticExeLauncher.BuildForDiffRevision(mWkInfo, mIsGluonMode, mShowDownloadPlasticExeWindow),
165 imageDiffLauncher: null);
166 }
167
168 void IDiffTreeViewMenuOperations.History()
169 {
170 ClientDiffInfo clientDiffInfo =
171 DiffSelection.GetSelectedDiff(mDiffTreeView);
172
173 mHistoryViewLauncher.ShowHistoryView(
174 clientDiffInfo.DiffWithMount.Mount.RepSpec,
175 clientDiffInfo.DiffWithMount.Difference.RevInfo.ItemId,
176 clientDiffInfo.DiffWithMount.Difference.Path,
177 clientDiffInfo.DiffWithMount.Difference.IsDirectory);
178 }
179
180 void IDiffTreeViewMenuOperations.RevertChanges()
181 {
182 RevertClientDiffsOperation.RevertChanges(
183 mWkInfo,
184 DiffSelection.GetSelectedDiffs(mDiffTreeView),
185 mWorkspaceWindow,
186 mProgressControls,
187 mGuiMessage,
188 AfterRevertOrUndeleteOperation);
189 }
190
191 void IDiffTreeViewMenuOperations.Undelete()
192 {
193 UndeleteClientDiffsOperation.Undelete(
194 mWkInfo,
195 DiffSelection.GetSelectedDiffs(mDiffTreeView),
196 mRefreshView,
197 mProgressControls,
198 this,
199 mGuiMessage,
200 AfterRevertOrUndeleteOperation);
201 }
202
203 void IDiffTreeViewMenuOperations.UndeleteToSpecifiedPaths()
204 {
205 UndeleteClientDiffsOperation.UndeleteToSpecifiedPaths(
206 mWkInfo,
207 DiffSelection.GetSelectedDiffs(mDiffTreeView),
208 mRefreshView,
209 mProgressControls,
210 this,
211 mGuiMessage,
212 AfterRevertOrUndeleteOperation);
213 }
214
215 void IDiffTreeViewMenuOperations.Annotate()
216 {
217 }
218
219 void IDiffTreeViewMenuOperations.CopyFilePath(bool relativePath)
220 {
221 EditorGUIUtility.systemCopyBuffer = GetFilePathList.FromClientDiffInfos(
222 DiffSelection.GetSelectedDiffsWithoutMeta(mDiffTreeView),
223 relativePath,
224 mWkInfo.ClientPath);
225 }
226
227 bool DiffTreeViewMenu.IMetaMenuOperations.SelectionHasMeta()
228 {
229 return mDiffTreeView.SelectionHasMeta();
230 }
231
232 void DiffTreeViewMenu.IMetaMenuOperations.DiffMeta()
233 {
234 ClientDiffInfo clientDiffInfo =
235 DiffSelection.GetSelectedDiff(mDiffTreeView);
236
237 ClientDiffInfo clientDiffInfoMeta =
238 mDiffTreeView.GetMetaDiff(clientDiffInfo);
239
240 DiffOperation.DiffClientDiff(
241 mWkInfo,
242 clientDiffInfoMeta.DiffWithMount.Mount.Mount,
243 clientDiffInfoMeta.DiffWithMount.Difference,
244 PlasticExeLauncher.BuildForDiffRevision(mWkInfo, mIsGluonMode, mShowDownloadPlasticExeWindow),
245 imageDiffLauncher: null);
246 }
247
248 GetRestorePathData
249 UndeleteClientDiffsOperation.IGetRestorePathDialog.GetRestorePath(
250 string wkPath, string restorePath, string explanation,
251 bool isDirectory, bool showSkipButton)
252 {
253 return GetRestorePathDialog.GetRestorePath(
254 wkPath, restorePath, explanation, isDirectory,
255 showSkipButton, mParentWindow);
256 }
257
258 void DiffTreeViewMenu.IMetaMenuOperations.HistoryMeta()
259 {
260 ClientDiffInfo clientDiffInfo =
261 DiffSelection.GetSelectedDiff(mDiffTreeView);
262
263 ClientDiffInfo clientDiffInfoMeta =
264 mDiffTreeView.GetMetaDiff(clientDiffInfo);
265
266 mHistoryViewLauncher.ShowHistoryView(
267 clientDiffInfoMeta.DiffWithMount.Mount.RepSpec,
268 clientDiffInfoMeta.DiffWithMount.Difference.RevInfo.ItemId,
269 clientDiffInfoMeta.DiffWithMount.Difference.Path,
270 clientDiffInfoMeta.DiffWithMount.Difference.IsDirectory);
271 }
272
273 void SearchField_OnDownOrUpArrowKeyPressed()
274 {
275 mDiffTreeView.SetFocusAndEnsureSelectedItem();
276 }
277
278 void AfterRevertOrUndeleteOperation()
279 {
280 RefreshAsset.UnityAssetDatabase();
281
282 mViewSwitcher.ShowPendingChanges();
283 }
284
285 void ClearData()
286 {
287 mSelectedMountWithPath = null;
288 mSelectedChangesetInfo = null;
289
290 mDiffs = null;
291
292 ClearDiffs();
293 }
294
295 void FillData(
296 MountPointWithPath mountWithPath,
297 ChangesetInfo csetInfo)
298 {
299 mSelectedMountWithPath = mountWithPath;
300 mSelectedChangesetInfo = csetInfo;
301
302 ((IProgressControls)mProgressControls).ShowProgress(
303 PlasticLocalization.GetString(PlasticLocalization.Name.Loading));
304
305 mIsSkipMergeTrackingButtonVisible = false;
306
307 IThreadWaiter waiter = ThreadWaiter.GetWaiter(100);
308 waiter.Execute(
309 /*threadOperationDelegate*/ delegate
310 {
311 mDiffs = PlasticGui.Plastic.API.GetChangesetDifferences(
312 mountWithPath, csetInfo);
313
314 mDiffsBranchResolver = BuildBranchResolver.ForDiffs(mDiffs);
315 },
316 /*afterOperationDelegate*/ delegate
317 {
318 ((IProgressControls)mProgressControls).HideProgress();
319
320 if (mSelectedMountWithPath != mountWithPath ||
321 mSelectedChangesetInfo != csetInfo)
322 return;
323
324 if (waiter.Exception != null)
325 {
326 ExceptionsHandler.LogException("DiffPanel", waiter.Exception);
327
328 ((IProgressControls)mProgressControls).ShowError(waiter.Exception.Message);
329
330 ClearDiffs();
331 return;
332 }
333
334 if (mDiffs == null || mDiffs.Count == 0)
335 {
336 ClearDiffs();
337 return;
338 }
339
340 mIsSkipMergeTrackingButtonVisible =
341 ClientDiffList.HasMerges(mDiffs);
342
343 bool skipMergeTracking =
344 mIsSkipMergeTrackingButtonVisible &&
345 mIsSkipMergeTrackingButtonChecked;
346
347 UpdateDiffTreeView(
348 mWkInfo,
349 mDiffs,
350 mDiffsBranchResolver,
351 skipMergeTracking,
352 mDiffTreeView);
353 });
354 }
355
356 void ClearDiffs()
357 {
358 mIsSkipMergeTrackingButtonVisible = false;
359
360 ClearDiffTreeView(mDiffTreeView);
361 }
362
363 static void ClearDiffTreeView(
364 DiffTreeView diffTreeView)
365 {
366 diffTreeView.ClearModel();
367
368 diffTreeView.Reload();
369 }
370
371 static void UpdateDiffTreeView(
372 WorkspaceInfo wkInfo,
373 List<ClientDiff> diffs,
374 BranchResolver brResolver,
375 bool skipMergeTracking,
376 DiffTreeView diffTreeView)
377 {
378 diffTreeView.BuildModel(
379 wkInfo, diffs, brResolver, skipMergeTracking);
380
381 diffTreeView.Refilter();
382
383 diffTreeView.Sort();
384
385 diffTreeView.Reload();
386 }
387
388 void DoActionsToolbar(
389 List<ClientDiff> diffs,
390 BranchResolver brResolver,
391 ProgressControlsForViews progressControls,
392 bool isSkipMergeTrackingButtonVisible,
393 bool isSkipMergeTrackingButtonChecked,
394 SearchField searchField,
395 DiffTreeView diffTreeView)
396 {
397 EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
398
399 if (progressControls.IsOperationRunning())
400 {
401 DrawProgressForViews.ForIndeterminateProgress(
402 progressControls.ProgressData);
403 }
404
405 GUILayout.FlexibleSpace();
406
407 if (isSkipMergeTrackingButtonVisible)
408 {
409 DoSkipMergeTrackingButton(
410 diffs, brResolver,
411 isSkipMergeTrackingButtonChecked,
412 diffTreeView);
413 }
414
415 DrawSearchField.For(
416 searchField,
417 diffTreeView,
418 UnityConstants.SEARCH_FIELD_WIDTH);
419
420 EditorGUILayout.EndHorizontal();
421 }
422
423 void DoSkipMergeTrackingButton(
424 List<ClientDiff> diffs,
425 BranchResolver brResolver,
426 bool isSkipMergeTrackingButtonChecked,
427 DiffTreeView diffTreeView)
428 {
429 bool wasChecked = isSkipMergeTrackingButtonChecked;
430
431 GUIContent buttonContent = new GUIContent(
432 PlasticLocalization.GetString(
433 PlasticLocalization.Name.SkipDiffMergeTracking));
434
435 GUIStyle buttonStyle = new GUIStyle(EditorStyles.toolbarButton);
436
437 float buttonWidth = buttonStyle.CalcSize(buttonContent).x + 10;
438
439 Rect toggleRect = GUILayoutUtility.GetRect(
440 buttonContent, buttonStyle, GUILayout.Width(buttonWidth));
441
442 bool isChecked = GUI.Toggle(
443 toggleRect, wasChecked, buttonContent, buttonStyle);
444
445 if (wasChecked == isChecked)
446 return;
447
448 // if user just checked the skip merge tracking button
449 if (isChecked)
450 {
451 TrackFeatureUseEvent.For(
452 PlasticGui.Plastic.API.GetRepositorySpec(mWkInfo),
453 TrackFeatureUseEvent.Features.ChangesetViewSkipMergeTrackingButton);
454 }
455
456 UpdateDiffTreeView(mWkInfo, diffs, brResolver, isChecked, diffTreeView);
457
458 mIsSkipMergeTrackingButtonChecked = isChecked;
459 }
460
461 static void DoDiffTreeViewArea(
462 DiffTreeView diffTreeView,
463 EmptyStateData emptyStateData,
464 bool isOperationRunning,
465 Action repaint)
466 {
467 GUI.enabled = !isOperationRunning;
468
469 Rect rect = GUILayoutUtility.GetRect(0, 100000, 0, 100000);
470
471 diffTreeView.OnGUI(rect);
472
473 emptyStateData.Update(
474 GetEmptyStateMessage(diffTreeView),
475 rect, Event.current.type, repaint);
476
477 if (!emptyStateData.IsEmpty())
478 DrawTreeViewEmptyState.For(emptyStateData);
479
480 GUI.enabled = true;
481 }
482
483 static string GetEmptyStateMessage(DiffTreeView diffTreeView)
484 {
485 if (diffTreeView.GetRows().Count > 0)
486 return string.Empty;
487
488 return string.IsNullOrEmpty(diffTreeView.searchString) ?
489 PlasticLocalization.Name.NoContentToCompareExplanation.GetString() :
490 PlasticLocalization.Name.DiffsEmptyState.GetString();
491 }
492
493 void BuildComponents()
494 {
495 mSearchField = new SearchField();
496 mSearchField.downOrUpArrowKeyPressed += SearchField_OnDownOrUpArrowKeyPressed;
497
498 mDiffTreeView = new DiffTreeView(new DiffTreeViewMenu(this, this));
499 mDiffTreeView.Reload();
500 }
501
502 bool mIsSkipMergeTrackingButtonVisible;
503 bool mIsSkipMergeTrackingButtonChecked;
504
505 ChangesetInfo mSelectedChangesetInfo;
506 MountPointWithPath mSelectedMountWithPath;
507
508 volatile List<ClientDiff> mDiffs;
509 volatile BranchResolver mDiffsBranchResolver;
510
511 SearchField mSearchField;
512 DiffTreeView mDiffTreeView;
513
514 readonly EmptyStateData mEmptyStateData = new EmptyStateData();
515 readonly ProgressControlsForViews mProgressControls;
516 readonly GuiMessage.IGuiMessage mGuiMessage;
517 readonly EditorWindow mParentWindow;
518 readonly IRefreshView mRefreshView;
519 readonly IWorkspaceWindow mWorkspaceWindow;
520 readonly IHistoryViewLauncher mHistoryViewLauncher;
521 readonly IViewSwitcher mViewSwitcher;
522 readonly LaunchTool.IShowDownloadPlasticExeWindow mShowDownloadPlasticExeWindow;
523 readonly WorkspaceInfo mWkInfo;
524 readonly bool mIsGluonMode;
525 }
526}