/// <summary> /// Removes the node from the project. /// </summary> /// <remarks>By default, this does nothing.</remarks> public virtual void RemoveFromProject() { // Don't do anything if we can't remove the node. if (!this.CanRemoveFromProject) { return; } FolderNode parentFolder = this.Parent; // We'd better have a parent. if (parentFolder == null) { Tracer.Fail("Cannot remove the root node from the project."); return; } // Our parent better have children. if (parentFolder.Children == null) { Tracer.Fail("This node's ({0}) parent ({1}) should have a non-null Children collection.", this.ToString(), parentFolder.ToString()); return; } // Before we remove ourself from the hierachy, make sure that we're not selected. // If we have to, we'll move the selection to the previous sibling. Node nodeToSelect = null; if (this.Selected) { // If the previous sibling is null, then select the root node. if (this.PreviousSibling != null) { nodeToSelect = this.PreviousSibling; } else { nodeToSelect = this.Hierarchy.RootNode; } } // Remove ourself from the parent. parentFolder.Children.Remove(this); // Now select the node. We have to do it here because the removal causes a refresh // on the hierarchy, which will select the root node by default. if (nodeToSelect != null) { nodeToSelect.Select(); } }
int IVsProject3.GenerateUniqueItemName(uint parentHierarchyId, string extension, string suggestedRoot, out string itemName) { FolderNode parentNode = this.GetNode(parentHierarchyId, true) as FolderNode; Tracer.Assert(parentNode != null, "The parent of the unique name better be a folder."); if (parentNode != null) { bool isFolder = (extension == null || extension.Length == 0); itemName = parentNode.GenerateUniqueName(suggestedRoot, extension, isFolder); } else { itemName = String.Empty; } return(NativeMethods.S_OK); }
/// <summary> /// Recursively writes all of the <File> nodes to the project file, one for each file /// in the parent node. /// </summary> /// <param name="writer">The file to which to write.</param> /// <param name="parent">The parent node to write.</param> protected void WriteFilesInNode(ProjectFileXmlWriter writer, FolderNode parent) { foreach (Node node in parent.Children) { if (node.IsFolder && !node.IsVirtual) { FolderNode folderNode = (FolderNode)node; // Recurse this.WriteFilesInNode(writer, folderNode); } else if (node.IsFile) { writer.WriteStartElement(ElementNames.File); writer.WriteAttributeString(AttributeNames.RelativePath, node.RelativePath); writer.WriteEndElement(); } } }
/// <summary> /// Searches our children for a node with the specified canonical name. /// </summary> /// <param name="canonicalName">The canonical name to search for.</param> /// <param name="recurse">Indicates whether to recursively search the children for the node.</param> /// <returns>The <see cref="Node"/> that was found or null if the node was not found.</returns> public Node FindByName(string canonicalName, bool recurse) { Node foundNode = null; if (PackageUtility.FileStringEquals(this.CanonicalName, canonicalName)) { foundNode = this; } else { // Do a linear search for the node. We do this for the following reasons: // 1) There usually aren't that many nodes in the project. Even if there are hundreds, a linear // search is almost neglible in this case. // 2) This isn't actually called that much. // 3) We could also have another hashtable lookup keyed by canonical name. However, this means // that we have to keep track of when the canonical names change. This was deemed more of // an overhead than it's worth. If we find the performance is bad we can always optimize // this later. foreach (Node node in this.Children) { FolderNode folderNode = node as FolderNode; if (PackageUtility.FileStringEquals(node.CanonicalName, canonicalName)) { foundNode = node; break; } else if (folderNode != null && recurse) { foundNode = folderNode.FindByName(canonicalName, true); if (foundNode != null) { break; } } } } return(foundNode); }
/// <summary> /// Called right after the value is removed from the collection. /// </summary> /// <param name="index">The index of the item removed from the collection.</param> /// <param name="value">The value just removed from the collection.</param> protected override void OnRemoveComplete(int index, object value) { Node node = (Node)value; // First remove the children of the node. FolderNode folderNode = node as FolderNode; if (folderNode != null) { folderNode.Children.Clear(); } // Remove the node from our lookup tables. this.idTable.Remove(node.HierarchyId); this.pathTable.Remove(node.AbsolutePath); // Set the node's parent to null. node.Parent = null; // This is useful information to trace, so we'll use the Hierarchy category and the Information level, // which will allow this to be traced by default. Tracer.WriteLineInformation(classType, "OnRemoveComplete", "Removed '{0}' from the hierarchy.", node); }
/// <summary> /// Adds a file to the hierarchy by copying it from its source path to the directory specified /// by the parent node. If the source file happens to reside in the same directory, then it /// is just added to the hierarchy. /// </summary> /// <param name="parentNode">The node that will contain the new file node.</param> /// <param name="sourcePath">The source path of the file to copy.</param> /// <param name="canceled">Indicates whether the user canceled the operation if there were prompts.</param> /// <returns>The <see cref="Node"/> of the file was copied and added to the hierarchy successfully; otherwise, null.</returns> public Node AddCopyOfFile(FolderNode parentNode, string sourcePath, out bool canceled) { Tracer.VerifyNonNullArgument(parentNode, "parentNode"); Tracer.VerifyStringArgument(sourcePath, "sourcePath"); string destinationPath = Path.Combine(parentNode.AbsoluteDirectory, Path.GetFileName(sourcePath)); Node addedNode = this.AddCopyOfFile(sourcePath, destinationPath, out canceled); return addedNode; }
/// <summary> /// Shows the standard Visual Studio "Add New File" or "Add Existing File" dialog. /// </summary> /// <param name="parent">The parent in which to add the file.</param> /// <param name="dialogType">Specifies whether the "Add New File" or "Add Existing File" dialog is shown.</param> public void ShowAddFileDialogBox(FolderNode parent, AddFileDialogType dialogType) { Tracer.VerifyNonNullArgument(parent, "parent"); // Get an instance of the dialog from Visual Studio. IVsAddProjectItemDlg dialog = (IVsAddProjectItemDlg)this.ServiceProvider.GetServiceOrThrow(typeof(SVsAddProjectItemDlg), typeof(IVsAddProjectItemDlg), classType, "ShowAddFileDialogBox"); uint flags = 0; switch (dialogType) { case AddFileDialogType.AddNew: flags = (uint)(__VSADDITEMFLAGS.VSADDITEM_AddNewItems | __VSADDITEMFLAGS.VSADDITEM_SuggestTemplateName); break; case AddFileDialogType.AddExisting: flags = (uint)(__VSADDITEMFLAGS.VSADDITEM_AddExistingItems | __VSADDITEMFLAGS.VSADDITEM_AllowMultiSelect | __VSADDITEMFLAGS.VSADDITEM_AllowStickyFilter); break; default: flags = 0; Tracer.Fail("Shouldn't be hitting here."); break; } IVsProject projectInterface = (IVsProject)this.AttachedProject; Guid projectTypeGuid = this.AttachedProject.ProjectTypeGuid; string browseLocation = parent.AbsoluteDirectory; string filter = this.NativeResources.GetString(ResId.IDS_OPENFILES_FILTER); int dontShowAgain; // TODO: Respect the sticky filter and "Don't show again" flag. int hr = dialog.AddProjectItemDlg(parent.HierarchyId, ref projectTypeGuid, projectInterface, flags, null, null, ref browseLocation, ref filter, out dontShowAgain); NativeMethods.ThrowOnFailure(hr, NativeMethods.OLE_E_PROMPTSAVECANCELLED); }
/// <summary> /// Does the actual work of changing the caption after all of the verifications have been done /// that it's Ok to move the file. /// </summary> /// <param name="newCaption">The new caption.</param> /// <param name="newPath">The new absolute path.</param> public override void MoveNodeOnCaptionChange(string newCaption, string newPath) { string oldPath = this.AbsolutePath; bool expandNewNode = this.Expanded; bool selected = this.Selected; // Make sure the environment says we can start the rename. if (!this.Hierarchy.AttachedProject.Tracker.CanRenameDirectory(oldPath, newPath)) { return; } // Create the new folder before moving any of the children. Getting the parent is tricky // because on recursion this.Parent will point to the same parent and not be remapped // to the new location yet. Therefore, we have to use this mechanism to get the right parent. FolderNode newParent = (this.Parent.NewNodeOnRename == null ? this.Parent : this.Parent.NewNodeOnRename); FolderNode newNode = this.Hierarchy.CreateAndAddFolder(newParent, newCaption); this.NewNodeOnRename = newNode; // Iterate through the children and change their hierarchy/file locations. // Do it on a cloned collection, however, since the collection will be // changing from underneath us. Note that the directory will automatically // be created when the first child is moved. ArrayList clone = new ArrayList(this.Children); foreach (Node child in clone) { string newChildPath = Path.Combine(newPath, child.Caption); child.MoveNodeOnCaptionChange(child.Caption, newChildPath); } // Move the rest of the files in the old directory to the new one. PackageUtility.XMove(oldPath, newPath); // Delete the old directory (it should be empty). DirectoryInfo oldDir = new DirectoryInfo(oldPath); if (oldDir.Exists && oldDir.GetFileSystemInfos().Length == 0) { oldDir.Delete(true); } // Remove us from the hierarchy. this.RemoveFromProject(); // Expand and select the new node if we were expanded. if (expandNewNode) { newNode.Expand(); } if (selected) { newNode.Select(); } // Tell the environment that we're done renaming the document. this.Hierarchy.AttachedProject.Tracker.OnDirectoryRenamed(oldPath, newPath); // Update the property browser. IVsUIShell vsUIShell = (IVsUIShell)this.Hierarchy.ServiceProvider.GetServiceOrThrow(typeof(SVsUIShell), typeof(IVsUIShell), classType, "MoveNodeOnCaptionChange"); vsUIShell.RefreshPropertyBrowser(0); }
//========================================================================================== // Constructors //========================================================================================== /// <summary> /// Initializes a new instance of the <see cref="FolderNodeProperties"/> class. /// </summary> public FolderNodeProperties(FolderNode node) : base(node) { }
int IVsProject3.AddItem(uint itemidLoc, VSADDITEMOPERATION dwAddItemOperation, string pszItemName, uint cFilesToOpen, string[] rgpszFilesToOpen, IntPtr hwndDlgOwner, VSADDRESULT[] pResult) { bool canceled = false; bool wereErrors = false; pResult[0] = VSADDRESULT.ADDRESULT_Failure; // Get the parent node to which it should be added. FolderNode parentNode = this.GetNode(itemidLoc, true) as FolderNode; if (parentNode == null) { string message = this.NativeResources.GetString(ResId.IDS_E_ADDITEMTOPROJECT, Path.GetFileName(this.FilePath)); Context.ShowErrorMessageBox(message); Tracer.Fail("The specified parent {0} is not a FolderNode so we can't add an item to it.", itemidLoc); return(NativeMethods.E_UNEXPECTED); } // Loop through the files that are to be added and add them, one by one. foreach (string sourcePath in rgpszFilesToOpen) { string destPath = null; Node addedNode = null; switch (dwAddItemOperation) { case VSADDITEMOPERATION.VSADDITEMOP_CLONEFILE: destPath = Path.Combine(parentNode.AbsoluteDirectory, pszItemName); addedNode = this.AddCopyOfFile(sourcePath, destPath, out canceled); break; case VSADDITEMOPERATION.VSADDITEMOP_OPENFILE: if (PackageUtility.IsRelative(this.RootDirectory, sourcePath)) { destPath = PackageUtility.MakeRelative(this.RootDirectory, sourcePath); addedNode = this.AddExistingFile(destPath, true); } else { destPath = Path.Combine(parentNode.AbsoluteDirectory, Path.GetFileName(sourcePath)); addedNode = this.AddCopyOfFile(sourcePath, destPath, out canceled); } break; case VSADDITEMOPERATION.VSADDITEMOP_LINKTOFILE: Tracer.Fail("NYI: VSADDITEMOPERATION.VSADDITEMOP_LINKTOFILE."); throw new NotImplementedException("Linking to files is not supported yet."); case VSADDITEMOPERATION.VSADDITEMOP_RUNWIZARD: Tracer.Fail("NYI: VSADDITEMOPERATION.VSADDITEMOP_RUNWIZARD."); throw new NotImplementedException("Running a wizard is not supported yet."); default: Tracer.Fail("Unknown VSADDDITEMOPERATION '{0}'", dwAddItemOperation); throw new ArgumentException(PackageUtility.SafeStringFormatInvariant("The dwAddItemOperation contains an unknown and unsupported value '{0}'.", dwAddItemOperation), "dwAddItemOperation"); } // There were errors if the node is still null at this point. wereErrors = (addedNode == null); } pResult[0] = (canceled ? VSADDRESULT.ADDRESULT_Cancel : (wereErrors ? VSADDRESULT.ADDRESULT_Failure : VSADDRESULT.ADDRESULT_Success)); return(NativeMethods.S_OK); }
/// <summary> /// Recursively copies all of the child file nodes from the source parent to the destination parent. Also copies the physical files. /// </summary> /// <param name="sourceParent">The node to copy from.</param> /// <param name="destinationProject">The project to place the copied nodes into.</param> /// <param name="destinationParent">The node to copy to.</param> private bool CopyNodeFiles(FolderNode sourceParent, Project destinationProject, FolderNode destinationParent) { bool canceled; foreach (Node sourceNode in sourceParent.Children) { if (sourceNode is FileNode) { string fileName = Path.GetFileName(sourceNode.AbsolutePath); string destinationPath = Path.Combine(destinationParent.AbsoluteDirectory, fileName); Node addedNode = destinationProject.AddCopyOfFile(sourceNode.AbsolutePath, destinationPath, out canceled); if (addedNode == null || canceled) { return(false); } } else if (sourceNode is FolderNode && !(sourceNode is ReferenceFolderNode)) { FolderNode destNode = destinationProject.CreateAndAddFolder(destinationParent, sourceNode.Caption); // Recure return(this.CopyNodeFiles((FolderNode)sourceNode, destinationProject, destNode)); } } return(true); }
/// <summary> /// Initializes a new instance of the <see cref="NodeCollection"/> class. /// </summary> /// <param name="parent">The collection's parent folder node.</param> /// <param name="comparer">A comparer to use for the collection.</param> protected NodeCollection(FolderNode parent, IComparer comparer) : base(comparer) { Tracer.VerifyNonNullArgument(parent, "parent"); this.parent = parent; }
//========================================================================================== // Constructors //========================================================================================== /// <summary> /// Initializes a new instance of the <see cref="NodeCollection"/> class. /// </summary> /// <param name="parent">The collection's parent folder node.</param> public NodeCollection(FolderNode parent) : this(parent, new FolderFirstComparer()) { }
/// <summary> /// Creates a new file system folder and adds it to the hierarchy. /// </summary> /// <param name="parentNode">The parent node on which to add the new folder node.</param> /// <param name="name">The name of the new folder, which must be unique.</param> /// <returns>The <see cref="FolderNode"/> that was added to the hierarchy.</returns> /// <remarks>Call <see cref="FolderNode.GenerateUniqueName"/> to find a unique name.</remarks> public FolderNode CreateAndAddFolder(FolderNode parentNode, string name) { Tracer.VerifyNonNullArgument(parentNode, "parentNode"); Tracer.VerifyStringArgument(name, "name"); string absolutePath = Path.Combine(parentNode.AbsoluteDirectory, name); // Create the physical directory if it doesn't already exist. if (!Directory.Exists(absolutePath)) { Directory.CreateDirectory(absolutePath); } // Add a new folder node to our hierarchy. FolderNode folderNode = new FolderNode(this, absolutePath); parentNode.Children.Add(folderNode); return folderNode; }
/// <summary> /// Ensures that the folder node is in the hierarchy and that it exists on disk. /// </summary> /// <param name="parentNode">The parent node that should contain the folder.</param> /// <param name="folderName">The name of the folder.</param> /// <returns>Either the existing folder node or the newly created folder node.</returns> public FolderNode EnsureFolder(FolderNode parentNode, string folderName) { Tracer.VerifyNonNullArgument(parentNode, "parentNode"); Tracer.VerifyStringArgument(folderName, "folderName"); // See if there is already a node in the hierarchy with this name. string folderPath = PackageUtility.CanonicalizeDirectoryPath(Path.Combine(parentNode.AbsoluteDirectory, folderName)); Node foundNode = this.GetNodeFromName(folderPath); FolderNode folderNode = foundNode as FolderNode; if (foundNode != null && folderNode == null) { Tracer.WriteLine(classType, "EnsureFolder", Tracer.Level.Warning, "There is already a non-folder node {0} in the hierarchy.", foundNode); } else if (foundNode == null || folderNode == null) { // We need to add the folder node to the hierarchy. folderNode = new FolderNode(this, folderPath); parentNode.Children.Add(folderNode); } // Make sure the folder exists. if (!Directory.Exists(folderPath)) { Tracer.WriteLineInformation(classType, "EnsureFolder", "Creating directory '{0}' in the file system.", folderPath); Directory.CreateDirectory(folderPath); } return folderNode; }
/// <summary> /// Recursively copies all of the child file nodes from the source parent to the destination parent. Also copies the physical files. /// </summary> /// <param name="sourceParent">The node to copy from.</param> /// <param name="destinationProject">The project to place the copied nodes into.</param> /// <param name="destinationParent">The node to copy to.</param> private bool CopyNodeFiles(FolderNode sourceParent, Project destinationProject, FolderNode destinationParent) { bool canceled; foreach (Node sourceNode in sourceParent.Children) { if (sourceNode is FileNode) { string fileName = Path.GetFileName(sourceNode.AbsolutePath); string destinationPath = Path.Combine(destinationParent.AbsoluteDirectory, fileName); Node addedNode = destinationProject.AddCopyOfFile(sourceNode.AbsolutePath, destinationPath, out canceled); if (addedNode == null || canceled) { return false; } } else if (sourceNode is FolderNode && !(sourceNode is ReferenceFolderNode)) { FolderNode destNode = destinationProject.CreateAndAddFolder(destinationParent, sourceNode.Caption); // Recure return this.CopyNodeFiles((FolderNode)sourceNode, destinationProject, destNode); } } return true; }
//========================================================================================== // Member Variables //========================================================================================== #endregion #region Constructors //========================================================================================== // Constructors //========================================================================================== /// <summary> /// Initializes a new instance of the <see cref="FolderNodeProperties"/> class. /// </summary> public FolderNodeProperties(FolderNode node) : base(node) { }