/// <summary>
/// Adds files to a project, potentially asking the user whether to move, copy or link the files.
/// </summary>
public IList<ProjectFile> AddFilesToProject (Project project, FilePath[] files, FilePath[] targetPaths,
string buildAction)
{
Debug.Assert (project != null);
Debug.Assert (files != null);
Debug.Assert (targetPaths != null);
Debug.Assert (files.Length == targetPaths.Length);
AddAction action = AddAction.Copy;
bool applyToAll = true;
bool dialogShown = false;
IProgressMonitor monitor = null;
if (files.Length > 10) {
monitor = new MessageDialogProgressMonitor (true);
monitor.BeginTask (GettextCatalog.GetString("Adding files..."), files.Length);
}
var newFileList = new List<ProjectFile> ();
//project.AddFile (string) does linear search for duplicate file, so instead we use this HashSet and
//and add the ProjectFiles directly. With large project and many files, this should really help perf.
//Also, this is a better check because we handle vpaths and links.
//FIXME: it would be really nice if project.Files maintained these hashmaps
var vpathsInProject = new HashSet<FilePath> (project.Files.Select (pf => pf.ProjectVirtualPath));
var filesInProject = new Dictionary<FilePath,ProjectFile> ();
foreach (var pf in project.Files)
filesInProject [pf.FilePath] = pf;
using (monitor)
{
for (int i = 0; i < files.Length; i++) {
FilePath file = files[i];
if (monitor != null) {
monitor.Log.WriteLine (file);
monitor.Step (1);
}
if (FileService.IsDirectory (file)) {
//FIXME: warning about skipping?
newFileList.Add (null);
continue;
}
FilePath targetPath = targetPaths[i].CanonicalPath;
Debug.Assert (targetPath.IsChildPathOf (project.BaseDirectory));
var vpath = targetPath.ToRelative (project.BaseDirectory);
if (vpathsInProject.Contains (vpath)) {
if (project.Files.GetFileWithVirtualPath (vpath).FilePath != file)
MessageService.ShowWarning (GettextCatalog.GetString (
"There is a already a file or link in the project with the name '{0}'", vpath));
continue;
}
string fileBuildAction = buildAction;
if (string.IsNullOrEmpty (buildAction))
fileBuildAction = project.GetDefaultBuildAction (targetPath);
//files in the target directory get added directly in their current location without moving/copying
if (file.CanonicalPath == targetPath) {
AddFileToFolder (newFileList, vpathsInProject, filesInProject, file, fileBuildAction);
continue;
}
//for files outside the project directory, we ask the user whether to move, copy or link
AddExternalFileDialog addExternalDialog = null;
if (!dialogShown || !applyToAll) {
addExternalDialog = new AddExternalFileDialog (file);
if (files.Length > 1) {
addExternalDialog.ApplyToAll = applyToAll;
addExternalDialog.ShowApplyAll = true;
}
if (file.IsChildPathOf (targetPath.ParentDirectory))
addExternalDialog.ShowKeepOption (file.ParentDirectory.ToRelative (targetPath.ParentDirectory));
else {
if (action == AddAction.Keep)
action = AddAction.Copy;
addExternalDialog.SelectedAction = action;
}
}
try {
if (!dialogShown || !applyToAll) {
if (MessageService.RunCustomDialog (addExternalDialog) == (int) Gtk.ResponseType.Cancel) {
project.Files.AddRange (newFileList.Where (f => f != null));
return newFileList;
}
action = addExternalDialog.SelectedAction;
applyToAll = addExternalDialog.ApplyToAll;
dialogShown = true;
}
if (action == AddAction.Keep) {
AddFileToFolder (newFileList, vpathsInProject, filesInProject, file, fileBuildAction);
continue;
}
if (action == AddAction.Link) {
ProjectFile pf = new ProjectFile (file, fileBuildAction) {
Link = vpath
};
vpathsInProject.Add (pf.ProjectVirtualPath);
filesInProject [pf.FilePath] = pf;
newFileList.Add (pf);
continue;
}
try {
if (!Directory.Exists (targetPath.ParentDirectory))
FileService.CreateDirectory (targetPath.ParentDirectory);
if (MoveCopyFile (file, targetPath, action == AddAction.Move)) {
var pf = new ProjectFile (targetPath, fileBuildAction);
vpathsInProject.Add (pf.ProjectVirtualPath);
filesInProject [pf.FilePath] = pf;
newFileList.Add (pf);
}
else {
newFileList.Add (null);
}
}
catch (Exception ex) {
MessageService.ShowException (ex, GettextCatalog.GetString (
"An error occurred while attempt to move/copy that file. Please check your permissions."));
newFileList.Add (null);
}
} finally {
if (addExternalDialog != null)
addExternalDialog.Destroy ();
}
}
}
project.Files.AddRange (newFileList.Where (f => f != null));
return newFileList;
}