using System; using System.Linq; using System.Text; using System.Threading.Tasks; using UnityEngine; using Codice.Client.Common; using Codice.Client.Common.Threading; using Codice.CM.Common; using Codice.LogWrapper; using PlasticGui; using PlasticGui.WorkspaceWindow; using PlasticGui.WorkspaceWindow.Topbar; using PlasticGui.WorkspaceWindow.Topbar.WorkingObjectInfo.BranchesList; using Unity.PlasticSCM.Editor.Toolbar.PopupWindow; using Unity.PlasticSCM.Editor.Toolbar.PopupWindow.Operations; using Unity.PlasticSCM.Editor.Toolbar.PopupWindow.BranchesList; namespace Unity.PlasticSCM.Editor.Toolbar { internal interface IUpdateToolbarButtonVisibility { void Show(); void Hide(); } internal class ToolbarController : UpdateWorkspaceInfoBar.IWorkingObjectInfoPanel, IRefreshableView, IUpdateToolbarButtonVisibility { public const string ToolbarButtonPath = "Services/Unity Version Control"; // used by Unity 6.3+ public event Action OnToolbarButtonInvalidated = delegate { }; public event Action OnToolbarInvalidated = delegate { }; internal ToolbarController(UVCSPlugin uvcsPlugin) { mUVCSPlugin = uvcsPlugin; } internal bool IsControlledProject() { return mWkInfo != null; } internal void PopupClicked(Rect rect) { Vector2 buttonBottom = new Vector2(rect.x, rect.y + rect.height); if (IsControlledProject()) { ShowControlledPopup(buttonBottom); return; } ShowUncontrolledPopup(buttonBottom); } internal void SetWorkspace( WorkspaceInfo wkInfo, bool isGluonMode) { mWkInfo = wkInfo; mIsGluonMode = isGluonMode; mModel = null; mWorkingBranch = null; if (mWkInfo != null) { // Workspace is controlled - refresh workspace info and load branches RefreshWorkspaceWorkingInfo(); LoadBranches(); } else { // Workspace is uncontrolled - reset to defaults and refresh the button ResetToolbarButtonData(); FireOnToolbarButtonInvalidated(); } } internal void LoadBranches() { if (mWkInfo == null) return; PlasticThreadPool.Run(delegate { SetCalculatedModel(CalculateBranchesListModel(mWkInfo)); }); } internal void RefreshWorkspaceWorkingInfo() { if (mWkInfo == null) return; UpdateWorkspaceInfoBar.UpdateWorkspaceInfo( mWkInfo, null, this); } internal void UpdateIcon(Texture icon) { mDropDownButtonData.Icon = icon; FireOnToolbarButtonInvalidated(); } internal void UpdatePendingChangesInfoTooltipText(string pendingChangesInfoTooltipText) { mButtonTooltipData.PendingChangesInfo = pendingChangesInfoTooltipText; UpdateButtonTooltip(mButtonTooltipData); } internal void UpdateIncomingChangesInfoTooltipText(string incomingChangesInfoTooltipText) { mButtonTooltipData.IncomingChangesInfo = incomingChangesInfoTooltipText; UpdateButtonTooltip(mButtonTooltipData); } internal UVCSToolbarButtonData GetButtonData() { if (mDropDownButtonData.Icon == null) mDropDownButtonData.Icon = UVCSPlugin.Instance.GetPluginStatusIcon(); return mDropDownButtonData; } internal void Enable() { mDropDownButtonData.IsVisible = UVCSToolbarButtonIsShownPreference.IsEnabled(); FireOnToolbarButtonInvalidated(); } internal void Disable() { mDropDownButtonData.IsVisible = false; FireOnToolbarButtonInvalidated(); } void IUpdateToolbarButtonVisibility.Show() { UVCSToolbarButtonIsShownPreference.Enable(); mDropDownButtonData.IsVisible = true; FireOnToolbarButtonInvalidated(); } void IUpdateToolbarButtonVisibility.Hide() { #if UNITY_6000_3_OR_NEWER UnityEditor.Toolbars.MainToolbar.HideAll(ToolbarButtonPath); #else UVCSToolbarButtonIsShownPreference.Disable(); mDropDownButtonData.IsVisible = false; FireOnToolbarButtonInvalidated(); #endif } void IRefreshableView.Refresh() { LoadBranches(); } void UpdateWorkspaceInfoBar.IWorkingObjectInfoPanel.UpdateInfo( string objectType, string objectName, string repositoryName, string serverName) { if (mWkInfo == null) return; mDropDownButtonData.Text = GetShorten.ObjectName( objectName, objectType); FireOnToolbarButtonInvalidated(); string serverForDisplay = ResolveServer.ToDisplayString(serverName); mButtonTooltipData.WorkingObjectType = objectType; mButtonTooltipData.WorkingObjectName = objectName; mButtonTooltipData.RepositoryName = repositoryName; mButtonTooltipData.ServerName = serverForDisplay; UpdateButtonTooltip(mButtonTooltipData); UpdateWorkingBranch( objectType, objectName, repositoryName, serverName); } void UpdateWorkspaceInfoBar.IWorkingObjectInfoPanel.UpdateComment( string comment, bool bFailed) { if (mWkInfo == null) return; string commentText = string.IsNullOrEmpty(comment) ? PlasticLocalization.Name.NoCommentSet.GetString() : comment; mButtonTooltipData.Comment = commentText; UpdateButtonTooltip(mButtonTooltipData); } void UpdateButtonTooltip(ButtonTooltipData buttonTooltipData) { if (mWkInfo == null) return; StringBuilder sb = new StringBuilder(); sb.Append(string.Format( "{0}: {1}@{2}@{3}", buttonTooltipData.WorkingObjectType, buttonTooltipData.WorkingObjectName, buttonTooltipData.RepositoryName, buttonTooltipData.ServerName)); if (!string.IsNullOrEmpty(buttonTooltipData.Comment)) { sb.AppendLine(); sb.Append(buttonTooltipData.Comment); } if (!string.IsNullOrEmpty(buttonTooltipData.IncomingChangesInfo)) { sb.AppendLine(); sb.Append(buttonTooltipData.IncomingChangesInfo); } if (!string.IsNullOrEmpty(buttonTooltipData.PendingChangesInfo)) { sb.AppendLine(); sb.Append(buttonTooltipData.PendingChangesInfo); } mDropDownButtonData.Tooltip = sb.ToString(); FireOnToolbarButtonInvalidated(); } void FireOnToolbarInvalidated() { if (OnToolbarInvalidated != null) OnToolbarInvalidated(); } void FireOnToolbarButtonInvalidated() { if (OnToolbarButtonInvalidated != null) OnToolbarButtonInvalidated(); } void SetWorkingBranch(BranchInfo branchInfo) { mWorkingBranch = branchInfo; } void UpdateWorkingBranch( string objectType, string objectName, string repository, string server) { RepositorySpec repSpec = RepositorySpec.BuildFromNameAndResolvedServer(repository, server); if (objectType != PlasticLocalization.Name.Branch.GetString()) { mWorkingBranch = null; return; } PlasticThreadPool.Run(delegate { try { mWorkingBranch = PlasticGui.Plastic.API.GetBranchInfo(repSpec, objectName); } catch (Exception ex) { mLog.ErrorFormat("Error loading the working branch: {0}", ex.Message); mLog.DebugFormat("Stack trace: {0}", ex.StackTrace); } }); } void ShowUncontrolledPopup(Vector2 popupPosition) { int popupHeight = PopupWindowDrawing.MENU_ITEM_HEIGHT * 4 + PopupWindowDrawing.DELIMITER_HEIGHT * 2; UncontrolledPopupWindow window = new UncontrolledPopupWindow( new UncontrolledPopupOperations(mUVCSPlugin, this), FireOnToolbarInvalidated, new Vector2(285, popupHeight)); UnityEditor.PopupWindow.Show( new Rect(popupPosition, Vector2.zero), window); } BranchesListModel GetCalculatedModel() { lock (mModelLock) { return mModel; } } void SetCalculatedModel(BranchesListModel branches) { lock (mModelLock) { mModel = branches; mMainBranch = (mModel.Branches == null) ? null : mModel.Branches.MainBranch; } } void ShowControlledPopup(Vector2 popupPosition) { ControlledPopupOperations operations = new ControlledPopupOperations( mWkInfo, mUVCSPlugin, mIsGluonMode, RefreshWorkspaceWorkingInfo, this, SetWorkingBranch, () => mModel == null ? null : mModel.RepSpec, () => GetMainBranch(mWkInfo), () => mWorkingBranch); ControlledPopupWindow window = new ControlledPopupWindow( operations, RefreshBranches, () => mWorkingBranch, FireOnToolbarInvalidated, new Vector2(375, 425)); BranchesListModel model = GetCalculatedModel(); if (model != null) model.ResetFilter(); window.SetModel(model); RefreshBranches(window); UnityEditor.PopupWindow.Show( new Rect(popupPosition, Vector2.zero), window); } void RefreshBranches(ControlledPopupWindow window) { bool isOperationFinished = false; Task.Delay(TimeSpan.FromMilliseconds(1500)).ContinueWith((task, state) => { if (window != null && !isOperationFinished) { window.ShowProgressBar(); } }, null); BranchesListModel model = null; IThreadWaiter waiter = ThreadWaiter.GetWaiter(50); waiter.Execute( threadOperationDelegate: delegate { model = CalculateBranchesListModel(mWkInfo); SetCalculatedModel(model); }, afterOperationDelegate: delegate { isOperationFinished = true; if (window == null) return; window.HideProgressBar(); window.SetModel(model); }); } void ResetToolbarButtonData() { mButtonTooltipData = new ButtonTooltipData(); mDropDownButtonData = UVCSToolbarButtonData.BuildDefault(); } static BranchesListModel CalculateBranchesListModel(WorkspaceInfo wkInfo) { try { RepositorySpec repSpec = PlasticGui.Plastic.API.GetRepositorySpec(wkInfo); QueryResult queryResult = PlasticGui.Plastic.API.FindQuery(wkInfo, "find branches"); if (repSpec == null || queryResult == null) return BranchesListModel.BuildEmpty(); ClassifiedBranchesList result = new ClassifiedBranchesList( queryResult.Result[0].Cast().ToList(), RecentBranchesSettings.GetRecentBranches(PlasticGuiConfig.GetConfigFile(), wkInfo.Id), WellKnownGuids.MainBranch); return BranchesListModel.FromBranches(result, repSpec); } catch (Exception ex) { mLog.ErrorFormat("Error loading the branches: {0}", ex.Message); mLog.DebugFormat("Stack trace: {0}", ex.StackTrace); return BranchesListModel.FromException(ex); } } BranchInfo GetMainBranch(WorkspaceInfo wkInfo) { if (mMainBranch != null) return mMainBranch; return PlasticGui.Plastic.API.GetMainBranch(wkInfo); } WorkspaceInfo mWkInfo; BranchInfo mMainBranch; BranchInfo mWorkingBranch; bool mIsGluonMode; BranchesListModel mModel = BranchesListModel.BuildEmpty(); ButtonTooltipData mButtonTooltipData = new ButtonTooltipData(); UVCSToolbarButtonData mDropDownButtonData = UVCSToolbarButtonData.BuildDefault(); readonly object mModelLock = new object(); readonly UVCSPlugin mUVCSPlugin; static readonly ILog mLog = PlasticApp.GetLogger("PlasticWindow"); class ButtonTooltipData { public string WorkingObjectType { get; set; } public string WorkingObjectName { get; set; } public string RepositoryName { get; set; } public string ServerName { get; set; } public string Comment { get; set; } public string PendingChangesInfo { get; set; } public string IncomingChangesInfo { get; set; } } } }