No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PlasticWindow.cs 42KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237
  1. using System;
  2. using System.Linq;
  3. using System.Threading.Tasks;
  4. using UnityEditor;
  5. using UnityEngine;
  6. using Codice.Client.Common;
  7. using Codice.Client.Common.EventTracking;
  8. using Codice.Client.Common.FsNodeReaders.Watcher;
  9. using Codice.Client.Common.Threading;
  10. using Codice.CM.Common;
  11. using Codice.LogWrapper;
  12. using GluonCheckIncomingChanges = PlasticGui.Gluon.WorkspaceWindow.CheckIncomingChanges;
  13. using GluonGui;
  14. using GluonNewIncomingChangesUpdater = PlasticGui.Gluon.WorkspaceWindow.NewIncomingChangesUpdater;
  15. using PlasticAssetModificationProcessor = Unity.PlasticSCM.Editor.AssetUtils.Processor.AssetModificationProcessor;
  16. using PlasticGui;
  17. using PlasticGui.WorkspaceWindow.NotificationBar;
  18. using Unity.PlasticSCM.Editor.AssetMenu;
  19. using Unity.PlasticSCM.Editor.AssetUtils;
  20. using Unity.PlasticSCM.Editor.Configuration;
  21. using Unity.PlasticSCM.Editor.Configuration.CloudEdition.Welcome;
  22. using Unity.PlasticSCM.Editor.Inspector;
  23. using Unity.PlasticSCM.Editor.Tool;
  24. using Unity.PlasticSCM.Editor.UI;
  25. using Unity.PlasticSCM.Editor.UI.Avatar;
  26. using Unity.PlasticSCM.Editor.UI.Progress;
  27. using Unity.PlasticSCM.Editor.UI.StatusBar;
  28. using Unity.PlasticSCM.Editor.Views.CreateWorkspace;
  29. using Unity.PlasticSCM.Editor.Views.Welcome;
  30. using Unity.PlasticSCM.Editor.WebApi;
  31. namespace Unity.PlasticSCM.Editor
  32. {
  33. internal class PlasticWindow : EditorWindow,
  34. PlasticGui.WorkspaceWindow.CheckIncomingChanges.IAutoRefreshIncomingChangesView,
  35. GluonCheckIncomingChanges.IAutoRefreshIncomingChangesView,
  36. CreateWorkspaceView.ICreateWorkspaceListener
  37. {
  38. internal WorkspaceWindow WorkspaceWindowForTesting { get { return mWorkspaceWindow; } }
  39. internal ViewSwitcher ViewSwitcherForTesting { get { return mViewSwitcher; } }
  40. internal CmConnection CmConnectionForTesting { get { return CmConnection.Get(); } }
  41. internal bool ShowWelcomeViewForTesting;
  42. internal void SetupWindowTitle(PlasticNotification.Status status)
  43. {
  44. Texture windowIcon = PlasticNotification.GetIcon(status);
  45. // The titleContent icon does not update unless we also update the title text
  46. // Temporarily doing it by adding space characters
  47. string title = UnityConstants.PLASTIC_WINDOW_TITLE;
  48. title += String.Concat(Enumerable.Repeat(" ", (int)status));
  49. titleContent = new GUIContent(title, windowIcon);
  50. }
  51. internal void DisableCollabIfEnabledWhenLoaded()
  52. {
  53. mDisableCollabIfEnabledWhenLoaded = true;
  54. }
  55. void PlasticGui.WorkspaceWindow.CheckIncomingChanges.IAutoRefreshIncomingChangesView.IfVisible()
  56. {
  57. mViewSwitcher.AutoRefreshIncomingChangesView();
  58. }
  59. void GluonCheckIncomingChanges.IAutoRefreshIncomingChangesView.IfVisible()
  60. {
  61. mViewSwitcher.AutoRefreshIncomingChangesView();
  62. }
  63. void CreateWorkspaceView.ICreateWorkspaceListener.OnWorkspaceCreated(
  64. WorkspaceInfo wkInfo, bool isGluonMode)
  65. {
  66. mWkInfo = wkInfo;
  67. mIsGluonMode = isGluonMode;
  68. mWelcomeView = null;
  69. PlasticPlugin.Enable();
  70. if (mIsGluonMode)
  71. ConfigurePartialWorkspace.AsFullyChecked(mWkInfo);
  72. InitializePlastic();
  73. Repaint();
  74. }
  75. internal void RefreshWorkspaceUI()
  76. {
  77. InitializePlastic();
  78. Repaint();
  79. OnFocus();
  80. }
  81. void OnEnable()
  82. {
  83. mLog.Debug("OnEnable");
  84. wantsMouseMove = true;
  85. if (mException != null)
  86. return;
  87. minSize = new Vector2(
  88. UnityConstants.PLASTIC_WINDOW_MIN_SIZE_WIDTH,
  89. UnityConstants.PLASTIC_WINDOW_MIN_SIZE_HEIGHT);
  90. SetupWindowTitle(PlasticNotification.Status.None);
  91. RegisterApplicationFocusHandlers(this);
  92. if (!PlasticPlugin.ConnectionMonitor.IsConnected)
  93. return;
  94. PlasticPlugin.Enable();
  95. InitializePlastic();
  96. }
  97. void OnDisable()
  98. {
  99. mLog.Debug("OnDisable");
  100. // We need to disable MonoFSWatcher because otherwise it hangs
  101. // when you move the window between monitors with different scale
  102. PlasticApp.DisableMonoFsWatcherIfNeeded();
  103. if (mException != null)
  104. return;
  105. if (mWkInfo == null)
  106. {
  107. ClosePlasticWindow(this);
  108. return;
  109. }
  110. mViewSwitcher.OnDisable();
  111. ClosePlasticWindow(this);
  112. }
  113. void OnDestroy()
  114. {
  115. mLog.Debug("OnDestroy");
  116. if (mException != null)
  117. return;
  118. if (mWkInfo == null)
  119. return;
  120. if (!mWorkspaceWindow.IsOperationInProgress())
  121. return;
  122. bool bCloseWindow = GuiMessage.ShowQuestion(
  123. PlasticLocalization.GetString(PlasticLocalization.Name.OperationRunning),
  124. PlasticLocalization.GetString(PlasticLocalization.Name.ConfirmClosingRunningOperation),
  125. PlasticLocalization.GetString(PlasticLocalization.Name.YesButton));
  126. if (bCloseWindow)
  127. return;
  128. mLog.Debug(
  129. "Show window again because the user doesn't want " +
  130. "to quit it due to there is an operation running");
  131. mForceToOpen = true;
  132. ShowPlasticWindow(this);
  133. }
  134. void OnFocus()
  135. {
  136. mLog.Debug("OnFocus");
  137. if (mException != null)
  138. return;
  139. if (mWkInfo == null)
  140. return;
  141. if (!PlasticPlugin.ConnectionMonitor.IsConnected)
  142. return;
  143. // We don't want to auto-refresh the views when the window
  144. // is focused due to a right mouse button click because
  145. // if there is no internet connection a dialog appears and
  146. // it prevents being able to open the context menu in order
  147. // to close the Plastic SCM window
  148. if (Mouse.IsRightMouseButtonPressed(Event.current))
  149. return;
  150. mCooldownAutoRefreshChangesAction.Ping();
  151. }
  152. void OnGUI()
  153. {
  154. if (!PlasticPlugin.ConnectionMonitor.IsConnected)
  155. {
  156. DoNotConnectedArea();
  157. return;
  158. }
  159. if (mException != null)
  160. {
  161. DoExceptionErrorArea();
  162. return;
  163. }
  164. try
  165. {
  166. // IMPORTANT: disable collab (if needed)
  167. // must be executed before the next if statement
  168. // where we check if collab is enabled
  169. if (mDisableCollabIfEnabledWhenLoaded)
  170. {
  171. mDisableCollabIfEnabledWhenLoaded = false;
  172. DisableCollabIfEnabled(ProjectPath.FromApplicationDataPath(
  173. ApplicationDataPath.Get()));
  174. }
  175. if (CollabPlugin.IsEnabled())
  176. {
  177. // execute Close() once after all inspectors update
  178. // to avoid our window to be drawn in back color
  179. EditorApplication.delayCall = Close;
  180. return;
  181. }
  182. bool clientNeedsConfiguration = UnityConfigurationChecker.NeedsConfiguration() || ShowWelcomeViewForTesting;
  183. var welcomeView = GetWelcomeView();
  184. if (clientNeedsConfiguration && welcomeView.autoLoginState == AutoLogin.State.Off)
  185. {
  186. welcomeView.autoLoginState = AutoLogin.State.Started;
  187. }
  188. if (welcomeView.autoLoginState == AutoLogin.State.OrganizationChoosed)
  189. {
  190. OnEnable();
  191. welcomeView.autoLoginState = AutoLogin.State.InitializingPlastic;
  192. }
  193. if (NeedsToDisplayWelcomeView(clientNeedsConfiguration, mWkInfo))
  194. {
  195. welcomeView.OnGUI(clientNeedsConfiguration);
  196. return;
  197. }
  198. //TODO: Codice - beta: hide the switcher until the update dialog is implemented
  199. //DrawGuiModeSwitcher.ForMode(
  200. // isGluonMode, plasticClient, changesTreeView, editorWindow);
  201. DoTabToolbar(
  202. mWkInfo,
  203. mViewSwitcher,
  204. mShowDownloadPlasticExeWindow,
  205. mProcessExecutor,
  206. mIsGluonMode,
  207. mIsCloudOrganization,
  208. mIsUGOSubscription,
  209. mUpgradePlanUrl);
  210. mViewSwitcher.TabViewGUI();
  211. if (mWorkspaceWindow.IsOperationInProgress())
  212. DrawProgressForOperations.For(
  213. mWorkspaceWindow, mWorkspaceWindow.Progress,
  214. position.width);
  215. mStatusBar.OnGUI(
  216. mWkInfo,
  217. mWorkspaceWindow,
  218. mViewSwitcher,
  219. mViewSwitcher,
  220. mIncomingChangesNotifier,
  221. mIsGluonMode);
  222. }
  223. catch (Exception ex)
  224. {
  225. if (IsExitGUIException(ex))
  226. throw;
  227. GUI.enabled = true;
  228. if (IsIMGUIPaintException(ex))
  229. {
  230. ExceptionsHandler.LogException("PlasticWindow", ex);
  231. return;
  232. }
  233. mException = ex;
  234. DoExceptionErrorArea();
  235. ExceptionsHandler.HandleException("OnGUI", ex);
  236. }
  237. }
  238. void Update()
  239. {
  240. if (mException != null)
  241. return;
  242. if (mWkInfo == null)
  243. return;
  244. try
  245. {
  246. double currentUpdateTime = EditorApplication.timeSinceStartup;
  247. double elapsedSeconds = currentUpdateTime - mLastUpdateTime;
  248. mViewSwitcher.Update();
  249. mWorkspaceWindow.OnParentUpdated(elapsedSeconds);
  250. if (mWelcomeView != null)
  251. mWelcomeView.Update();
  252. mLastUpdateTime = currentUpdateTime;
  253. }
  254. catch (Exception ex)
  255. {
  256. mException = ex;
  257. ExceptionsHandler.HandleException("Update", ex);
  258. }
  259. }
  260. internal void InitializePlastic()
  261. {
  262. if (mForceToOpen)
  263. {
  264. mForceToOpen = false;
  265. return;
  266. }
  267. try
  268. {
  269. if (UnityConfigurationChecker.NeedsConfiguration())
  270. return;
  271. mWkInfo = FindWorkspace.InfoForApplicationPath(
  272. ApplicationDataPath.Get(), PlasticGui.Plastic.API);
  273. if (mWkInfo == null)
  274. return;
  275. PlasticPlugin.EnableForWorkspace();
  276. DisableVCSIfEnabled(mWkInfo.ClientPath);
  277. mIsGluonMode = PlasticGui.Plastic.API.IsGluonWorkspace(mWkInfo);
  278. ViewHost viewHost = new ViewHost();
  279. mStatusBar = new StatusBar();
  280. mViewSwitcher = new ViewSwitcher(
  281. PlasticGui.Plastic.API.GetRepositorySpec(mWkInfo),
  282. mWkInfo,
  283. viewHost,
  284. mIsGluonMode,
  285. PlasticPlugin.AssetStatusCache,
  286. mShowDownloadPlasticExeWindow,
  287. mProcessExecutor,
  288. PlasticPlugin.WorkspaceOperationsMonitor,
  289. mStatusBar,
  290. this);
  291. InitializeNewIncomingChanges(mWkInfo, mIsGluonMode, mViewSwitcher);
  292. // Create a CooldownWindowDelayer to make the auto-refresh changes delayed.
  293. // In this way, we cover the following scenario:
  294. // * When Unity Editor window is activated it writes some files to its Temp
  295. // folder. This causes the fswatcher to process those events.
  296. // * We need to wait until the fswatcher finishes processing the events,
  297. // otherwise the NewChangesInWk method will return TRUE because there
  298. // are pending events to process, which causes an unwanted 'get pending
  299. // changes' operation when there are no new changes.
  300. // * So, we need to delay the auto-refresh call in order
  301. // to give the fswatcher enough time to process the events.
  302. mCooldownAutoRefreshChangesAction = new CooldownWindowDelayer(
  303. () =>
  304. {
  305. mViewSwitcher.AutoRefreshPendingChangesView();
  306. mViewSwitcher.AutoRefreshIncomingChangesView();
  307. },
  308. UnityConstants.AUTO_REFRESH_CHANGES_DELAYED_INTERVAL);
  309. mWorkspaceWindow = new WorkspaceWindow(
  310. mWkInfo,
  311. viewHost,
  312. mViewSwitcher,
  313. mViewSwitcher,
  314. mDeveloperNewIncomingChangesUpdater,
  315. this);
  316. mViewSwitcher.SetWorkspaceWindow(mWorkspaceWindow);
  317. mViewSwitcher.ShowInitialView();
  318. PlasticPlugin.WorkspaceOperationsMonitor.RegisterWindow(
  319. mWorkspaceWindow,
  320. viewHost,
  321. mDeveloperNewIncomingChangesUpdater);
  322. UnityStyles.Initialize(Repaint);
  323. AssetMenuItems.BuildOperations(
  324. mWkInfo,
  325. mWorkspaceWindow,
  326. mViewSwitcher,
  327. mViewSwitcher,
  328. viewHost,
  329. PlasticPlugin.WorkspaceOperationsMonitor,
  330. mDeveloperNewIncomingChangesUpdater,
  331. PlasticPlugin.AssetStatusCache,
  332. mViewSwitcher,
  333. mViewSwitcher,
  334. mShowDownloadPlasticExeWindow,
  335. mIsGluonMode);
  336. DrawInspectorOperations.BuildOperations(
  337. mWkInfo,
  338. mWorkspaceWindow,
  339. mViewSwitcher,
  340. mViewSwitcher,
  341. viewHost,
  342. PlasticPlugin.WorkspaceOperationsMonitor,
  343. mDeveloperNewIncomingChangesUpdater,
  344. PlasticPlugin.AssetStatusCache,
  345. mViewSwitcher,
  346. mViewSwitcher,
  347. mShowDownloadPlasticExeWindow,
  348. mIsGluonMode);
  349. mLastUpdateTime = EditorApplication.timeSinceStartup;
  350. mViewSwitcher.ShowBranchesViewIfNeeded();
  351. mViewSwitcher.ShowLocksViewIfNeeded();
  352. if (!EditionToken.IsCloudEdition())
  353. return;
  354. InitializeNotificationBarUpdater(
  355. mWkInfo, mStatusBar.NotificationBar);
  356. GetSubscriptionDetails(mWkInfo);
  357. }
  358. catch (Exception ex)
  359. {
  360. mException = ex;
  361. ExceptionsHandler.HandleException("InitializePlastic", ex);
  362. }
  363. }
  364. void InitializeNewIncomingChanges(
  365. WorkspaceInfo wkInfo,
  366. bool bIsGluonMode,
  367. ViewSwitcher viewSwitcher)
  368. {
  369. if (bIsGluonMode)
  370. {
  371. Gluon.IncomingChangesNotifier gluonNotifier =
  372. new Gluon.IncomingChangesNotifier(this);
  373. mGluonNewIncomingChangesUpdater =
  374. NewIncomingChanges.BuildUpdaterForGluon(
  375. wkInfo, viewSwitcher, gluonNotifier, this, gluonNotifier,
  376. new GluonCheckIncomingChanges.CalculateIncomingChanges());
  377. mIncomingChangesNotifier = gluonNotifier;
  378. return;
  379. }
  380. Developer.IncomingChangesNotifier developerNotifier =
  381. new Developer.IncomingChangesNotifier(this);
  382. mDeveloperNewIncomingChangesUpdater =
  383. NewIncomingChanges.BuildUpdaterForDeveloper(
  384. wkInfo, viewSwitcher, developerNotifier,
  385. this, developerNotifier);
  386. mIncomingChangesNotifier = developerNotifier;
  387. }
  388. void InitializeNotificationBarUpdater(
  389. WorkspaceInfo wkInfo,
  390. INotificationBar notificationBar)
  391. {
  392. mNotificationBarUpdater = new NotificationBarUpdater(
  393. notificationBar,
  394. PlasticGui.Plastic.WebRestAPI,
  395. new UnityPlasticTimerBuilder(),
  396. new NotificationBarUpdater.NotificationBarConfig());
  397. mNotificationBarUpdater.Start();
  398. mNotificationBarUpdater.SetWorkspace(wkInfo);
  399. }
  400. void GetSubscriptionDetails(WorkspaceInfo wkInfo)
  401. {
  402. mIsCloudOrganization = false;
  403. mIsUGOSubscription = false;
  404. mUpgradePlanUrl = string.Empty;
  405. RepositorySpec repSpec = PlasticGui.Plastic.API.GetRepositorySpec(wkInfo);
  406. if (repSpec == null)
  407. {
  408. return;
  409. }
  410. mIsCloudOrganization = PlasticGui.Plastic.API.IsCloud(repSpec.Server);
  411. string organizationName = ServerOrganizationParser.GetOrganizationFromServer(repSpec.Server);
  412. Task.Run(
  413. () =>
  414. {
  415. string authToken = AuthToken.GetForServer(repSpec.Server);
  416. if (string.IsNullOrEmpty(authToken))
  417. return null;
  418. return WebRestApiClient.PlasticScm.GetSubscriptionDetails(
  419. organizationName,
  420. authToken);
  421. }).ContinueWith(
  422. t =>
  423. {
  424. if (t.Result == null)
  425. {
  426. mLog.DebugFormat("Error getting Subscription details for organization {0}", organizationName);
  427. return;
  428. }
  429. if (t.Result.OrganizationName != organizationName)
  430. {
  431. mLog.DebugFormat("Not requested organization ({0}) subscription has been received", t.Result.OrganizationName);
  432. return;
  433. }
  434. mIsUGOSubscription = t.Result.OrderSource == UGO_ORDER_SOURCE;
  435. mUpgradePlanUrl = t.Result.ExtraData.ContainsKey(UGO_CONSUMPTION_URL_KEY) && t.Result.IsAdmin ?
  436. t.Result.ExtraData[UGO_CONSUMPTION_URL_KEY].ToString() : UnityUrl.UnityDashboard.Plastic.Get();
  437. });
  438. }
  439. void OnApplicationActivated()
  440. {
  441. mLog.Debug("OnApplicationActivated");
  442. if (mException != null)
  443. return;
  444. if (!PlasticPlugin.ConnectionMonitor.IsConnected)
  445. return;
  446. Reload.IfWorkspaceConfigChanged(
  447. PlasticGui.Plastic.API, mWkInfo, mIsGluonMode,
  448. ExecuteFullReload);
  449. if (mWkInfo == null)
  450. return;
  451. ((IWorkspaceWindow)mWorkspaceWindow).UpdateTitle();
  452. NewIncomingChanges.LaunchUpdater(
  453. mDeveloperNewIncomingChangesUpdater,
  454. mGluonNewIncomingChangesUpdater);
  455. mCooldownAutoRefreshChangesAction.Ping();
  456. }
  457. void OnApplicationDeactivated()
  458. {
  459. mLog.Debug("OnApplicationDeactivated");
  460. if (mException != null)
  461. return;
  462. if (mWkInfo == null)
  463. return;
  464. if (!PlasticPlugin.ConnectionMonitor.IsConnected)
  465. return;
  466. NewIncomingChanges.StopUpdater(
  467. mDeveloperNewIncomingChangesUpdater,
  468. mGluonNewIncomingChangesUpdater);
  469. }
  470. void ExecuteFullReload()
  471. {
  472. mException = null;
  473. DisposeNewIncomingChanges(this);
  474. DisposeNotificationBarUpdater(this);
  475. InitializePlastic();
  476. }
  477. void DoNotConnectedArea()
  478. {
  479. string labelText = PlasticLocalization.GetString(
  480. PlasticLocalization.Name.NotConnectedTryingToReconnect);
  481. string buttonText = PlasticLocalization.GetString(
  482. PlasticLocalization.Name.TryNowButton);
  483. GUI.enabled = !PlasticPlugin.ConnectionMonitor.IsTryingReconnection;
  484. DrawActionHelpBox.For(
  485. Images.GetInfoDialogIcon(), labelText, buttonText,
  486. PlasticPlugin.ConnectionMonitor.CheckConnection);
  487. GUI.enabled = true;
  488. }
  489. void DoExceptionErrorArea()
  490. {
  491. string labelText = PlasticLocalization.GetString(
  492. PlasticLocalization.Name.UnexpectedError);
  493. string buttonText = PlasticLocalization.GetString(
  494. PlasticLocalization.Name.ReloadButton);
  495. DrawActionHelpBox.For(
  496. Images.GetErrorDialogIcon(), labelText, buttonText,
  497. ExecuteFullReload);
  498. }
  499. internal WelcomeView GetWelcomeView()
  500. {
  501. if (mWelcomeView != null)
  502. return mWelcomeView;
  503. mWelcomeView = new WelcomeView(
  504. this,
  505. this,
  506. PlasticGui.Plastic.API,
  507. PlasticGui.Plastic.WebRestAPI,
  508. CmConnection.Get());
  509. return mWelcomeView;
  510. }
  511. static void DoSearchField(ViewSwitcher viewSwitcher)
  512. {
  513. if (viewSwitcher.IsViewSelected(ViewSwitcher.SelectedTab.PendingChanges))
  514. {
  515. viewSwitcher.PendingChangesTab.DrawSearchFieldForPendingChangesTab();
  516. return;
  517. }
  518. if (viewSwitcher.IsViewSelected(ViewSwitcher.SelectedTab.Changesets))
  519. {
  520. viewSwitcher.ChangesetsTab.DrawSearchFieldForChangesetsTab();
  521. return;
  522. }
  523. if (viewSwitcher.IsViewSelected(ViewSwitcher.SelectedTab.History))
  524. {
  525. viewSwitcher.HistoryTab.DrawSearchFieldForHistoryTab();
  526. return;
  527. }
  528. if (viewSwitcher.IsViewSelected(ViewSwitcher.SelectedTab.Branches))
  529. {
  530. viewSwitcher.BranchesTab.DrawSearchFieldForBranchesTab();
  531. return;
  532. }
  533. if (viewSwitcher.IsViewSelected(ViewSwitcher.SelectedTab.Locks))
  534. {
  535. viewSwitcher.LocksTab.DrawSearchFieldForLocksTab();
  536. return;
  537. }
  538. }
  539. static void DoTabToolbar(
  540. WorkspaceInfo workspaceInfo,
  541. ViewSwitcher viewSwitcher,
  542. LaunchTool.IShowDownloadPlasticExeWindow showDownloadPlasticExeWindow,
  543. LaunchTool.IProcessExecutor processExecutor,
  544. bool isGluonMode,
  545. bool isCloudOrganization,
  546. bool isUGOSubscription,
  547. string upgradePlanUrl)
  548. {
  549. EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
  550. viewSwitcher.TabButtonsGUI();
  551. GUILayout.FlexibleSpace();
  552. DoSearchField(viewSwitcher);
  553. DoToolbarButtons(
  554. workspaceInfo,
  555. viewSwitcher,
  556. showDownloadPlasticExeWindow,
  557. processExecutor,
  558. isGluonMode,
  559. isCloudOrganization,
  560. isUGOSubscription,
  561. upgradePlanUrl);
  562. EditorGUILayout.EndHorizontal();
  563. }
  564. static void OpenBranchListViewAndSendEvent(
  565. WorkspaceInfo wkInfo,
  566. ViewSwitcher viewSwitcher)
  567. {
  568. viewSwitcher.ShowBranchesView();
  569. TrackFeatureUseEvent.For(
  570. PlasticGui.Plastic.API.GetRepositorySpec(wkInfo),
  571. TrackFeatureUseEvent.Features.OpenBranchesView);
  572. }
  573. static void ShowBranchesContextMenu(
  574. WorkspaceInfo wkInfo,
  575. ViewSwitcher viewSwitcher,
  576. LaunchTool.IShowDownloadPlasticExeWindow showDownloadPlasticExeWindow,
  577. LaunchTool.IProcessExecutor processExecutor,
  578. bool isGluonMode)
  579. {
  580. GenericMenu menu = new GenericMenu();
  581. string branchesListView = PlasticLocalization.GetString(
  582. PlasticLocalization.Name.Branches);
  583. menu.AddItem(
  584. new GUIContent(branchesListView),
  585. false,
  586. () => OpenBranchListViewAndSendEvent(wkInfo, viewSwitcher));
  587. string branchExplorer = PlasticLocalization.GetString(
  588. PlasticLocalization.Name.BranchExplorerMenu);
  589. menu.AddItem(
  590. new GUIContent(branchExplorer),
  591. false,
  592. () => LaunchTool.OpenBranchExplorer(
  593. showDownloadPlasticExeWindow,
  594. processExecutor,
  595. wkInfo,
  596. isGluonMode));
  597. menu.ShowAsContext();
  598. }
  599. static void OpenLocksViewAndSendEvent(
  600. WorkspaceInfo wkInfo,
  601. ViewSwitcher viewSwitcher)
  602. {
  603. viewSwitcher.ShowLocksView();
  604. TrackFeatureUseEvent.For(
  605. PlasticGui.Plastic.API.GetRepositorySpec(wkInfo),
  606. TrackFeatureUseEvent.Features.OpenLocksView);
  607. }
  608. static void ShowSettingsContextMenu(
  609. LaunchTool.IShowDownloadPlasticExeWindow showDownloadPlasticExeWindow,
  610. LaunchTool.IProcessExecutor processExecutor,
  611. WorkspaceInfo wkInfo,
  612. bool isGluonMode)
  613. {
  614. GenericMenu menu = new GenericMenu();
  615. string openToolText = isGluonMode ?
  616. PlasticLocalization.Name.OpenInGluon.GetString() :
  617. PlasticLocalization.Name.OpenInDesktopApp.GetString();
  618. menu.AddItem(
  619. new GUIContent(openToolText),
  620. false,
  621. () => LaunchTool.OpenGUIForMode(
  622. showDownloadPlasticExeWindow,
  623. processExecutor,
  624. wkInfo,
  625. isGluonMode));
  626. menu.AddSeparator("");
  627. menu.AddItem(
  628. new GUIContent(PlasticLocalization.Name.Options.GetString()),
  629. false,
  630. () => SettingsService.OpenProjectSettings(UnityConstants.PROJECT_SETTINGS_TAB_PATH));
  631. menu.AddItem(
  632. new GUIContent(PlasticAssetModificationProcessor.ForceCheckout ?
  633. PlasticLocalization.Name.DisableForcedCheckout.GetString() :
  634. PlasticLocalization.Name.EnableForcedCheckout.GetString()),
  635. false,
  636. ForceCheckout_Clicked,
  637. null);
  638. menu.ShowAsContext();
  639. }
  640. static void DoToolbarButtons(
  641. WorkspaceInfo wkInfo,
  642. ViewSwitcher viewSwitcher,
  643. LaunchTool.IShowDownloadPlasticExeWindow showDownloadPlasticExeWindow,
  644. LaunchTool.IProcessExecutor processExecutor,
  645. bool isGluonMode,
  646. bool isCloudOrganization,
  647. bool isUGOSubscription,
  648. string upgradePlanUrl)
  649. {
  650. //TODO: Codice - beta: hide the diff button until the behavior is implemented
  651. /*GUILayout.Button(PlasticLocalization.GetString(
  652. PlasticLocalization.Name.DiffWindowMenuItemDiff),
  653. EditorStyles.toolbarButton,
  654. GUILayout.Width(UnityConstants.REGULAR_BUTTON_WIDTH));*/
  655. if (viewSwitcher.IsViewSelected(ViewSwitcher.SelectedTab.Changesets))
  656. {
  657. viewSwitcher.ChangesetsTab.DrawDateFilter();
  658. }
  659. if (viewSwitcher.IsViewSelected(ViewSwitcher.SelectedTab.Branches))
  660. {
  661. viewSwitcher.BranchesTab.DrawDateFilter();
  662. }
  663. Texture refreshIcon = Images.GetRefreshIcon();
  664. string refreshIconTooltip = PlasticLocalization.GetString(
  665. PlasticLocalization.Name.RefreshButton);
  666. if (DrawToolbarButton(refreshIcon, refreshIconTooltip))
  667. {
  668. viewSwitcher.RefreshSelectedView();
  669. }
  670. if (viewSwitcher.IsViewSelected(ViewSwitcher.SelectedTab.PendingChanges))
  671. {
  672. Texture2D icon = Images.GetUndoIcon();
  673. string tooltip = PlasticLocalization.GetString(
  674. PlasticLocalization.Name.UndoSelectedChanges);
  675. if (DrawToolbarButton(icon, tooltip))
  676. {
  677. TrackFeatureUseEvent.For(
  678. PlasticGui.Plastic.API.GetRepositorySpec(wkInfo),
  679. TrackFeatureUseEvent.Features.UndoIconButton);
  680. viewSwitcher.PendingChangesTab.UndoForMode(wkInfo, isGluonMode);
  681. }
  682. }
  683. if (isGluonMode)
  684. {
  685. string label = PlasticLocalization.GetString(PlasticLocalization.Name.ConfigureGluon);
  686. if (DrawActionButton.For(label))
  687. LaunchTool.OpenWorkspaceConfiguration(
  688. showDownloadPlasticExeWindow,
  689. processExecutor,
  690. wkInfo,
  691. isGluonMode);
  692. }
  693. else
  694. {
  695. Texture2D icon = Images.GetBranchIcon();
  696. string tooltip = PlasticLocalization.GetString(PlasticLocalization.Name.Branches);
  697. if (DrawToolbarButton(icon, tooltip))
  698. {
  699. ShowBranchesContextMenu(
  700. wkInfo,
  701. viewSwitcher,
  702. showDownloadPlasticExeWindow,
  703. processExecutor,
  704. isGluonMode);
  705. }
  706. }
  707. if (DrawToolbarButton(Images.GetLockIcon(), PlasticLocalization.Name.ShowLocks.GetString()))
  708. {
  709. OpenLocksViewAndSendEvent(wkInfo, viewSwitcher);
  710. }
  711. if (isCloudOrganization)
  712. {
  713. if (DrawToolbarButton(
  714. Images.GetInviteUsersIcon(),
  715. PlasticLocalization.Name.InviteMembers.GetString()))
  716. {
  717. InviteMembersToOrganization(wkInfo);
  718. }
  719. if (isUGOSubscription)
  720. {
  721. if (DrawToolbarTextButton(
  722. PlasticLocalization.Name.UpgradePlan.GetString()))
  723. {
  724. UpgradePlan(upgradePlanUrl);
  725. }
  726. }
  727. }
  728. //TODO: Add settings button tooltip localization
  729. if (DrawToolbarButton(Images.GetSettingsIcon(), string.Empty))
  730. {
  731. ShowSettingsContextMenu(
  732. showDownloadPlasticExeWindow,
  733. processExecutor,
  734. wkInfo,
  735. isGluonMode);
  736. }
  737. }
  738. static bool DrawToolbarButton(Texture icon, string tooltip)
  739. {
  740. return GUILayout.Button(
  741. new GUIContent(icon, tooltip),
  742. EditorStyles.toolbarButton,
  743. GUILayout.Width(26));
  744. }
  745. static bool DrawToolbarTextButton(string text)
  746. {
  747. return GUILayout.Button(
  748. new GUIContent(text, string.Empty),
  749. EditorStyles.toolbarButton);
  750. }
  751. static void UpgradePlan(string upgradePlanUrl)
  752. {
  753. Application.OpenURL(upgradePlanUrl);
  754. }
  755. static void InviteMembersToOrganization(WorkspaceInfo wkInfo)
  756. {
  757. RepositorySpec repSpec = PlasticGui.Plastic.API.GetRepositorySpec(wkInfo);
  758. string organizationName = ServerOrganizationParser.
  759. GetOrganizationFromServer(repSpec.Server);
  760. CurrentUserAdminCheckResponse response = null;
  761. IThreadWaiter waiter = ThreadWaiter.GetWaiter(50);
  762. waiter.Execute(
  763. /*threadOperationDelegate*/
  764. delegate
  765. {
  766. string authToken = AuthToken.GetForServer(repSpec.Server);
  767. if (string.IsNullOrEmpty(authToken))
  768. {
  769. return;
  770. }
  771. response = WebRestApiClient.PlasticScm.IsUserAdmin(
  772. organizationName,
  773. authToken);
  774. },
  775. /*afterOperationDelegate*/
  776. delegate
  777. {
  778. if (waiter.Exception != null)
  779. {
  780. ExceptionsHandler.LogException(
  781. "IsUserAdmin",
  782. waiter.Exception);
  783. OpenUnityDashboardInviteUsersUrl(organizationName);
  784. return;
  785. }
  786. if (response == null)
  787. {
  788. mLog.DebugFormat(
  789. "Error checking if the user is the organization admin for {0}",
  790. organizationName);
  791. OpenUnityDashboardInviteUsersUrl(organizationName);
  792. return;
  793. }
  794. if (response.Error != null)
  795. {
  796. mLog.DebugFormat(
  797. "Error checking if the user is the organization admin: {0}",
  798. string.Format("Unable to get IsUserAdminResponse: {0} [code {1}]",
  799. response.Error.Message,
  800. response.Error.ErrorCode));
  801. OpenUnityDashboardInviteUsersUrl(organizationName);
  802. return;
  803. }
  804. if (response.IsCurrentUserAdmin)
  805. {
  806. OpenUnityDashboardInviteUsersUrl(response.OrganizationName);
  807. return;
  808. }
  809. GuiMessage.ShowInformation(
  810. PlasticLocalization.GetString(PlasticLocalization.Name.InviteMembersTitle),
  811. PlasticLocalization.GetString(PlasticLocalization.Name.InviteMembersMessage));
  812. });
  813. }
  814. static void OpenUnityDashboardInviteUsersUrl(string organization)
  815. {
  816. Application.OpenURL(UnityUrl.UnityDashboard.Plastic.GetForInviteUsers(
  817. organization, UnityUrl.UnityDashboard.UnityCloudRequestSource.Editor));
  818. }
  819. static void ForceCheckout_Clicked(object obj)
  820. {
  821. PlasticAssetModificationProcessor.SetForceCheckoutOption(
  822. !PlasticAssetModificationProcessor.ForceCheckout);
  823. }
  824. static void DisableVCSIfEnabled(string projectPath)
  825. {
  826. if (!VCSPlugin.IsEnabled())
  827. return;
  828. VCSPlugin.Disable();
  829. mLog.DebugFormat("Disabled VCS Plugin on Project: {0}",
  830. projectPath);
  831. }
  832. static void DisposeNewIncomingChanges(PlasticWindow window)
  833. {
  834. NewIncomingChanges.DisposeUpdater(
  835. window.mDeveloperNewIncomingChangesUpdater,
  836. window.mGluonNewIncomingChangesUpdater);
  837. window.mDeveloperNewIncomingChangesUpdater = null;
  838. window.mGluonNewIncomingChangesUpdater = null;
  839. }
  840. static void DisposeNotificationBarUpdater(PlasticWindow window)
  841. {
  842. if (window.mNotificationBarUpdater == null)
  843. return;
  844. window.mNotificationBarUpdater.Dispose();
  845. window.mNotificationBarUpdater = null;
  846. }
  847. static void RegisterApplicationFocusHandlers(PlasticWindow window)
  848. {
  849. EditorWindowFocus.OnApplicationActivated += window.OnApplicationActivated;
  850. EditorWindowFocus.OnApplicationDeactivated += window.OnApplicationDeactivated;
  851. }
  852. static void UnRegisterApplicationFocusHandlers(PlasticWindow window)
  853. {
  854. EditorWindowFocus.OnApplicationActivated -= window.OnApplicationActivated;
  855. EditorWindowFocus.OnApplicationDeactivated -= window.OnApplicationDeactivated;
  856. }
  857. static bool IsExitGUIException(Exception ex)
  858. {
  859. return ex is ExitGUIException;
  860. }
  861. static bool IsIMGUIPaintException(Exception ex)
  862. {
  863. if (!(ex is ArgumentException))
  864. return false;
  865. return ex.Message.StartsWith("Getting control") &&
  866. ex.Message.Contains("controls when doing repaint");
  867. }
  868. static void ClosePlasticWindow(PlasticWindow window)
  869. {
  870. UnRegisterApplicationFocusHandlers(window);
  871. if (PlasticPlugin.WorkspaceOperationsMonitor != null)
  872. PlasticPlugin.WorkspaceOperationsMonitor.UnRegisterWindow();
  873. DisposeNewIncomingChanges(window);
  874. DisposeNotificationBarUpdater(window);
  875. AvatarImages.Dispose();
  876. }
  877. static void ShowPlasticWindow(PlasticWindow window)
  878. {
  879. EditorWindow dockWindow = FindEditorWindow.ToDock<PlasticWindow>();
  880. PlasticWindow newPlasticWindow = InstantiateFrom(window);
  881. if (DockEditorWindow.IsAvailable())
  882. DockEditorWindow.To(dockWindow, newPlasticWindow);
  883. newPlasticWindow.Show();
  884. newPlasticWindow.Focus();
  885. }
  886. static bool NeedsToDisplayWelcomeView(
  887. bool clientNeedsConfiguration,
  888. WorkspaceInfo wkInfo)
  889. {
  890. if (clientNeedsConfiguration)
  891. return true;
  892. if (wkInfo == null)
  893. return true;
  894. return false;
  895. }
  896. static void DisableCollabIfEnabled(string projectPath)
  897. {
  898. if (!CollabPlugin.IsEnabled())
  899. return;
  900. CollabPlugin.Disable();
  901. mLog.DebugFormat("Disabled Collab Plugin on Project: {0}",
  902. projectPath);
  903. }
  904. static PlasticWindow InstantiateFrom(PlasticWindow window)
  905. {
  906. PlasticWindow result = Instantiate(window);
  907. result.mWkInfo = window.mWkInfo;
  908. result.mWorkspaceWindow = window.mWorkspaceWindow;
  909. result.mViewSwitcher = window.mViewSwitcher;
  910. result.mCooldownAutoRefreshChangesAction = window.mCooldownAutoRefreshChangesAction;
  911. result.mDeveloperNewIncomingChangesUpdater = window.mDeveloperNewIncomingChangesUpdater;
  912. result.mGluonNewIncomingChangesUpdater = window.mGluonNewIncomingChangesUpdater;
  913. result.mException = window.mException;
  914. result.mLastUpdateTime = window.mLastUpdateTime;
  915. result.mIsGluonMode = window.mIsGluonMode;
  916. result.mIncomingChangesNotifier = window.mIncomingChangesNotifier;
  917. result.mStatusBar = window.mStatusBar;
  918. result.mWelcomeView = window.mWelcomeView;
  919. result.mNotificationBarUpdater = window.mNotificationBarUpdater;
  920. return result;
  921. }
  922. internal PlasticProjectSettingsProvider.IAutoRefreshView GetPendingChangesView()
  923. {
  924. return mViewSwitcher != null ? mViewSwitcher.PendingChangesTab : null;
  925. }
  926. static class Reload
  927. {
  928. internal static void IfWorkspaceConfigChanged(
  929. IPlasticAPI plasticApi,
  930. WorkspaceInfo lastWkInfo,
  931. bool lastIsGluonMode,
  932. Action reloadAction)
  933. {
  934. string applicationPath = ApplicationDataPath.Get();
  935. bool isGluonMode = false;
  936. WorkspaceInfo wkInfo = null;
  937. IThreadWaiter waiter = ThreadWaiter.GetWaiter(10);
  938. waiter.Execute(
  939. /*threadOperationDelegate*/ delegate
  940. {
  941. wkInfo = FindWorkspace.
  942. InfoForApplicationPath(applicationPath, plasticApi);
  943. if (wkInfo != null)
  944. isGluonMode = plasticApi.IsGluonWorkspace(wkInfo);
  945. },
  946. /*afterOperationDelegate*/ delegate
  947. {
  948. if (waiter.Exception != null)
  949. return;
  950. if (!IsWorkspaceConfigChanged(
  951. lastWkInfo, wkInfo,
  952. lastIsGluonMode, isGluonMode))
  953. return;
  954. reloadAction();
  955. });
  956. }
  957. static bool IsWorkspaceConfigChanged(
  958. WorkspaceInfo lastWkInfo,
  959. WorkspaceInfo currentWkInfo,
  960. bool lastIsGluonMode,
  961. bool currentIsGluonMode)
  962. {
  963. if (lastIsGluonMode != currentIsGluonMode)
  964. return true;
  965. if (lastWkInfo == null || currentWkInfo == null)
  966. return true;
  967. return !lastWkInfo.Equals(currentWkInfo);
  968. }
  969. }
  970. [SerializeField]
  971. bool mForceToOpen;
  972. [NonSerialized]
  973. WorkspaceInfo mWkInfo;
  974. Exception mException;
  975. internal IIncomingChangesNotifier mIncomingChangesNotifier;
  976. double mLastUpdateTime = 0f;
  977. CooldownWindowDelayer mCooldownAutoRefreshChangesAction;
  978. internal ViewSwitcher mViewSwitcher;
  979. WelcomeView mWelcomeView;
  980. StatusBar mStatusBar;
  981. NotificationBarUpdater mNotificationBarUpdater;
  982. PlasticGui.WorkspaceWindow.NewIncomingChangesUpdater mDeveloperNewIncomingChangesUpdater;
  983. GluonNewIncomingChangesUpdater mGluonNewIncomingChangesUpdater;
  984. WorkspaceWindow mWorkspaceWindow;
  985. bool mIsGluonMode;
  986. bool mDisableCollabIfEnabledWhenLoaded;
  987. bool mIsCloudOrganization;
  988. bool mIsUGOSubscription;
  989. string mUpgradePlanUrl;
  990. LaunchTool.IShowDownloadPlasticExeWindow mShowDownloadPlasticExeWindow =
  991. new LaunchTool.ShowDownloadPlasticExeWindow();
  992. LaunchTool.IProcessExecutor mProcessExecutor =
  993. new LaunchTool.ProcessExecutor();
  994. const string UGO_CONSUMPTION_URL_KEY = "consumptionUrl";
  995. const string UGO_ORDER_SOURCE = "UGO";
  996. static readonly ILog mLog = PlasticApp.GetLogger("PlasticWindow");
  997. }
  998. }