using System; using System.Collections.Generic; using UnityEditor; using UnityEditor.IMGUI.Controls; using UnityEngine; using Codice.Client.BaseCommands; using Codice.Client.Commands; using Codice.Client.Common; using Codice.Client.Common.EventTracking; using Codice.Client.Common.FsNodeReaders; using Codice.Client.Common.Threading; using Codice.CM.Common; using Codice.CM.Common.Merge; using Codice.CM.Common.Mount; using Codice.LogWrapper; using GluonGui; using GluonGui.WorkspaceWindow.Views.Checkin.Operations; using GluonGui.WorkspaceWindow.Views.Shelves; using PlasticGui; using PlasticGui.Help.Actions; using PlasticGui.Help.Conditions; using PlasticGui.WorkspaceWindow; using PlasticGui.WorkspaceWindow.Diff; using PlasticGui.WorkspaceWindow.Items; using PlasticGui.WorkspaceWindow.Open; using PlasticGui.WorkspaceWindow.PendingChanges; using PlasticGui.WorkspaceWindow.PendingChanges.Changelists; using Unity.PlasticSCM.Editor.AssetsOverlays.Cache; using Unity.PlasticSCM.Editor.AssetUtils; using Unity.PlasticSCM.Editor.AssetUtils.Processor; using Unity.PlasticSCM.Editor.Gluon.Errors; using Unity.PlasticSCM.Editor.Settings; using Unity.PlasticSCM.Editor.Tool; using Unity.PlasticSCM.Editor.UI; using Unity.PlasticSCM.Editor.UI.Progress; using Unity.PlasticSCM.Editor.UI.StatusBar; using Unity.PlasticSCM.Editor.UI.Tree; using Unity.PlasticSCM.Editor.Views.PendingChanges.Dialogs; using Unity.PlasticSCM.Editor.Views.PendingChanges.PendingMergeLinks; using Unity.PlasticSCM.Editor.Views.Changesets; using GluonNewIncomingChangesUpdater = PlasticGui.Gluon.WorkspaceWindow.NewIncomingChangesUpdater; namespace Unity.PlasticSCM.Editor.Views.PendingChanges { internal partial class PendingChangesTab : IRefreshableView, PendingChangesOptionsFoldout.IAutoRefreshView, IPendingChangesView, CheckinUIOperation.ICheckinView, ShelveOperations.ICheckinView, PendingChangesViewPendingChangeMenu.IMetaMenuOperations, IPendingChangesMenuOperations, IChangelistMenuOperations, IOpenMenuOperations, PendingChangesViewPendingChangeMenu.IAdvancedUndoMenuOperations, IFilesFilterPatternsMenuOperations, PendingChangesViewMenu.IGetSelectedNodes, ChangesetsTab.IRevertToChangesetListener { internal IProgressControls ProgressControls { get { return mProgressControls; } } internal PendingChangesTreeView Table { get { return mPendingChangesTreeView; } } internal string CommentText { get { return mCommentText; } } internal bool ForceToShowComment { get { return mForceToShowComment; } } internal void SetMergeLinksForTesting( IDictionary> mergeLinks) { mPendingMergeLinks = mergeLinks; UpdateMergeLinksList(); } internal PendingChangesTab( WorkspaceInfo wkInfo, RepositorySpec repSpec, ViewHost viewHost, bool isGluonMode, WorkspaceWindow workspaceWindow, IViewSwitcher viewSwitcher, IMergeViewLauncher mergeViewLauncher, IHistoryViewLauncher historyViewLauncher, LaunchTool.IShowDownloadPlasticExeWindow showDownloadPlasticExeWindow, WorkspaceOperationsMonitor workspaceOperationsMonitor, NewIncomingChangesUpdater developerNewIncomingChangesUpdater, GluonNewIncomingChangesUpdater gluonNewIncomingChangesUpdater, IAssetStatusCache assetStatusCache, StatusBar statusBar, EditorWindow parentWindow) { mWkInfo = wkInfo; mRepSpec = repSpec; mViewHost = viewHost; mIsGluonMode = isGluonMode; mWorkspaceWindow = workspaceWindow; mViewSwitcher = viewSwitcher; mHistoryViewLauncher = historyViewLauncher; mShowDownloadPlasticExeWindow = showDownloadPlasticExeWindow; mWorkspaceOperationsMonitor = workspaceOperationsMonitor; mDeveloperNewIncomingChangesUpdater = developerNewIncomingChangesUpdater; mGluonNewIncomingChangesUpdater = gluonNewIncomingChangesUpdater; mAssetStatusCache = assetStatusCache; mStatusBar = statusBar; mParentWindow = parentWindow; mGuiMessage = new UnityPlasticGuiMessage(); mCheckedStateManager = new PendingChangesViewCheckedStateManager(); mNewChangesInWk = NewChangesInWk.Build( wkInfo, new BuildWorkspacekIsRelevantNewChange()); BuildComponents(viewSwitcher, isGluonMode); mProgressControls = new ProgressControlsForViews(); mCooldownClearOperationSuccessAction = new CooldownWindowDelayer( DelayedClearOperationSuccess, UnityConstants.NOTIFICATION_CLEAR_INTERVAL); if (mErrorsPanel != null) { mErrorsSplitterState = PlasticSplitterGUILayout.InitSplitterState( new float[] { 0.75f, 0.25f }, new int[] { 100, 100 }, new int[] { 100000, 100000 } ); } workspaceWindow.RegisterPendingChangesProgressControls(mProgressControls); mPendingChangesOperations = new PendingChangesOperations( wkInfo, workspaceWindow, viewSwitcher, mergeViewLauncher, this, mProgressControls, workspaceWindow, null, null, null, null); mCommentText = SessionState.GetString( UnityConstants.PENDING_CHANGES_CI_COMMENTS_KEY_NAME, string.Empty); InitIgnoreRulesAndRefreshView(wkInfo.ClientPath, this); } internal void AutoRefresh() { if (mIsAutoRefreshDisabled) return; if (!PlasticGuiConfig.Get().Configuration.CommitAutoRefresh) return; if (mNewChangesInWk != null && !mNewChangesInWk.Detected()) return; ((IRefreshableView)this).Refresh(); } internal void ClearIsCommentWarningNeeded() { mIsEmptyCheckinCommentWarningNeeded = false; mIsEmptyShelveCommentWarningNeeded = false; } internal void UpdateIsCheckinCommentWarningNeeded(string comment) { mIsEmptyCheckinCommentWarningNeeded = string.IsNullOrEmpty(comment) && PlasticGuiConfig.Get().Configuration.ShowEmptyCommentWarning; mNeedsToShowEmptyCheckinCommentDialog = mIsEmptyCheckinCommentWarningNeeded; } internal void UpdateIsShelveCommentWarningNeeded(string comment) { mIsEmptyShelveCommentWarningNeeded = string.IsNullOrEmpty(comment) && PlasticGuiConfig.Get().Configuration.ShowEmptyShelveCommentWarning; mNeedsToShowEmptyShelveCommentDialog = mIsEmptyShelveCommentWarningNeeded; } internal void OnEnable() { mIsEnabled = true; mSearchField.downOrUpArrowKeyPressed += SearchField_OnDownOrUpArrowKeyPressed; } internal void OnDisable() { mIsEnabled = false; mSearchField.downOrUpArrowKeyPressed -= SearchField_OnDownOrUpArrowKeyPressed; SessionState.SetString( UnityConstants.PENDING_CHANGES_CI_COMMENTS_KEY_NAME, mCommentText); TreeViewSessionState.Save( mPendingChangesTreeView, UnityConstants.PENDING_CHANGES_UNCHECKED_ITEMS_KEY_NAME); TreeHeaderSettings.Save( mPendingChangesTreeView.multiColumnHeader.state, UnityConstants.PENDING_CHANGES_TABLE_SETTINGS_NAME); if (mErrorsPanel != null) mErrorsPanel.OnDisable(); } internal void Update() { mProgressControls.UpdateProgress(mParentWindow); // Display the empty comment dialog here, otherwise it causes errors in the OnGUI method if (mNeedsToShowEmptyCheckinCommentDialog) { mNeedsToShowEmptyCheckinCommentDialog = false; mHasPendingCheckinFromPreviousUpdate = EmptyCommentDialog.ShouldContinueWithCheckin(mParentWindow, mWkInfo); mIsEmptyCheckinCommentWarningNeeded = !mHasPendingCheckinFromPreviousUpdate; } if (mNeedsToShowEmptyShelveCommentDialog) { mNeedsToShowEmptyShelveCommentDialog = false; mHasPendingShelveFromPreviousUpdate = EmptyCommentDialog.ShouldContinueWithShelve(mParentWindow, mWkInfo); mIsEmptyShelveCommentWarningNeeded = !mHasPendingShelveFromPreviousUpdate; } } internal void OnGUI() { if (mErrorsPanel != null && mErrorsPanel.IsVisible) PlasticSplitterGUILayout.BeginVerticalSplit(mErrorsSplitterState); EditorGUILayout.BeginVertical(); if (!string.IsNullOrEmpty(mGluonWarningMessage)) DoWarningMessage(mGluonWarningMessage); DoActionsToolbar(mProgressControls); DoChangesArea( mRepSpec, mPendingChangesTreeView, mEmptyStateContent, mProgressControls.IsOperationRunning(), mOperationSuccessfulMessage, mCanInviteMembersFromPendingChanges); if (HasPendingMergeLinks() && !mHasPendingMergeLinksFromRevert) DoMergeLinksArea(mMergeLinksListView, mParentWindow.position.width); EditorGUILayout.EndVertical(); if (mErrorsPanel != null && mErrorsPanel.IsVisible) { mErrorsPanel.OnGUI(); PlasticSplitterGUILayout.EndVerticalSplit(); } // Border Rect result = GUILayoutUtility.GetRect(mParentWindow.position.width, 1); EditorGUI.DrawRect(result, UnityStyles.Colors.BarBorder); DoCommentsSection(); if (mProgressControls.HasNotification()) { DrawProgressForViews.ForNotificationArea(mProgressControls.ProgressData); } } internal void DrawSearchFieldForTab() { DrawSearchField.For( mSearchField, mPendingChangesTreeView, UnityConstants.SEARCH_FIELD_WIDTH); } internal void UpdateComment(string comment) { mCommentText = comment; mForceToShowComment = false; } void IPendingChangesView.SetDefaultComment(string defaultComment) { } void IPendingChangesView.ClearComments() { ClearComments(); } void IRefreshableView.Refresh() { if (mProgressControls.IsOperationRunning()) return; if (!mAreIgnoreRulesInitialized) return; if (mDeveloperNewIncomingChangesUpdater != null) mDeveloperNewIncomingChangesUpdater.Update(DateTime.Now); if (mGluonNewIncomingChangesUpdater != null) mGluonNewIncomingChangesUpdater.Update(DateTime.Now); FillPendingChanges(mNewChangesInWk); } void PendingChangesOptionsFoldout.IAutoRefreshView.DisableAutoRefresh() { mIsAutoRefreshDisabled = true; } void PendingChangesOptionsFoldout.IAutoRefreshView.EnableAutoRefresh() { mIsAutoRefreshDisabled = false; } void PendingChangesOptionsFoldout.IAutoRefreshView.ForceRefresh() { ((IRefreshableView)this).Refresh(); } void IPendingChangesView.ClearChangesToCheck(List changes) { mCheckedStateManager.ClearChangesToCheck(changes); mParentWindow.Repaint(); } void IPendingChangesView.CleanCheckedElements(List checkedChanges) { mCheckedStateManager.Clean(checkedChanges); mParentWindow.Repaint(); } void IPendingChangesView.CheckChanges(List changesToCheck) { mCheckedStateManager.SetChangesToCheck(changesToCheck); mParentWindow.Repaint(); } bool IPendingChangesView.IncludeDependencies( IList changesDependencies, string operation) { return DependenciesDialog.IncludeDependencies( mWkInfo, changesDependencies, operation, mParentWindow); } SearchMatchesData IPendingChangesView.AskForMatches(string changePath) { throw new NotImplementedException(); } void IPendingChangesView.CleanLinkedTasks() { } void CheckinUIOperation.ICheckinView.CollapseWarningMessagePanel() { mGluonWarningMessage = string.Empty; mParentWindow.Repaint(); } void CheckinUIOperation.ICheckinView.ExpandWarningMessagePanel(string text) { mGluonWarningMessage = text; mParentWindow.Repaint(); } void CheckinUIOperation.ICheckinView.ClearComments() { ClearComments(); } void ShelveOperations.ICheckinView.OnShelvesetApplied(List errorMessages) { mViewSwitcher.ShowPendingChanges(); mErrorsPanel.UpdateErrorsList(errorMessages); } bool PendingChangesViewPendingChangeMenu.IMetaMenuOperations.SelectionHasMeta() { return mPendingChangesTreeView.SelectionHasMeta(); } void PendingChangesViewPendingChangeMenu.IMetaMenuOperations.DiffMeta() { ChangeInfo selectedChange = PendingChangesSelection .GetSelectedChange(mPendingChangesTreeView); ChangeInfo selectedChangeMeta = mPendingChangesTreeView.GetMetaChange( selectedChange); ChangeInfo changedForMoved = mPendingChangesTreeView.GetChangedForMoved(selectedChange); ChangeInfo changedForMovedMeta = (changedForMoved == null) ? null : mPendingChangesTreeView.GetMetaChange(changedForMoved); DiffOperation.DiffWorkspaceContent( mWkInfo, selectedChangeMeta, changedForMovedMeta, mProgressControls, PlasticExeLauncher.BuildForDiffWorkspaceContent(mWkInfo, mIsGluonMode, mShowDownloadPlasticExeWindow), null); } void PendingChangesViewPendingChangeMenu.IMetaMenuOperations.HistoryMeta() { ChangeInfo selectedChange = PendingChangesSelection .GetSelectedChange(mPendingChangesTreeView); ChangeInfo selectedChangeMeta = mPendingChangesTreeView.GetMetaChange( selectedChange); mHistoryViewLauncher.ShowHistoryView( selectedChangeMeta.RepositorySpec, selectedChangeMeta.RevInfo.ItemId, selectedChangeMeta.Path, selectedChangeMeta.IsDirectory); } void PendingChangesViewPendingChangeMenu.IMetaMenuOperations.OpenMeta() { List selectedPaths = PendingChangesSelection .GetSelectedMetaPaths(mPendingChangesTreeView); FileSystemOperation.Open(selectedPaths); } void PendingChangesViewPendingChangeMenu.IMetaMenuOperations.OpenMetaWith() { List selectedPaths = PendingChangesSelection .GetSelectedMetaPaths(mPendingChangesTreeView); OpenOperation.OpenWith( FileSystemOperation.GetExePath(), selectedPaths); } void PendingChangesViewPendingChangeMenu.IMetaMenuOperations.OpenMetaInExplorer() { List selectedPaths = PendingChangesSelection .GetSelectedMetaPaths(mPendingChangesTreeView); if (selectedPaths.Count < 1) return; FileSystemOperation.OpenInExplorer(selectedPaths[0]); } SelectedChangesGroupInfo IPendingChangesMenuOperations.GetSelectedChangesGroupInfo() { return PendingChangesSelection.GetSelectedChangesGroupInfo( mWkInfo.ClientPath, mPendingChangesTreeView); } void IPendingChangesMenuOperations.Diff() { ChangeInfo selectedChange = PendingChangesSelection .GetSelectedChange(mPendingChangesTreeView); DiffOperation.DiffWorkspaceContent( mWkInfo, selectedChange, mPendingChangesTreeView.GetChangedForMoved(selectedChange), null, PlasticExeLauncher.BuildForDiffWorkspaceContent(mWkInfo, mIsGluonMode, mShowDownloadPlasticExeWindow), null); } void IPendingChangesMenuOperations.UndoChanges() { List changesToUndo = PendingChangesSelection .GetSelectedChanges(mPendingChangesTreeView); List dependenciesCandidates = mPendingChangesTreeView.GetDependenciesCandidates(changesToUndo, true); UndoChangesForMode(mIsGluonMode, false, changesToUndo, dependenciesCandidates); } void IPendingChangesMenuOperations.SearchMatches() { ChangeInfo selectedChange = PendingChangesSelection .GetSelectedChange(mPendingChangesTreeView); if (selectedChange == null) return; SearchMatchesOperation operation = new SearchMatchesOperation( mWkInfo, mWorkspaceWindow, this, mProgressControls, mDeveloperNewIncomingChangesUpdater, null); operation.SearchMatches( selectedChange, PendingChangesSelection.GetAllChanges(mPendingChangesTreeView), null); } void IPendingChangesMenuOperations.ApplyLocalChanges() { List selectedChanges = PendingChangesSelection .GetSelectedChanges(mPendingChangesTreeView); if (selectedChanges.Count == 0) return; ApplyLocalChangesOperation operation = new ApplyLocalChangesOperation( mWkInfo, mWorkspaceWindow, this, mProgressControls, mDeveloperNewIncomingChangesUpdater, null); operation.ApplyLocalChanges( selectedChanges, PendingChangesSelection.GetAllChanges(mPendingChangesTreeView), null); } void IPendingChangesMenuOperations.CopyFilePath(bool relativePath) { EditorGUIUtility.systemCopyBuffer = GetFilePathList.FromSelectedPaths( PendingChangesSelection.GetSelectedPathsWithoutMeta(mPendingChangesTreeView), relativePath, mWkInfo.ClientPath); } void IPendingChangesMenuOperations.Delete() { List privateDirectoriesToDelete; List privateFilesToDelete; if (!mPendingChangesTreeView.GetSelectedPathsToDelete( out privateDirectoriesToDelete, out privateFilesToDelete)) return; DeleteOperation.Delete( mProgressControls, privateDirectoriesToDelete, privateFilesToDelete, mDeveloperNewIncomingChangesUpdater, null, RefreshAsset.UnityAssetDatabase, () => ((IWorkspaceWindow)mWorkspaceWindow).RefreshView(ViewType.ItemsView), () => ((IWorkspaceWindow)mWorkspaceWindow).RefreshView(ViewType.PendingChangesView)); } void IPendingChangesMenuOperations.Annotate() { throw new NotImplementedException(); } void IPendingChangesMenuOperations.History() { ChangeInfo selectedChange = PendingChangesSelection. GetSelectedChange(mPendingChangesTreeView); mHistoryViewLauncher.ShowHistoryView( selectedChange.RepositorySpec, selectedChange.RevInfo.ItemId, selectedChange.Path, selectedChange.IsDirectory); } SelectedChangesGroupInfo IChangelistMenuOperations.GetSelectedChangesGroupInfo() { return PendingChangesSelection.GetSelectedChangesGroupInfo( mWkInfo.ClientPath, mPendingChangesTreeView); } List IChangelistMenuOperations.GetSelectedChangelistInfos() { return PendingChangesSelection.GetSelectedChangeListInfos( mPendingChangesTreeView); } void IChangelistMenuOperations.Checkin() { List changesToCheckin; List dependenciesCandidates; mPendingChangesTreeView.GetCheckedChanges( PendingChangesSelection.GetSelectedChangelistNodes(mPendingChangesTreeView), false, out changesToCheckin, out dependenciesCandidates); CheckinChangesForMode( changesToCheckin, dependenciesCandidates, mIsGluonMode, mKeepItemsLocked); } void IChangelistMenuOperations.Shelve() { List changesToShelve; List dependenciesCandidates; mPendingChangesTreeView.GetCheckedChanges( PendingChangesSelection.GetSelectedChangelistNodes(mPendingChangesTreeView), false, out changesToShelve, out dependenciesCandidates); ShelveChangesForMode( changesToShelve, dependenciesCandidates, mIsGluonMode, mKeepItemsLocked); } void IChangelistMenuOperations.Undo() { List changesToUndo; List dependenciesCandidates; mPendingChangesTreeView.GetCheckedChanges( PendingChangesSelection.GetSelectedChangelistNodes(mPendingChangesTreeView), true, out changesToUndo, out dependenciesCandidates); UndoChangesForMode(mIsGluonMode, false, changesToUndo, dependenciesCandidates); } void IChangelistMenuOperations.CreateNew() { ChangelistCreationData changelistCreationData = CreateChangelistDialog.CreateChangelist(mWkInfo, mParentWindow); ChangelistOperations.CreateNew(mWkInfo, this, changelistCreationData); } void IChangelistMenuOperations.MoveToNewChangelist(List changes) { ChangelistCreationData changelistCreationData = CreateChangelistDialog.CreateChangelist(mWkInfo, mParentWindow); if (!changelistCreationData.Result) return; ChangelistOperations.CreateNew(mWkInfo, this, changelistCreationData); ChangelistOperations.MoveToChangelist( mWkInfo, this, changes, changelistCreationData.ChangelistInfo.Name); } void IChangelistMenuOperations.Edit() { ChangeListInfo changelistToEdit = PendingChangesSelection.GetSelectedChangeListInfo( mPendingChangesTreeView); ChangelistCreationData changelistCreationData = CreateChangelistDialog.EditChangelist( mWkInfo, changelistToEdit, mParentWindow); ChangelistOperations.Edit(mWkInfo, this, changelistToEdit, changelistCreationData); } void IChangelistMenuOperations.Delete() { ChangelistOperations.Delete( mWkInfo, this, PendingChangesSelection.GetSelectedChangelistNodes(mPendingChangesTreeView)); } void IChangelistMenuOperations.MoveToChangelist( List changes, string targetChangelist) { ChangelistOperations.MoveToChangelist( mWkInfo, this, changes, targetChangelist); } void IOpenMenuOperations.Open() { List selectedPaths = PendingChangesSelection. GetSelectedPathsWithoutMeta(mPendingChangesTreeView); FileSystemOperation.Open(selectedPaths); } void IOpenMenuOperations.OpenWith() { List selectedPaths = PendingChangesSelection. GetSelectedPathsWithoutMeta(mPendingChangesTreeView); OpenOperation.OpenWith( FileSystemOperation.GetExePath(), selectedPaths); } void IOpenMenuOperations.OpenWithCustom(string exePath, string args) { List selectedPaths = PendingChangesSelection. GetSelectedPathsWithoutMeta(mPendingChangesTreeView); OpenOperation.OpenWith(exePath, selectedPaths); } void IOpenMenuOperations.OpenInExplorer() { List selectedPaths = PendingChangesSelection .GetSelectedPathsWithoutMeta(mPendingChangesTreeView); if (selectedPaths.Count < 1) return; FileSystemOperation.OpenInExplorer(selectedPaths[0]); } void IFilesFilterPatternsMenuOperations.AddFilesFilterPatterns( FilterTypes type, FilterActions action, FilterOperationType operation) { List selectedPaths = PendingChangesSelection.GetSelectedPaths( mPendingChangesTreeView); string[] rules = FilterRulesGenerator.GenerateRules( selectedPaths, mWkInfo.ClientPath, action, operation); bool isApplicableToAllWorkspaces = !mIsGluonMode; bool isAddOperation = operation == FilterOperationType.Add; FilterRulesConfirmationData filterRulesConfirmationData = FilterRulesConfirmationDialog.AskForConfirmation( rules, isAddOperation, isApplicableToAllWorkspaces, mParentWindow); AddFilesFilterPatternsOperation.Run( mWkInfo, mWorkspaceWindow, type, operation, filterRulesConfirmationData); } void PendingChangesViewPendingChangeMenu.IAdvancedUndoMenuOperations.UndoUnchanged() { UndoUnchangedChanges(PendingChangesSelection. GetSelectedChanges(mPendingChangesTreeView)); } void PendingChangesViewPendingChangeMenu.IAdvancedUndoMenuOperations.UndoCheckoutsKeepingChanges() { UndoCheckoutChangesKeepingLocalChanges(PendingChangesSelection. GetSelectedChanges(mPendingChangesTreeView)); } List PendingChangesViewMenu.IGetSelectedNodes.GetSelectedNodes() { return mPendingChangesTreeView.GetSelectedNodes(); } void ChangesetsTab.IRevertToChangesetListener.OnSuccessOperation() { mHasPendingMergeLinksFromRevert = true; } void SearchField_OnDownOrUpArrowKeyPressed() { mPendingChangesTreeView.SetFocusAndEnsureSelectedItem(); } void DelayedClearOperationSuccess() { mOperationSuccessfulMessage = string.Empty; mCanInviteMembersFromPendingChanges = false; } void InitIgnoreRulesAndRefreshView( string wkPath, IRefreshableView view) { IThreadWaiter waiter = ThreadWaiter.GetWaiter(10); waiter.Execute( /*threadOperationDelegate*/ delegate { if (IsIgnoreConfigDefined.InWorkspace(wkPath)) { AddIgnoreRules.WriteRules( wkPath, UnityConditions.GetMissingIgnoredRuleForUnityDirMonSyncFile(wkPath)); return; } if (IsIgnoreConfigDefined.InGlobalConfig(wkPath)) return; AddIgnoreRules.WriteRules( wkPath, UnityConditions.GetMissingIgnoredRules(wkPath)); }, /*afterOperationDelegate*/ delegate { mAreIgnoreRulesInitialized = true; view.Refresh(); if (waiter.Exception == null) return; mLog.ErrorFormat( "Error adding ignore rules for Unity: {0}", waiter.Exception); mLog.DebugFormat( "Stack trace: {0}", waiter.Exception.StackTrace); }); } void FillPendingChanges(INewChangesInWk newChangesInWk) { if (mIsRefreshing) return; mIsRefreshing = true; List changesToSelect = PendingChangesSelection.GetChangesToFocus(mPendingChangesTreeView); ((IProgressControls)mProgressControls).ShowProgress(PlasticLocalization. GetString(PlasticLocalization.Name.LoadingPendingChanges)); IDictionary> mergeLinks = null; WorkspaceStatusResult status = null; IThreadWaiter waiter = ThreadWaiter.GetWaiter(); waiter.Execute( /*threadOperationDelegate*/ delegate { FilterManager.Get().Reload(mWkInfo); WorkspaceStatusOptions options = WorkspaceStatusOptions.None; options |= WorkspaceStatusOptions.FindAdded; options |= WorkspaceStatusOptions.FindDeleted; options |= WorkspaceStatusOptions.FindMoved; options |= WorkspaceStatusOptions.SplitModifiedMoved; options |= PendingChangesOptions.GetWorkspaceStatusOptions(); if (newChangesInWk != null) newChangesInWk.Detected(); status = GetStatus.ForWorkspace( mWkInfo, options, PendingChangesOptions.GetMovedMatchingOptions()); mergeLinks = PlasticGui.Plastic.API.GetPendingMergeLinks(mWkInfo); }, /*afterOperationDelegate*/ delegate { mPendingMergeLinks = mergeLinks; try { if (waiter.Exception != null) { ExceptionsHandler.DisplayException(waiter.Exception); return; } UpdateChangesTree(status.Changes); RestoreData(); UpdateMergeLinksList(); PendingChangesSelection.SelectChanges( mPendingChangesTreeView, changesToSelect); ClearAssetStatusCache(); } finally { ((IProgressControls)mProgressControls).HideProgress(); UpdateNotificationPanel(); mIsRefreshing = false; } }); } void DoCommentsSection() { EditorGUILayout.BeginVertical(UnityStyles.PendingChangesTab.Comment); EditorGUILayout.Space(10); EditorGUILayout.BeginHorizontal(); EditorGUILayout.Space(2); EditorGUILayout.BeginVertical(); GUILayout.FlexibleSpace(); DrawUserIcon.ForPendingChangesTab(); GUILayout.FlexibleSpace(); EditorGUILayout.EndVertical(); float buttonsWidth = 306f; float areaWidth = Mathf.Clamp(mParentWindow.position.width, 300f, 820f); float width = areaWidth - buttonsWidth; DrawCommentTextArea.For( this, width, mProgressControls.IsOperationRunning()); EditorGUILayout.Space(2); // To center the action buttons vertically EditorGUILayout.BeginVertical(); GUILayout.FlexibleSpace(); DoOperationsToolbar( mIsGluonMode, mShelveDropdownMenu, mUndoDropdownMenu, mProgressControls.IsOperationRunning()); GUILayout.FlexibleSpace(); EditorGUILayout.EndVertical(); GUILayout.FlexibleSpace(); EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(10); EditorGUILayout.EndVertical(); } void DoOperationsToolbar( bool isGluonMode, GenericMenu shelveDropdownMenu, GenericMenu undoDropdownMenu, bool isOperationRunning) { string checkinButtonText = PlasticLocalization.GetString( PlasticLocalization.Name.Checkin); string shelveButtonText = PlasticLocalization.GetString( PlasticLocalization.Name.Shelve); string undoButtonText = PlasticLocalization.GetString( PlasticLocalization.Name.UndoChanges); mOperationButtonWidth = InitializeOperationButtonWidth( DrawActionButton.ButtonStyle, DrawActionButtonWithMenu.ButtonStyle, mOperationButtonWidth, checkinButtonText, shelveButtonText, undoButtonText); EditorGUILayout.BeginHorizontal(); using (new GuiEnabled(!isOperationRunning)) { if (mHasPendingCheckinFromPreviousUpdate) { mHasPendingCheckinFromPreviousUpdate = false; CheckinForMode(isGluonMode, mKeepItemsLocked); } if (mHasPendingShelveFromPreviousUpdate) { mHasPendingShelveFromPreviousUpdate = false; ShelveForMode(isGluonMode, mKeepItemsLocked); } if (DrawActionButton.ForCommentSection( checkinButtonText, mOperationButtonWidth)) { CheckinAction(isGluonMode); } GUILayout.Space(2); DrawActionButtonWithMenu.For( shelveButtonText, string.Empty, mOperationButtonWidth, () => ShelveAction(isGluonMode), shelveDropdownMenu); GUILayout.Space(2); DoUndoButton(isGluonMode, undoButtonText, mOperationButtonWidth, undoDropdownMenu); if (isGluonMode) { mKeepItemsLocked = EditorGUILayout.ToggleLeft( PlasticLocalization.GetString(PlasticLocalization.Name.KeepLocked), mKeepItemsLocked, GUILayout.Width(UnityConstants.EXTRA_LARGE_BUTTON_WIDTH)); } } EditorGUILayout.EndHorizontal(); } void DoUndoButton( bool isGluonMode, string undoButtonText, float buttonWidth, GenericMenu undoDropdownMenu) { if (isGluonMode) { if (DrawActionButton.ForCommentSection(undoButtonText, buttonWidth)) UndoChangesAction(true); return; } DrawActionButtonWithMenu.For( undoButtonText, string.Empty, buttonWidth, () => UndoChangesAction(false), undoDropdownMenu); } void CheckinAction(bool isGluonMode) { UpdateIsCheckinCommentWarningNeeded(mCommentText); if (mIsEmptyCheckinCommentWarningNeeded) return; CheckinForMode(isGluonMode, mKeepItemsLocked); } void ShelveAction(bool isGluonMode) { UpdateIsShelveCommentWarningNeeded(mCommentText); if (mIsEmptyShelveCommentWarningNeeded) return; ShelveForMode(isGluonMode, mKeepItemsLocked); } void UndoChangesAction(bool isGluonMode) { TrackFeatureUseEvent.For( mRepSpec, TrackFeatureUseEvent.Features.UndoTextButton); UndoForMode(isGluonMode, false); } void UpdateChangesTree(List changes) { mPendingChangesTreeView.BuildModel(changes, mCheckedStateManager); mPendingChangesTreeView.Refilter(); mPendingChangesTreeView.Sort(); mPendingChangesTreeView.Reload(); } static void DoWarningMessage(string message) { GUILayout.Label(message, UnityStyles.WarningMessage); } void UpdateMergeLinksList() { mMergeLinksListView.BuildModel(mPendingMergeLinks); mMergeLinksListView.Reload(); if (!HasPendingMergeLinks()) mHasPendingMergeLinksFromRevert = false; } void ClearAssetStatusCache() { if (mAssetStatusCache != null) mAssetStatusCache.Clear(); ProjectWindow.Repaint(); RepaintInspector.All(); } void UpdateNotificationPanel() { if (PlasticGui.Plastic.API.IsFsReaderWatchLimitReached(mWkInfo)) { ((IProgressControls)mProgressControls).ShowWarning(PlasticLocalization. GetString(PlasticLocalization.Name.NotifyLinuxWatchLimitWarning)); return; } } void DoActionsToolbar(ProgressControlsForViews progressControls) { EditorGUILayout.BeginHorizontal(EditorStyles.toolbar); if (progressControls.IsOperationRunning()) { DrawProgressForViews.ForIndeterminateProgress( progressControls.ProgressData); } GUILayout.FlexibleSpace(); EditorGUILayout.EndHorizontal(); } static void DoChangesArea( RepositorySpec repSpec, PendingChangesTreeView changesTreeView, GUIContent emptyStateContent, bool isOperationRunning, string operationSuccessfulMessage, bool canInviteMembersFromPendingChanges) { GUI.enabled = !isOperationRunning; Rect rect = GUILayoutUtility.GetRect(0, 100000, 0, 100000); changesTreeView.OnGUI(rect); if (changesTreeView.GetTotalItemCount() == 0) { DrawEmptyState( repSpec, rect, emptyStateContent, isOperationRunning, operationSuccessfulMessage, canInviteMembersFromPendingChanges); } GUI.enabled = true; } static void DrawEmptyState( RepositorySpec repSpec, Rect rect, GUIContent emptyStateContent, bool isOperationRunning, string operationSuccessfulMessage, bool canInviteMembersFromPendingChanges) { if (!string.IsNullOrEmpty(operationSuccessfulMessage)) { DrawOperationSuccessfulEmptyState( repSpec, rect, emptyStateContent, operationSuccessfulMessage, canInviteMembersFromPendingChanges); return; } if (isOperationRunning) return; DrawNoPendingChangesEmptyState(rect, emptyStateContent); } static void DrawNoPendingChangesEmptyState( Rect rect, GUIContent emptyStateContent) { emptyStateContent.text = PlasticLocalization.Name.EmptyPendingChangesMessage.GetString(); DrawTreeViewEmptyState.For(rect, emptyStateContent); } static void DrawOperationSuccessfulEmptyState( RepositorySpec repSpec, Rect rect, GUIContent emptyStateContent, string operationSuccessfulMessage, bool canInviteMembersFromPendingChanges) { emptyStateContent.text = operationSuccessfulMessage; if (canInviteMembersFromPendingChanges) { DrawTreeViewEmptyState.ForInviteMembers( rect, emptyStateContent, Images.GetStepOkIcon(), repSpec); return; } DrawTreeViewEmptyState.For( rect, emptyStateContent, Images.GetStepOkIcon()); } bool HasPendingMergeLinks() { if (mPendingMergeLinks == null) return false; return mPendingMergeLinks.Count > 0; } static void DoMergeLinksArea( MergeLinksListView mergeLinksListView, float width) { GUILayout.Label( PlasticLocalization.GetString( PlasticLocalization.Name.MergeLinkDescriptionColumn), EditorStyles.boldLabel); float desiredTreeHeight = mergeLinksListView.DesiredHeight; Rect treeRect = GUILayoutUtility.GetRect( 0, width, desiredTreeHeight, desiredTreeHeight); mergeLinksListView.OnGUI(treeRect); } void ClearComments() { mCommentText = string.Empty; mForceToShowComment = true; SessionState.EraseString(UnityConstants.PENDING_CHANGES_CI_COMMENTS_KEY_NAME); mParentWindow.Repaint(); } void RestoreData() { if (!mRestoreData || !mIsEnabled) return; TreeViewSessionState.Restore( mPendingChangesTreeView, UnityConstants.PENDING_CHANGES_UNCHECKED_ITEMS_KEY_NAME); mRestoreData = false; } static float InitializeOperationButtonWidth( GUIStyle actionButtonStyle, GUIStyle actionButtonWithMenuStyle, float operationButtonWidth, params string[] texts) { if (operationButtonWidth != -1) return operationButtonWidth; float actionButtonWidth = MeasureMaxWidth. ForTexts(actionButtonStyle, texts); float actionButtonWithMenuWidth = MeasureMaxWidth. ForTexts(actionButtonWithMenuStyle, texts); return Math.Max(actionButtonWidth, actionButtonWithMenuWidth); } void BuildComponents( IViewSwitcher viewSwitcher, bool isGluonMode) { mShelveDropdownMenu = new GenericMenu(); mShelveDropdownMenu.AddItem( new GUIContent(PlasticLocalization.GetString( PlasticLocalization.Name.ShowShelvesButton)), false, () => ShowShelvesView(viewSwitcher)); mUndoDropdownMenu = new GenericMenu(); mUndoDropdownMenu.AddItem( new GUIContent(PlasticLocalization.GetString( PlasticLocalization.Name.UndoUnchangedButton)), false, UndoUnchanged); mUndoDropdownMenu.AddItem( new GUIContent(PlasticLocalization.GetString( PlasticLocalization.Name.UndoCheckoutsKeepingChanges)), false, UndoCheckoutsKeepingLocalChanges); mSearchField = new SearchField(); mSearchField.downOrUpArrowKeyPressed += SearchField_OnDownOrUpArrowKeyPressed; PendingChangesTreeHeaderState headerState = PendingChangesTreeHeaderState.GetDefault(isGluonMode); TreeHeaderSettings.Load(headerState, UnityConstants.PENDING_CHANGES_TABLE_SETTINGS_NAME, (int)PendingChangesTreeColumn.Item, true); mPendingChangesTreeView = new PendingChangesTreeView( mWkInfo, isGluonMode, headerState, PendingChangesTreeHeaderState.GetColumnNames(), new PendingChangesViewMenu( mWkInfo, this, this, this, this, this, this, this, isGluonMode), mAssetStatusCache); mPendingChangesTreeView.Reload(); mMergeLinksListView = new MergeLinksListView(); mMergeLinksListView.Reload(); if (isGluonMode) mErrorsPanel = new ErrorsPanel( PlasticLocalization.Name.ChangesCannotBeApplied.GetString(), UnityConstants.PENDING_CHANGES_ERRORS_TABLE_SETTINGS_NAME); } float mOperationButtonWidth = -1; GenericMenu mShelveDropdownMenu; GenericMenu mUndoDropdownMenu; SearchField mSearchField; PendingChangesTreeView mPendingChangesTreeView; MergeLinksListView mMergeLinksListView; ErrorsPanel mErrorsPanel; object mErrorsSplitterState; IDictionary> mPendingMergeLinks; volatile bool mAreIgnoreRulesInitialized = false; bool mIsRefreshing; bool mIsAutoRefreshDisabled; bool mIsEmptyCheckinCommentWarningNeeded = false; bool mNeedsToShowEmptyCheckinCommentDialog = false; bool mHasPendingCheckinFromPreviousUpdate = false; bool mIsEmptyShelveCommentWarningNeeded = false; bool mNeedsToShowEmptyShelveCommentDialog = false; bool mHasPendingShelveFromPreviousUpdate = false; bool mHasPendingMergeLinksFromRevert = false; bool mKeepItemsLocked; bool mForceToShowComment; string mOperationSuccessfulMessage; bool mCanInviteMembersFromPendingChanges; bool mRestoreData = true; bool mIsEnabled = true; string mCommentText; string mGluonWarningMessage; readonly GUIContent mEmptyStateContent = new GUIContent(string.Empty); readonly INewChangesInWk mNewChangesInWk; readonly ProgressControlsForViews mProgressControls; readonly EditorWindow mParentWindow; readonly StatusBar mStatusBar; readonly CooldownWindowDelayer mCooldownClearOperationSuccessAction; readonly IAssetStatusCache mAssetStatusCache; readonly PendingChangesOperations mPendingChangesOperations; readonly PendingChangesViewCheckedStateManager mCheckedStateManager; readonly GuiMessage.IGuiMessage mGuiMessage; readonly NewIncomingChangesUpdater mDeveloperNewIncomingChangesUpdater; readonly GluonNewIncomingChangesUpdater mGluonNewIncomingChangesUpdater; readonly WorkspaceOperationsMonitor mWorkspaceOperationsMonitor; readonly LaunchTool.IShowDownloadPlasticExeWindow mShowDownloadPlasticExeWindow; readonly IHistoryViewLauncher mHistoryViewLauncher; readonly WorkspaceWindow mWorkspaceWindow; readonly IViewSwitcher mViewSwitcher; readonly ViewHost mViewHost; readonly WorkspaceInfo mWkInfo; readonly bool mIsGluonMode; readonly RepositorySpec mRepSpec; static readonly ILog mLog = PlasticApp.GetLogger("PendingChangesTab"); } }