void PopulateFolder(FolderToClean Folder) { if (!bAbortScan) { try { if ((Folder.Directory.Attributes & FileAttributes.ReparsePoint) == 0) { foreach (DirectoryInfo SubDirectory in Folder.Directory.EnumerateDirectories()) { FolderToClean SubFolder = new FolderToClean(SubDirectory); Folder.NameToSubFolder[SubFolder.Name] = SubFolder; QueueFolderToPopulate(SubFolder); } foreach (FileInfo File in Folder.Directory.EnumerateFiles()) { FileAttributes Attributes = File.Attributes; // Force the value to be cached. Folder.NameToFile[File.Name] = File; } } } catch (Exception Ex) { string NewError = String.Format("Unable to enumerate contents of {0} due to an error:\n\n{1}", Folder.Directory.FullName, Ex); Interlocked.CompareExchange(ref ScanError, NewError, null); bAbortScan = true; } } if (Interlocked.Decrement(ref RemainingFoldersToScan) == 0) { FinishedScan.Set(); } }
private CleanWorkspaceWindow(PerforceConnection PerforceClient, FolderToClean RootFolderToClean) { this.PerforceClient = PerforceClient; this.RootFolderToClean = RootFolderToClean; InitializeComponent(); }
void PopulateFolder(FolderToClean Folder) { if (!bAbortScan) { if ((Folder.Directory.Attributes & FileAttributes.ReparsePoint) == 0) { foreach (DirectoryInfo SubDirectory in Folder.Directory.EnumerateDirectories()) { FolderToClean SubFolder = new FolderToClean(SubDirectory); Folder.NameToSubFolder[SubFolder.Name] = SubFolder; QueueFolderToPopulate(SubFolder); } foreach (FileInfo File in Folder.Directory.EnumerateFiles()) { FileAttributes Attributes = File.Attributes; // Force the value to be cached. Folder.NameToFile[File.Name] = File; } } } if (Interlocked.Decrement(ref RemainingFoldersToScan) == 0) { FinishedScan.Set(); } }
public static void DoClean(PerforceConnection PerforceClient, string LocalRootPath, string ClientRootPath, IReadOnlyList <string> SyncPaths, TextWriter Log) { // Figure out which folders to clean FolderToClean RootFolderToClean = new FolderToClean(new DirectoryInfo(LocalRootPath)); using (FindFoldersToCleanTask QueryWorkspace = new FindFoldersToCleanTask(PerforceClient, RootFolderToClean, ClientRootPath, SyncPaths, Log)) { string ErrorMessage; if (!ModalTaskWindow.Execute(QueryWorkspace, "Clean Workspace", "Querying files in Perforce, please wait...", out ErrorMessage)) { if (!String.IsNullOrEmpty(ErrorMessage)) { MessageBox.Show(ErrorMessage); } return; } } // If there's nothing to delete, don't bother displaying the dialog at all if (RootFolderToClean.FilesToClean.Count == 0 && RootFolderToClean.SubFolders.Count == 0) { MessageBox.Show("You have no local files which are not in Perforce.", "Workspace Clean", MessageBoxButtons.OK); return; } // Populate the tree CleanWorkspaceWindow CleanWorkspace = new CleanWorkspaceWindow(RootFolderToClean); CleanWorkspace.ShowDialog(); }
void MergeTrees(FolderToClean LocalFolder, PerforceHaveFolder PerforceFolder, HashSet <string> OpenClientPaths) { if (PerforceFolder == null) { // Loop through all the local sub-folders foreach (FolderToClean LocalSubFolder in LocalFolder.NameToSubFolder.Values) { MergeTrees(LocalSubFolder, null, OpenClientPaths); } // Delete everything LocalFolder.FilesToDelete.AddRange(LocalFolder.NameToFile.Values); } else { // Loop through all the local sub-folders foreach (FolderToClean LocalSubFolder in LocalFolder.NameToSubFolder.Values) { PerforceHaveFolder PerforceSubFolder; PerforceFolder.NameToSubFolder.TryGetValue(LocalSubFolder.Name, out PerforceSubFolder); MergeTrees(LocalSubFolder, PerforceSubFolder, OpenClientPaths); } // Also merge all the Perforce folders that no longer exist foreach (KeyValuePair <string, PerforceHaveFolder> PerforceSubFolderPair in PerforceFolder.NameToSubFolder) { FolderToClean LocalSubFolder; if (!LocalFolder.NameToSubFolder.TryGetValue(PerforceSubFolderPair.Key, out LocalSubFolder)) { LocalSubFolder = new FolderToClean(new DirectoryInfo(Path.Combine(LocalFolder.Directory.FullName, PerforceSubFolderPair.Key))); MergeTrees(LocalSubFolder, PerforceSubFolderPair.Value, OpenClientPaths); LocalFolder.NameToSubFolder.Add(LocalSubFolder.Name, LocalSubFolder); } } // Find all the files that need to be re-synced foreach (KeyValuePair <string, PerforceFileRecord> FilePair in PerforceFolder.NameToFile) { FileInfo LocalFile; if (!LocalFolder.NameToFile.TryGetValue(FilePair.Key, out LocalFile)) { LocalFolder.FilesToSync.Add(new FileInfo(Path.Combine(LocalFolder.Directory.FullName, FilePair.Key))); } else if ((FilePair.Value.Flags & PerforceFileFlags.AlwaysWritable) == 0 && (LocalFile.Attributes & FileAttributes.ReadOnly) == 0 && !OpenClientPaths.Contains(FilePair.Value.ClientPath)) { LocalFolder.FilesToSync.Add(LocalFile); } } // Find all the files that should be deleted LocalFolder.FilesToDelete.AddRange(LocalFolder.NameToFile.Values.Where(x => !PerforceFolder.NameToFile.ContainsKey(x.Name))); } // Figure out if this folder is just an empty directory that needs to be removed LocalFolder.bEmptyLeaf = LocalFolder.NameToFile.Count == 0 && LocalFolder.NameToSubFolder.Count == 0 && LocalFolder.FilesToSync.Count == 0; // Figure out if it the folder will be empty after the clean operation LocalFolder.bEmptyAfterClean = LocalFolder.NameToSubFolder.Values.All(x => x.bEmptyAfterClean) && LocalFolder.FilesToDelete.Count == LocalFolder.NameToFile.Count && LocalFolder.FilesToSync.Count == 0; }
void RemoveEmptyFolders(FolderToClean Folder) { foreach (FolderToClean SubFolder in Folder.SubFolders) { RemoveEmptyFolders(SubFolder); } Folder.SubFolders.RemoveAll(x => x.SubFolders.Count == 0 && x.FilesToClean.Count == 0); }
void QueueFolderToPopulate(FolderToClean Folder) { if (Interlocked.Increment(ref RemainingFoldersToScan) == 1) { FinishedScan.Reset(); } ThreadPool.QueueUserWorkItem(x => PopulateFolder(Folder)); }
private TreeNode BuildTreeViewStructure(FolderToClean Folder, string FolderPath, bool bParentFolderSelected, int Depth) { bool bSelectFolder = bParentFolderSelected || IsSafeToDeleteFolder(FolderPath); TreeNodeData FolderNodeData = new TreeNodeData(); FolderNodeData.Folder = Folder; TreeNode FolderNode = new TreeNode(); FolderNode.Text = Folder.Name; FolderNode.Tag = FolderNodeData; foreach (FolderToClean SubFolder in Folder.SubFolders.OrderBy(x => x.Name)) { TreeNode ChildNode = BuildTreeViewStructure(SubFolder, FolderPath + SubFolder.Name.ToLowerInvariant() + "/", bSelectFolder, Depth + 1); FolderNode.Nodes.Add(ChildNode); TreeNodeData ChildNodeData = (TreeNodeData)ChildNode.Tag; FolderNodeData.NumFiles += ChildNodeData.NumFiles; FolderNodeData.NumSelectedFiles += ChildNodeData.NumSelectedFiles; } foreach (FileInfo File in Folder.FilesToClean.OrderBy(x => x.Name)) { string Name = File.Name.ToLowerInvariant(); bool bSelectFile = bSelectFolder || IsSafeToDeleteFile(FolderPath, File.Name.ToLowerInvariant()); TreeNodeData FileNodeData = new TreeNodeData(); FileNodeData.File = File; FileNodeData.NumFiles = 1; FileNodeData.NumSelectedFiles = bSelectFile? 1 : 0; TreeNode FileNode = new TreeNode(); FileNode.Text = File.Name; FileNode.Tag = FileNodeData; FolderNode.Nodes.Add(FileNode); UpdateImage(FileNode); FolderNodeData.NumFiles++; FolderNodeData.NumSelectedFiles += FileNodeData.NumSelectedFiles; } if (FolderNodeData.NumSelectedFiles > 0 && !FolderNodeData.Folder.bEmptyAfterDelete && Depth < 2) { FolderNode.Expand(); } else { FolderNode.Collapse(); } UpdateImage(FolderNode); return(FolderNode); }
void RemoveEmptyFolders(FolderToClean Folder) { foreach (FolderToClean SubFolder in Folder.NameToSubFolder.Values) { RemoveEmptyFolders(SubFolder); } Folder.NameToSubFolder = Folder.NameToSubFolder.Values.Where(x => x.NameToSubFolder.Count > 0 || x.FilesToSync.Count > 0 || x.FilesToDelete.Count > 0 || x.bEmptyLeaf).ToDictionary(x => x.Name, x => x, StringComparer.InvariantCultureIgnoreCase); }
private CleanWorkspaceWindow(PerforceConnection PerforceClient, FolderToClean RootFolderToClean, string[] ExtraSafeToDeleteFolders, string[] ExtraSafeToDeleteExtensions) { this.PerforceClient = PerforceClient; this.RootFolderToClean = RootFolderToClean; this.ExtraSafeToDeleteFolders = ExtraSafeToDeleteFolders.Select(x => x.Trim().Replace('\\', '/').Trim('/')).Where(x => x.Length > 0).Select(x => String.Format("/{0}/", x.ToLowerInvariant())).ToArray(); this.ExtraSafeToDeleteExtensions = ExtraSafeToDeleteExtensions.Select(x => x.Trim().ToLowerInvariant()).Where(x => x.Length > 0).ToArray(); InitializeComponent(); }
public FindFoldersToCleanTask(PerforceConnection InPerforceClient, FolderToClean InRootFolderToClean, string InClientRootPath, IReadOnlyList <string> InSyncPaths, TextWriter InLog) { PerforceClient = InPerforceClient; ClientRootPath = InClientRootPath.TrimEnd('/') + "/"; SyncPaths = new List <string>(InSyncPaths); Log = InLog; RootFolderToClean = InRootFolderToClean; FinishedScan = new ManualResetEvent(true); }
void RemoveFilesToKeep(FolderToClean Folder, PerforceHaveFolder KeepFolder) { foreach (FolderToClean SubFolder in Folder.SubFolders) { PerforceHaveFolder KeepSubFolder; if (KeepFolder.SubFolders.TryGetValue(SubFolder.Directory.Name, out KeepSubFolder)) { RemoveFilesToKeep(SubFolder, KeepSubFolder); Folder.bEmptyAfterDelete &= SubFolder.bEmptyAfterDelete; } } Folder.bEmptyAfterDelete &= (Folder.FilesToClean.RemoveAll(x => KeepFolder.Files.Contains(x.Name)) == 0); }
void PopulateFolder(FolderToClean Folder) { if (!bAbortScan) { foreach (DirectoryInfo SubDirectory in Folder.Directory.EnumerateDirectories()) { FolderToClean SubFolder = new FolderToClean(SubDirectory); Folder.SubFolders.Add(SubFolder); QueueFolderToPopulate(SubFolder); } Folder.FilesToClean = Folder.Directory.EnumerateFiles().ToList(); } if (Interlocked.Decrement(ref RemainingFoldersToScan) == 0) { FinishedScan.Set(); } }
public FindFoldersToCleanTask(PerforceConnection InPerforceClient, FolderToClean InRootFolderToClean, string InClientRootPath, IReadOnlyList <string> InSyncPaths, TextWriter InLog) { PerforceClient = InPerforceClient; ClientRootPath = InClientRootPath.TrimEnd('/') + "/"; SyncPaths = new List <string>(InSyncPaths); Log = InLog; RootFolderToClean = InRootFolderToClean; FinishedScan = new ManualResetEvent(true); foreach (string SyncPath in SyncPaths) { Debug.Assert(SyncPath.StartsWith(ClientRootPath)); if (SyncPath.StartsWith(ClientRootPath, StringComparison.InvariantCultureIgnoreCase)) { string[] Fragments = SyncPath.Substring(ClientRootPath.Length).Split('/'); FolderToClean SyncFolder = RootFolderToClean; for (int Idx = 0; Idx < Fragments.Length - 1; Idx++) { FolderToClean NextSyncFolder = SyncFolder.SubFolders.FirstOrDefault(x => x.Name.Equals(Fragments[Idx], StringComparison.InvariantCultureIgnoreCase)); if (NextSyncFolder == null) { NextSyncFolder = new FolderToClean(new DirectoryInfo(Path.Combine(SyncFolder.Directory.FullName, Fragments[Idx]))); SyncFolder.SubFolders.Add(NextSyncFolder); } SyncFolder = NextSyncFolder; } string Wildcard = Fragments[Fragments.Length - 1]; if (Wildcard == "...") { QueueFolderToPopulate(SyncFolder); } else if (SyncFolder.Directory.Exists) { SyncFolder.FilesToClean = SyncFolder.FilesToClean.Union(SyncFolder.Directory.GetFiles(Wildcard)).GroupBy(x => x.Name).Select(x => x.First()).ToList(); } } } }
public FindFoldersToCleanTask(PerforceConnection InPerforceClient, FolderToClean InRootFolderToClean, string InClientRootPath, IReadOnlyList<string> InSyncPaths, TextWriter InLog) { PerforceClient = InPerforceClient; ClientRootPath = InClientRootPath.TrimEnd('/') + "/"; SyncPaths = new List<string>(InSyncPaths); Log = InLog; RootFolderToClean = InRootFolderToClean; FinishedScan = new ManualResetEvent(true); foreach(string SyncPath in SyncPaths) { Debug.Assert(SyncPath.StartsWith(ClientRootPath)); if(SyncPath.StartsWith(ClientRootPath, StringComparison.InvariantCultureIgnoreCase)) { string[] Fragments = SyncPath.Substring(ClientRootPath.Length).Split('/'); FolderToClean SyncFolder = RootFolderToClean; for(int Idx = 0; Idx < Fragments.Length - 1; Idx++) { FolderToClean NextSyncFolder = SyncFolder.SubFolders.FirstOrDefault(x => x.Name.Equals(Fragments[Idx], StringComparison.InvariantCultureIgnoreCase)); if(NextSyncFolder == null) { NextSyncFolder = new FolderToClean(new DirectoryInfo(Path.Combine(SyncFolder.Directory.FullName, Fragments[Idx]))); SyncFolder.SubFolders.Add(NextSyncFolder); } SyncFolder = NextSyncFolder; } string Wildcard = Fragments[Fragments.Length - 1]; if(Wildcard == "...") { QueueFolderToPopulate(SyncFolder); } else if(SyncFolder.Directory.Exists) { SyncFolder.FilesToClean = SyncFolder.FilesToClean.Union(SyncFolder.Directory.GetFiles(Wildcard)).GroupBy(x => x.Name).Select(x => x.First()).ToList(); } } } }
void PopulateFolder(FolderToClean Folder) { if (!bAbortScan) { if ((Folder.Directory.Attributes & FileAttributes.ReparsePoint) == 0) { foreach (DirectoryInfo SubDirectory in Folder.Directory.EnumerateDirectories()) { FolderToClean SubFolder = new FolderToClean(SubDirectory); Folder.SubFolders.Add(SubFolder); QueueFolderToPopulate(SubFolder); } Folder.FilesToClean = Folder.Directory.EnumerateFiles().ToList(); } Folder.bEmptyLeaf = Folder.SubFolders.Count == 0 && Folder.FilesToClean.Count == 0; } if (Interlocked.Decrement(ref RemainingFoldersToScan) == 0) { FinishedScan.Set(); } }
public bool Run(out string ErrorMessage) { Log.WriteLine("Finding files in workspace..."); Log.WriteLine(); // Start enumerating all the files that exist locally foreach (string SyncPath in SyncPaths) { Debug.Assert(SyncPath.StartsWith(ClientRootPath)); if (SyncPath.StartsWith(ClientRootPath, StringComparison.InvariantCultureIgnoreCase)) { string[] Fragments = SyncPath.Substring(ClientRootPath.Length).Split('/'); FolderToClean SyncFolder = RootFolderToClean; for (int Idx = 0; Idx < Fragments.Length - 1; Idx++) { FolderToClean NextSyncFolder; if (!SyncFolder.NameToSubFolder.TryGetValue(Fragments[Idx], out NextSyncFolder)) { NextSyncFolder = new FolderToClean(new DirectoryInfo(Path.Combine(SyncFolder.Directory.FullName, Fragments[Idx]))); SyncFolder.NameToSubFolder[NextSyncFolder.Name] = NextSyncFolder; } SyncFolder = NextSyncFolder; } string Wildcard = Fragments[Fragments.Length - 1]; if (Wildcard == "...") { QueueFolderToPopulate(SyncFolder); } else { if (SyncFolder.Directory.Exists) { foreach (FileInfo File in SyncFolder.Directory.EnumerateFiles(Wildcard)) { SyncFolder.NameToFile[File.Name] = File; } } } } } // Get the prefix for any local file string LocalRootPrefix = RootFolderToClean.Directory.FullName.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar; // Query the have table and build a separate tree from it PerforceHaveFolder RootHaveFolder = new PerforceHaveFolder(); foreach (string SyncPath in SyncPaths) { List <PerforceFileRecord> FileRecords; if (!PerforceClient.Stat(String.Format("{0}#have", SyncPath), out FileRecords, Log)) { ErrorMessage = "Couldn't query have table from Perforce."; return(false); } foreach (PerforceFileRecord FileRecord in FileRecords) { if (!FileRecord.ClientPath.StartsWith(LocalRootPrefix, StringComparison.InvariantCultureIgnoreCase)) { ErrorMessage = String.Format("Failed to get have table; file '{0}' doesn't start with root path ('{1}')", FileRecord.ClientPath, RootFolderToClean.Directory.FullName); return(false); } string[] Tokens = FileRecord.ClientPath.Substring(LocalRootPrefix.Length).Split('/', '\\'); PerforceHaveFolder FileFolder = RootHaveFolder; for (int Idx = 0; Idx < Tokens.Length - 1; Idx++) { PerforceHaveFolder NextFileFolder; if (!FileFolder.NameToSubFolder.TryGetValue(Tokens[Idx], out NextFileFolder)) { NextFileFolder = new PerforceHaveFolder(); FileFolder.NameToSubFolder.Add(Tokens[Idx], NextFileFolder); } FileFolder = NextFileFolder; } FileFolder.NameToFile[Tokens[Tokens.Length - 1]] = FileRecord; } } // Find all the files which are currently open for edit. We don't want to force sync these. List <PerforceFileRecord> OpenFileRecords; if (!PerforceClient.GetOpenFiles("//...", out OpenFileRecords, Log)) { ErrorMessage = "Couldn't query open files from Perforce."; return(false); } // Build a set of all the open local files HashSet <string> OpenLocalFiles = new HashSet <string>(StringComparer.InvariantCultureIgnoreCase); foreach (PerforceFileRecord OpenFileRecord in OpenFileRecords) { if (!OpenFileRecord.ClientPath.StartsWith(ClientRootPath, StringComparison.InvariantCultureIgnoreCase)) { ErrorMessage = String.Format("Failed to get open file list; file '{0}' doesn't start with client root path ('{1}')", OpenFileRecord.ClientPath, ClientRootPath); return(false); } OpenLocalFiles.Add(LocalRootPrefix + PerforceUtils.UnescapePath(OpenFileRecord.ClientPath).Substring(ClientRootPath.Length).Replace('/', Path.DirectorySeparatorChar)); } // Wait to finish scanning the directory FinishedScan.WaitOne(); // Find the value of the P4CONFIG variable string PerforceConfigFile; PerforceClient.GetSetting("P4CONFIG", out PerforceConfigFile, Log); // Merge the trees MergeTrees(RootFolderToClean, RootHaveFolder, OpenLocalFiles, PerforceConfigFile); // Remove all the empty folders RemoveEmptyFolders(RootFolderToClean); ErrorMessage = null; return(true); }
void RemoveEmptyFolders(FolderToClean Folder) { foreach(FolderToClean SubFolder in Folder.SubFolders) { RemoveEmptyFolders(SubFolder); } Folder.SubFolders.RemoveAll(x => x.SubFolders.Count == 0 && x.FilesToClean.Count == 0 && !x.bEmptyLeaf); }
void RemoveFilesToKeep(FolderToClean Folder, PerforceHaveFolder KeepFolder) { foreach(FolderToClean SubFolder in Folder.SubFolders) { PerforceHaveFolder KeepSubFolder; if(KeepFolder.SubFolders.TryGetValue(SubFolder.Directory.Name, out KeepSubFolder)) { RemoveFilesToKeep(SubFolder, KeepSubFolder); Folder.bEmptyAfterDelete &= SubFolder.bEmptyAfterDelete; } } Folder.bEmptyAfterDelete &= (Folder.FilesToClean.RemoveAll(x => KeepFolder.Files.Contains(x.Name)) == 0); }
private TreeNode BuildTreeViewStructure(FolderToClean Folder, string FolderPath, bool bParentFolderSelected, int Depth) { bool bSelectFolder = bParentFolderSelected || IsSafeToDeleteFolder(FolderPath) || Folder.bEmptyLeaf; TreeNodeData FolderNodeData = new TreeNodeData(); FolderNodeData.Folder = Folder; TreeNode FolderNode = new TreeNode(); FolderNode.Text = Folder.Name; FolderNode.Tag = FolderNodeData; foreach (FolderToClean SubFolder in Folder.NameToSubFolder.OrderBy(x => x.Key).Select(x => x.Value)) { TreeNode ChildNode = BuildTreeViewStructure(SubFolder, FolderPath + SubFolder.Name.ToLowerInvariant() + "/", bSelectFolder, Depth + 1); FolderNode.Nodes.Add(ChildNode); TreeNodeData ChildNodeData = (TreeNodeData)ChildNode.Tag; FolderNodeData.NumFiles += ChildNodeData.NumFiles; FolderNodeData.NumSelectedFiles += ChildNodeData.NumSelectedFiles; FolderNodeData.NumEmptySelectedFiles += ChildNodeData.NumEmptySelectedFiles; FolderNodeData.NumMissingSelectedFiles += ChildNodeData.NumMissingSelectedFiles; FolderNodeData.NumDefaultSelectedFiles += ChildNodeData.NumDefaultSelectedFiles; } foreach (FileInfo File in Folder.FilesToSync.OrderBy(x => x.Name)) { TreeNodeData FileNodeData = new TreeNodeData(); FileNodeData.Action = TreeNodeAction.Sync; FileNodeData.File = File; FileNodeData.NumFiles = 1; FileNodeData.NumSelectedFiles = 1; FileNodeData.NumEmptySelectedFiles = 0; FileNodeData.NumMissingSelectedFiles = 1; FileNodeData.NumDefaultSelectedFiles = FileNodeData.NumSelectedFiles; TreeNode FileNode = new TreeNode(); FileNode.Text = File.Name + " (sync)"; FileNode.Tag = FileNodeData; FolderNode.Nodes.Add(FileNode); UpdateImage(FileNode); FolderNodeData.NumFiles++; FolderNodeData.NumSelectedFiles += FileNodeData.NumSelectedFiles; FolderNodeData.NumEmptySelectedFiles += FileNodeData.NumEmptySelectedFiles; FolderNodeData.NumMissingSelectedFiles += FileNodeData.NumMissingSelectedFiles; FolderNodeData.NumDefaultSelectedFiles += FileNodeData.NumDefaultSelectedFiles; } foreach (FileInfo File in Folder.FilesToDelete.OrderBy(x => x.Name)) { string Name = File.Name.ToLowerInvariant(); bool bSelectFile = bSelectFolder || IsSafeToDeleteFile(FolderPath, File.Name.ToLowerInvariant()); TreeNodeData FileNodeData = new TreeNodeData(); FileNodeData.Action = TreeNodeAction.Delete; FileNodeData.File = File; FileNodeData.NumFiles = 1; FileNodeData.NumSelectedFiles = bSelectFile? 1 : 0; FileNodeData.NumEmptySelectedFiles = 0; FileNodeData.NumMissingSelectedFiles = 0; FileNodeData.NumDefaultSelectedFiles = FileNodeData.NumSelectedFiles; TreeNode FileNode = new TreeNode(); FileNode.Text = File.Name; FileNode.Tag = FileNodeData; FolderNode.Nodes.Add(FileNode); UpdateImage(FileNode); FolderNodeData.NumFiles++; FolderNodeData.NumSelectedFiles += FileNodeData.NumSelectedFiles; FolderNodeData.NumEmptySelectedFiles += FileNodeData.NumEmptySelectedFiles; FolderNodeData.NumMissingSelectedFiles += FileNodeData.NumMissingSelectedFiles; FolderNodeData.NumDefaultSelectedFiles += FileNodeData.NumDefaultSelectedFiles; } if (FolderNodeData.Folder.bEmptyLeaf) { FolderNodeData.NumFiles++; FolderNodeData.NumSelectedFiles++; FolderNodeData.NumEmptySelectedFiles++; FolderNodeData.NumDefaultSelectedFiles++; } if (FolderNodeData.NumSelectedFiles > 0 && !FolderNodeData.Folder.bEmptyAfterClean && Depth < 2) { FolderNode.Expand(); } else { FolderNode.Collapse(); } UpdateImage(FolderNode); return(FolderNode); }
private CleanWorkspaceWindow(FolderToClean InRootFolderToClean) { RootFolderToClean = InRootFolderToClean; InitializeComponent(); }
void QueueFolderToPopulate(FolderToClean Folder) { if(Interlocked.Increment(ref RemainingFoldersToScan) == 1) { FinishedScan.Reset(); } ThreadPool.QueueUserWorkItem(x => PopulateFolder(Folder)); }
void PopulateFolder(FolderToClean Folder) { if(!bAbortScan) { foreach(DirectoryInfo SubDirectory in Folder.Directory.EnumerateDirectories()) { FolderToClean SubFolder = new FolderToClean(SubDirectory); Folder.SubFolders.Add(SubFolder); QueueFolderToPopulate(SubFolder); } Folder.FilesToClean = Folder.Directory.EnumerateFiles().ToList(); Folder.bEmptyLeaf = Folder.SubFolders.Count == 0 && Folder.FilesToClean.Count == 0; } if(Interlocked.Decrement(ref RemainingFoldersToScan) == 0) { FinishedScan.Set(); } }
private TreeNode BuildTreeViewStructure(FolderToClean Folder, string FolderPath, bool bParentFolderSelected, int Depth) { bool bSelectFolder = bParentFolderSelected || IsSafeToDeleteFolder(FolderPath) || Folder.bEmptyLeaf; TreeNodeData FolderNodeData = new TreeNodeData(); FolderNodeData.Folder = Folder; TreeNode FolderNode = new TreeNode(); FolderNode.Text = Folder.Name; FolderNode.Tag = FolderNodeData; foreach(FolderToClean SubFolder in Folder.SubFolders.OrderBy(x => x.Name)) { TreeNode ChildNode = BuildTreeViewStructure(SubFolder, FolderPath + SubFolder.Name.ToLowerInvariant() + "/", bSelectFolder, Depth + 1); FolderNode.Nodes.Add(ChildNode); TreeNodeData ChildNodeData = (TreeNodeData)ChildNode.Tag; FolderNodeData.NumFiles += ChildNodeData.NumFiles; FolderNodeData.NumSelectedFiles += ChildNodeData.NumSelectedFiles; FolderNodeData.NumEmptySelectedFiles += ChildNodeData.NumEmptySelectedFiles; FolderNodeData.NumDefaultSelectedFiles += ChildNodeData.NumDefaultSelectedFiles; } foreach(FileInfo File in Folder.FilesToClean.OrderBy(x => x.Name)) { string Name = File.Name.ToLowerInvariant(); bool bSelectFile = bSelectFolder || IsSafeToDeleteFile(FolderPath, File.Name.ToLowerInvariant()); TreeNodeData FileNodeData = new TreeNodeData(); FileNodeData.File = File; FileNodeData.NumFiles = 1; FileNodeData.NumSelectedFiles = bSelectFile? 1 : 0; FileNodeData.NumEmptySelectedFiles = 0; FileNodeData.NumDefaultSelectedFiles = FileNodeData.NumSelectedFiles; TreeNode FileNode = new TreeNode(); FileNode.Text = File.Name; FileNode.Tag = FileNodeData; FolderNode.Nodes.Add(FileNode); UpdateImage(FileNode); FolderNodeData.NumFiles++; FolderNodeData.NumSelectedFiles += FileNodeData.NumSelectedFiles; FolderNodeData.NumEmptySelectedFiles += FileNodeData.NumEmptySelectedFiles; FolderNodeData.NumDefaultSelectedFiles += FileNodeData.NumDefaultSelectedFiles; } if(FolderNodeData.Folder.bEmptyLeaf) { FolderNodeData.NumFiles++; FolderNodeData.NumSelectedFiles++; FolderNodeData.NumEmptySelectedFiles++; FolderNodeData.NumDefaultSelectedFiles++; } if(FolderNodeData.NumSelectedFiles > 0 && !FolderNodeData.Folder.bEmptyAfterDelete && Depth < 2) { FolderNode.Expand(); } else { FolderNode.Collapse(); } UpdateImage(FolderNode); return FolderNode; }
public static void DoClean(PerforceConnection PerforceClient, string LocalRootPath, string ClientRootPath, IReadOnlyList<string> SyncPaths, TextWriter Log) { // Figure out which folders to clean FolderToClean RootFolderToClean = new FolderToClean(new DirectoryInfo(LocalRootPath)); using(FindFoldersToCleanTask QueryWorkspace = new FindFoldersToCleanTask(PerforceClient, RootFolderToClean, ClientRootPath, SyncPaths, Log)) { string ErrorMessage; if(!ModalTaskWindow.Execute(QueryWorkspace, "Clean Workspace", "Querying files in Perforce, please wait...", out ErrorMessage)) { if(!String.IsNullOrEmpty(ErrorMessage)) { MessageBox.Show(ErrorMessage); } return; } } // If there's nothing to delete, don't bother displaying the dialog at all if(RootFolderToClean.FilesToClean.Count == 0 && RootFolderToClean.SubFolders.Count == 0) { MessageBox.Show("You have no local files which are not in Perforce.", "Workspace Clean", MessageBoxButtons.OK); return; } // Populate the tree CleanWorkspaceWindow CleanWorkspace = new CleanWorkspaceWindow(RootFolderToClean); CleanWorkspace.ShowDialog(); }