A game about forced loneliness, made by TACStudios
1using System;
2using System.Threading;
3using System.Threading.Tasks;
4
5using UnityEditor;
6using UnityEngine;
7
8using Codice.Client.BaseCommands;
9using Codice.Client.Common;
10using Codice.Client.Common.EventTracking;
11using Codice.Client.Common.Threading;
12using Codice.CM.Common;
13using Codice.LogWrapper;
14using CodiceApp.EventTracking.Plastic;
15using CodiceApp.EventTracking;
16using GluonGui;
17using PlasticGui;
18using PlasticGui.WorkspaceWindow;
19using PlasticGui.WorkspaceWindow.Merge;
20using PlasticGui.WorkspaceWindow.NotificationBar;
21using Unity.PlasticSCM.Editor.AssetMenu;
22using Unity.PlasticSCM.Editor.AssetUtils;
23using Unity.PlasticSCM.Editor.Configuration;
24using Unity.PlasticSCM.Editor.Configuration.CloudEdition.Welcome;
25using Unity.PlasticSCM.Editor.Developer;
26using Unity.PlasticSCM.Editor.Inspector;
27using Unity.PlasticSCM.Editor.Settings;
28using Unity.PlasticSCM.Editor.Tool;
29using Unity.PlasticSCM.Editor.UI;
30using Unity.PlasticSCM.Editor.UI.Avatar;
31using Unity.PlasticSCM.Editor.UI.Progress;
32using Unity.PlasticSCM.Editor.UI.StatusBar;
33using Unity.PlasticSCM.Editor.Views.CreateWorkspace;
34using Unity.PlasticSCM.Editor.Views.Welcome;
35using Unity.PlasticSCM.Editor.WebApi;
36
37using GluonCheckIncomingChanges = PlasticGui.Gluon.WorkspaceWindow.CheckIncomingChanges;
38using GluonNewIncomingChangesUpdater = PlasticGui.Gluon.WorkspaceWindow.NewIncomingChangesUpdater;
39using GluonShelvedChangesNotification = Unity.PlasticSCM.Editor.Gluon.ShelvedChangesNotification;
40using PlasticAssetModificationProcessor = Unity.PlasticSCM.Editor.AssetUtils.Processor.AssetModificationProcessor;
41using ShelvedChangesNotification = Unity.PlasticSCM.Editor.Developer.ShelvedChangesNotification;
42
43namespace Unity.PlasticSCM.Editor
44{
45 internal class PlasticWindow : EditorWindow,
46 CheckIncomingChanges.IAutoRefreshIncomingChangesView,
47 GluonCheckIncomingChanges.IAutoRefreshIncomingChangesView,
48 CheckShelvedChanges.IAutoRefreshApplyShelveView,
49 CreateWorkspaceView.ICreateWorkspaceListener
50 {
51 internal bool ShowWelcomeViewForTesting { get; set; }
52
53 internal WelcomeView WelcomeViewForTesting { get { return mWelcomeView; } }
54
55 internal WorkspaceWindow WorkspaceWindowForTesting { get { return mWorkspaceWindow; } }
56
57 internal ViewSwitcher ViewSwitcherForTesting { get { return mViewSwitcher; } }
58
59 internal ViewHost ViewHostForTesting { get { return mViewHost; } }
60
61 internal CmConnection CmConnectionForTesting { get { return CmConnection.Get(); } }
62
63 internal IShelvedChangesUpdater ShelvedChangesUpdater { get { return mShelvedChangesUpdater; } }
64
65 internal WelcomeView GetWelcomeView()
66 {
67 if (mWelcomeView != null)
68 return mWelcomeView;
69
70 mWelcomeView = new WelcomeView(
71 this,
72 this,
73 PlasticGui.Plastic.API,
74 PlasticGui.Plastic.WebRestAPI);
75
76 return mWelcomeView;
77 }
78
79 internal PendingChangesOptionsFoldout.IAutoRefreshView GetPendingChangesView()
80 {
81 return mViewSwitcher != null ? mViewSwitcher.PendingChangesTab : null;
82 }
83
84 internal void UpdateWindowIcon(PlasticNotification.Status status)
85 {
86 Texture windowIcon = PlasticNotification.GetIcon(status);
87
88 if (titleContent.image != windowIcon)
89 titleContent.image = windowIcon;
90 }
91
92 internal void RefreshWorkspaceUI()
93 {
94 InitializePlastic();
95 Repaint();
96
97 OnFocus();
98 }
99
100 internal void InitializePlastic()
101 {
102 if (mForceToReOpen)
103 {
104 mForceToReOpen = false;
105 return;
106 }
107
108 try
109 {
110 if (UnityConfigurationChecker.NeedsConfiguration() ||
111 ShowWelcomeViewForTesting)
112 return;
113
114 mWkInfo = FindWorkspace.InfoForApplicationPath(
115 ApplicationDataPath.Get(), PlasticGui.Plastic.API);
116
117 if (mWkInfo == null)
118 return;
119
120 PlasticPlugin.EnableForWorkspace(mWkInfo);
121
122 // PlasticPlugin.EnableForWorkspace may trigger a workspace metadata
123 // upgrade that modifies the repSpec. So, we need to calculate the repSpec
124 // after calling it to ensure it is up-to-date.
125 mRepSpec = PlasticGui.Plastic.API.GetRepositorySpec(mWkInfo);
126
127 DisableVCSIfEnabled(mWkInfo.ClientPath);
128
129 mIsGluonMode = PlasticGui.Plastic.API.IsGluonWorkspace(mWkInfo);
130
131 mViewHost = new ViewHost();
132
133 mStatusBar = new StatusBar();
134
135 mSaveAssets = new SaveAssets();
136
137 mViewSwitcher = new ViewSwitcher(
138 mRepSpec,
139 mWkInfo,
140 mViewHost,
141 mIsGluonMode,
142 PlasticPlugin.AssetStatusCache,
143 mShowDownloadPlasticExeWindow,
144 mProcessExecutor,
145 PlasticPlugin.WorkspaceOperationsMonitor,
146 mSaveAssets,
147 mStatusBar,
148 this);
149
150 InitializeNewIncomingChanges(
151 mWkInfo, mViewSwitcher, mIsGluonMode);
152
153 InitializeShelvedChanges(
154 mWkInfo,
155 mRepSpec,
156 mViewSwitcher,
157 mShowDownloadPlasticExeWindow,
158 mIsGluonMode);
159
160 // Create a CooldownWindowDelayer to make the auto-refresh changes delayed.
161 // In this way, we cover the following scenario:
162 // * When Unity Editor window is activated it writes some files to its Temp
163 // folder. This causes the fswatcher to process those events.
164 // * We need to wait until the fswatcher finishes processing the events,
165 // otherwise the NewChangesInWk method will return TRUE because there
166 // are pending events to process, which causes an unwanted 'get pending
167 // changes' operation when there are no new changes.
168 // * So, we need to delay the auto-refresh call in order
169 // to give the fswatcher enough time to process the events.
170 mCooldownAutoRefreshChangesAction = new CooldownWindowDelayer(
171 () =>
172 {
173 mViewSwitcher.AutoRefreshPendingChangesView();
174 mViewSwitcher.AutoRefreshIncomingChangesView();
175 },
176 UnityConstants.AUTO_REFRESH_CHANGES_DELAYED_INTERVAL);
177
178 mWorkspaceWindow = new WorkspaceWindow(
179 mWkInfo,
180 mViewHost,
181 mViewSwitcher,
182 mStatusBar,
183 mViewSwitcher,
184 mDeveloperNewIncomingChangesUpdater,
185 mShelvedChangesUpdater,
186 this);
187
188 mViewSwitcher.SetWorkspaceWindow(mWorkspaceWindow);
189
190 mStatusBar.Initialize(
191 mWorkspaceWindow,
192 mIncomingChangesNotification,
193 mShelvedChangesNotification);
194
195 mViewSwitcher.InitializeFromState(mViewSwitcherState);
196
197 PlasticApp.RegisterWorkspaceWindow(mWorkspaceWindow);
198 PlasticPlugin.WorkspaceOperationsMonitor.RegisterWindow(
199 mWorkspaceWindow,
200 mViewHost,
201 mDeveloperNewIncomingChangesUpdater);
202
203 UnityStyles.Initialize(Repaint);
204
205 AssetMenuItems.BuildOperations(
206 mWkInfo,
207 PlasticGui.Plastic.API,
208 mWorkspaceWindow,
209 mViewSwitcher,
210 mViewSwitcher,
211 mViewHost,
212 PlasticPlugin.WorkspaceOperationsMonitor,
213 mSaveAssets,
214 mDeveloperNewIncomingChangesUpdater,
215 mShelvedChangesUpdater,
216 PlasticPlugin.AssetStatusCache,
217 mViewSwitcher,
218 mViewSwitcher,
219 mShowDownloadPlasticExeWindow,
220 mIsGluonMode);
221
222 DrawInspectorOperations.BuildOperations(
223 mWkInfo,
224 PlasticGui.Plastic.API,
225 mWorkspaceWindow,
226 mViewSwitcher,
227 mViewSwitcher,
228 mViewHost,
229 PlasticPlugin.WorkspaceOperationsMonitor,
230 mSaveAssets,
231 mDeveloperNewIncomingChangesUpdater,
232 mShelvedChangesUpdater,
233 PlasticPlugin.AssetStatusCache,
234 mViewSwitcher,
235 mViewSwitcher,
236 mShowDownloadPlasticExeWindow,
237 mIsGluonMode);
238
239 mLastUpdateTime = EditorApplication.timeSinceStartup;
240
241 mViewSwitcher.ShowBranchesViewIfNeeded();
242 mViewSwitcher.ShowShelvesViewIfNeeded();
243 mViewSwitcher.ShowLocksViewIfNeeded();
244 MergeInProgress.ShowIfNeeded(mWkInfo, mViewSwitcher);
245
246 // Note: this need to be initialized regardless of the type of the UVCS Edition installed
247 InitializeCloudSubscriptionData();
248 InitializeCurrentUser();
249
250 if (!EditionToken.IsCloudEdition())
251 return;
252
253 InitializeNotificationBarUpdater(
254 mWkInfo, mStatusBar.NotificationBar);
255 }
256 catch (Exception ex)
257 {
258 mException = ex;
259
260 ExceptionsHandler.HandleException("InitializePlastic", ex);
261 }
262 }
263
264 void CheckIncomingChanges.IAutoRefreshIncomingChangesView.IfVisible()
265 {
266 mViewSwitcher.AutoRefreshIncomingChangesView();
267 }
268
269 void GluonCheckIncomingChanges.IAutoRefreshIncomingChangesView.IfVisible()
270 {
271 mViewSwitcher.AutoRefreshIncomingChangesView();
272 }
273
274 void CheckShelvedChanges.IAutoRefreshApplyShelveView.IfVisible()
275 {
276 mViewSwitcher.AutoRefreshMergeView();
277 }
278
279 void CreateWorkspaceView.ICreateWorkspaceListener.OnWorkspaceCreated(
280 WorkspaceInfo wkInfo, bool isGluonMode)
281 {
282 mWkInfo = wkInfo;
283 mRepSpec = PlasticGui.Plastic.API.GetRepositorySpec(wkInfo);
284 mIsGluonMode = isGluonMode;
285 mWelcomeView = null;
286
287 PlasticPlugin.Enable();
288
289 if (mIsGluonMode)
290 ConfigurePartialWorkspace.AsFullyChecked(mWkInfo);
291
292 InitializePlastic();
293 Repaint();
294 }
295
296 void OnEnable()
297 {
298 // Note: this log isn't visible if the window is opened automatically at startup,
299 // as the logs are not initialized yet (later, conditionally, in PlasticPlugin.Enable())
300 mLog.Debug("OnEnable");
301
302 wantsMouseMove = true;
303
304 if (mException != null)
305 return;
306
307 minSize = new Vector2(
308 UnityConstants.PLASTIC_WINDOW_MIN_SIZE_WIDTH,
309 UnityConstants.PLASTIC_WINDOW_MIN_SIZE_HEIGHT);
310
311 UpdateWindowIcon(PlasticNotification.Status.None);
312
313 RegisterApplicationFocusHandlers(this);
314
315 if (!PlasticPlugin.ConnectionMonitor.IsConnected)
316 return;
317
318 PlasticPlugin.Enable();
319
320 InitializePlastic();
321 }
322
323 void OnDisable()
324 {
325 mLog.Debug("OnDisable");
326
327 // We need to disable MonoFSWatcher because otherwise it hangs
328 // when you move the window between monitors with different scale
329 PlasticApp.DisableMonoFsWatcherIfNeeded();
330
331 if (mException != null)
332 return;
333
334 UnRegisterApplicationFocusHandlers(this);
335
336 ClosePlastic(this);
337 }
338
339 void OnDestroy()
340 {
341 mLog.Debug("OnDestroy");
342
343 if (mException != null)
344 return;
345
346 if (mWkInfo == null)
347 return;
348
349 if (!PlasticApp.HasRunningOperation())
350 return;
351
352 bool bCloseWindow = GuiMessage.ShowQuestion(
353 PlasticLocalization.GetString(PlasticLocalization.Name.OperationRunning),
354 PlasticLocalization.GetString(PlasticLocalization.Name.ConfirmClosingRunningOperation),
355 PlasticLocalization.GetString(PlasticLocalization.Name.YesButton));
356
357 if (bCloseWindow)
358 return;
359
360 mLog.Debug(
361 "Show window again because the user doesn't want " +
362 "to quit it due to there is an operation running");
363
364 mForceToReOpen = true;
365
366 ReOpenPlasticWindow(this);
367 }
368
369 void OnFocus()
370 {
371 mLog.Debug("OnFocus");
372
373 if (mException != null)
374 return;
375
376 if (mWkInfo == null)
377 return;
378
379 if (!PlasticPlugin.ConnectionMonitor.IsConnected)
380 return;
381
382 // We don't want to auto-refresh the views when the window
383 // is focused due to a right mouse button click because
384 // if there is no internet connection a dialog appears and
385 // it prevents being able to open the context menu in order
386 // to close the Plastic SCM window
387 if (Mouse.IsRightMouseButtonPressed(Event.current))
388 return;
389
390 mCooldownAutoRefreshChangesAction.Ping();
391 }
392
393 void OnGUI()
394 {
395 if (!PlasticPlugin.ConnectionMonitor.IsConnected)
396 {
397 DoNotConnectedArea();
398 return;
399 }
400
401 if (mException != null)
402 {
403 DoExceptionErrorArea();
404 return;
405 }
406
407 try
408 {
409 bool clientNeedsConfiguration = UnityConfigurationChecker.NeedsConfiguration() || ShowWelcomeViewForTesting;
410
411 WelcomeView welcomeView = GetWelcomeView();
412
413 if (clientNeedsConfiguration && welcomeView.autoLoginState == AutoLogin.State.Off)
414 {
415 welcomeView.autoLoginState = AutoLogin.State.Started;
416 }
417
418 if (NeedsToDisplayWelcomeView(clientNeedsConfiguration, mWkInfo))
419 {
420 welcomeView.OnGUI(clientNeedsConfiguration);
421 return;
422 }
423
424 //TODO: Codice - beta: hide the switcher until the update dialog is implemented
425 //DrawGuiModeSwitcher.ForMode(
426 // isGluonMode, plasticClient, changesTreeView, editorWindow);
427
428 DoTabToolbar(
429 mWkInfo,
430 mRepSpec,
431 mViewSwitcher,
432 mShowDownloadPlasticExeWindow,
433 mProcessExecutor,
434 mIsGluonMode,
435 mIsCloudOrganization,
436 mIsUnityOrganization,
437 mIsUGOSubscription);
438
439 mViewSwitcher.TabViewGUI(GetCurrentUser());
440
441 if (mWorkspaceWindow.IsOperationInProgress())
442 DrawProgressForOperations.For(
443 mWorkspaceWindow, mWorkspaceWindow.Progress,
444 position.width);
445
446 mStatusBar.OnGUI();
447 }
448 catch (Exception ex)
449 {
450 if (IsExitGUIException(ex))
451 throw;
452
453 GUI.enabled = true;
454
455 if (IsIMGUIPaintException(ex))
456 {
457 ExceptionsHandler.LogException("PlasticWindow", ex);
458 return;
459 }
460
461 mException = ex;
462
463 DoExceptionErrorArea();
464
465 ExceptionsHandler.HandleException("OnGUI", ex);
466 }
467 }
468
469 void Update()
470 {
471 if (mException != null)
472 return;
473
474 if (mWkInfo == null)
475 return;
476
477 try
478 {
479 double currentUpdateTime = EditorApplication.timeSinceStartup;
480 double elapsedSeconds = currentUpdateTime - mLastUpdateTime;
481
482 mViewSwitcher.Update();
483 mWorkspaceWindow.OnParentUpdated(elapsedSeconds);
484
485 if (mWelcomeView != null)
486 mWelcomeView.Update();
487
488 mLastUpdateTime = currentUpdateTime;
489 }
490 catch (Exception ex)
491 {
492 mException = ex;
493
494 ExceptionsHandler.HandleException("Update", ex);
495 }
496 }
497
498 void OnApplicationActivated()
499 {
500 mLog.Debug("OnApplicationActivated");
501
502 if (mException != null)
503 return;
504
505 if (!PlasticPlugin.ConnectionMonitor.IsConnected)
506 return;
507
508 if (UnityConfigurationChecker.NeedsConfiguration() ||
509 ShowWelcomeViewForTesting)
510 return;
511
512 Reload.IfWorkspaceConfigChanged(
513 PlasticGui.Plastic.API, mWkInfo, mIsGluonMode,
514 ExecuteFullReload);
515
516 if (mWkInfo == null)
517 return;
518
519 NewIncomingChanges.LaunchUpdater(
520 mDeveloperNewIncomingChangesUpdater,
521 mGluonNewIncomingChangesUpdater);
522
523 mShelvedChangesUpdater.Start();
524 mShelvedChangesUpdater.Update(DateTime.Now);
525
526 if (!PlasticApp.HasRunningOperation())
527 mCooldownAutoRefreshChangesAction.Ping();
528
529 ((IWorkspaceWindow)mWorkspaceWindow).UpdateTitle();
530 }
531
532 void OnApplicationDeactivated()
533 {
534 mLog.Debug("OnApplicationDeactivated");
535
536 if (mException != null)
537 return;
538
539 if (mWkInfo == null)
540 return;
541
542 if (!PlasticPlugin.ConnectionMonitor.IsConnected)
543 return;
544
545 NewIncomingChanges.StopUpdater(
546 mDeveloperNewIncomingChangesUpdater,
547 mGluonNewIncomingChangesUpdater);
548
549 mShelvedChangesUpdater.Stop();
550 }
551
552 void ExecuteFullReload()
553 {
554 mException = null;
555
556 ClosePlastic(this);
557
558 InitializePlastic();
559 }
560
561 void InitializeCloudSubscriptionData()
562 {
563 mIsCloudOrganization = false;
564 mIsUnityOrganization = false;
565 mIsUGOSubscription = false;
566
567 if (mRepSpec == null)
568 return;
569
570 mIsCloudOrganization = PlasticGui.Plastic.API.IsCloud(mRepSpec.Server);
571
572 if (!mIsCloudOrganization)
573 return;
574
575 mIsUnityOrganization = OrganizationsInformation.IsUnityOrganization(mRepSpec.Server);
576
577 string organizationName = ServerOrganizationParser.GetOrganizationFromServer(mRepSpec.Server);
578
579 Task.Run(
580 () =>
581 {
582 string authToken = AuthToken.GetForServer(mRepSpec.Server);
583
584 if (string.IsNullOrEmpty(authToken))
585 return null;
586
587 return WebRestApiClient.PlasticScm.GetSubscriptionDetails(
588 organizationName, authToken);
589 }).ContinueWith(
590 t =>
591 {
592 if (t.Result == null)
593 {
594 mLog.DebugFormat(
595 "Error getting Subscription details for organization {0}",
596 organizationName);
597 return;
598 }
599
600 mIsUGOSubscription = t.Result.OrderSource == UGO_ORDER_SOURCE;
601 });
602 }
603
604 void InitializeCurrentUser()
605 {
606 PlasticThreadPool.Run(new WaitCallback(delegate
607 {
608 try
609 {
610 SetCurrentUser(PlasticGui.Plastic.
611 API.GetCurrentUser(mRepSpec.Server));
612 }
613 catch (Exception ex)
614 {
615 mLog.ErrorFormat("Error loading the current user: {0}", ex.Message);
616 mLog.DebugFormat("Stack trace: {0}", ex.StackTrace);
617 }
618 }));
619 }
620
621 void DoNotConnectedArea()
622 {
623 string labelText = PlasticLocalization.GetString(
624 PlasticLocalization.Name.NotConnectedTryingToReconnect);
625
626 string buttonText = PlasticLocalization.GetString(
627 PlasticLocalization.Name.TryNowButton);
628
629 GUI.enabled = !PlasticPlugin.ConnectionMonitor.IsTryingReconnection;
630
631 DrawActionHelpBox.For(
632 Images.GetInfoDialogIcon(), labelText, buttonText,
633 PlasticPlugin.ConnectionMonitor.CheckConnection);
634
635 GUI.enabled = true;
636 }
637
638 void DoExceptionErrorArea()
639 {
640 string labelText = PlasticLocalization.GetString(
641 PlasticLocalization.Name.UnexpectedError);
642
643 string buttonText = PlasticLocalization.GetString(
644 PlasticLocalization.Name.ReloadButton);
645
646 DrawActionHelpBox.For(
647 Images.GetErrorDialogIcon(), labelText, buttonText,
648 ExecuteFullReload);
649 }
650
651 void InitializeNewIncomingChanges(
652 WorkspaceInfo wkInfo,
653 ViewSwitcher viewSwitcher,
654 bool bIsGluonMode)
655 {
656 if (bIsGluonMode)
657 {
658 Gluon.IncomingChangesNotification gluonNotification =
659 new Gluon.IncomingChangesNotification(wkInfo, viewSwitcher, this);
660 mGluonNewIncomingChangesUpdater =
661 NewIncomingChanges.BuildUpdaterForGluon(
662 wkInfo, viewSwitcher, gluonNotification, this, gluonNotification,
663 new GluonCheckIncomingChanges.CalculateIncomingChanges());
664 mIncomingChangesNotification = gluonNotification;
665 return;
666 }
667
668 IncomingChangesNotification developerNotification =
669 new IncomingChangesNotification(wkInfo, viewSwitcher, this);
670 mDeveloperNewIncomingChangesUpdater =
671 NewIncomingChanges.BuildUpdaterForDeveloper(
672 wkInfo, viewSwitcher, developerNotification,
673 this, developerNotification);
674 mIncomingChangesNotification = developerNotification;
675 }
676
677 void InitializeShelvedChanges(
678 WorkspaceInfo wkInfo,
679 RepositorySpec repSpec,
680 ViewSwitcher viewSwitcher,
681 LaunchTool.IShowDownloadPlasticExeWindow showDownloadPlasticExeWindow,
682 bool bIsGluonMode)
683 {
684 mShelvedChangesNotification = bIsGluonMode ?
685 new GluonShelvedChangesNotification(
686 wkInfo,
687 repSpec,
688 viewSwitcher,
689 showDownloadPlasticExeWindow,
690 this) :
691 new ShelvedChangesNotification(
692 wkInfo,
693 repSpec,
694 viewSwitcher,
695 this) as StatusBar.IShelvedChangesNotification;
696
697 mShelvedChangesUpdater = new ShelvedChangesUpdater(
698 wkInfo,
699 new UnityPlasticTimerBuilder(),
700 this,
701 new CalculateShelvedChanges(new BaseCommandsImpl()),
702 mShelvedChangesNotification);
703
704 viewSwitcher.SetShelvedChanges(mShelvedChangesUpdater, mShelvedChangesNotification);
705 mShelvedChangesNotification.SetShelvedChangesUpdater(mShelvedChangesUpdater);
706
707 mShelvedChangesUpdater.Start();
708 }
709
710 void InitializeNotificationBarUpdater(
711 WorkspaceInfo wkInfo,
712 INotificationBar notificationBar)
713 {
714 mNotificationBarUpdater = new NotificationBarUpdater(
715 notificationBar,
716 PlasticGui.Plastic.WebRestAPI,
717 new UnityPlasticTimerBuilder(),
718 new NotificationBarUpdater.NotificationBarConfig(),
719 BuildEventModel.CurrentApplicationString,
720 UVCPackageVersion.Value,
721 BuildEvent.CurrentPlatform.ToString());
722 mNotificationBarUpdater.Start();
723 mNotificationBarUpdater.SetWorkspace(wkInfo);
724 }
725
726 void SetCurrentUser(ResolvedUser currentUser)
727 {
728 lock (mCurrentUserLock)
729 {
730 mCurrentUser = currentUser;
731 }
732 }
733
734 ResolvedUser GetCurrentUser()
735 {
736 lock (mCurrentUserLock)
737 {
738 return mCurrentUser;
739 }
740 }
741
742 static void DoTabToolbar(
743 WorkspaceInfo workspaceInfo,
744 RepositorySpec repSpec,
745 ViewSwitcher viewSwitcher,
746 LaunchTool.IShowDownloadPlasticExeWindow showDownloadPlasticExeWindow,
747 LaunchTool.IProcessExecutor processExecutor,
748 bool isGluonMode,
749 bool isCloudOrganization,
750 bool isUnityOrganization,
751 bool isUGOSubscription)
752 {
753 EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
754
755 viewSwitcher.TabButtonsGUI();
756
757 GUILayout.FlexibleSpace();
758
759 EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
760
761 GUILayout.Space(2);
762
763 DoSearchField(viewSwitcher);
764
765 GUILayout.Space(2);
766
767 EditorGUILayout.EndHorizontal();
768
769 EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
770
771 DoToolbarButtons(
772 workspaceInfo,
773 repSpec,
774 viewSwitcher,
775 showDownloadPlasticExeWindow,
776 processExecutor,
777 isGluonMode,
778 isCloudOrganization,
779 isUnityOrganization,
780 isUGOSubscription);
781
782 EditorGUILayout.EndHorizontal();
783
784 EditorGUILayout.EndHorizontal();
785 }
786
787 static void DoSearchField(ViewSwitcher viewSwitcher)
788 {
789 if (viewSwitcher.IsViewSelected(ViewSwitcher.SelectedTab.PendingChanges))
790 {
791 viewSwitcher.PendingChangesTab.DrawSearchFieldForTab();
792 return;
793 }
794
795 if (viewSwitcher.IsViewSelected(ViewSwitcher.SelectedTab.IncomingChanges))
796 {
797 viewSwitcher.IncomingChangesTab.DrawSearchFieldForTab();
798 return;
799 }
800
801 if (viewSwitcher.IsViewSelected(ViewSwitcher.SelectedTab.Changesets))
802 {
803 viewSwitcher.ChangesetsTab.DrawSearchFieldForTab();
804 return;
805 }
806
807 if (viewSwitcher.IsViewSelected(ViewSwitcher.SelectedTab.Branches))
808 {
809 viewSwitcher.BranchesTab.DrawSearchFieldForTab();
810 return;
811 }
812
813 if (viewSwitcher.IsViewSelected(ViewSwitcher.SelectedTab.Shelves))
814 {
815 viewSwitcher.ShelvesTab.DrawSearchFieldForTab();
816 return;
817 }
818
819 if (viewSwitcher.IsViewSelected(ViewSwitcher.SelectedTab.Locks))
820 {
821 viewSwitcher.LocksTab.DrawSearchFieldForTab();
822 return;
823 }
824
825 if (viewSwitcher.IsViewSelected(ViewSwitcher.SelectedTab.Merge))
826 {
827 viewSwitcher.MergeTab.DrawSearchFieldForTab();
828 return;
829 }
830
831 if (viewSwitcher.IsViewSelected(ViewSwitcher.SelectedTab.History))
832 {
833 viewSwitcher.HistoryTab.DrawSearchFieldForTab();
834 return;
835 }
836 }
837
838 static void DoToolbarButtons(
839 WorkspaceInfo wkInfo,
840 RepositorySpec repSpec,
841 ViewSwitcher viewSwitcher,
842 LaunchTool.IShowDownloadPlasticExeWindow showDownloadPlasticExeWindow,
843 LaunchTool.IProcessExecutor processExecutor,
844 bool isGluonMode,
845 bool isCloudOrganization,
846 bool isUnityOrganization,
847 bool isUGOSubscription)
848 {
849 if (viewSwitcher.IsViewSelected(ViewSwitcher.SelectedTab.Changesets))
850 {
851 viewSwitcher.ChangesetsTab.DrawDateFilter();
852 }
853 else if (viewSwitcher.IsViewSelected(ViewSwitcher.SelectedTab.Branches))
854 {
855 viewSwitcher.BranchesTab.DrawShowHiddenBranchesButton();
856 viewSwitcher.BranchesTab.DrawDateFilter();
857 }
858 else
859 {
860 DrawStaticElement.Empty();
861 }
862
863 if (viewSwitcher.IsViewSelected(ViewSwitcher.SelectedTab.Shelves))
864 {
865 viewSwitcher.ShelvesTab.DrawOwnerFilter();
866 }
867
868 if (DrawToolbarButton(
869 Images.GetRefreshIcon(),
870 PlasticLocalization.Name.RefreshButton.GetString()))
871 {
872 viewSwitcher.RefreshSelectedView();
873 }
874
875 if (isGluonMode)
876 {
877 if (DrawActionButton.For(PlasticLocalization.Name.Configure.GetString()))
878 {
879 LaunchTool.OpenWorkspaceConfiguration(
880 showDownloadPlasticExeWindow, processExecutor, wkInfo, isGluonMode);
881 }
882 }
883 else
884 {
885 DrawStaticElement.Empty();
886 }
887
888 if (DrawToolbarButton(
889 Images.GetShelveIcon(),
890 PlasticLocalization.Name.ShowShelvesButton.GetString()))
891 {
892 TrackFeatureUseEvent.For(
893 repSpec,
894 TrackFeatureUseEvent.Features.UnityPackage.ShowShelvesViewFromToolbarButton);
895
896 viewSwitcher.ShowShelvesView();
897 }
898
899 if (DrawToolbarButton(
900 Images.GetBranchesIcon(),
901 PlasticLocalization.Name.Branches.GetString()))
902 {
903 viewSwitcher.ShowBranchesView();
904 }
905
906 if (!isGluonMode)
907 {
908 if (DrawToolbarButton(
909 Images.GetBranchExplorerIcon(),
910 PlasticLocalization.Name.BranchExplorerMenu.GetString()))
911 {
912 LaunchTool.OpenBranchExplorer(
913 showDownloadPlasticExeWindow, processExecutor, wkInfo, isGluonMode);
914 }
915 }
916 else
917 {
918 DrawStaticElement.Empty();
919 }
920
921 if (DrawToolbarButton(
922 Images.GetLockIcon(),
923 PlasticLocalization.Name.ShowLocks.GetString()))
924 {
925 viewSwitcher.ShowLocksView();
926 }
927
928 if (isCloudOrganization)
929 {
930 if (DrawToolbarButton(
931 Images.GetInviteUsersIcon(),
932 isUnityOrganization
933 ? PlasticLocalization.Name.InviteMembersToProject.GetString()
934 : PlasticLocalization.Name.InviteMembersToOrganization.GetString()))
935 {
936 InviteMembers(repSpec);
937 }
938 }
939 else
940 {
941 DrawStaticElement.Empty();
942 }
943
944 if (isCloudOrganization && isUGOSubscription)
945 {
946 if (DrawToolbarTextButton(PlasticLocalization.Name.UpgradePlan.GetString()))
947 {
948 OpenDevOpsUpgradePlanUrl();
949 }
950 }
951 else
952 {
953 DrawStaticElement.Empty();
954 }
955
956 //TODO: Add settings button tooltip localization
957 if (DrawToolbarButton(Images.GetSettingsIcon(), string.Empty))
958 {
959 ShowSettingsContextMenu(
960 showDownloadPlasticExeWindow,
961 processExecutor,
962 wkInfo,
963 isGluonMode,
964 isCloudOrganization);
965 }
966 }
967
968 static bool DrawToolbarButton(Texture icon, string tooltip)
969 {
970 return GUILayout.Button(
971 new GUIContent(icon, tooltip),
972 EditorStyles.toolbarButton,
973 GUILayout.Width(26));
974 }
975
976 static bool DrawToolbarTextButton(string text)
977 {
978 return GUILayout.Button(
979 new GUIContent(text, string.Empty),
980 EditorStyles.toolbarButton);
981 }
982
983 static void InviteMembers(RepositorySpec repSpec)
984 {
985 string organizationName = ServerOrganizationParser.GetOrganizationFromServer(repSpec.Server);
986
987 CurrentUserAdminCheckResponse response = null;
988
989 IThreadWaiter waiter = ThreadWaiter.GetWaiter(50);
990 waiter.Execute(
991 /*threadOperationDelegate*/
992 delegate
993 {
994 string authToken = AuthToken.GetForServer(repSpec.Server);
995
996 if (string.IsNullOrEmpty(authToken))
997 {
998 return;
999 }
1000
1001 response = WebRestApiClient.PlasticScm.IsUserAdmin(organizationName, authToken);
1002 },
1003 /*afterOperationDelegate*/
1004 delegate
1005 {
1006 if (waiter.Exception != null)
1007 {
1008 ExceptionsHandler.LogException("IsUserAdmin", waiter.Exception);
1009
1010 OpenUnityDashboardInviteUsersUrl(repSpec);
1011 return;
1012 }
1013
1014 if (response == null)
1015 {
1016 mLog.DebugFormat(
1017 "Error checking if the user is the organization admin for {0}",
1018 organizationName);
1019
1020 OpenUnityDashboardInviteUsersUrl(repSpec);
1021 return;
1022 }
1023
1024 if (response.Error != null)
1025 {
1026 mLog.DebugFormat(
1027 "Error checking if the user is the organization admin: {0}",
1028 string.Format("Unable to get IsUserAdminResponse: {0} [code {1}]",
1029 response.Error.Message,
1030 response.Error.ErrorCode));
1031
1032 OpenUnityDashboardInviteUsersUrl(repSpec);
1033 return;
1034 }
1035
1036 if (!response.IsCurrentUserAdmin)
1037 {
1038 GuiMessage.ShowInformation(
1039 PlasticLocalization.GetString(PlasticLocalization.Name.InviteMembersTitle),
1040 PlasticLocalization.GetString(PlasticLocalization.Name.InviteMembersToOrganizationNotAdminError));
1041
1042 return;
1043 }
1044
1045 OpenUnityDashboardInviteUsersUrl(repSpec);
1046 });
1047 }
1048
1049 static void OpenUnityDashboardInviteUsersUrl(RepositorySpec repSpec)
1050 {
1051 OpenInviteUsersPage.Run(repSpec, UnityUrl.UnityDashboard.UnityCloudRequestSource.Editor);
1052 }
1053
1054 static void ShowSettingsContextMenu(
1055 LaunchTool.IShowDownloadPlasticExeWindow showDownloadPlasticExeWindow,
1056 LaunchTool.IProcessExecutor processExecutor,
1057 WorkspaceInfo wkInfo,
1058 bool isGluonMode,
1059 bool isCloudOrganization)
1060 {
1061 GenericMenu menu = new GenericMenu();
1062
1063 string openToolText = isGluonMode ?
1064 PlasticLocalization.Name.OpenInGluon.GetString() :
1065 PlasticLocalization.Name.OpenInDesktopApp.GetString();
1066
1067 menu.AddItem(
1068 new GUIContent(openToolText),
1069 false,
1070 () => LaunchTool.OpenGUIForMode(
1071 showDownloadPlasticExeWindow,
1072 processExecutor,
1073 wkInfo,
1074 isGluonMode));
1075
1076 if (isCloudOrganization)
1077 {
1078 menu.AddItem(
1079 new GUIContent(PlasticLocalization.Name.OpenInUnityCloud.GetString()),
1080 false,
1081 () => OpenUnityCloudRepository.Run(wkInfo));
1082 }
1083
1084 menu.AddSeparator(string.Empty);
1085
1086 menu.AddItem(
1087 new GUIContent(PlasticLocalization.Name.Settings.GetString()),
1088 false,
1089 OpenPlasticProjectSettings.ByDefault);
1090
1091 menu.AddItem(
1092 new GUIContent(PlasticAssetModificationProcessor.IsManualCheckoutEnabled ?
1093 PlasticLocalization.Name.DisableForcedCheckout.GetString() :
1094 PlasticLocalization.Name.EnableForcedCheckout.GetString()),
1095 false,
1096 () => PlasticAssetModificationProcessor.SetManualCheckoutPreference(
1097 !PlasticAssetModificationProcessor.IsManualCheckoutEnabled));
1098
1099 menu.ShowAsContext();
1100 }
1101
1102 static void OpenDevOpsUpgradePlanUrl()
1103 {
1104 Application.OpenURL(UnityUrl.DevOps.GetSignUp());
1105 }
1106
1107 static void DisableVCSIfEnabled(string projectPath)
1108 {
1109 if (!VCSPlugin.IsEnabled())
1110 return;
1111
1112 VCSPlugin.Disable();
1113
1114 mLog.DebugFormat("Disabled VCS Plugin on Project: {0}",
1115 projectPath);
1116 }
1117
1118 static void DisposeNewIncomingChanges(PlasticWindow window)
1119 {
1120 NewIncomingChanges.DisposeUpdater(
1121 window.mDeveloperNewIncomingChangesUpdater,
1122 window.mGluonNewIncomingChangesUpdater);
1123
1124 window.mDeveloperNewIncomingChangesUpdater = null;
1125 window.mGluonNewIncomingChangesUpdater = null;
1126 }
1127
1128 static void DisposeShelvedChanges(PlasticWindow window)
1129 {
1130 if (window.mShelvedChangesUpdater == null)
1131 return;
1132
1133 window.mShelvedChangesUpdater.Dispose();
1134 window.mShelvedChangesUpdater = null;
1135 }
1136
1137 static void DisposeNotificationBarUpdater(PlasticWindow window)
1138 {
1139 if (window.mNotificationBarUpdater == null)
1140 return;
1141
1142 window.mNotificationBarUpdater.Dispose();
1143 window.mNotificationBarUpdater = null;
1144 }
1145
1146 static void RegisterApplicationFocusHandlers(PlasticWindow window)
1147 {
1148 EditorWindowFocus.OnApplicationActivated += window.OnApplicationActivated;
1149 EditorWindowFocus.OnApplicationDeactivated += window.OnApplicationDeactivated;
1150 }
1151
1152 static void UnRegisterApplicationFocusHandlers(PlasticWindow window)
1153 {
1154 EditorWindowFocus.OnApplicationActivated -= window.OnApplicationActivated;
1155 EditorWindowFocus.OnApplicationDeactivated -= window.OnApplicationDeactivated;
1156 }
1157
1158 static void InitializePlasticOnForceToReOpen(PlasticWindow window)
1159 {
1160 if (window.mWkInfo == null)
1161 return;
1162
1163 window.mViewSwitcher.OnEnable();
1164
1165 window.InitializeNewIncomingChanges(
1166 window.mWkInfo,
1167 window.mViewSwitcher,
1168 window.mIsGluonMode);
1169 window.InitializeShelvedChanges(
1170 window.mWkInfo,
1171 window.mRepSpec,
1172 window.mViewSwitcher,
1173 window.mShowDownloadPlasticExeWindow,
1174 window.mIsGluonMode);
1175
1176 PlasticApp.RegisterWorkspaceWindow(
1177 window.mWorkspaceWindow);
1178
1179 if (PlasticPlugin.WorkspaceOperationsMonitor != null)
1180 {
1181 PlasticPlugin.WorkspaceOperationsMonitor.RegisterWindow(
1182 window.mWorkspaceWindow,
1183 window.mViewHost,
1184 window.mDeveloperNewIncomingChangesUpdater);
1185 }
1186
1187 if (!EditionToken.IsCloudEdition())
1188 return;
1189
1190 window.InitializeNotificationBarUpdater(
1191 window.mWkInfo,
1192 window.mStatusBar.NotificationBar);
1193 }
1194
1195 static void ClosePlastic(PlasticWindow window)
1196 {
1197 if (window.mViewSwitcher != null)
1198 window.mViewSwitcher.OnDisable();
1199
1200 PlasticApp.UnRegisterWorkspaceWindow();
1201
1202 if (PlasticPlugin.WorkspaceOperationsMonitor != null)
1203 PlasticPlugin.WorkspaceOperationsMonitor.UnRegisterWindow();
1204
1205 DisposeNewIncomingChanges(window);
1206 DisposeShelvedChanges(window);
1207
1208 DisposeNotificationBarUpdater(window);
1209
1210 AvatarImages.Dispose();
1211 }
1212
1213 static void ReOpenPlasticWindow(PlasticWindow closedWindow)
1214 {
1215 EditorWindow dockWindow = FindEditorWindow.ToDock<PlasticWindow>();
1216
1217 PlasticWindow newWindow = InstantiateFrom(closedWindow);
1218
1219 InitializePlasticOnForceToReOpen(newWindow);
1220
1221 if (DockEditorWindow.IsAvailable())
1222 DockEditorWindow.To(dockWindow, newWindow);
1223
1224 newWindow.Show();
1225 newWindow.Focus();
1226 }
1227
1228 static bool NeedsToDisplayWelcomeView(
1229 bool clientNeedsConfiguration,
1230 WorkspaceInfo wkInfo)
1231 {
1232 if (clientNeedsConfiguration)
1233 return true;
1234
1235 if (wkInfo == null)
1236 return true;
1237
1238 return false;
1239 }
1240
1241 static bool IsExitGUIException(Exception ex)
1242 {
1243 return ex is ExitGUIException;
1244 }
1245
1246 static bool IsIMGUIPaintException(Exception ex)
1247 {
1248 if (!(ex is ArgumentException))
1249 return false;
1250
1251 return ex.Message.StartsWith("Getting control") &&
1252 ex.Message.Contains("controls when doing repaint");
1253 }
1254
1255 static PlasticWindow InstantiateFrom(PlasticWindow window)
1256 {
1257 PlasticWindow result = Instantiate(window);
1258 result.mIsGluonMode = window.mIsGluonMode;
1259 result.mIsCloudOrganization = window.mIsCloudOrganization;
1260 result.mIsUnityOrganization = window.mIsUnityOrganization;
1261 result.mIsUGOSubscription = window.mIsUGOSubscription;
1262 result.mLastUpdateTime = window.mLastUpdateTime;
1263 result.mViewSwitcherState = window.mViewSwitcherState;
1264 result.mCurrentUser = window.mCurrentUser;
1265 result.mException = window.mException;
1266 result.mWkInfo = window.mWkInfo;
1267 result.mRepSpec = window.mRepSpec;
1268 result.mWelcomeView = window.mWelcomeView;
1269 result.mViewSwitcher = window.mViewSwitcher;
1270 result.mWorkspaceWindow = window.mWorkspaceWindow;
1271 result.mStatusBar = window.mStatusBar;
1272 result.mViewHost = window.mViewHost;
1273 result.mCooldownAutoRefreshChangesAction = window.mCooldownAutoRefreshChangesAction;
1274 result.mIncomingChangesNotification = window.mIncomingChangesNotification;
1275 result.mShelvedChangesNotification = window.mShelvedChangesNotification;
1276 result.mNotificationBarUpdater = window.mNotificationBarUpdater;
1277 result.mDeveloperNewIncomingChangesUpdater = window.mDeveloperNewIncomingChangesUpdater;
1278 result.mGluonNewIncomingChangesUpdater = window.mGluonNewIncomingChangesUpdater;
1279 result.mShelvedChangesUpdater = window.mShelvedChangesUpdater;
1280 result.mSaveAssets = window.mSaveAssets;
1281 return result;
1282 }
1283
1284 static class Reload
1285 {
1286 internal static void IfWorkspaceConfigChanged(
1287 IPlasticAPI plasticApi,
1288 WorkspaceInfo lastWkInfo,
1289 bool lastIsGluonMode,
1290 Action reloadAction)
1291 {
1292 string applicationPath = ApplicationDataPath.Get();
1293
1294 bool isGluonMode = false;
1295 WorkspaceInfo wkInfo = null;
1296
1297 IThreadWaiter waiter = ThreadWaiter.GetWaiter(10);
1298 waiter.Execute(
1299 /*threadOperationDelegate*/ delegate
1300 {
1301 wkInfo = FindWorkspace.
1302 InfoForApplicationPath(applicationPath, plasticApi);
1303
1304 if (wkInfo != null)
1305 isGluonMode = plasticApi.IsGluonWorkspace(wkInfo);
1306 },
1307 /*afterOperationDelegate*/ delegate
1308 {
1309 if (waiter.Exception != null)
1310 return;
1311
1312 if (!IsWorkspaceConfigChanged(
1313 lastWkInfo, wkInfo,
1314 lastIsGluonMode, isGluonMode))
1315 return;
1316
1317 reloadAction();
1318 });
1319 }
1320
1321 static bool IsWorkspaceConfigChanged(
1322 WorkspaceInfo lastWkInfo,
1323 WorkspaceInfo currentWkInfo,
1324 bool lastIsGluonMode,
1325 bool currentIsGluonMode)
1326 {
1327 if (lastIsGluonMode != currentIsGluonMode)
1328 return true;
1329
1330 if (lastWkInfo == null)
1331 return currentWkInfo != null;
1332
1333 return !lastWkInfo.Equals(currentWkInfo);
1334 }
1335 }
1336
1337 [SerializeField]
1338 bool mForceToReOpen;
1339 bool mIsGluonMode;
1340 bool mIsCloudOrganization;
1341 bool mIsUnityOrganization;
1342 bool mIsUGOSubscription;
1343 double mLastUpdateTime = 0f;
1344 ViewSwitcherState mViewSwitcherState = new ViewSwitcherState();
1345
1346 ResolvedUser mCurrentUser;
1347
1348 Exception mException;
1349 object mCurrentUserLock = new object();
1350 WelcomeView mWelcomeView;
1351 WorkspaceWindow mWorkspaceWindow;
1352 StatusBar mStatusBar;
1353
1354 [NonSerialized]
1355 WorkspaceInfo mWkInfo;
1356 RepositorySpec mRepSpec;
1357 ViewSwitcher mViewSwitcher;
1358 CooldownWindowDelayer mCooldownAutoRefreshChangesAction;
1359 StatusBar.IIncomingChangesNotification mIncomingChangesNotification;
1360 StatusBar.IShelvedChangesNotification mShelvedChangesNotification;
1361 ViewHost mViewHost;
1362 NotificationBarUpdater mNotificationBarUpdater;
1363 NewIncomingChangesUpdater mDeveloperNewIncomingChangesUpdater;
1364 GluonNewIncomingChangesUpdater mGluonNewIncomingChangesUpdater;
1365 ShelvedChangesUpdater mShelvedChangesUpdater;
1366 ISaveAssets mSaveAssets;
1367
1368 LaunchTool.IShowDownloadPlasticExeWindow mShowDownloadPlasticExeWindow =
1369 new LaunchTool.ShowDownloadPlasticExeWindow();
1370 LaunchTool.IProcessExecutor mProcessExecutor =
1371 new LaunchTool.ProcessExecutor();
1372
1373 const string UGO_ORDER_SOURCE = "UGO";
1374
1375 static readonly ILog mLog = PlasticApp.GetLogger("PlasticWindow");
1376 }
1377}