using UnityEngine; using UnityEditor; using System; using System.Collections.Generic; using System.Linq; using System.Text; using Perforce.P4; 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 { 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.PerforceEnabled) { Engine.PerformOpenConnectionOperation(con => _CurrentInstance.UpdateList(con)); } else { _CurrentInstance.Clear(); } _CurrentInstance.Repaint(); } } public IEnumerable<string> SelectedAssets { get { return _CurrentSelectedChanges.Select(ch => ch.EffectiveFilePath); } } List<PendingAssetAndMeta> _PendingChanges; Dictionary<string, bool> _CheckedFiles; Vector2 _ScrollVector; string _ChangeListDescription; Texture2D _HighlightedBackground; Texture2D _SelectedBackground; // User selection PendingAssetAndMeta _LastSelectedChange; DateTime _LastSelectedChangeTime; HashSet<PendingAssetAndMeta> _CurrentSelectedChanges; // Sorting algorithms System.Func<PendingAssetAndMeta, string> _CurrentSortingFunc; bool _CurrentSortingAscending; public PendingChanges() { Init(); } void Init() { _LastSelectedChange = null; _CurrentSelectedChanges = new HashSet<PendingAssetAndMeta>(); _PendingChanges = new List<PendingAssetAndMeta>(); _CheckedFiles = new Dictionary<string, bool>(); Engine.PerformOpenConnectionOperation(con => 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() { _PendingChanges.Clear(); _CheckedFiles.Clear(); _ScrollVector = Vector2.zero; // User selection _LastSelectedChange = null; _CurrentSelectedChanges.Clear(); } void OnOperationPerformed(PerforceConnection aConnection) { OpenedConnection con = new OpenedConnection(aConnection); UpdateList(con); Repaint(); } void UpdateList(OpenedConnection aConnection) { Profiler.BeginSample("UpdateList"); _PendingChanges.Clear(); if (Config.PerforceEnabled) { List<string> openedFiles = new List<string>(aConnection.LocalOpenedFiles); if (openedFiles != null) { // Parse the list, seeing if files have metas and such while (openedFiles.Count > 0) { Profiler.BeginSample("UpdateList - count" + openedFiles.Count); if (openedFiles[0].StartsWith(Config.ProjectRoot)) { string filePath = Utils.LocalPathToAssetPath(Utils.UnescapeFilename(openedFiles[0])); 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(Utils.AssetPathToLocalPath(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(Utils.AssetPathToLocalPath(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 _PendingChanges.Add(assetAndMeta); } // No matter what, remove the file openedFiles.RemoveAt(0); Profiler.EndSample(); } // Fetch the state of the file List<string> paths = new List<string>(_PendingChanges.Select(pc => pc.EffectiveFilePath)); List<AssetStatusCache.AssetStatus> statuses = new List<AssetStatusCache.AssetStatus>(); AssetStatusCache.GetAssetData(aConnection.Connection, paths, statuses); for (int i = 0; i < paths.Count; ++i) { _PendingChanges[i].State = statuses[i]; } // Sort the changes SortPendingChanges(); } } Profiler.EndSample(); } void OnGUI() { 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)) { Engine.PerformOpenConnectionOperation(UpdateList); } 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<PendingAssetAndMeta> newSelection = new HashSet<PendingAssetAndMeta>(); 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<FileSpec> specsToSubmit = new List<FileSpec>(); // 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 = 30; bool moreFiles = _PendingChanges.Count >= maxCount; 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 (i < maxCount) builder.AppendLine(filepath); } break; case FileAndMetaType.MetaOnly: { string filepath = Utils.AssetPathToLocalPath(change.MetaPath); specsToSubmit.Add(FileSpec.LocalSpec(filepath)); if (i < maxCount) builder.AppendLine(filepath); } break; case FileAndMetaType.FileAndMeta: { string filepath = Utils.AssetPathToLocalPath(change.AssetPath); specsToSubmit.Add(FileSpec.LocalSpec(filepath)); if (i < maxCount) builder.AppendLine(filepath); } { string filepath = Utils.AssetPathToLocalPath(change.MetaPath); specsToSubmit.Add(FileSpec.LocalSpec(filepath)); if (i < maxCount) builder.AppendLine(filepath); } break; } } } if (moreFiles) { 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<string> filesToRevert = new List<string>(); 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<PendingAssetAndMeta> newList = null; if (_CurrentSortingAscending) newList = new List<PendingAssetAndMeta>(_PendingChanges.OrderBy(_CurrentSortingFunc)); else newList = new List<PendingAssetAndMeta>(_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<PendingAssetAndMeta> 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<string> aEffectiveFilePaths) { // Deselect all List<string> files = new List<string>(_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; } } } } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#18 | 16251 | Norman Morse | Update Dev branch to match reorganization in workshop | ||
#17 | 15075 | Norman Morse |
Rewrote AssetStatusCache to Cache AssetStatuses and FileMetaData Fixed Edge conditions on Engine Operations Change Debug output defaults. Will now Checkout files which request to be "added" but which already exist in perforce. Output P4Connect version to log on initialization. |
||
#16 | 14800 | Norman Morse |
Fixed Exception in debug statements Improved performance of pending assets menu on large change sets, some other minor premature optimizations Allowed saving of configuration while Perforce is disabled. Improved restoring connection after recompile. |
||
#15 | 14236 | Norman Morse | update dev branch with GA.8 release files | ||
#14 | 14234 | Norman Morse |
Rollback //guest/norman_morse/dev/p4connect to changelist 14232 Accidently checked in a bunch of old dev branch shelves. Rolling Back |
||
#13 | 14233 | Norman Morse | Dev Branch Cache code | ||
#12 | 12863 | Norman Morse |
import from main Recently updated from internal Several Bugfixes logging clean up |
||
#11 | 12564 | Norman Morse |
Made ChangeManager into Static Class. Improved close window behavior for when connection is invalid Fixed localOpenFiles not updating on submit |
||
#10 | 12558 | Norman Morse | Integrate from main to dev | ||
#9 | 12501 | Norman Morse |
Rewrote Configuration Window. Provided buttons to read values and to save configurations. Removed P4Config settings, replaced with P4Config button and reports to Debug.Log() Removed SaveConfigurationAsset Setting, replaced with "Save as Asset" button. Made Connection Configuration read only while connected. Made dialogs disappear if they become disconnected during OnGui() Bumped Beta Value. |
||
#8 | 12491 | Norman Morse | More minor fixes | ||
#7 | 12480 | Norman Morse |
Fixed crash in logging. Worked on UI issues. Cleaned up some connection usage. Still looking for the problem with pending changes. |
||
#6 | 12473 | Norman Morse | More minor fixes to logging and config | ||
#5 | 12467 | Norman Morse |
Many minor changes to improve logging Added a filter for Console log display where you can select a level Hooked the log file location to the Configuration Log Entry. |
||
#4 | 12445 | Norman Morse |
Integrated log4net and nunit into P4Connect. Still need cleanup and debugging, good enough for dev tree Also added ChangeManager and ChangeLists Classes for future use with multiple changes. |
||
#3 | 12363 | Norman Morse |
Merging //guest/perforce_software/p4connect/src/... to //guest/norman_morse/dev/p4connect/src/... |
||
#2 | 11172 | Norman Morse | P4Config integration. | ||
#1 | 10941 | Norman Morse | Create dev branch from workshop | ||
//guest/perforce_software/p4connect/src/P4Connect/P4Connect/P4Connect.PendingChanges.cs | |||||
#1 | 10940 | Norman Morse |
Inital Workshop release of P4Connect. Released under BSD-2 license |