using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine; using Codice.Client.Common; using Codice.Client.Common.EventTracking; using Codice.CM.Common; using GluonGui; using PlasticGui; using PlasticGui.Gluon; using PlasticGui.WorkspaceWindow; using Unity.PlasticSCM.Editor.AssetsOverlays; using Unity.PlasticSCM.Editor.AssetsOverlays.Cache; using Unity.PlasticSCM.Editor.AssetUtils; using Unity.PlasticSCM.Editor.AssetUtils.Processor; using Unity.PlasticSCM.Editor.UI; using Unity.PlasticSCM.Editor.UI.Progress; using Unity.PlasticSCM.Editor.UI.Tree; namespace Unity.PlasticSCM.Editor.AssetMenu.Dialogs { internal class CheckinDialog : PlasticDialog { protected override Rect DefaultRect { get { var baseRect = base.DefaultRect; return new Rect(baseRect.x, baseRect.y, 700, 450); } } protected override string GetTitle() { return PlasticLocalization.GetString( PlasticLocalization.Name.CheckinChanges); } internal static bool CheckinPaths( WorkspaceInfo wkInfo, List paths, IAssetStatusCache assetStatusCache, bool isGluonMode, IWorkspaceWindow workspaceWindow, ViewHost viewHost, WorkspaceOperationsMonitor workspaceOperationsMonitor, IPendingChangesUpdater pendingChangesUpdater, ISaveAssets saveAssets, GuiMessage.IGuiMessage guiMessage, IMergeViewLauncher mergeViewLauncher, IGluonViewSwitcher gluonViewSwitcher) { MetaCache metaCache = new MetaCache(); metaCache.Build(paths); CheckinDialog dialog = Create( wkInfo, paths, assetStatusCache, metaCache, isGluonMode, new ProgressControlsForDialogs(), workspaceWindow, viewHost, workspaceOperationsMonitor, pendingChangesUpdater, saveAssets, guiMessage, mergeViewLauncher, gluonViewSwitcher); return dialog.RunModal(focusedWindow) == ResponseType.Ok; } protected override void OnModalGUI() { Title(PlasticLocalization.GetString(PlasticLocalization.Name.CheckinOnlyComment)); GUI.SetNextControlName(CHECKIN_TEXTAREA_NAME); mComment = GUILayout.TextArea( mComment, EditorStyles.textArea, GUILayout.MinHeight(120)); if (!mTextAreaFocused) { EditorGUI.FocusTextInControl(CHECKIN_TEXTAREA_NAME); mTextAreaFocused = true; } Title(PlasticLocalization.GetString(PlasticLocalization.Name.Files)); DoFileList( mWkInfo, mPaths, mAssetStatusCache, mMetaCache); DrawProgressForDialogs.For( mProgressControls.ProgressData); DoButtonsArea(); mProgressControls.ForcedUpdateProgress(this); } void DoFileList( WorkspaceInfo wkInfo, List paths, IAssetStatusCache assetStatusCache, MetaCache metaCache) { mFileListScrollPosition = GUILayout.BeginScrollView( mFileListScrollPosition, EditorStyles.helpBox, GUILayout.ExpandHeight(true)); foreach (string path in paths) { if (MetaPath.IsMetaPath(path)) continue; Texture fileIcon = Directory.Exists(path) ? Images.GetFolderIcon() : Images.GetFileIcon(path); string label = WorkspacePath.GetWorkspaceRelativePath( wkInfo.ClientPath, path); if (metaCache.HasMeta(path)) label = string.Concat(label, UnityConstants.TREEVIEW_META_LABEL); AssetsOverlays.AssetStatus assetStatus = assetStatusCache.GetStatus(path); Rect selectionRect = EditorGUILayout.GetControlRect(); DoListViewItem(selectionRect, fileIcon, label, assetStatus); } GUILayout.EndScrollView(); } void DoListViewItem( Rect itemRect, Texture fileIcon, string label, AssetsOverlays.AssetStatus statusToDraw) { Texture overlayIcon = DrawAssetOverlayIcon.GetOverlayIcon(statusToDraw); itemRect = DrawTreeViewItem.DrawIconLeft( itemRect, UnityConstants.TREEVIEW_ROW_HEIGHT, fileIcon, overlayIcon); GUI.Label(itemRect, label); } void DoButtonsArea() { using (new EditorGUILayout.HorizontalScope()) { GUILayout.FlexibleSpace(); if (Application.platform == RuntimePlatform.WindowsEditor) { DoCheckinButton(); DoCancelButton(); return; } DoCancelButton(); DoCheckinButton(); } } void DoCheckinButton() { GUI.enabled = IsCheckinButtonEnabled(); try { if (!AcceptButton(PlasticLocalization.GetString( PlasticLocalization.Name.CheckinButton))) return; } finally { if (!mSentCheckinTrackEvent) { TrackFeatureUseEvent.For( PlasticGui.Plastic.API.GetRepositorySpec(mWkInfo), TrackFeatureUseEvent.Features.UnityPackage.ContextMenuCheckinDialogCheckin); mSentCheckinTrackEvent = true; } GUI.enabled = true; } OkButtonWithCheckinAction(); } void DoCancelButton() { if (!NormalButton(PlasticLocalization.GetString( PlasticLocalization.Name.CancelButton))) return; if (!mSentCancelTrackEvent) { TrackFeatureUseEvent.For( PlasticGui.Plastic.API.GetRepositorySpec(mWkInfo), TrackFeatureUseEvent.Features.UnityPackage.ContextMenuCheckinDialogCancel); mSentCancelTrackEvent = true; } CancelButtonAction(); } void OkButtonWithCheckinAction() { if (!IsCheckinButtonEnabled()) return; bool isCancelled; mSaveAssets.ForPathsWithConfirmation( mWkInfo.ClientPath, mPaths, mWorkspaceOperationsMonitor, out isCancelled); if (isCancelled) return; mIsRunningCheckin = true; mPaths.AddRange(mMetaCache.GetExistingMeta(mPaths)); if (mIsGluonMode) { CheckinDialogOperations.CheckinPathsPartial( mWkInfo, mPaths, mComment, mViewHost, this, mGuiMessage, mProgressControls, mGluonViewSwitcher, mPendingChangesUpdater); return; } CheckinDialogOperations.CheckinPaths( mWkInfo, mPaths, mComment, mWorkspaceWindow, this, mGuiMessage, mProgressControls, mMergeViewLauncher, mPendingChangesUpdater); } bool IsCheckinButtonEnabled() { return !string.IsNullOrEmpty(mComment) && !mIsRunningCheckin; } static CheckinDialog Create( WorkspaceInfo wkInfo, List paths, IAssetStatusCache assetStatusCache, MetaCache metaCache, bool isGluonMode, ProgressControlsForDialogs progressControls, IWorkspaceWindow workspaceWindow, ViewHost viewHost, WorkspaceOperationsMonitor workspaceOperationsMonitor, IPendingChangesUpdater pendingChangesUpdater, ISaveAssets saveAssets, GuiMessage.IGuiMessage guiMessage, IMergeViewLauncher mergeViewLauncher, IGluonViewSwitcher gluonViewSwitcher) { var instance = CreateInstance(); instance.IsResizable = true; instance.minSize = new Vector2(520, 370); instance.mWkInfo = wkInfo; instance.mPaths = paths; instance.mAssetStatusCache = assetStatusCache; instance.mMetaCache = metaCache; instance.mIsGluonMode = isGluonMode; instance.mProgressControls = progressControls; instance.mWorkspaceWindow = workspaceWindow; instance.mViewHost = viewHost; instance.mWorkspaceOperationsMonitor = workspaceOperationsMonitor; instance.mPendingChangesUpdater = pendingChangesUpdater; instance.mSaveAssets = saveAssets; instance.mGuiMessage = guiMessage; instance.mMergeViewLauncher = mergeViewLauncher; instance.mGluonViewSwitcher = gluonViewSwitcher; instance.mEnterKeyAction = instance.OkButtonWithCheckinAction; instance.AddControlConsumingEnterKey(CHECKIN_TEXTAREA_NAME); instance.mEscapeKeyAction = instance.CancelButtonAction; return instance; } WorkspaceInfo mWkInfo; List mPaths; IAssetStatusCache mAssetStatusCache; MetaCache mMetaCache; bool mIsGluonMode; bool mTextAreaFocused; string mComment; bool mIsRunningCheckin; Vector2 mFileListScrollPosition; // IMGUI evaluates every frame, need to make sure feature tracks get sent only once bool mSentCheckinTrackEvent = false; bool mSentCancelTrackEvent = false; ProgressControlsForDialogs mProgressControls; IWorkspaceWindow mWorkspaceWindow; WorkspaceOperationsMonitor mWorkspaceOperationsMonitor; IPendingChangesUpdater mPendingChangesUpdater; ISaveAssets mSaveAssets; ViewHost mViewHost; IMergeViewLauncher mMergeViewLauncher; IGluonViewSwitcher mGluonViewSwitcher; GuiMessage.IGuiMessage mGuiMessage; const string CHECKIN_TEXTAREA_NAME = "checkin_textarea"; class MetaCache { internal bool HasMeta(string path) { return mCache.Contains(MetaPath.GetMetaPath(path)); } internal List GetExistingMeta(List paths) { List result = new List(); foreach (string path in paths) { string metaPath = MetaPath.GetMetaPath(path); if (!mCache.Contains(metaPath)) continue; result.Add(metaPath); } return result; } internal void Build(List paths) { HashSet indexedKeys = BuildIndexedKeys(paths); for (int i = paths.Count - 1; i >= 0; i--) { string currentPath = paths[i]; if (!MetaPath.IsMetaPath(currentPath)) continue; string realPath = MetaPath.GetPathFromMetaPath(currentPath); if (!indexedKeys.Contains(realPath)) continue; // found foo.c and foo.c.meta // with the same chage types - move .meta to cache mCache.Add(currentPath); paths.RemoveAt(i); } } static HashSet BuildIndexedKeys(List paths) { HashSet result = new HashSet(); foreach (string path in paths) { if (MetaPath.IsMetaPath(path)) continue; result.Add(path); } return result; } HashSet mCache = new HashSet(); } } }