in Common/Product/SharedProject/ProjectNode.cs [4167:4495]
internal int AddItemWithSpecificInternal(uint itemIdLoc, VSADDITEMOPERATION op, string itemName, uint filesToOpen, string[] files, IntPtr dlgOwner, uint editorFlags, ref Guid editorType, string physicalView, ref Guid logicalView, VSADDRESULT[] result, bool? promptOverwrite = null) {
if (files == null || result == null || files.Length == 0 || result.Length == 0) {
return VSConstants.E_INVALIDARG;
}
// Locate the node to be the container node for the file(s) being added
// only projectnode or foldernode and file nodes are valid container nodes
// We need to locate the parent since the item wizard expects the parent to be passed.
HierarchyNode n = this.NodeFromItemId(itemIdLoc);
if (n == null) {
return VSConstants.E_INVALIDARG;
}
while (!n.CanAddFiles && (!this.CanFileNodesHaveChilds || !(n is FileNode))) {
n = n.Parent;
}
Debug.Assert(n != null, "We should at this point have either a ProjectNode or FolderNode or a FileNode as a container for the new filenodes");
// handle link and runwizard operations at this point
bool isLink = false;
switch (op) {
case VSADDITEMOPERATION.VSADDITEMOP_LINKTOFILE:
// we do not support this right now
isLink = true;
break;
case VSADDITEMOPERATION.VSADDITEMOP_RUNWIZARD:
result[0] = this.RunWizard(n, itemName, files[0], dlgOwner);
return VSConstants.S_OK;
}
string[] actualFiles = new string[files.Length];
VSQUERYADDFILEFLAGS[] flags = this.GetQueryAddFileFlags(files);
string baseDir = this.GetBaseDirectoryForAddingFiles(n);
// If we did not get a directory for node that is the parent of the item then fail.
if (String.IsNullOrEmpty(baseDir)) {
return VSConstants.E_FAIL;
}
// Pre-calculates some paths that we can use when calling CanAddItems
List<string> filesToAdd = new List<string>();
foreach (var file in files) {
string fileName;
string newFileName = String.Empty;
switch (op) {
case VSADDITEMOPERATION.VSADDITEMOP_CLONEFILE:
fileName = Path.GetFileName(itemName ?? file);
newFileName = CommonUtils.GetAbsoluteFilePath(baseDir, fileName);
break;
case VSADDITEMOPERATION.VSADDITEMOP_LINKTOFILE:
case VSADDITEMOPERATION.VSADDITEMOP_OPENFILE:
fileName = Path.GetFileName(file);
newFileName = CommonUtils.GetAbsoluteFilePath(baseDir, fileName);
if (isLink && CommonUtils.IsSubpathOf(ProjectHome, file)) {
// creating a link to a file that's actually in the project, it's not really a link.
isLink = false;
// If the file is not going to be added in its
// current path (GetDirectoryName(file) != baseDir),
// we need to update the filename and also update
// the destination node (n). Otherwise, we don't
// want to change the destination node (previous
// behavior) - just trust that our caller knows
// what they are doing. (Web Essentials relies on
// this.)
if (!CommonUtils.IsSameDirectory(baseDir, Path.GetDirectoryName(file))) {
newFileName = file;
n = this.CreateFolderNodes(Path.GetDirectoryName(file));
}
}
break;
}
filesToAdd.Add(newFileName);
}
// Ask tracker objects if we can add files
if (!this.tracker.CanAddItems(filesToAdd.ToArray(), flags)) {
// We were not allowed to add the files
return VSConstants.E_FAIL;
}
if (!this.QueryEditProjectFile(false)) {
throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED);
}
// Add the files to the hierarchy
int actualFilesAddedIndex = 0;
var itemsToInvalidate = new List<HierarchyNode>();
for (int index = 0; index < filesToAdd.Count; index++) {
HierarchyNode child;
bool overwrite = false;
MsBuildProjectElement linkedFile = null;
string newFileName = filesToAdd[index];
string file = files[index];
result[0] = VSADDRESULT.ADDRESULT_Failure;
child = this.FindNodeByFullPath(newFileName);
if (child != null) {
// If the file to be added is an existing file part of the hierarchy then continue.
if (CommonUtils.IsSamePath(file, newFileName)) {
if (child.IsNonMemberItem) {
for (var node = child; node != null; node = node.Parent) {
itemsToInvalidate.Add(node);
// We want to include the first member item, so
// this test is not part of the loop condition.
if (!node.IsNonMemberItem) {
break;
}
}
// https://pytools.codeplex.com/workitem/1251
ErrorHandler.ThrowOnFailure(child.IncludeInProjectWithRefresh(false));
}
result[0] = VSADDRESULT.ADDRESULT_Cancel;
continue;
} else if (isLink) {
string message = "There is already a file of the same name in this folder.";
string title = string.Empty;
OLEMSGICON icon = OLEMSGICON.OLEMSGICON_QUERY;
OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK;
OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST;
VsShellUtilities.ShowMessageBox(this.Site, title, message, icon, buttons, defaultButton);
result[0] = VSADDRESULT.ADDRESULT_Cancel;
return (int)OleConstants.OLECMDERR_E_CANCELED;
} else {
int canOverWriteExistingItem = CanOverwriteExistingItem(file, newFileName, !child.IsNonMemberItem);
if (canOverWriteExistingItem == E_CANCEL_FILE_ADD) {
result[0] = VSADDRESULT.ADDRESULT_Cancel;
return (int)OleConstants.OLECMDERR_E_CANCELED;
} else if (canOverWriteExistingItem == (int)OleConstants.OLECMDERR_E_CANCELED) {
result[0] = VSADDRESULT.ADDRESULT_Cancel;
return canOverWriteExistingItem;
} else if (canOverWriteExistingItem == VSConstants.S_OK) {
overwrite = true;
} else {
return canOverWriteExistingItem;
}
}
} else {
if (isLink) {
child = this.FindNodeByFullPath(file);
if (child != null) {
string message = String.Format("There is already a link to '{0}'. A project cannot have more than one link to the same file.", file);
string title = string.Empty;
OLEMSGICON icon = OLEMSGICON.OLEMSGICON_QUERY;
OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK;
OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST;
VsShellUtilities.ShowMessageBox(this.Site, title, message, icon, buttons, defaultButton);
result[0] = VSADDRESULT.ADDRESULT_Cancel;
return (int)OleConstants.OLECMDERR_E_CANCELED;
}
}
if (newFileName.Length >= NativeMethods.MAX_PATH) {
OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL;
OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK;
OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST;
VsShellUtilities.ShowMessageBox(Site, FolderNode.PathTooLongMessage, null, icon, buttons, defaultButton);
result[0] = VSADDRESULT.ADDRESULT_Cancel;
return (int)OleConstants.OLECMDERR_E_CANCELED;
}
// we need to figure out where this file would be added and make sure there's
// not an existing link node at the same location
string filename = Path.GetFileName(newFileName);
var folder = this.FindNodeByFullPath(Path.GetDirectoryName(newFileName));
if (folder != null) {
if (folder.FindImmediateChildByName(filename) != null) {
string message = "There is already a file of the same name in this folder.";
string title = string.Empty;
OLEMSGICON icon = OLEMSGICON.OLEMSGICON_QUERY;
OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK;
OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST;
VsShellUtilities.ShowMessageBox(this.Site, title, message, icon, buttons, defaultButton);
result[0] = VSADDRESULT.ADDRESULT_Cancel;
return (int)OleConstants.OLECMDERR_E_CANCELED;
}
}
}
// If the file to be added is not in the same path copy it.
if (!CommonUtils.IsSamePath(file, newFileName) || Directory.Exists(newFileName)) {
if (!overwrite && File.Exists(newFileName)) {
var existingChild = this.FindNodeByFullPath(file);
if (existingChild == null || !existingChild.IsLinkFile) {
string message = SR.GetString(SR.FileAlreadyExists, newFileName);
string title = string.Empty;
OLEMSGICON icon = OLEMSGICON.OLEMSGICON_QUERY;
OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_YESNO;
OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST;
if (isLink) {
message = "There is already a file of the same name in this folder.";
buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK;
}
int messageboxResult = VsShellUtilities.ShowMessageBox(this.Site, title, message, icon, buttons, defaultButton);
if (messageboxResult != NativeMethods.IDYES) {
result[0] = VSADDRESULT.ADDRESULT_Cancel;
return (int)OleConstants.OLECMDERR_E_CANCELED;
}
}
}
var updatingNode = this.FindNodeByFullPath(file);
if (updatingNode != null && updatingNode.IsLinkFile) {
// we just need to update the link to the new path.
linkedFile = updatingNode.ItemNode as MsBuildProjectElement;
} else if (Directory.Exists(file)) {
// http://pytools.codeplex.com/workitem/546
int hr = AddDirectory(result, n, file, promptOverwrite);
if (ErrorHandler.Failed(hr)) {
return hr;
}
result[0] = VSADDRESULT.ADDRESULT_Success;
continue;
} else if (!isLink) {
// Copy the file to the correct location.
// We will suppress the file change events to be triggered to this item, since we are going to copy over the existing file and thus we will trigger a file change event.
// We do not want the filechange event to ocur in this case, similar that we do not want a file change event to occur when saving a file.
IVsFileChangeEx fileChange = this.site.GetService(typeof(SVsFileChangeEx)) as IVsFileChangeEx;
Utilities.CheckNotNull(fileChange);
try {
ErrorHandler.ThrowOnFailure(fileChange.IgnoreFile(VSConstants.VSCOOKIE_NIL, newFileName, 1));
if (op == VSADDITEMOPERATION.VSADDITEMOP_CLONEFILE) {
this.AddFileFromTemplate(file, newFileName);
} else {
PackageUtilities.CopyUrlToLocal(new Uri(file), newFileName);
// Reset RO attribute on file if present - for example, if source file was under TFS control and not checked out.
try {
var fileInfo = new FileInfo(newFileName);
if (fileInfo.Attributes.HasFlag(FileAttributes.ReadOnly)) {
fileInfo.Attributes &= ~FileAttributes.ReadOnly;
}
} catch (Exception ex) {
// Best-effort, but no big deal if this fails.
if (ex.IsCriticalException()) {
throw;
}
}
}
} finally {
ErrorHandler.ThrowOnFailure(fileChange.IgnoreFile(VSConstants.VSCOOKIE_NIL, newFileName, 0));
}
}
}
if (overwrite) {
if (child.IsNonMemberItem) {
ErrorHandler.ThrowOnFailure(child.IncludeInProjectWithRefresh(false));
}
} else if (linkedFile != null || isLink) {
// files not moving, add the old name, and set the link.
var friendlyPath = CommonUtils.GetRelativeFilePath(ProjectHome, file);
FileNode newChild;
if (linkedFile == null) {
Debug.Assert(!CommonUtils.IsSubpathOf(ProjectHome, file), "Should have cleared isLink above for file in project dir");
newChild = CreateFileNode(file);
} else {
newChild = CreateFileNode(linkedFile);
}
newChild.SetIsLinkFile(true);
newChild.ItemNode.SetMetadata(ProjectFileConstants.Link, CommonUtils.CreateFriendlyFilePath(ProjectHome, newFileName));
n.AddChild(newChild);
DocumentManager.RenameDocument(site, file, file, n.ID);
LinkFileAdded(file);
} else {
//Add new filenode/dependentfilenode
this.AddNewFileNodeToHierarchy(n, newFileName);
}
result[0] = VSADDRESULT.ADDRESULT_Success;
actualFiles[actualFilesAddedIndex++] = newFileName;
}
// Notify listeners that items were appended.
if (actualFilesAddedIndex > 0)
OnItemsAppended(n);
foreach (var node in itemsToInvalidate.Where(node => node != null).Reverse()) {
OnInvalidateItems(node);
}
//Open files if this was requested through the editorFlags
bool openFiles = (editorFlags & (uint)__VSSPECIFICEDITORFLAGS.VSSPECIFICEDITOR_DoOpen) != 0;
if (openFiles && actualFiles.Length <= filesToOpen) {
for (int i = 0; i < filesToOpen; i++) {
if (!String.IsNullOrEmpty(actualFiles[i])) {
string name = actualFiles[i];
HierarchyNode child = this.FindNodeByFullPath(name);
Debug.Assert(child != null, "We should have been able to find the new element in the hierarchy");
if (child != null) {
IVsWindowFrame frame;
if (editorType == Guid.Empty) {
Guid view = child.DefaultOpensWithDesignView ? VSConstants.LOGVIEWID.Designer_guid : Guid.Empty;
ErrorHandler.ThrowOnFailure(this.OpenItem(child.ID, ref view, IntPtr.Zero, out frame));
} else {
ErrorHandler.ThrowOnFailure(this.OpenItemWithSpecific(child.ID, editorFlags, ref editorType, physicalView, ref logicalView, IntPtr.Zero, out frame));
}
// Show the window frame in the UI and make it the active window
if (frame != null) {
ErrorHandler.ThrowOnFailure(frame.Show());
}
}
}
}
}
return VSConstants.S_OK;
}