using UnityEditor; using UnityEngine; using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Text.RegularExpressions; using System.Linq; using System.IO; using Perforce.P4; namespace P4Connect { [Serializable] public class ConfigAsset : ScriptableObject { // These are the config values // We serialize this Class and place it in the P4Connect/Editor Asset Hierarchy public string ServerURI; public string Username; public string Password; public string Workspace; public bool UseP4Config; public string P4ConfigName; public bool UnityVSSupport; public bool IncludeProjectFiles; public bool IncludeSolutionFiles; public bool ShowPaths; public bool AskBeforeCheckout; public bool DisplayStatusIcons; public string Hostname; public string Charset; public string DiffToolPathname; public bool DisplayP4Timings; public bool DisplayP4Commands; public bool CheckStatusForMenus; public int CheckStatusForMenusMaxItems; public int ConnectionTimeOut; public string ProjectRoot; public bool WarnOnSpecialCharacters; public bool UseIgnore; public string IgnoreName; public bool EnableLog; public string LogPath; public bool SaveConfigAsset; public string IgnoreLines; // Copy the contents of this ConfigAsset into the P4Connect Config class. public void CopyAssetToConfig() { Config.ServerURI = ServerURI; Config.Username = Username; Config.Password = Password; Config.Workspace = Workspace; Config.UseP4Config = UseP4Config; Config.UnityVSSupport = UnityVSSupport; Config.IncludeProjectFiles = IncludeProjectFiles; Config.IncludeSolutionFiles = IncludeSolutionFiles; Config.ShowPaths = ShowPaths; Config.AskBeforeCheckout = AskBeforeCheckout; Config.DisplayStatusIcons = DisplayStatusIcons; Config.Hostname = Hostname; Config.Charset = Charset; Config.DiffToolPathname = DiffToolPathname; Config.DisplayP4Timings = DisplayP4Timings; Config.DisplayP4Commands = DisplayP4Commands; Config.CheckStatusForMenus = CheckStatusForMenus; Config.CheckStatusForMenusMaxItems = CheckStatusForMenusMaxItems; Config.ConnectionTimeOut = ConnectionTimeOut; Config.ProjectRoot = ProjectRoot; Config.WarnOnSpecialCharacters = WarnOnSpecialCharacters; Config.UseIgnore = UseIgnore; Config.IgnoreName = IgnoreName; Config.EnableLog = EnableLog; Config.LogPath = LogPath; Config.P4ConfigName = P4ConfigName; Config.SaveConfigAsset = SaveConfigAsset; Config.IgnoreLines = IgnoreLines; } // Seed a ConfigAsset with data from the Config Class public void CopyConfigToAsset() { ServerURI = Config.ServerURI; Username = Config.Username; Password = Config.Password; Workspace = Config.Workspace; UseP4Config = Config.UseP4Config; UnityVSSupport = Config.UnityVSSupport; IncludeProjectFiles = Config.IncludeProjectFiles; IncludeSolutionFiles = Config.IncludeSolutionFiles; ShowPaths = Config.ShowPaths; AskBeforeCheckout = Config.AskBeforeCheckout; DisplayStatusIcons = Config.DisplayStatusIcons; Hostname = Config.Hostname; Charset = Config.Charset; DiffToolPathname = Config.DiffToolPathname; DisplayP4Timings = Config.DisplayP4Timings; DisplayP4Commands = Config.DisplayP4Commands; CheckStatusForMenus = Config.CheckStatusForMenus; CheckStatusForMenusMaxItems = Config.CheckStatusForMenusMaxItems; ConnectionTimeOut = Config.ConnectionTimeOut; ProjectRoot = Config.ProjectRoot; WarnOnSpecialCharacters = Config.WarnOnSpecialCharacters; UseIgnore = Config.UseIgnore; IgnoreName = Config.IgnoreName; EnableLog = Config.EnableLog; LogPath = Config.LogPath; P4ConfigName = Config.P4ConfigName; SaveConfigAsset = Config.SaveConfigAsset; IgnoreLines = Config.IgnoreLines; } } // This Editor window allows the user to set and store // Perforce connection settings that will be used to // Check out files on Save / Move / Delete / etc... // The connection parameters are saved as Editor Preferences // which means they go the registry. public class Config : EditorWindow { // Event triggered when the configuration changes public delegate void OnPrefsChanged(); public static event OnPrefsChanged PrefsChanged; #region Properties // Give access to the Server URI public static string ServerURI { get; set; } // Give access to the User Name public static string Username { get; set; } // Give access to the Password public static string Password { get; set; } // Give access to the Workspace Name public static string Workspace { get; set; } // Give access to the DLL Location public static string DLLLocation { get; set; } // Look for p4config files to setup configuration? public static bool UseP4Config { get; set; } // Give access to whether we integrate with UnityVS public static bool UnityVSSupport { get; set; } // Give access to whether the integration is turned on or not public static bool PerforceEnabled { get; set; } // Give access to whether we check out solution files public static bool IncludeSolutionFiles { get; set; } // Give access to whether we check out project files public static bool IncludeProjectFiles { get; set; } // Give access to whether we print out paths or just filenames public static bool ShowPaths { get; set; } // Give access to whether we automatically check out on edit public static bool AskBeforeCheckout { get; set; } // Give access to whether we want to display Icons in the Project view public static bool DisplayStatusIcons { get; set; } // Whether to override P4Host public static bool OverrideHostname { get; set; } // The host name, if different than the local machine public static string Hostname { get; set; } // Whether to override P4Charset public static bool OverrideCharset { get; set; } // The host name, if different than the local machine public static string Charset { get; set; } // The location of the Diff tool public static string DiffToolPathname { get; set; } // Whether or not to display timing info for debugging public static bool DisplayP4Timings { get; set; } // Whether to display the commands sent to P4 public static bool DisplayP4Commands { get; set; } // Whether or not to check the status of files before showing the contextual menu public static bool CheckStatusForMenus { get; set; } // Whether or not to warn when adding files with special characters public static bool WarnOnSpecialCharacters { get; set; } // Whether or not to check the status of files before showing the contextual menu public static int CheckStatusForMenusMaxItems { get; set; } // How many files to submit at once public static int OperationBatchCount { get; set; } // How long to keep connections open public static int ConnectionTimeOut { get; set; } // The location of the project root in relation to the workspace root public static string ProjectRoot { get; set; } // Enable the Ignore file public static bool UseIgnore { get; set; } // Provide an Ignore file name public static string IgnoreName { get; set; } // Enable p4bridge Logging public static bool EnableLog { get; set; } // Where to save log output public static string LogPath { get; set; } // Provide a P4config filename public static string P4ConfigName { get; set; } // Set to enable saving config asset public static bool SaveConfigAsset { get; set; } // Additional Ignore Lines public static string IgnoreLines { get; set; } #endregion static bool FoundConfigAsset; // Helper property to indicate that P4 can be used public static bool ValidConfiguration { get { return PerforceEnabled && _CurrentState == ConfigurationState.SettingsValid; } } public const string P4BridgeDLLName = "p4bridge.dll"; public const string P4BridgeDYLIBName = "libp4bridge.dylib"; public const int MaxPendingItems = 200; #region Registry Names // These are the names under which the connection settings are stored in the registry public const string ServerURIPrefName = "ServerURI"; public const string UserNamePrefName = "UserName"; public const string PasswordPrefName = "Password"; public const string WorkspacePrefName = "Workspace"; public const string P4ConfigPrefName = "UseP4Config"; public const string DLLLocationPrefName = "DLLLocation"; public const string PerforceEnabledPrefName = "Enabled"; public const string UnityVSSupportPrefName = "UnityVSSupport"; public const string IncludeProjectFilesPrefName = "IncludeProjectFiles"; public const string IncludeSolutionFilesPrefName = "IncludeSolutionFiles"; public const string ShowPathsPrefName = "ShowPaths"; public const string AskBeforeCheckoutPrefName = "AskBeforeCheckout"; public const string DisplayStatusIconsPrefName = "DisplayStatusIcons"; public const string HostnamePrefName = "Hostname"; public const string DiffToolPathnamePrefName = "DiffToolPathname"; public const string DisplayP4TimingsPrefName = "DisplayTimings"; public const string DisplayP4CommandsPrefName = "DisplayCommands"; public const string CheckStatusForMenusPrefName = "CheckStatusForMenus"; public const string CheckStatusForMenusMaxItemsPrefName = "CheckStatusForMenusMaxItems"; public const string OperationBatchCountPrefName = "OperationBatchCount"; public const string ConnectionTimeOutPrefName = "ConnectionTimeOut"; public const string OverrideHostnamePrefName = "OverrideHostname"; public const string WarnOnSpecialCharactersPrefName = "WarnOnSpecialCharacters"; public const string UseIgnorePrefName = "UseIgnore"; public const string IgnoreNamePrefName = "IgnoreName"; public const string EnableLogPrefName = "EnableLog"; public const string LogPathPrefName = "LogPath"; public const string P4ConfigNamePrefName = "p4ConfigName"; public const string SaveConfigAssetPrefName = "SaveConfigAsset"; public const string IgnoreLinesPrefName = "IgnoreLines"; #endregion // Used to create controls static GUIContent _RevertSettings = new GUIContent("Revert", "Revert settings to previously saved value"); static GUIContent _SaveSettings = new GUIContent("Save", "Saves the current settings"); static GUILayoutOption _ButtonWidth = GUILayout.MaxWidth(50.0f); static GUILayoutOption _RevertSaveButtonWidth = GUILayout.MaxWidth(80.0f); static GUILayoutOption _LabelWidth = GUILayout.MaxWidth(300.0f); public static string p4configFile; // The current state of the configuration, whether it is valid to be used enum ConfigurationState { Unknown = 0, MetaFilesInvalid, MetaFilesValid, P4ConfigValid, ServerInvalid, ServerValid, UsernamePassInvalid, UsernamePassValid, WorkspaceInvalid, WorkspaceValid, ProjectRootInvalid, ProjectRootValid, SettingsValid, } // The current state of the configuration, whether it is valid to be used static ConfigurationState _CurrentState = ConfigurationState.Unknown; // Toggles the extra options int _FrameIndex = 0; bool _SettingsChanged = false; bool _Repaint = false; /// <summary> /// Set some default configuration values /// </summary> static Config() { // Set some default values ServerURI = "YourServer:1666"; Username = "YourUsername"; Password = ""; Workspace = "Username_Workspace_Machine"; UseP4Config = true; UseIgnore = true; DLLLocation = "P4Connect/Editor/"; UnityVSSupport = false; PerforceEnabled = true; IncludeProjectFiles = true; IncludeSolutionFiles = true; ShowPaths = false; AskBeforeCheckout = false; DisplayStatusIcons = true; Hostname = ""; Charset = ""; DiffToolPathname = ""; DisplayP4Timings = false; DisplayP4Commands = false; CheckStatusForMenus = true; CheckStatusForMenusMaxItems = 10; ConnectionTimeOut = 30; ProjectRoot = ""; WarnOnSpecialCharacters = true; FoundConfigAsset = false; UseIgnore = false; IgnoreName = ".p4ignore"; EnableLog = false; LogPath = "p4connect.log"; P4ConfigName = ".p4config"; SaveConfigAsset = false; IgnoreLines = ""; } // Add menu item to show the configuration panel [MenuItem("Edit/Perforce Settings", false, 300)] public static void ShowWindow() { // Show existing window instance. If one doesn't exist, make one. Config window = EditorWindow.GetWindow<Config>("P4 Settings"); window.name = "P4 Settings"; window.title = "P4 Settings"; window.minSize = new UnityEngine.Vector2(500.0f, 500.0f); } /// <summary> /// Static Constructor, reads connection settings from Prefs at least once /// </summary> public static void Initialize() { FoundConfigAsset = readConfigAsset(); if (! FoundConfigAsset) ReadPrefs(); CachedSerializationMode = EditorSettings.serializationMode; _CurrentState = ConfigurationState.Unknown; CheckSettings(); // Check to see if the settings are good. } /// <summary> /// Forces the config to be invalid, usually after an error /// </summary> public static void NeedToCheckSettings() { _CurrentState = ConfigurationState.Unknown; Debug.LogWarning("P4Connect - Perforce integration is enabled but inactive. Go to Edit->Perforce Settings to update your settings"); } /// <summary> /// Checks the that settings are valid /// </summary> public static void CheckSettings() { if (PerforceEnabled) { // We only display a message when something changes ConfigurationState prevState = _CurrentState; _CurrentState = ConfigurationState.Unknown; // Check the config UpdateConfigState(); // And notify user if (_CurrentState != prevState) { Icons.UpdateDisplay(); if (_CurrentState != ConfigurationState.SettingsValid) { Debug.LogWarning("P4Connect - Perforce integration is enabled but inactive. Go to Edit->Perforce Settings to update your settings"); } else if (prevState != ConfigurationState.SettingsValid) { Debug.Log("P4Connect - Perforce Integration is Active"); } } if (_CurrentState == ConfigurationState.SettingsValid) { SetProjectRootDirectory(); } } } public static void SetProjectRootDirectory() { if (Config.ValidConfiguration) { Engine.PerformConnectionOperation(con => { var spec = FileSpec.LocalSpec(System.IO.Path.Combine(Main.RootPath, "...")); var mappings = con.P4Client.GetClientFileMappings(spec); if (mappings != null && mappings.Count > 0) { string clientProjectRoot = mappings[0].ClientPath.Path; int projectRootOffset = clientProjectRoot.ToLowerInvariant().IndexOf(Config.Workspace.ToLowerInvariant()); if (projectRootOffset != -1) { ProjectRoot = clientProjectRoot.Substring(projectRootOffset + Config.Workspace.Length); ProjectRoot = ProjectRoot.TrimStart(System.IO.Path.DirectorySeparatorChar, '/'); if (ProjectRoot.EndsWith("...")) ProjectRoot = ProjectRoot.Substring(0, ProjectRoot.Length - 3); ProjectRoot = ProjectRoot.TrimEnd(System.IO.Path.DirectorySeparatorChar, '/'); ProjectRoot = ProjectRoot.Replace('/', System.IO.Path.DirectorySeparatorChar); } else { Debug.LogError("P4Connect - Your Project does not appear to be under your workspace root."); } } }); } } /// <summary> /// Updates the current configuration state after checking all the settings /// </summary> public static void UpdateConfigState() { switch (_CurrentState) { case ConfigurationState.Unknown: // The unknown state starts by checking the meta files goto case ConfigurationState.MetaFilesInvalid; case ConfigurationState.MetaFilesInvalid: if (P4Connect.VerifySettings.CheckMetaFiles()) { // Meta files are valid, move on _CurrentState = ConfigurationState.MetaFilesValid; goto case ConfigurationState.MetaFilesValid; } else { // Stay in this state _CurrentState = ConfigurationState.MetaFilesInvalid; break; } case ConfigurationState.MetaFilesValid: goto case ConfigurationState.P4ConfigValid; case ConfigurationState.P4ConfigValid: { if (UseP4Config) { p4configFile = FindP4ConfigFile(); if (p4configFile != null) LoadP4ConfigFile(p4configFile); } // in either case, move on _CurrentState = ConfigurationState.ServerInvalid; goto case ConfigurationState.ServerInvalid; } case ConfigurationState.ServerInvalid: if (P4Connect.VerifySettings.CheckServerURI()) { // Server is valid, move on _CurrentState = ConfigurationState.ServerValid; goto case ConfigurationState.ServerValid; } else { // Stay here _CurrentState = ConfigurationState.ServerInvalid; break; } case ConfigurationState.ServerValid: goto case ConfigurationState.UsernamePassInvalid; case ConfigurationState.UsernamePassInvalid: if (P4Connect.VerifySettings.CheckUsernamePassword()) { // Username / Password is valid, move on _CurrentState = ConfigurationState.UsernamePassValid; goto case ConfigurationState.UsernamePassValid; } else { // Stay here _CurrentState = ConfigurationState.UsernamePassInvalid; break; } case ConfigurationState.UsernamePassValid: goto case ConfigurationState.WorkspaceInvalid; case ConfigurationState.WorkspaceInvalid: if (P4Connect.VerifySettings.CheckWorkspace()) { // Workspace is valid _CurrentState = ConfigurationState.WorkspaceValid; goto case ConfigurationState.WorkspaceValid; } else { // Stay here _CurrentState = ConfigurationState.WorkspaceInvalid; break; } case ConfigurationState.WorkspaceValid: goto case ConfigurationState.ProjectRootInvalid; case ConfigurationState.ProjectRootInvalid: if (P4Connect.VerifySettings.CheckProjectRoot()) { // Root is valid _CurrentState = ConfigurationState.ProjectRootValid; goto case ConfigurationState.ProjectRootValid; } else { // Stay here _CurrentState = ConfigurationState.ProjectRootInvalid; break; } case ConfigurationState.ProjectRootValid: _CurrentState = ConfigurationState.SettingsValid; goto case ConfigurationState.SettingsValid; case ConfigurationState.SettingsValid: break; } // Clean up the connection PerforceConnection.ForceCloseConnection(); } /// <summary> /// Called by Unity when the windows needs to be updated /// </summary> void OnGUI() { // This will store whether the user requested to check the settings bool checkSettings = false; // Track scroll position in the additional ignore TextArea Vector2 scroll = new Vector2(0,0); // Add a global checkbox to turn perforce integration on/off bool newPerforceEnabled = EditorGUILayout.BeginToggleGroup("Enable Perforce Integration", PerforceEnabled); if (newPerforceEnabled != PerforceEnabled) { PerforceEnabled = newPerforceEnabled; PendingChanges.UpdateDisplay(); Icons.UpdateDisplay(); if (PerforceEnabled) Initialize(); } if (PerforceEnabled) { float checkWidth = 16.0f; float intWidth = 30.0f; float textWidth = 250.0f; float secondtextWidth = 200.0f; float pathWidth = 300.0f; int maxFrameCount = 2; // seems to be the right number of repaints needed to display something if (_FrameIndex < maxFrameCount) { EditorGUILayout.HelpBox("Please Wait! Checking Perforce Settings. This can take up to 20 seconds...", MessageType.Info); if (Event.current.type == EventType.Repaint) { ++_FrameIndex; _Repaint = true; } if (_FrameIndex == maxFrameCount) { // Force update the config state at least once ConfigurationState oldState = _CurrentState; _CurrentState = ConfigurationState.Unknown; UpdateConfigState(); if (_CurrentState != oldState) { _SettingsChanged = true; } } } else { GUILayout.Label("Connection Settings", EditorStyles.boldLabel); if (_CurrentState >= ConfigurationState.MetaFilesValid) { EditorGUILayout.BeginHorizontal(); GUILayout.Label("\tUse P4Config", GUILayout.Width(textWidth)); bool newUseP4Config = EditorGUILayout.Toggle(UseP4Config, GUILayout.Width(checkWidth)); if (newUseP4Config != UseP4Config) { UseP4Config = newUseP4Config; _SettingsChanged = true; } EditorGUI.BeginDisabledGroup(! UseP4Config); { EditorGUILayout.BeginHorizontal(GUILayout.ExpandWidth(true)); GUILayout.Label("Name of P4Config File", GUILayout.Width(secondtextWidth)); string newP4ConfigName = EditorGUILayout.TextField(P4ConfigName, GUILayout.Width(120.0f)); if (newP4ConfigName != P4ConfigName) { P4ConfigName = newP4ConfigName; _CurrentState = ConfigurationState.MetaFilesValid; _SettingsChanged = true; } EditorGUILayout.EndHorizontal(); } EditorGUI.EndDisabledGroup(); EditorGUILayout.EndHorizontal(); if (UseP4Config) { EditorGUILayout.BeginHorizontal(); if (string.IsNullOrEmpty(p4configFile)) { GUILayout.Label("\t P4 Config file Not Found"); } else { GUILayout.Label("\t Found at: " + Path.GetFullPath(p4configFile)); // Show the path to the config file } EditorGUILayout.EndHorizontal(); } // Don't let the user change any of the remaining settings unless the DLL is valid EditorGUILayout.BeginHorizontal(); { EditorGUILayout.BeginVertical(); // The edit field for the server string newServerURI = EditorGUILayout.TextField("\tServer URI", ServerURI); if (newServerURI != ServerURI) { ServerURI = newServerURI; _CurrentState = ConfigurationState.MetaFilesValid; _SettingsChanged = true; } EditorGUILayout.EndVertical(); // The button to verify it bool serverValid = _CurrentState >= ConfigurationState.ServerValid; EditorGUI.BeginDisabledGroup(serverValid); { if (GUILayout.Button(serverValid ? "Valid!" : "Verify", EditorStyles.miniButton, _ButtonWidth)) { checkSettings = true; } } EditorGUI.EndDisabledGroup(); } EditorGUILayout.EndHorizontal(); if (_CurrentState == ConfigurationState.ServerInvalid) { EditorGUILayout.HelpBox("Invalid Server URI", MessageType.Error); } EditorGUI.BeginDisabledGroup(_CurrentState < ConfigurationState.ServerValid); { EditorGUILayout.BeginHorizontal(); { EditorGUILayout.BeginVertical(); // The edit field for the username string newUsername = EditorGUILayout.TextField("\tUsername", Username); if (newUsername != Username) { Username = newUsername; _CurrentState = ConfigurationState.ServerValid; _SettingsChanged = true; } // The edit field for the password string newPassword = EditorGUILayout.PasswordField("\tPassword", Password); if (newPassword != Password) { Password = newPassword; _CurrentState = ConfigurationState.ServerValid; _SettingsChanged = true; } EditorGUILayout.EndVertical(); // And the button to verify it bool passwordValid = _CurrentState >= ConfigurationState.UsernamePassValid; EditorGUI.BeginDisabledGroup(passwordValid); { if (GUILayout.Button(passwordValid ? "Valid!" : "Verify", EditorStyles.miniButton, _ButtonWidth)) { checkSettings = true; } } EditorGUI.EndDisabledGroup(); } EditorGUILayout.EndHorizontal(); if (_CurrentState == ConfigurationState.UsernamePassInvalid) { EditorGUILayout.HelpBox("Invalid Username / Password", MessageType.Error); } EditorGUI.BeginDisabledGroup(_CurrentState < ConfigurationState.UsernamePassValid); { EditorGUILayout.BeginHorizontal(); { // The edit field for the workspace string newWorkspace = EditorGUILayout.TextField("\tWorkspace", Workspace); if (newWorkspace != Workspace) { Workspace = newWorkspace; _CurrentState = ConfigurationState.UsernamePassValid; _SettingsChanged = true; } // And the check button bool workspaceValid = _CurrentState >= ConfigurationState.ProjectRootValid; EditorGUI.BeginDisabledGroup(workspaceValid); { if (GUILayout.Button(workspaceValid ? "Valid!" : "Verify", EditorStyles.miniButton, _ButtonWidth)) { checkSettings = true; } } EditorGUI.EndDisabledGroup(); } EditorGUILayout.EndHorizontal(); if (_CurrentState == ConfigurationState.WorkspaceInvalid) { EditorGUILayout.HelpBox("Invalid Workspace", MessageType.Error); } if (_CurrentState == ConfigurationState.ProjectRootInvalid) { // The project isn't under the workspace root string perforcePath = "//" + Workspace + "/..."; string rootPath = P4Connect.Main.RootPath; EditorGUILayout.HelpBox("Invalid Workspace. The client path:\n" + "\t" + perforcePath + "\n" + "maps to this folder:\n" + "\t" + VerifySettings.LastWorkspaceMapping + "\n" + "which is not a parent directory of the project's root:\n" + "\t" + rootPath, MessageType.Error); } } EditorGUI.EndDisabledGroup(); } EditorGUI.EndDisabledGroup(); EditorGUILayout.BeginHorizontal(); GUILayout.Label("\tUse P4Ignore", GUILayout.Width(textWidth)); bool newUseIgnore = EditorGUILayout.Toggle(UseIgnore, GUILayout.Width(checkWidth)); if (newUseIgnore != UseIgnore) { UseIgnore = newUseIgnore; _SettingsChanged = true; } EditorGUI.BeginDisabledGroup(!UseIgnore); { GUILayout.Label("Name of P4Ignore File", GUILayout.Width(secondtextWidth)); string newIgnoreName = EditorGUILayout.TextField(IgnoreName, GUILayout.Width(120.0f)); if (newIgnoreName != IgnoreName) { IgnoreName = newIgnoreName; _CurrentState = ConfigurationState.MetaFilesValid; _SettingsChanged = true; } } EditorGUI.EndDisabledGroup(); EditorGUILayout.EndHorizontal(); GUILayout.Label("Interface", EditorStyles.boldLabel); EditorGUILayout.BeginHorizontal(); GUILayout.Label("\tDisplay Status Icons", GUILayout.Width(textWidth)); bool newDisplayIcons = EditorGUILayout.Toggle(DisplayStatusIcons, GUILayout.Width(checkWidth)); if (newDisplayIcons != DisplayStatusIcons) { DisplayStatusIcons = newDisplayIcons; _SettingsChanged = true; Icons.UpdateDisplay(); PendingChanges.UpdateDisplay(); } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); GUILayout.Label("\tShow File Paths in Console", GUILayout.Width(textWidth)); bool newShowPaths = EditorGUILayout.Toggle(ShowPaths, GUILayout.Width(checkWidth)); if (newShowPaths != ShowPaths) { ShowPaths = newShowPaths; _SettingsChanged = true; } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); GUILayout.Label("\tDiff Tool Executable", GUILayout.Width(textWidth)); string newDiffToolPathname = EditorGUILayout.TextField("", DiffToolPathname); if (newDiffToolPathname != DiffToolPathname) { DiffToolPathname = newDiffToolPathname; _SettingsChanged = true; } if (GUILayout.Button("Browse...", EditorStyles.miniButton, GUILayout.Width(80.0f))) { GUI.FocusControl(""); DiffToolPathname = EditorUtility.OpenFilePanel("Choose Diff Tool Executable", System.Environment.GetFolderPath(System.Environment.SpecialFolder.ProgramFiles), ""); _SettingsChanged = true; } EditorGUILayout.EndHorizontal(); GUILayout.Label("Behaviour", EditorStyles.boldLabel); EditorGUILayout.BeginHorizontal(); GUILayout.Label("\tAsk before checkout on edit", GUILayout.Width(textWidth)); bool newAskBeforeCheckout = EditorGUILayout.Toggle(AskBeforeCheckout, GUILayout.Width(checkWidth)); if (newAskBeforeCheckout != AskBeforeCheckout) { AskBeforeCheckout = newAskBeforeCheckout; _SettingsChanged = true; } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); GUILayout.Label("\tShow warning when adding files with reserved characters (@#*%)", GUILayout.Width(textWidth)); bool newWarnOnSpecialChar = EditorGUILayout.Toggle(WarnOnSpecialCharacters, GUILayout.Width(checkWidth)); if (newWarnOnSpecialChar != WarnOnSpecialCharacters) { WarnOnSpecialCharacters = newWarnOnSpecialChar; _SettingsChanged = true; } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); GUILayout.Label("\tInclude Solution Files", GUILayout.Width(textWidth)); bool newIncludeSolutionFiles = EditorGUILayout.Toggle(IncludeSolutionFiles, GUILayout.Width(checkWidth)); if (newIncludeSolutionFiles != IncludeSolutionFiles) { IncludeSolutionFiles = newIncludeSolutionFiles; _SettingsChanged = true; } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); GUILayout.Label("\tInclude Projects Files", GUILayout.Width(textWidth)); bool newIncludeProjectFiles = EditorGUILayout.Toggle(IncludeProjectFiles, GUILayout.Width(checkWidth)); if (newIncludeProjectFiles != IncludeProjectFiles) { IncludeProjectFiles = newIncludeProjectFiles; _SettingsChanged = true; } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); GUILayout.Label("\tIntegrate with UnityVS", GUILayout.Width(textWidth)); bool newUnityVSSupport = EditorGUILayout.Toggle(UnityVSSupport, GUILayout.Width(checkWidth)); if (newUnityVSSupport != UnityVSSupport) { UnityVSSupport = newUnityVSSupport; _SettingsChanged = true; } EditorGUILayout.EndHorizontal(); GUILayout.Label("Advanced", EditorStyles.boldLabel); EditorGUILayout.BeginHorizontal(); GUILayout.Label("\tOverride P4HOST variable", GUILayout.Width(textWidth)); bool newOverrideP4Host = EditorGUILayout.Toggle(OverrideHostname, GUILayout.Width(checkWidth)); if (newOverrideP4Host != OverrideHostname) { OverrideHostname = newOverrideP4Host; _CurrentState = ConfigurationState.MetaFilesValid; _SettingsChanged = true; } EditorGUI.BeginDisabledGroup(!OverrideHostname); GUILayout.Label("Hostname", GUILayout.Width(secondtextWidth)); string newHost = EditorGUILayout.TextField(Hostname, GUILayout.Width(120.0f)); if (newHost != Hostname) { Hostname = newHost; _CurrentState = ConfigurationState.MetaFilesValid; _SettingsChanged = true; } EditorGUI.EndDisabledGroup(); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); GUILayout.Label("\tOverride P4CHARSET variable", GUILayout.Width(textWidth)); bool newOverrideCharset = EditorGUILayout.Toggle(OverrideCharset, GUILayout.Width(checkWidth)); if (newOverrideCharset != OverrideCharset) { OverrideCharset = newOverrideCharset; _CurrentState = ConfigurationState.ServerValid; _SettingsChanged = true; } EditorGUI.BeginDisabledGroup(!OverrideCharset); GUILayout.Label("Charset", GUILayout.Width(secondtextWidth)); string newCharset = EditorGUILayout.TextField(Charset, GUILayout.Width(120.0f)); if (newCharset != Charset) { Charset = newCharset; _CurrentState = ConfigurationState.ServerValid; _SettingsChanged = true; } EditorGUI.EndDisabledGroup(); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); GUILayout.Label("\tGray out invalid menu options", GUILayout.Width(textWidth)); bool newCheckStatus = EditorGUILayout.Toggle(CheckStatusForMenus, GUILayout.Width(checkWidth)); if (newCheckStatus != CheckStatusForMenus) { CheckStatusForMenus = newCheckStatus; _SettingsChanged = true; } EditorGUI.BeginDisabledGroup(!CheckStatusForMenus); GUILayout.Label("Don't check if more than", GUILayout.Width(secondtextWidth)); int newCheckStatusCount = EditorGUILayout.IntField(CheckStatusForMenusMaxItems, GUILayout.Width(intWidth)); if (newCheckStatusCount != CheckStatusForMenusMaxItems) { CheckStatusForMenusMaxItems = newCheckStatusCount; _SettingsChanged = true; } GUILayout.Label("files selected"); GUILayout.FlexibleSpace(); EditorGUI.EndDisabledGroup(); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); GUILayout.Label("\tClose Perforce connection after", GUILayout.Width(textWidth)); int newTimeOut = EditorGUILayout.IntField(ConnectionTimeOut, GUILayout.Width(intWidth)); if (newTimeOut != ConnectionTimeOut) { ConnectionTimeOut = newTimeOut; _SettingsChanged = true; } GUILayout.Label("seconds"); GUILayout.FlexibleSpace(); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); GUILayout.Label("\tDisplay P4 Timings", GUILayout.Width(textWidth)); bool newDisplayTimings = EditorGUILayout.Toggle(DisplayP4Timings, GUILayout.Width(checkWidth)); if (newDisplayTimings != DisplayP4Timings) { DisplayP4Timings = newDisplayTimings; _SettingsChanged = true; } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); GUILayout.Label("\tEnable Logging", GUILayout.Width(textWidth)); bool newEnableLog = EditorGUILayout.Toggle(EnableLog, GUILayout.Width(checkWidth)); if (newEnableLog != EnableLog) { EnableLog = newEnableLog; if (EnableLog) Main.InitializeLogging(); } EditorGUI.BeginDisabledGroup(! EnableLog); GUILayout.Label("Log File"); string newLogPath = EditorGUILayout.TextField(LogPath); if (newLogPath != LogPath) { LogPath = newLogPath; Main.InitializeLogging(); } if (GUILayout.Button("Browse...", EditorStyles.miniButton, GUILayout.Width(80.0f))) { GUI.FocusControl(""); LogPath = EditorUtility.SaveFilePanel("Save P4Connect Log", System.Environment.GetFolderPath(System.Environment.SpecialFolder.DesktopDirectory), "p4connect","log"); P4Connect.Main.InitializeLogging(); _SettingsChanged = true; } EditorGUI.EndDisabledGroup(); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); GUILayout.Label("\tUse Configuration Asset", GUILayout.Width(textWidth)); bool newSaveConfigAsset = EditorGUILayout.Toggle(SaveConfigAsset, GUILayout.Width(checkWidth)); if (newSaveConfigAsset != SaveConfigAsset) { SaveConfigAsset = newSaveConfigAsset; _SettingsChanged = true; } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); // LogPath = EditorUtility.OpenFolderPanel(.SaveFilePanel("Add Ignore Line", Config.ProjectRoot), "p4connect", "log"); GUILayout.Label("\tAdditional Ignore Lines", GUILayout.Width(textWidth)); scroll = EditorGUILayout.BeginScrollView(scroll); string newIgnoreLines = EditorGUILayout.TextArea(IgnoreLines, GUILayout.Height(position.height - 30), GUILayout.Width(pathWidth)); if (newIgnoreLines != IgnoreLines) { IgnoreLines = newIgnoreLines; _SettingsChanged = true; } EditorGUILayout.EndScrollView(); EditorGUILayout.EndHorizontal(); } else { EditorGUILayout.HelpBox("You must set the Editor Version Control to \"Meta Files\" under Edit->Project Settings->Editor", MessageType.Info); if (VerifySettings.CheckMetaFiles()) { _SettingsChanged = true; checkSettings = true; } } } if (checkSettings) { ConfigurationState prevState = _CurrentState; _CurrentState = ConfigurationState.Unknown; // Check the config CheckSettings(); // And notify user if (_CurrentState != prevState) { Icons.UpdateDisplay(); PendingChanges.UpdateDisplay(); } } } EditorGUILayout.EndToggleGroup(); // Info message GUILayout.FlexibleSpace(); if (PerforceEnabled && _CurrentState != ConfigurationState.SettingsValid) { EditorGUILayout.HelpBox("Perforce Integration is inactive. Please Verify your Settings!", MessageType.Warning); } else if (_SettingsChanged) { if (PerforceEnabled) { EditorGUILayout.HelpBox("Perforce Integration is active.", MessageType.Info); } else { EditorGUILayout.HelpBox("Perforce Integration is turned off.", MessageType.Info); } if (Config.SaveConfigAsset ) { writeConfigAsset(); // This will update the config asset! } else { WritePrefs(); } } EditorGUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); GUILayout.Label("P4Connect " + Version.unityVersion + " " + Version.build, EditorStyles.miniLabel); if (GUILayout.Button("Visit Website", EditorStyles.miniButton)) { System.Diagnostics.Process.Start("http://www.perforce.com/perforce/doc.current/manuals/p4connectguide/index.html"); } EditorGUILayout.EndHorizontal(); } void OnSelectionChange() { // UnityEngine.Object cursel = Selection.activeObject; // if (cursel == ) } void OnInspectorUpdate() { if (_Repaint) { _Repaint = false; Repaint(); } } #region EditorPreferences // Utility method to read connection prefs from the registry public static void ReadPrefs() { // Debug.Log("Reading Config from EditorPrefs"); // Check if the keys exist, and if so, read the values out // Otherwise, leave the default values if (HasPref(ServerURIPrefName)) ServerURI = GetPrefString(ServerURIPrefName); if (HasPref(UserNamePrefName)) Username = GetPrefString(UserNamePrefName); if (HasPref(PasswordPrefName)) Password = Secure.DecryptString(GetPrefString(PasswordPrefName)); if (HasPref(WorkspacePrefName)) Workspace = GetPrefString(WorkspacePrefName); if (HasPref(P4ConfigPrefName)) UseP4Config = GetPrefBool(P4ConfigPrefName); if (HasPref(DLLLocationPrefName)) DLLLocation = GetPrefString(DLLLocationPrefName); if (HasPref(UnityVSSupportPrefName)) UnityVSSupport = GetPrefBool(UnityVSSupportPrefName); if (HasPref(IncludeProjectFilesPrefName)) IncludeProjectFiles = GetPrefBool(IncludeProjectFilesPrefName); if (HasPref(IncludeSolutionFilesPrefName)) IncludeSolutionFiles = GetPrefBool(IncludeSolutionFilesPrefName); if (HasPref(ShowPathsPrefName)) ShowPaths = GetPrefBool(ShowPathsPrefName); if (HasPref(AskBeforeCheckoutPrefName)) AskBeforeCheckout = GetPrefBool(AskBeforeCheckoutPrefName); if (HasPref(DisplayStatusIconsPrefName)) DisplayStatusIcons = GetPrefBool(DisplayStatusIconsPrefName); if (HasPref(HostnamePrefName)) Hostname = GetPrefString(HostnamePrefName); if (HasPref(DiffToolPathnamePrefName)) DiffToolPathname = GetPrefString(DiffToolPathnamePrefName); if (HasPref(DisplayP4TimingsPrefName)) DisplayP4Timings = GetPrefBool(DisplayP4TimingsPrefName); if (HasPref(DisplayP4CommandsPrefName)) DisplayP4Commands = GetPrefBool(DisplayP4CommandsPrefName); if (HasPref(CheckStatusForMenusPrefName)) CheckStatusForMenus = GetPrefBool(CheckStatusForMenusPrefName); if (HasPref(CheckStatusForMenusMaxItemsPrefName)) CheckStatusForMenusMaxItems = GetPrefInt(CheckStatusForMenusMaxItemsPrefName); if (HasPref(OperationBatchCountPrefName)) OperationBatchCount = GetPrefInt(OperationBatchCountPrefName); if (HasPref(ConnectionTimeOutPrefName)) ConnectionTimeOut = GetPrefInt(ConnectionTimeOutPrefName); if (HasPref(OverrideHostnamePrefName)) OverrideHostname = GetPrefBool(OverrideHostnamePrefName); if (HasPref(WarnOnSpecialCharactersPrefName)) WarnOnSpecialCharacters = GetPrefBool(WarnOnSpecialCharactersPrefName); if (HasPref(UseIgnorePrefName)) UseIgnore = GetPrefBool(UseIgnorePrefName); if (HasPref(IgnoreNamePrefName)) IgnoreName = GetPrefString(IgnoreNamePrefName); if (HasPref(EnableLogPrefName)) EnableLog = GetPrefBool(EnableLogPrefName); if (HasPref(P4ConfigNamePrefName)) P4ConfigName = GetPrefString(P4ConfigNamePrefName); if (HasPref(IgnoreLinesPrefName)) IgnoreLines = GetPrefString(IgnoreLinesPrefName); // Notify users that prefs changed if (PrefsChanged != null) PrefsChanged(); } // Utility method to write our the connection prefs to the registry public static void WritePrefs() { SetPrefString(ServerURIPrefName, ServerURI); SetPrefString(UserNamePrefName, Username); if (Password != null && Password.Length > 0) SetPrefString(PasswordPrefName, Secure.EncryptString(Password)); SetPrefString(WorkspacePrefName, Workspace); SetPrefBool(P4ConfigPrefName, UseP4Config); SetPrefString(DLLLocationPrefName, DLLLocation); SetPrefBool(UnityVSSupportPrefName, UnityVSSupport); SetPrefBool(IncludeProjectFilesPrefName, IncludeProjectFiles); SetPrefBool(IncludeSolutionFilesPrefName, IncludeSolutionFiles); SetPrefBool(ShowPathsPrefName, ShowPaths); SetPrefBool(AskBeforeCheckoutPrefName, AskBeforeCheckout); SetPrefBool(DisplayStatusIconsPrefName, DisplayStatusIcons); SetPrefString(HostnamePrefName, Hostname); SetPrefString(DiffToolPathnamePrefName, DiffToolPathname); SetPrefBool(DisplayP4TimingsPrefName, DisplayP4Timings); SetPrefBool(DisplayP4CommandsPrefName, DisplayP4Commands); SetPrefBool(CheckStatusForMenusPrefName, CheckStatusForMenus); SetPrefInt(CheckStatusForMenusMaxItemsPrefName, CheckStatusForMenusMaxItems); SetPrefInt(OperationBatchCountPrefName, OperationBatchCount); SetPrefInt(ConnectionTimeOutPrefName, ConnectionTimeOut); SetPrefBool(OverrideHostnamePrefName, OverrideHostname); SetPrefBool(WarnOnSpecialCharactersPrefName, WarnOnSpecialCharacters); SetPrefBool(UseIgnorePrefName, UseIgnore); SetPrefString(IgnoreNamePrefName,IgnoreName); SetPrefBool(EnableLogPrefName,EnableLog); SetPrefString(LogPathPrefName,LogPath); SetPrefString(P4ConfigNamePrefName, P4ConfigName); SetPrefString(IgnoreLinesPrefName, IgnoreLines); // Notify users that prefs changed if (PrefsChanged != null) PrefsChanged(); } static string GetFullPrefName(string aPrefName) { return "P4Connect_" + Main.ProjectName + "_" + aPrefName; } static bool HasPref(string aPrefName) { return EditorPrefs.HasKey(GetFullPrefName(aPrefName)); } static void SetPrefString(string aPrefName, string aPref) { EditorPrefs.SetString(GetFullPrefName(aPrefName), aPref); } static void SetPrefInt(string aPrefName, int aPref) { EditorPrefs.SetInt(GetFullPrefName(aPrefName), aPref); } static void SetPrefBool(string aPrefName, bool aPref) { EditorPrefs.SetBool(GetFullPrefName(aPrefName), aPref); } static string GetPrefString(string aPrefName) { return EditorPrefs.GetString(GetFullPrefName(aPrefName)); } static int GetPrefInt(string aPrefName) { return EditorPrefs.GetInt(GetFullPrefName(aPrefName)); } static bool GetPrefBool(string aPrefName) { return EditorPrefs.GetBool(GetFullPrefName(aPrefName)); } #endregion public static SerializationMode CachedSerializationMode { get; private set; } #region P4CONFIG public static string FindP4ConfigFile() { string p4config = Environment.GetEnvironmentVariable("P4CONFIG"); Config.P4ConfigName = p4config; return (FindP4ConfigFile(p4config)); } public static string FindP4ConfigFile(string config) { string directoryName; if (! String.IsNullOrEmpty(config)) { string path = Application.dataPath; while (path != null) { directoryName = Path.GetDirectoryName(path); string[] files = System.IO.Directory.GetFiles(directoryName, config); if (files.Count() > 0) { return files[0]; } path = directoryName; } } return null; } public static void LoadP4ConfigFile(string path) { string line; char[] equalsChars = { '=' }; System.IO.StreamReader file = new System.IO.StreamReader(path); while ((line = file.ReadLine()) != null) { string[] segments = line.Split(equalsChars); if (segments.Length >= 2) { string key = segments[0]; string val = segments[1]; // Debug.Log("Key: " + key); // Debug.Log("Value: " + val); switch (segments[0]) { case "P4PORT": { ServerURI = val; } break; case "P4USER": { Username = val; } break; case "P4CLIENT": { Workspace = val; } break; case "P4PASSWD": { Password = val; } break; } } } file.Close(); } #endregion #region ConfigAsset static string configAssetPath = "Assets/P4Connect/Editor/Config.asset"; public static void writeConfigAsset() { ConfigAsset asset = ScriptableObject.CreateInstance<ConfigAsset>(); asset.CopyConfigToAsset(); AssetDatabase.CreateAsset(asset, configAssetPath); AssetDatabase.SaveAssets(); } public static bool readConfigAsset() { ConfigAsset asset = (ConfigAsset)AssetDatabase.LoadAssetAtPath(configAssetPath, typeof(ConfigAsset)); if (asset != null) { asset.CopyAssetToConfig(); return true; } else { return false; } } #endregion } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#29 | 16210 | Norman Morse | Remove files from old locations | ||
#28 | 16163 | Norman Morse |
Import latest changes from 2015.2/1245676 Fixes disconnect issue on "run" Fixes SSL versioning problem on OSX Added VS2015 support to project files |
||
#27 | 16117 | Norman Morse |
Fix for EditorPrefs related Disconnection. Cleaned up some code. Removed Spurious Comments Initialize Config from within Main |
||
#26 | 15424 | Norman Morse |
Fixed exceptions in Dialogs. Updated release Notes. Added checks to menus for PerforceEnabled |
||
#25 | 15401 | Norman Morse |
Fixed serialization of Config so P4Connect remains connected after a Game Run Cleaned up some menus. |
||
#24 | 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 |
||
#23 | 15298 | Norman Morse | Fix Version Stamping to not use UpdateVersion.exe for personal (workshop) builds. | ||
#22 | 15266 | Norman Morse |
Integrated "UpdateVersion" tool to update the VersionInfo and the DLL properties with information from "Version" EC generates the Version file for us in builds. Workshop users need to generate their own Release number with two zeros (like 2015.2.0.0) which will have the last two numbers replaced with change ID. |
||
#21 | 15244 | Norman Morse |
Better Directory support in "add" "get latest" "refresh" and other commands. Improved Project Root detection Various Bug Fixes and Clean up |
||
#20 | 15146 | Norman Morse |
Rewrote Config Dialog to resize well and work both vertically and horizontally. Fixed some internal issues in file handling. Removed .bytes from default type of "text" |
||
#19 | 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. |
||
#18 | 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. |
||
#17 | 14193 | Norman Morse |
GA.7 release Refactor Pending Changes Resolve Submit issues. Fixed Menu entries. Handle mismatched file and meta states. |
||
#16 | 13864 | Norman Morse | Final fixes for GA.5 release. | ||
#15 | 13813 | Norman Morse |
Changed Config to call CheckProjectRoot which creates ClientProjectRoot and DepotProjectRoot Filter files in changelists to include only files under the Project root. Run fstat on files in the default change to make sure we have complete metadata |
||
#14 | 13596 | Norman Morse |
GA.3 fixes Update release notes. Fix config dialog initialization, update version Disable warnings for PackageIcons.cs, Fix crash in GetLockState() call |
||
#13 | 13269 | Norman Morse |
Bumped version to 2.7 GA 2 Fixed problem with Hung Config Window and no previous settings Fixed code to automattically attempt to connect to Perforce Server if configuration allows. |
||
#12 | 12862 | Norman Morse |
Fixed problem with an empty default change list not refresshing. Fixed crash in is_ignored Removed a lot of log output |
||
#11 | 12568 | Norman Morse | Fixed some error handling during Perforce Configuration | ||
#10 | 12566 | Norman Morse |
Fixed hang when restarting after rebuild Updated Release String to GA |
||
#9 | 12554 | Norman Morse |
Changed OSX DLL checking code. Improved re-connection after restart |
||
#8 | 12553 | Norman Morse |
integrate from internal main Build fixes for EC. Major changes to Configuration and re-initialization code. Bug fixes |
||
#7 | 12512 | Norman Morse | Integrate from Dev branch, preparing for Beta3 release | ||
#6 | 12368 | Norman Morse |
Improved config dialog. Perforce defaults to Enabled |
||
#5 | 12251 | Norman Morse |
Fixes for Beta 2 release Mostly Configuration dialog bug fixes |
||
#4 | 12135 | Norman Morse |
Integrate dev branch changes into main. This code is the basiis of the 2.7 BETA release which provides Unity 5 compatibility |
||
#3 | 11378 | Norman Morse |
P4Config support. Debugging DLL Loading issues |
||
#2 | 11224 | Norman Morse |
Add P4Config support to P4Connect. It checks for a P4Config Environment variable, and if found, looks for the controlling P4Config file. It then pre-populates the settings with information from P4Config and kicks off a validate. You can re-run the P4Config routine by turning on and off the checkbox from within the P4Connect settings. |
||
#1 | 10940 | Norman Morse |
Inital Workshop release of P4Connect. Released under BSD-2 license |