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 "My Window" 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 | |
---|---|---|---|---|---|
#15 | 16210 | Norman Morse | Remove files from old locations | ||
#14 | 16117 | Norman Morse |
Fix for EditorPrefs related Disconnection. Cleaned up some code. Removed Spurious Comments Initialize Config from within Main |
||
#13 | 15424 | Norman Morse |
Fixed exceptions in Dialogs. Updated release Notes. Added checks to menus for PerforceEnabled |
||
#12 | 15383 | Norman Morse |
Improved Diagnostics, cleaned up unnecessary log output Moved some Dialog Initialization to OnEnable() Fixed Unity 5.1.1 incompatibilities Added Operation support for In Depot Deleted files |
||
#11 | 15244 | Norman Morse |
Better Directory support in "add" "get latest" "refresh" and other commands. Improved Project Root detection Various Bug Fixes and Clean up |
||
#10 | 15079 | 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. |
||
#9 | 14801 | Norman Morse |
GA.9 changes. Fixed debug message exceptions Improved Pending Changes dialog for large changesets Changed configuration to allow saving configuration with Perforce disabled. Improved restart after recompile, automatically attempts connection now unless disabled. |
||
#8 | 14193 | Norman Morse |
GA.7 release Refactor Pending Changes Resolve Submit issues. Fixed Menu entries. Handle mismatched file and meta states. |
||
#7 | 12862 | Norman Morse |
Fixed problem with an empty default change list not refresshing. Fixed crash in is_ignored Removed a lot of log output |
||
#6 | 12565 | Norman Morse |
Integrated from Dev Branch Made ChangeManager into Static Class. Improved close window behavior for when connection is invalid Fixed localOpenFiles not updating on submit |
||
#5 | 12553 | Norman Morse |
integrate from internal main Build fixes for EC. Major changes to Configuration and re-initialization code. Bug fixes |
||
#4 | 12512 | Norman Morse | Integrate from Dev branch, preparing for Beta3 release | ||
#3 | 12362 | Norman Morse |
Added Debug Logging for p4log Fixed some path comparison issues. Created a CaseSensitivity test |
||
#2 | 11223 | Norman Morse | Fixed typo in comment | ||
#1 | 10940 | Norman Morse |
Inital Workshop release of P4Connect. Released under BSD-2 license |