/// <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; IVsUIShell vsUIShell = (IVsUIShell)this.Hierarchy.ServiceProvider.GetServiceOrThrow(typeof(SVsUIShell), typeof(IVsUIShell), classType, "MoveNodeOnCaptionChange"); // Tell the environment to stop listening to file change events on the old file. using (FileChangeNotificationSuspender notificationSuspender = new FileChangeNotificationSuspender(oldPath)) { // Make sure the environment says we can start the rename. if (!this.Hierarchy.AttachedProject.Tracker.CanRenameProject(oldPath, newPath)) { // If the user chose to not check out the solution file, then we want to throw the // save cancelled HRESULT. throw new COMException("User cancelled the solution file check out.", NativeMethods.OLE_E_PROMPTSAVECANCELLED); } // Move the file on the file system to match the new name. if (File.Exists(oldPath) && !File.Exists(newPath)) { Tracer.WriteLineInformation(classType, "MoveNodeOnCaptionChange", "Renaming the project file '{0}' to '{1}'.", oldPath, newPath); File.Move(oldPath, newPath); } // Tell the environment that we're done renaming the document. this.Hierarchy.AttachedProject.Tracker.OnProjectRenamed(oldPath, newPath); // Update the property browser. vsUIShell.RefreshPropertyBrowser(0); } }
/// <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 oldCaption = this.Caption; string oldPath = this.AbsolutePath; string oldRelativePath = this.RelativePath; bool updatedWindowCaptions = false; bool removedNode = false; Node newNode = null; // If we are currently selected, cache the value so we can restore the selection // after the addition. bool wasSelected = this.Selected; // Tell the environment to stop listening to file change events on the old file. using (FileChangeNotificationSuspender notificationSuspender = new FileChangeNotificationSuspender(oldPath)) { // Make sure the environment says we can start the rename. if (!this.Hierarchy.AttachedProject.Tracker.CanRenameFile(oldPath, newPath)) { return; } // Move the file on the file system to match the new name. if (!this.IsVirtual && File.Exists(oldPath) && !File.Exists(newPath)) { Tracer.WriteLineInformation(classType, "MoveNodeOnCaptionChange", "Renaming the file '{0}' to '{1}'.", oldPath, newPath); string newDirectoryName = Path.GetDirectoryName(newPath); if (!Directory.Exists(newDirectoryName)) { Directory.CreateDirectory(newDirectoryName); } File.Move(oldPath, newPath); } try { // Update all of the windows that currently have this file opened in an editor. this.UpdateOpenWindowCaptions(newCaption); updatedWindowCaptions = true; // We have to remove the node and re-add it so that we can have the sorting preserved. // Also, if the extension has changed then we'll have to recreate a new type-specific // FileNode. The easy way is to remove ourself from the project then tell the project // to add an existing file. string newRelativePath = PackageUtility.MakeRelative(this.Hierarchy.RootNode.AbsoluteDirectory, newPath); // Remove ourself from the hierarchy. this.Parent.Children.Remove(this); removedNode = true; // We have now been removed from the hierarchy. Do NOT call any virtual methods or // methods that depend on our state after this point. // Re-add ourself as a new incarnation (different object). Our life ends here. newNode = this.Hierarchy.AddExistingFile(newRelativePath, true); if (newNode != null) { // We need to set our hierarchy Id to match the new hierachy Id in case Visual Studio // calls back into us for something. this.SetHierarchyId(newNode.HierarchyId); // Select the new node if we were previously selected. if (wasSelected) { newNode.Select(); } // Tell the RDT to rename the document. Context.RunningDocumentTable.RenameDocument(oldPath, newPath, newNode.HierarchyId); } } catch (Exception e) { if (ErrorUtility.IsExceptionUnrecoverable(e)) { throw; } // Rollback the file move Tracer.WriteLineWarning(classType, "MoveNodeOnCaptionChange", "There was an error in renaming the document. Exception: {0}", e); File.Move(newPath, oldPath); // Remove the node that we just added. if (newNode != null) { newNode.RemoveFromProject(); } // Re-add a new node since we've already removed the old node. if (removedNode || this.Parent == null) { newNode = this.Hierarchy.AddExistingFile(oldRelativePath, true); this.SetHierarchyId(newNode.HierarchyId); if (wasSelected) { newNode.Select(); } } // Rollback the caption update on open windows if (updatedWindowCaptions) { this.UpdateOpenWindowCaptions(oldCaption); } // Rethrow the exception throw; } // Tell the environment that we're done renaming the document. this.Hierarchy.AttachedProject.Tracker.OnFileRenamed(oldPath, newPath); // Update the property browser. IVsUIShell vsUIShell = (IVsUIShell)this.Hierarchy.ServiceProvider.GetServiceOrThrow(typeof(SVsUIShell), typeof(IVsUIShell), classType, "MoveNodeOnCaptionChange"); vsUIShell.RefreshPropertyBrowser(0); } }