in vsintegration/src/FSharp.ProjectSystem.Base/Project/ProjectNode.cs [4846:5113]
internal int AddItemWithSpecific(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 bTrackChanges, Func<uint> getIdOfExisingItem = null, AddItemContext context = AddItemContext.Unknown)
{
if (files == null || result == null || files.Length == 0 || result.Length == 0)
{
return VSConstants.E_INVALIDARG;
}
if (getIdOfExisingItem != null && files.Length != 1)
{
// only 1 file can participate in renaming
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 is ProjectNode) && !(n is FolderNode))
{
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
switch (op)
{
case VSADDITEMOPERATION.VSADDITEMOP_LINKTOFILE:
return AddLinkedItem(n, files, result);
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>();
for (int index = 0; index < files.Length; index++)
{
string newFileName = String.Empty;
string file = files[index];
switch (op)
{
case VSADDITEMOPERATION.VSADDITEMOP_CLONEFILE:
// New item added. Need to copy template to new location and then add new location
newFileName = Path.Combine(baseDir, itemName);
break;
case VSADDITEMOPERATION.VSADDITEMOP_OPENFILE:
{
string fileName = Path.GetFileName(file);
if (context == AddItemContext.Paste && FindChild(file) != null)
{
newFileName = Path.Combine(baseDir, fileName);
if (FindChild(newFileName) != null)
{
// if we are doing 'Paste' and source file belongs to current project - generate fresh unique name
newFileName = GenerateCopyOfFileName(baseDir, fileName);
}
}
else if (!IsContainedWithinProjectDirectory(file))
{
// if the file isn't contained within the project directory,
// copy it to be a child of the node we're adding to.
newFileName = Path.Combine(baseDir, fileName);
}
else
{
newFileName = 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.ProjectMgr.QueryEditProjectFile(false))
{
throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED);
}
// Add the files to the hierarchy
int actualFilesAddedIndex = 0;
for (int index = 0; index < filesToAdd.Count; index++)
{
HierarchyNode child;
bool overwrite = false;
string newFileName = filesToAdd[index];
string file = files[index];
result[0] = VSADDRESULT.ADDRESULT_Failure;
child = this.FindChild(newFileName);
if (child != null)
{
// If the file to be added is an existing file part of the hierarchy then continue.
if (NativeMethods.IsSamePath(file, newFileName))
{
result[0] = VSADDRESULT.ADDRESULT_Cancel;
continue;
}
int canOverWriteExistingItem = this.CanOverwriteExistingItem(file, newFileName);
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;
}
}
// If the file to be added is not in the same path copy it.
if (NativeMethods.IsSamePath(file, newFileName) == false)
{
if (!overwrite && FSLib.Shim.FileSystem.SafeExists(newFileName))
{
string message = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.FileAlreadyExists, CultureInfo.CurrentUICulture), newFileName);
string title = string.Empty;
OLEMSGICON icon = OLEMSGICON.OLEMSGICON_QUERY;
OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_YESNO;
OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST;
int messageboxResult = VsShellUtilities.ShowMessageBox(this.Site, title, message, icon, buttons, defaultButton);
if (messageboxResult == NativeMethods.IDNO)
{
result[0] = VSADDRESULT.ADDRESULT_Cancel;
return (int)OleConstants.OLECMDERR_E_CANCELED;
}
}
// 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;
if (fileChange == null)
{
throw new InvalidOperationException();
}
try
{
fileChange.IgnoreFile(VSConstants.VSCOOKIE_NIL, newFileName, 1);
if (op == VSADDITEMOPERATION.VSADDITEMOP_CLONEFILE)
{
this.AddFileFromTemplate(file, newFileName);
}
else
{
PackageUtilities.CopyUrlToLocal(new Uri(file), newFileName);
}
}
finally
{
fileChange.IgnoreFile(VSConstants.VSCOOKIE_NIL, newFileName, 0);
}
}
if (overwrite)
{
this.OverwriteExistingItem(child);
}
else
{
if (getIdOfExisingItem != null)
{
// this is rename operation
//Add new filenode/dependentfilenode
var exisingId = getIdOfExisingItem();
this.AddNewFileNodeToHierarchyCore(n, newFileName, exisingId);
}
else
{
// ordinary add
//Add new filenode/dependentfilenode
this.AddNewFileNodeToHierarchyCore(n, newFileName);
}
if (bTrackChanges)
{
FireAddNodeEvent(newFileName);
}
}
result[0] = VSADDRESULT.ADDRESULT_Success;
actualFiles[actualFilesAddedIndex++] = newFileName;
}
// Notify listeners that items were appended.
if (actualFilesAddedIndex > 0)
n.OnItemsAppended(n);
//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.FindChild(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 = 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());
}
}
}
}
}
for (int i = 0; i < actualFilesAddedIndex; ++i)
{
string absolutePath = actualFiles[i];
var fileNode = this.FindChild(absolutePath) as FileNode;
Debug.Assert(fileNode != null, $"Unable to find added child node {absolutePath}");
MoveFileToBottomIfNoOtherPendingMove(fileNode);
}
return VSConstants.S_OK;
}