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}