using UnityEngine; using UnityEditor; using System; using System.Collections.Generic; using System.Linq; using System.Text; using Perforce.P4; using log4net; namespace P4Connect { class PendingAssetAndMeta { public string AssetPath; public string MetaPath; public FileAndMetaType Type; public AssetStatusCache.AssetStatus State; public string EffectiveFilePath { get { switch (Type) { case FileAndMetaType.None: return ""; case FileAndMetaType.FileOnly: return AssetPath; case FileAndMetaType.MetaOnly: return MetaPath; case FileAndMetaType.FileAndMeta: return AssetPath; default: throw new System.Exception("Unhandled case"); } } } public string TypeString { get { switch (Type) { case FileAndMetaType.None: return ""; case FileAndMetaType.FileOnly: return "Asset"; case FileAndMetaType.MetaOnly: return "Meta"; case FileAndMetaType.FileAndMeta: return "Asset+Meta"; default: throw new System.Exception("Unhandled case"); } } } public PendingAssetAndMeta() { AssetPath = ""; MetaPath = ""; Type = FileAndMetaType.None; State = new AssetStatusCache.AssetStatus(); } } public class PendingChanges : EditorWindow { private static readonly ILog log = LogManager.GetLogger(typeof(PendingChanges)); const float iconSize = 20.0f; const float spaceBeforeIcon = 8.0f; const double DoubleClickTime = 500; // ms static PendingChanges _CurrentInstance; // Add menu named "Perforce" to the Window menu [MenuItem("Window/Perforce", false, 1000)] public static void ShowWindow() { // Get existing open window or if none, make a new one: PendingChanges window = EditorWindow.GetWindow(typeof(PendingChanges), false, "Perforce") as PendingChanges; window.title = "Perforce"; window.name = "Perforce"; } public static void UpdateDisplay() { if (_CurrentInstance != null) { if (Config.ValidConfiguration) { using (PerforceConnection con = new PerforceConnection()) { _CurrentInstance.UpdateList(con); } } else { _CurrentInstance.Clear(); } _CurrentInstance.Repaint(); } } public IEnumerable SelectedAssets { get { return _CurrentSelectedChanges.Select(ch => ch.EffectiveFilePath); } } List _PendingChanges; Dictionary _CheckedFiles; Vector2 _ScrollVector; string _ChangeListDescription; Texture2D _HighlightedBackground; Texture2D _SelectedBackground; // User selection PendingAssetAndMeta _LastSelectedChange; DateTime _LastSelectedChangeTime; HashSet _CurrentSelectedChanges; // Sorting algorithms System.Func _CurrentSortingFunc; bool _CurrentSortingAscending; public PendingChanges() { Init(); } void Init() { _LastSelectedChange = null; _CurrentSelectedChanges = new HashSet(); _PendingChanges = new List(); _CheckedFiles = new Dictionary(); if (!Config.ValidConfiguration) return; ChangeManager.GetInstance.Refresh(ChangeManager.DEFAULT_CHANGE); using (PerforceConnection con = new PerforceConnection()) { UpdateList(con); } AssetStatusCache.OnAssetStatusChanged -= OnOperationPerformed; AssetStatusCache.OnAssetStatusChanged += OnOperationPerformed; _HighlightedBackground = new Texture2D(1, 1); _HighlightedBackground.SetPixel(0, 0, new Color(61.0f / 255.0f, 96.0f / 255.0f, 145.0f / 255.0f)); _HighlightedBackground.Apply(); _SelectedBackground = new Texture2D(1, 1); _SelectedBackground.SetPixel(0, 0, new Color(72.0f / 255.0f, 72.0f / 255.0f, 72.0f / 255.0f)); _SelectedBackground.Apply(); _CurrentSortingFunc = FilenameSortingFunc; _CurrentSortingAscending = true; _CurrentInstance = this; } void CheckInit() { if (_HighlightedBackground == null) { Init(); } } public void OnDestroy() { _CurrentInstance = null; Clear(); AssetStatusCache.OnAssetStatusChanged -= OnOperationPerformed; } void Clear() { if (_HighlightedBackground == null) return; _PendingChanges.Clear(); _CheckedFiles.Clear(); _ScrollVector = Vector2.zero; // User selection _LastSelectedChange = null; _CurrentSelectedChanges.Clear(); } void OnOperationPerformed(PerforceConnection con) { UpdateList(con); Repaint(); } void UpdateList(PerforceConnection con) { Profiler.BeginSample("UpdateList"); log.Debug("UpdateList"); _PendingChanges.Clear(); if (Config.ValidConfiguration) { List openedFiles = new List(ChangeManager.GetInstance.LocalOpenedFiles); log.DebugFormat("openedFiles: {0}", Logger.StringArrayToString(openedFiles.ToArray())); if (openedFiles != null) { // Parse the list, seeing if files have metas and such while (openedFiles.Count > 0) { Profiler.BeginSample("UpdateList - count" + openedFiles.Count); //log.DebugFormat("Main.Rootpath: {0} ", Main.RootPath); //if (openedFiles[0].StartsWith(Main.RootPath, !Utils.IsCaseSensitive(), null)) //{ string filePath = Utils.UnescapeFilename(openedFiles[0]); log.DebugFormat("filePath: {0}", filePath); PendingAssetAndMeta assetAndMeta = new PendingAssetAndMeta(); if (filePath.EndsWith(".meta")) { // It's a meta file, is there an asset in the list as well? assetAndMeta.MetaPath = filePath; assetAndMeta.AssetPath = Utils.AssetFromMeta(filePath); int assetIndex = openedFiles.IndexOf(assetAndMeta.AssetPath); if (assetIndex != -1) { // Yes, there is an asset file assetAndMeta.Type = FileAndMetaType.FileAndMeta; // Remove both entries openedFiles.RemoveAt(assetIndex); } else { // No, this is a meta only assetAndMeta.Type = FileAndMetaType.MetaOnly; } } else { // It's not a meta file, is there a meta as well? assetAndMeta.AssetPath = filePath; assetAndMeta.MetaPath = Utils.MetaFromAsset(filePath); int metaIndex = openedFiles.IndexOf(Utils.EscapeFilename(assetAndMeta.MetaPath)); if (metaIndex != -1) { // Yes, there is a meta file assetAndMeta.Type = FileAndMetaType.FileAndMeta; // Remove both entries openedFiles.RemoveAt(metaIndex); } else { // No, this is a file only assetAndMeta.Type = FileAndMetaType.FileOnly; } } // Check whether we knew about this file and wanted it selected if (!_CheckedFiles.ContainsKey(assetAndMeta.EffectiveFilePath)) { // If we don't know the file, say that it's selected _CheckedFiles.Add(assetAndMeta.EffectiveFilePath, true); } // Finally, add the asset and meta to the list log.DebugFormat(" Adding to Pending Changes: {0}", assetAndMeta.EffectiveFilePath); _PendingChanges.Add(assetAndMeta); //} // No matter what, remove the file openedFiles.RemoveAt(0); Profiler.EndSample(); } // Fetch the state of the file List paths = new List(_PendingChanges.Select(pc => pc.EffectiveFilePath)); List statuses = new List(); AssetStatusCache.GetAssetData(con, paths, statuses); for (int i = 0; i < paths.Count; ++i) { _PendingChanges[i].State = statuses[i]; } // Sort the changes SortPendingChanges(); } } Profiler.EndSample(); } void OnGUI() { if (!Config.ValidConfiguration) { this.Close(); // If the perforce connection is broken, close the pending changes window Debug.Log("Closed PendingChanges window because Connection is invalid"); return; } CheckInit(); Event evt = Event.current; // Header GUIStyle changelistStyle = new GUIStyle(EditorStyles.toolbarButton); changelistStyle.normal.textColor = Color.grey; changelistStyle.alignment = TextAnchor.MiddleLeft; // Buttons GUILayout.BeginHorizontal(EditorStyles.toolbar); EditorGUI.BeginDisabledGroup(!Config.ValidConfiguration); GUILayout.Label("Default Changelist: " + _PendingChanges.Count + " assets", changelistStyle, GUILayout.Width(200.0f)); if (GUILayout.Button("Refresh", EditorStyles.toolbarButton)) { using (PerforceConnection con = new PerforceConnection()) { ChangeManager.GetInstance.Refresh(-1); UpdateList(con); } } if (GUILayout.Button("Revert All Unchanged", EditorStyles.toolbarButton)) { RevertUnchanged(); } if (GUILayout.Button("Get Latest Assets", EditorStyles.toolbarButton)) { if (EditorUtility.DisplayDialog("Get Latest Assets?", "Are you sure you want to update all assets to the latest revision?", "Ok", "Cancel")) { if (Engine.GetLatestAsset("", false).Count == 0) { Debug.Log("P4Connect - Get Latest Revision - No Change"); } } } EditorGUI.EndDisabledGroup(); GUILayout.FlexibleSpace(); if (GUILayout.Button("Help", EditorStyles.toolbarButton)) { System.Diagnostics.Process.Start("http://www.perforce.com/perforce/doc.current/manuals/p4connectguide/index.html"); } if (GUILayout.Button("Settings...", EditorStyles.toolbarButton)) { Config.ShowWindow(); } GUILayout.EndHorizontal(); EditorGUI.BeginDisabledGroup(!Config.ValidConfiguration); GUILayout.BeginHorizontal(); _ScrollVector = GUILayout.BeginScrollView(_ScrollVector); // Header GUIStyle HeaderStyle = new GUIStyle(EditorStyles.miniButtonMid); HeaderStyle.normal.textColor = Color.white; HeaderStyle.padding = new RectOffset(0, 0, 0, 0); HeaderStyle.margin = new RectOffset(0, 0, 0, 0); HeaderStyle.overflow = new RectOffset(0, 0, 0, 0); GUIStyle HeaderButtons = new GUIStyle(EditorStyles.miniButtonMid); HeaderButtons.alignment = TextAnchor.MiddleLeft; HeaderStyle.padding = new RectOffset(0, 0, 0, 2); HeaderButtons.margin = new RectOffset(0, 0, 0, 0); HeaderButtons.overflow = new RectOffset(0, 0, 0, 2); GUIStyle ToggleStyle = new GUIStyle(EditorStyles.toggle); RectOffset margin = ToggleStyle.margin; margin.top = 0; margin.bottom = 0; ToggleStyle.margin = margin; GUIStyle normalStyle = new GUIStyle(GUIStyle.none); normalStyle.padding = new RectOffset(0, 0, 0, 0); normalStyle.margin = new RectOffset(0, 0, 0, 0); normalStyle.border = new RectOffset(0, 0, 0, 0); GUIStyle highlightedStyle = new GUIStyle(GUIStyle.none); highlightedStyle.normal.background = _HighlightedBackground; highlightedStyle.padding = new RectOffset(0, 0, 0, 0); highlightedStyle.margin = new RectOffset(0, 0, 0, 0); highlightedStyle.border = new RectOffset(0, 0, 0, 0); GUIStyle selectedStyle = new GUIStyle(GUIStyle.none); selectedStyle.normal.background = _SelectedBackground; selectedStyle.padding = new RectOffset(0, 0, 0, 0); selectedStyle.margin = new RectOffset(0, 0, 0, 0); selectedStyle.border = new RectOffset(0, 0, 0, 0); GUILayout.BeginHorizontal(HeaderStyle); { bool allSelected = _PendingChanges.Count(pc => _CheckedFiles[pc.EffectiveFilePath]) == _PendingChanges.Count; bool newAllSelected = GUILayout.Toggle(allSelected, "", ToggleStyle, GUILayout.Width(10.0f)); if (newAllSelected != allSelected) { foreach (var pc in _PendingChanges) { _CheckedFiles[pc.EffectiveFilePath] = newAllSelected; } } if (GUILayout.Button("", HeaderButtons, GUILayout.Width(spaceBeforeIcon))) { if (GUI.GetNameOfFocusedControl() == "DescriptionBox") { GUI.FocusControl(""); } // Sort the files according to their extension if (_CurrentSortingFunc == SelectedSortingFunc) { // Reverse the order if the sorting was already according to whether they are selected _CurrentSortingAscending = !_CurrentSortingAscending; } else { // Make sorting according to the selection and reset the direction _CurrentSortingFunc = SelectedSortingFunc; _CurrentSortingAscending = true; } SortPendingChanges(); } if (GUILayout.Button("", HeaderButtons, GUILayout.Width(iconSize))) { if (GUI.GetNameOfFocusedControl() == "DescriptionBox") { GUI.FocusControl(""); } // Sort the files according to their extension if (_CurrentSortingFunc == ExtensionSortingFunc) { // Reverse the order if the sorting was already according to the extension _CurrentSortingAscending = !_CurrentSortingAscending; } else { // Make sorting according to the extension and reset the direction _CurrentSortingFunc = ExtensionSortingFunc; _CurrentSortingAscending = true; } SortPendingChanges(); } if (GUILayout.Button("Filename", HeaderButtons, GUILayout.Width(250.0f))) { if (GUI.GetNameOfFocusedControl() == "DescriptionBox") { GUI.FocusControl(""); } // Sort the files according to their name if (_CurrentSortingFunc == FilenameSortingFunc) { // Reverse the order if the sorting was already according to the name _CurrentSortingAscending = !_CurrentSortingAscending; } else { // Make sorting according to the name and reset the direction _CurrentSortingFunc = FilenameSortingFunc; _CurrentSortingAscending = true; } SortPendingChanges(); } if (GUILayout.Button("in Directory", HeaderButtons, GUILayout.MinWidth(250.0f))) { if (GUI.GetNameOfFocusedControl() == "DescriptionBox") { GUI.FocusControl(""); } if (_CurrentSortingFunc == DirectorySortingFunc) { _CurrentSortingAscending = !_CurrentSortingAscending; } else { _CurrentSortingFunc = DirectorySortingFunc; _CurrentSortingAscending = true; } SortPendingChanges(); } //GUILayout.FlexibleSpace(); if (GUILayout.Button("Type", HeaderButtons, GUILayout.Width(80.0f))) { if (GUI.GetNameOfFocusedControl() == "DescriptionBox") { GUI.FocusControl(""); } if (_CurrentSortingFunc == TypeSortingFunc) { _CurrentSortingAscending = !_CurrentSortingAscending; } else { _CurrentSortingFunc = TypeSortingFunc; _CurrentSortingAscending = true; } SortPendingChanges(); } if (GUILayout.Button("Change", HeaderButtons, GUILayout.Width(80.0f))) { if (GUI.GetNameOfFocusedControl() == "DescriptionBox") { GUI.FocusControl(""); } if (_CurrentSortingFunc == StateSortingFunc) { _CurrentSortingAscending = !_CurrentSortingAscending; } else { _CurrentSortingFunc = StateSortingFunc; _CurrentSortingAscending = true; } SortPendingChanges(); } GUILayout.Space(32.0f); } GUILayout.EndHorizontal(); // Add all the items foreach (var change in _PendingChanges) { if (_LastSelectedChange == change && focusedWindow == this) { if (_CurrentSelectedChanges.Contains(_LastSelectedChange)) { GUILayout.BeginHorizontal(highlightedStyle); } else { GUILayout.BeginHorizontal(normalStyle); } } else if (_CurrentSelectedChanges.Contains(change)) { GUILayout.BeginHorizontal(selectedStyle); } else { GUILayout.BeginHorizontal(normalStyle); } // Draw a selection box bool itemChecked = _CheckedFiles[change.EffectiveFilePath]; bool newItemChecked = GUILayout.Toggle(itemChecked, "", EditorStyles.toggle, GUILayout.Width(10.0f)); if (newItemChecked != itemChecked) { if (_CurrentSelectedChanges.Contains(change)) { foreach (var ch in _CurrentSelectedChanges) { _CheckedFiles[ch.EffectiveFilePath] = newItemChecked; } } else { _CheckedFiles[change.EffectiveFilePath] = newItemChecked; } } GUILayout.Space(spaceBeforeIcon); // Fetch the asset's icon and draw it Texture assetTexture = Icons.GetAssetIcon(change.AssetPath); GUILayout.Label(assetTexture, GUILayout.Width(iconSize), GUILayout.Height(iconSize)); // Then decorate it with status icons Rect iconRect = GUILayoutUtility.GetLastRect(); iconRect.yMin -= 1.0f; iconRect.xMax -= 0.5f; iconRect.yMax += 16.0f; Icons.DrawItemIcons(change.EffectiveFilePath, iconRect, false); // And the name of the asset GUILayout.Label(System.IO.Path.GetFileName(change.AssetPath), GUILayout.Width(250.0f)); GUILayout.Label(System.IO.Path.GetDirectoryName(change.AssetPath), GUILayout.MinWidth(250.0f)); GUILayout.FlexibleSpace(); // Now indicate if we have the file and/or the meta GUILayout.Label(Utils.GetStorageTypeString(change.State.StorageType), GUILayout.Width(80.0f)); GUILayout.Label(Utils.GetFileStateString(change.State.LocalState), GUILayout.Width(80.0f)); GUILayout.Label(Icons.GetFileAndMetaTypeIcon(change.Type), GUILayout.Width(iconSize), GUILayout.Height(iconSize)); GUILayout.EndHorizontal(); Rect lastRect = GUILayoutUtility.GetLastRect(); if (_LastSelectedChange == change && focusedWindow == this && !_CurrentSelectedChanges.Contains(_LastSelectedChange)) { Rect topLine = lastRect; topLine.yMax = topLine.yMin + 1; Rect bottomLine = lastRect; bottomLine.yMin = bottomLine.yMax - 1; Rect leftLine = lastRect; leftLine.xMin += 1; leftLine.xMax = leftLine.xMin + 1; Rect rightLine = lastRect; rightLine.xMin = rightLine.xMax - 1; GUI.Box(topLine, GUIContent.none, highlightedStyle); GUI.Box(bottomLine, GUIContent.none, highlightedStyle); GUI.Box(leftLine, GUIContent.none, highlightedStyle); GUI.Box(rightLine, GUIContent.none, highlightedStyle); } if (lastRect.Contains(evt.mousePosition)) { // Handle events here if (evt.isMouse) { if (evt.type == EventType.MouseDown) { if (GUI.GetNameOfFocusedControl() == "DescriptionBox") { GUI.FocusControl(""); } if (evt.button == 0) { if ((evt.modifiers & EventModifiers.Control) != 0) { // Ctrl means Add/remove from selection if (_CurrentSelectedChanges.Contains(change)) { _CurrentSelectedChanges.Remove(change); } else { _CurrentSelectedChanges.Add(change); } } else if ((evt.modifiers & EventModifiers.Shift) != 0) { // Shift means select all between this and previous selected foreach (var ch in GetChangesBetweenChanges(_LastSelectedChange, change)) { _CurrentSelectedChanges.Add(ch); } } else { if (change == _LastSelectedChange) { // Is this a double click? if ((DateTime.Now - _LastSelectedChangeTime).TotalMilliseconds < DoubleClickTime) { // Yes, open the asset if (_LastSelectedChange.AssetPath != null || _LastSelectedChange.AssetPath != "") { UnityEngine.Object asset = AssetDatabase.LoadMainAssetAtPath(_LastSelectedChange.AssetPath); AssetDatabase.OpenAsset(asset); } } } else { // Clear selection and set it to new element _CurrentSelectedChanges.Clear(); _CurrentSelectedChanges.Add(change); } } // In all cases, we remember the last selected change _LastSelectedChange = change; _LastSelectedChangeTime = DateTime.Now; evt.Use(); Repaint(); } else if (evt.button == 1) { // In all cases, we remember the last selected change _LastSelectedChange = change; evt.Use(); Repaint(); } } else if (evt.type == EventType.MouseUp) { if (evt.button == 1) { if (!_CurrentSelectedChanges.Contains(change) && (evt.modifiers & EventModifiers.Control) == 0) { // Anything other than Ctrl Deselects _CurrentSelectedChanges.Clear(); _CurrentSelectedChanges.Add(change); } // Display contextual menu Rect menuRect = lastRect; menuRect.xMin = evt.mousePosition.x; menuRect.yMin = evt.mousePosition.y; EditorUtility.DisplayPopupMenu(menuRect, "CONTEXT/Perforce/", new MenuCommand(this)); evt.Use(); } } } } } GUILayout.EndScrollView(); GUILayout.EndHorizontal(); EditorGUI.EndDisabledGroup(); if (!Config.PerforceEnabled) { EditorGUILayout.HelpBox("Perforce integration is disabled", MessageType.Info); } else if (!Config.ValidConfiguration) { EditorGUILayout.HelpBox("Pending Changes are unavailable because your settings are invalid.\nPlease go to Edit->Perforce Settings to update them.", MessageType.Warning); } EditorGUI.BeginDisabledGroup(!Config.ValidConfiguration); GUILayout.Label("Description", EditorStyles.boldLabel); GUI.SetNextControlName("DescriptionBox"); _ChangeListDescription = EditorGUILayout.TextArea(_ChangeListDescription, GUILayout.MinHeight(50.0f)); GUILayout.BeginHorizontal(); int unresolvedCount = _PendingChanges.Count(ch => _CheckedFiles[ch.EffectiveFilePath] && ch.State.ResolvedState == ResolvedState.NeedsResolve); int outOfDateCount = _PendingChanges.Count(ch => _CheckedFiles[ch.EffectiveFilePath] && ch.State.RevisionState == RevisionState.OutOfDate); if (unresolvedCount > 0) { Color prevColor = GUI.color; GUI.color = Color.yellow; GUILayout.Label(unresolvedCount.ToString() + " unresolved assets selected!"); GUI.color = prevColor; } else if (outOfDateCount > 0) { Color prevColor = GUI.color; GUI.color = Color.yellow; GUILayout.Label(outOfDateCount.ToString() + " out of date assets selected!"); GUI.color = prevColor; } GUILayout.FlexibleSpace(); int assetCount = 0; int fileCount = 0; GetSelectedCounts(out assetCount, out fileCount); GUILayout.Label(assetCount.ToString() + " assets selected (" + fileCount.ToString() + " files)"); EditorGUI.BeginDisabledGroup(_ChangeListDescription == null || _ChangeListDescription.Length == 0 || unresolvedCount > 0 || outOfDateCount > 0); if (GUILayout.Button("Submit Selected")) { SubmitFiles(); } EditorGUI.EndDisabledGroup(); GUILayout.EndHorizontal(); EditorGUI.EndDisabledGroup(); // Handle key events if (evt.isKey) { if (evt.type == EventType.KeyDown) { if (evt.keyCode == KeyCode.Return) { // Toggle all selected changes to the new state of the selected item bool itemChecked = _CheckedFiles[_LastSelectedChange.EffectiveFilePath]; foreach (var ch in _CurrentSelectedChanges) { _CheckedFiles[ch.EffectiveFilePath] = !itemChecked; } evt.Use(); Repaint(); } else if (evt.keyCode == KeyCode.DownArrow) { // Update highlight _LastSelectedChange = GetNextPendingChange(_LastSelectedChange); // Handle shift selection if ((evt.modifiers & EventModifiers.Shift) == 0 && (evt.modifiers & EventModifiers.Control) == 0) { _CurrentSelectedChanges.Clear(); } _CurrentSelectedChanges.Add(_LastSelectedChange); evt.Use(); Repaint(); } else if (evt.keyCode == KeyCode.UpArrow) { _LastSelectedChange = GetPrevPendingChange(_LastSelectedChange); if ((evt.modifiers & EventModifiers.Shift) == 0 && (evt.modifiers & EventModifiers.Control) == 0) { _CurrentSelectedChanges.Clear(); } _CurrentSelectedChanges.Add(_LastSelectedChange); evt.Use(); Repaint(); } else if (evt.keyCode == KeyCode.A) { _CurrentSelectedChanges.Clear(); foreach (var change in _PendingChanges) { _CurrentSelectedChanges.Add(change); } evt.Use(); Repaint(); } else if (evt.keyCode == KeyCode.D) { foreach (var change in _CurrentSelectedChanges) { Utils.LaunchDiffAgainstHaveRev(change.EffectiveFilePath); } evt.Use(); Repaint(); } else if (evt.keyCode == KeyCode.I) { HashSet newSelection = new HashSet(); foreach (var change in _PendingChanges) { if (!_CurrentSelectedChanges.Contains(change)) { newSelection.Add(change); } } _CurrentSelectedChanges = newSelection; evt.Use(); Repaint(); } } } } void GetSelectedCounts(out int aOutAssetCount, out int aOutFileCount) { int assetCount = 0; int fileCount = 0; for (int i = 0; i < _PendingChanges.Count; i++) { var change = _PendingChanges[i]; if (_CheckedFiles[change.EffectiveFilePath]) { assetCount += 1; switch (change.Type) { case FileAndMetaType.FileOnly: fileCount += 1; break; case FileAndMetaType.MetaOnly: fileCount += 1; break; case FileAndMetaType.FileAndMeta: fileCount += 2; break; } } } aOutAssetCount = assetCount; aOutFileCount = fileCount; } void SubmitFiles() { List specsToSubmit = new List(); // Build a string of the names StringBuilder builder = new StringBuilder(); builder.AppendLine("Are you sure you want to submit the following files?"); builder.AppendLine(); int maxCount = 20; int curCount = 0; for (int i = 0; i < _PendingChanges.Count; i++) { var change = _PendingChanges[i]; if (_CheckedFiles[change.EffectiveFilePath]) { switch (change.Type) { case FileAndMetaType.FileOnly: { string filepath = Utils.AssetPathToLocalPath(change.AssetPath); specsToSubmit.Add(FileSpec.LocalSpec(filepath)); if (++curCount <= maxCount) builder.AppendLine(change.AssetPath); } break; case FileAndMetaType.MetaOnly: { string filepath = Utils.AssetPathToLocalPath(change.MetaPath); specsToSubmit.Add(FileSpec.LocalSpec(filepath)); if (++curCount <= maxCount) builder.AppendLine(change.MetaPath); } break; case FileAndMetaType.FileAndMeta: { string filepath = Utils.AssetPathToLocalPath(change.AssetPath); specsToSubmit.Add(FileSpec.LocalSpec(filepath)); if (++curCount <= maxCount) builder.AppendLine(change.AssetPath); } { string filepath = Utils.AssetPathToLocalPath(change.MetaPath); specsToSubmit.Add(FileSpec.LocalSpec(filepath)); if (++curCount <= maxCount) builder.AppendLine(change.MetaPath); } break; } } } if (curCount > maxCount) { builder.Append("..."); } if (EditorUtility.DisplayDialog("Submit changes?", builder.ToString(), "Submit", "Cancel")) { Engine.PerformConnectionOperation(con => Engine.SubmitFiles(con, _ChangeListDescription, specsToSubmit)); _ChangeListDescription = ""; GUI.FocusControl(""); } } void RevertUnchanged() { List filesToRevert = new List(); for (int i = 0; i < _PendingChanges.Count; i++) { var change = _PendingChanges[i]; switch (change.Type) { case FileAndMetaType.FileOnly: filesToRevert.Add(change.AssetPath); break; case FileAndMetaType.MetaOnly: filesToRevert.Add(change.MetaPath); break; case FileAndMetaType.FileAndMeta: filesToRevert.Add(change.AssetPath); filesToRevert.Add(change.MetaPath); break; } } if (Engine.RevertAssets(filesToRevert.ToArray(), false).Count == 0) { Debug.Log("P4Connect - Revert if Unchanged - No Change"); } } string SelectedSortingFunc(PendingAssetAndMeta aAssetAndMeta) { return _CheckedFiles[aAssetAndMeta.EffectiveFilePath] ? "A" : "B"; } static string ExtensionSortingFunc(PendingAssetAndMeta aAssetAndMeta) { return System.IO.Path.GetExtension(aAssetAndMeta.EffectiveFilePath); } static string FilenameSortingFunc(PendingAssetAndMeta aAssetAndMeta) { return System.IO.Path.GetFileName(aAssetAndMeta.EffectiveFilePath); } static string DirectorySortingFunc(PendingAssetAndMeta aAssetAndMeta) { return System.IO.Path.GetDirectoryName(aAssetAndMeta.EffectiveFilePath); } static string StateSortingFunc(PendingAssetAndMeta aAssetAndMeta) { return Utils.GetFileStateString(aAssetAndMeta.State.LocalState); } static string TypeSortingFunc(PendingAssetAndMeta aAssetAndMeta) { return Utils.GetStorageTypeString(aAssetAndMeta.State.StorageType); } void SortPendingChanges() { if (_CurrentSortingFunc != null) { List newList = null; if (_CurrentSortingAscending) newList = new List(_PendingChanges.OrderBy(_CurrentSortingFunc)); else newList = new List(_PendingChanges.OrderByDescending(_CurrentSortingFunc)); _PendingChanges = newList; } } PendingAssetAndMeta GetPrevPendingChange(PendingAssetAndMeta aCurrent) { int index = _PendingChanges.IndexOf(aCurrent); return _PendingChanges[Mathf.Clamp(index - 1, 0, _PendingChanges.Count)]; } PendingAssetAndMeta GetNextPendingChange(PendingAssetAndMeta aCurrent) { int index = _PendingChanges.IndexOf(aCurrent); return _PendingChanges[Mathf.Clamp(index + 1, 0, _PendingChanges.Count)]; } IEnumerable GetChangesBetweenChanges(PendingAssetAndMeta aStart, PendingAssetAndMeta aEndIncluded) { int indexStart = _PendingChanges.IndexOf(aStart); int indexEnd = _PendingChanges.IndexOf(aEndIncluded); for (int i = indexStart; i <= indexEnd; ++i) { yield return _PendingChanges[i]; } } public void SelectAssets(IEnumerable aEffectiveFilePaths) { // Deselect all List files = new List(_CheckedFiles.Keys); foreach (var file in files) { _CheckedFiles[file] = false; } // Select files passed in foreach (var file in aEffectiveFilePaths) { bool state = false; if (_CheckedFiles.TryGetValue(file, out state)) { _CheckedFiles[file] = true; } } } } }