public static string CreateNewProject(string projName, string projFolder, ProjectTemplateInfo template) { // Determine the current executing directory, in addition to Environment.CurrentDirectory Assembly execAssembly = Assembly.GetEntryAssembly() ?? typeof(DualityEditorApp).Assembly; string execDirectory = Path.GetFullPath(Path.GetDirectoryName(execAssembly.Location)); // Create project folder projFolder = Path.Combine(projFolder, projName); if (!Directory.Exists(projFolder)) { Directory.CreateDirectory(projFolder); } // Extract template if (template.SpecialTag == ProjectTemplateInfo.SpecialInfo.None) { template.ExtractTo(projFolder); // Update main directory foreach (string srcFile in Directory.GetFiles(Environment.CurrentDirectory, "*", SearchOption.TopDirectoryOnly)) { if (Path.GetFileName(srcFile) == "appdata.dat") { continue; } if (Path.GetFileName(srcFile) == "defaultuserdata.dat") { continue; } string dstFile = Path.Combine(projFolder, Path.GetFileName(srcFile)); File.Copy(srcFile, dstFile, true); } // Update plugin directory foreach (string dstFile in Directory.GetFiles(Path.Combine(projFolder, DualityApp.PluginDirectory), "*", SearchOption.AllDirectories)) { string srcFileWorking = Path.Combine(DualityApp.PluginDirectory, Path.GetFileName(dstFile)); string srcFileExec = Path.Combine(PathHelper.ExecutingAssemblyDir, DualityApp.PluginDirectory, Path.GetFileName(dstFile)); if (File.Exists(srcFileWorking)) { File.Copy(srcFileWorking, dstFile, true); } else if (File.Exists(srcFileExec)) { File.Copy(srcFileExec, dstFile, true); } } } else if (template.SpecialTag == ProjectTemplateInfo.SpecialInfo.Current) { DualityEditorApp.SaveAllProjectData(); Predicate <string> copyPredicate = delegate(string path) { bool isDir = Directory.Exists(path); string fullPath = Path.GetFullPath(path); if (isDir) { return (!PathOp.ArePathsEqual(fullPath, EditorHelper.BackupDirectory) && !PathOp.ArePathsEqual(fullPath, Path.Combine(execDirectory, EditorHelper.BackupDirectory))); } else { return(true); } }; if (!PathOp.ArePathsEqual(execDirectory, Environment.CurrentDirectory)) { PathHelper.CopyDirectory(execDirectory, projFolder, true, copyPredicate); } PathHelper.CopyDirectory(Environment.CurrentDirectory, projFolder, true, copyPredicate); } else { Predicate <string> copyPredicate = delegate(string path) { bool isDir = Directory.Exists(path); string fullPath = Path.GetFullPath(path); if (isDir) { return (!PathOp.ArePathsEqual(fullPath, DualityApp.DataDirectory) && !PathOp.ArePathsEqual(fullPath, EditorHelper.SourceDirectory) && !PathOp.ArePathsEqual(fullPath, EditorHelper.BackupDirectory) && !PathOp.ArePathsEqual(fullPath, Path.Combine(execDirectory, DualityApp.DataDirectory)) && !PathOp.ArePathsEqual(fullPath, Path.Combine(execDirectory, EditorHelper.SourceDirectory)) && !PathOp.ArePathsEqual(fullPath, Path.Combine(execDirectory, EditorHelper.BackupDirectory))); } else { string fileName = Path.GetFileName(fullPath); return(fileName != "appdata.dat" && fileName != "defaultuserdata.dat" && fileName != "designtimedata.dat"); } }; if (!PathOp.ArePathsEqual(execDirectory, Environment.CurrentDirectory)) { PathHelper.CopyDirectory(execDirectory, projFolder, true, copyPredicate); } PathHelper.CopyDirectory(Environment.CurrentDirectory, projFolder, true, copyPredicate); } // Adjust current directory and perform init operations in the new project folder string oldPath = Environment.CurrentDirectory; Environment.CurrentDirectory = projFolder; try { // Initialize AppData DualityAppData data; data = Serializer.TryReadObject <DualityAppData>(DualityApp.AppDataPath) ?? new DualityAppData(); data.AppName = projName; data.AuthorName = Environment.UserName; data.Version = 0; Serializer.WriteObject(data, DualityApp.AppDataPath, typeof(XmlSerializer)); // Read content source code data (needed to rename classes / namespaces) string oldRootNamespaceNameCore; string newRootNamespaceNameCore; DualityEditorApp.ReadPluginSourceCodeContentData(out oldRootNamespaceNameCore, out newRootNamespaceNameCore); // Initialize source code DualityEditorApp.InitPluginSourceCode(); // Force re-init to update namespaces, etc. DualityEditorApp.UpdatePluginSourceCode(); // Add SerializeErrorHandler class to handle renamed Types if (Directory.Exists(DualityApp.DataDirectory)) { // Add error handler source file to project XDocument coreProject = XDocument.Load(SourceCodeProjectCorePluginFile); string relErrorHandlerPath = PathHelper.MakeFilePathRelative( SourceCodeErrorHandlerFile, Path.GetDirectoryName(SourceCodeProjectCorePluginFile)); if (!coreProject.Descendants("Compile", true).Any(c => string.Equals(c.GetAttributeValue("Include"), relErrorHandlerPath))) { XElement compileElement = coreProject.Descendants("Compile", true).FirstOrDefault(); XElement newCompileElement = new XElement( XName.Get("Compile", compileElement.Name.NamespaceName), new XAttribute("Include", relErrorHandlerPath)); compileElement.AddAfterSelf(newCompileElement); } coreProject.Save(SourceCodeProjectCorePluginFile); // Generate and save error handler source code File.WriteAllText( EditorHelper.SourceCodeErrorHandlerFile, EditorHelper.GenerateErrorHandlersSrcFile(oldRootNamespaceNameCore, newRootNamespaceNameCore)); } // Compile plugins BuildHelper.BuildSolutionFile(EditorHelper.SourceCodeSolutionFilePath, "Release"); } finally { Environment.CurrentDirectory = oldPath; } return(Path.Combine(projFolder, "DualityEditor.exe")); }
private static void GenerateGameResSrcFile_ScanDir(StringBuilder builder, string dirPath, int indent, out string className) { if (!PathHelper.IsPathVisible(dirPath)) { className = null; return; } dirPath = dirPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); string indentStr = new string('\t', indent); className = GenerateGameResSrcFile_ClassName(dirPath); // ---------- Begin class ---------- builder.Append(indentStr); builder.Append("public static class "); builder.Append(className); builder.AppendLine(" {"); // ---------- Sub directories ---------- string[] subDirs = Directory.GetDirectories(dirPath); List <string> dirClassNames = new List <string>(); foreach (string dir in subDirs) { string dirClassName; GenerateGameResSrcFile_ScanDir(builder, dir, indent + 1, out dirClassName); if (!string.IsNullOrEmpty(dirClassName)) { dirClassNames.Add(dirClassName); } } // ---------- Files ---------- string[] files = Directory.GetFiles(dirPath); List <string> filePropNames = new List <string>(); foreach (string file in files) { string propName; GenerateGameResSrcFile_ScanFile(builder, file, indent + 1, out propName); if (!string.IsNullOrEmpty(propName)) { filePropNames.Add(propName); } } // ---------- LoadAll() method ---------- builder.Append(indentStr); builder.Append('\t'); builder.AppendLine("public static void LoadAll() {"); foreach (string dirClassName in dirClassNames) { builder.Append(indentStr); builder.Append('\t'); builder.Append('\t'); builder.Append(dirClassName); builder.AppendLine(".LoadAll();"); } foreach (string propName in filePropNames) { builder.Append(indentStr); builder.Append('\t'); builder.Append('\t'); builder.Append(propName); builder.AppendLine(".MakeAvailable();"); } builder.Append(indentStr); builder.Append('\t'); builder.AppendLine("}"); // ---------- End class ---------- builder.Append(indentStr); builder.AppendLine("}"); }
private static void HandleDataDirEvents(FileEventQueue eventQueue) { // Gather a list of all externally modified resources that are currently loaded. // We'll use this to later tell the editor about the changes we've applied. List <Resource> modifiedLoadedResources = null; foreach (FileEvent fileEvent in eventQueue.Items) { if (fileEvent.IsDirectory) { continue; } // We're only interested in the path where we would find the Resource right now, // because we need the instance for the change notification string contentPath = (fileEvent.Type == FileEventType.Renamed) ? fileEvent.OldPath : fileEvent.Path; ContentRef <Resource> content = new ContentRef <Resource>(contentPath); // Some editor modules rely on change notifications to re-establish links to previously // removed Resources. In order to do that, we'll speculatively load newly arrived Resources // so we can put out a change notification for them. // Note: If ObjectSelection supported ContentRefs, we could improve and do that without the // speculative load, triggering a load only when someone was actually interested. if (content.IsLoaded || fileEvent.Type == FileEventType.Created) { if (modifiedLoadedResources == null) { modifiedLoadedResources = new List <Resource>(); } modifiedLoadedResources.Add(content.Res); } } // Handle each event according to its type List <FileEvent> renameEventBuffer = null; HashSet <string> sourceMediaDeleteSchedule = null; foreach (FileEvent fileEvent in eventQueue.Items) { if (fileEvent.Type == FileEventType.Changed) { HandleDataDirChangeEvent(fileEvent); } else if (fileEvent.Type == FileEventType.Deleted) { HandleDataDirDeleteEvent(fileEvent, ref sourceMediaDeleteSchedule); } else if (fileEvent.Type == FileEventType.Renamed) { HandleDataDirRenameEvent(fileEvent, ref renameEventBuffer); } } // If we scheduled source / media files for deletion, do it now at once if (sourceMediaDeleteSchedule != null) { // Gather a list of directories from which we're removing HashSet <string> affectedDirectories = new HashSet <string>(); foreach (string file in sourceMediaDeleteSchedule) { affectedDirectories.Add(Path.GetDirectoryName(file)); } // Send all the files to the recycle bin RecycleBin.SendSilent(sourceMediaDeleteSchedule); // Remove directories that are now empty foreach (string dir in affectedDirectories) { PathHelper.DeleteEmptyDirectory(dir, true); } } // If required, perform a global rename operation in all existing content if (renameEventBuffer != null) { // Don't do it now - schedule it for the main form event loop so we don't block here. DualityEditorApp.MainForm.BeginInvoke((Action) delegate() { ProcessingBigTaskDialog taskDialog = new ProcessingBigTaskDialog( Properties.GeneralRes.TaskRenameContentRefs_Caption, Properties.GeneralRes.TaskRenameContentRefs_Desc, async_RenameContentRefs, renameEventBuffer); taskDialog.ShowDialog(DualityEditorApp.MainForm); }); } // Notify the editor about externally modified resources if (modifiedLoadedResources != null) { DualityEditorApp.NotifyObjPropChanged( null, new ObjectSelection(modifiedLoadedResources), false); } }
private static void MoveSourceMediaFile(FileEvent renameEvent, string[] oldMediaPaths) { if (!renameEvent.IsDirectory) { string[] newMediaPaths = AssetManager.GetAssetSourceFiles(new ContentRef <Resource>(renameEvent.Path)); for (int i = 0; i < oldMediaPaths.Length; i++) { string oldPath = oldMediaPaths[i]; string newPath = newMediaPaths.Length > i ? newMediaPaths[i] : oldPath; // Move the media file to mirror the data files movement if (!PathOp.ArePathsEqual(oldPath, newPath)) { if (File.Exists(oldPath) && !File.Exists(newPath)) { Directory.CreateDirectory(Path.GetDirectoryName(newPath)); try { File.Move(oldPath, newPath); } catch (IOException exception) { Logs.Editor.WriteWarning( "Unable to move source media file '{0}' to '{1}' ({2}). Copying the file instead.", oldPath, newPath, exception.Message); File.Copy(oldPath, newPath); } PathHelper.DeleteEmptyDirectory(Path.GetDirectoryName(oldPath), true); } } } } else { // Determine which source/media directory we're going to move string oldMediaPath = Path.Combine( EditorHelper.ImportDirectory, PathHelper.MakeFilePathRelative(renameEvent.OldPath, DualityApp.DataDirectory)); // Determine where that old source/media directory needs to be moved string newMediaPath = Path.Combine( EditorHelper.ImportDirectory, PathHelper.MakeFilePathRelative(renameEvent.Path, DualityApp.DataDirectory)); // Move the media directory to mirror the data directories movement if (!PathOp.ArePathsEqual(newMediaPath, oldMediaPath)) { if (Directory.Exists(oldMediaPath) && !Directory.Exists(newMediaPath)) { Directory.CreateDirectory(Path.GetDirectoryName(newMediaPath)); try { Directory.Move(oldMediaPath, newMediaPath); } catch (IOException exception) { Logs.Editor.WriteWarning( "Unable to move source media directory '{0}' to '{1}' ({2}). Copying the directory instead.", oldMediaPath, newMediaPath, exception.Message); PathHelper.CopyDirectory(oldMediaPath, newMediaPath); } PathHelper.DeleteEmptyDirectory(Path.GetDirectoryName(oldMediaPath), true); } } } }
private static void ProcessDataDirEvents() { List <ResourceRenamedEventArgs> renameEventBuffer = null; HashSet <string> sourceMediaDeleteSchedule = null; // Process events while (dataDirEventBuffer.Count > 0) { FileSystemEventArgs e = FetchFileSystemEvent(dataDirEventBuffer, DualityApp.DataDirectory); if (e == null) { continue; } // Determine whether we're dealing with a directory bool isDirectory = Directory.Exists(e.FullPath); { // If this is a deletion, nothing exists anymore, rely on metadata instead. DeletedEventArgsExt de = e as DeletedEventArgsExt; if (de != null && de.IsDirectory) { isDirectory = true; } } if (e.ChangeType == WatcherChangeTypes.Changed) { // Ignore stuff saved by the editor itself if (IsPathEditorModified(e.FullPath)) { continue; } if (Resource.IsResourceFile(e.FullPath) || isDirectory) { ContentRef <Resource> resRef = new ContentRef <Resource>(null, e.FullPath); // Unregister outdated resources, if modified outside the editor if (!isDirectory && ContentProvider.HasContent(e.FullPath)) { bool isCurrentScene = resRef.Is <Scene>() && Scene.Current == resRef.Res; if (isCurrentScene || DualityEditorApp.IsResourceUnsaved(e.FullPath)) { DialogResult result = MessageBox.Show( String.Format(Properties.GeneralRes.Msg_ConfirmReloadResource_Text, e.FullPath), Properties.GeneralRes.Msg_ConfirmReloadResource_Caption, MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation); if (result == DialogResult.Yes) { string curScenePath = Scene.CurrentPath; ContentProvider.RemoveContent(e.FullPath); if (isCurrentScene) { Scene.SwitchTo(ContentProvider.RequestContent <Scene>(curScenePath), true); } } } else { ContentProvider.RemoveContent(e.FullPath); } } if (ResourceModified != null) { ResourceModified(null, new ResourceEventArgs(e.FullPath, isDirectory)); } } } else if (e.ChangeType == WatcherChangeTypes.Created) { if (File.Exists(e.FullPath)) { // Register newly detected Resource file if (Resource.IsResourceFile(e.FullPath)) { if (ResourceCreated != null) { ResourceCreated(null, new ResourceEventArgs(e.FullPath, false)); } } } else if (Directory.Exists(e.FullPath)) { // Register newly detected Resource directory if (ResourceCreated != null) { ResourceCreated(null, new ResourceEventArgs(e.FullPath, true)); } } } else if (e.ChangeType == WatcherChangeTypes.Deleted) { // Is it a Resource file or just something else? if (Resource.IsResourceFile(e.FullPath) || isDirectory) { ResourceEventArgs args = new ResourceEventArgs(e.FullPath, isDirectory); // Schedule Source/Media file deletion to keep it organized / synced with Resource Data if (sourceMediaDeleteSchedule == null) { sourceMediaDeleteSchedule = new HashSet <string>(); } GetDeleteSourceMediaFilePaths(args, sourceMediaDeleteSchedule); // Unregister no-more existing resources if (isDirectory) { ContentProvider.RemoveContentTree(args.Path); } else { ContentProvider.RemoveContent(args.Path); } if (ResourceDeleted != null) { ResourceDeleted(null, args); } } } else if (e.ChangeType == WatcherChangeTypes.Renamed) { // Is it a Resource file or just something else? RenamedEventArgs re = e as RenamedEventArgs; ResourceRenamedEventArgs args = new ResourceRenamedEventArgs(re.FullPath, re.OldFullPath, isDirectory); if (Resource.IsResourceFile(e.FullPath) || isDirectory) { // Determine which Source / Media files would belong to this Resource - before moving it string[] oldMediaPaths = PreMoveSourceMediaFile(args);; // Rename content registerations if (isDirectory) { ContentProvider.RenameContentTree(args.OldPath, args.Path); } else { ContentProvider.RenameContent(args.OldPath, args.Path); } // Query skipped paths bool isEmptyDir = isDirectory && !Directory.EnumerateFileSystemEntries(args.Path).Any(); bool isSkippedPath = isEmptyDir; if (!isSkippedPath && BeginGlobalRename != null) { BeginGlobalRenameEventArgs beginGlobalRenameArgs = new BeginGlobalRenameEventArgs(args.Path, args.OldPath, isDirectory); BeginGlobalRename(null, beginGlobalRenameArgs); isSkippedPath = beginGlobalRenameArgs.Cancel; } if (!isSkippedPath) { // Buffer rename event to perform the global rename for all at once. if (renameEventBuffer == null) { renameEventBuffer = new List <ResourceRenamedEventArgs>(); } renameEventBuffer.Add(args); } if (ResourceRenamed != null) { ResourceRenamed(null, args); } if (!isSkippedPath) { // Organize the Source/Media directory accordingly MoveSourceMediaFile(args, oldMediaPaths); } } } } // If we scheduled source / media files for deletion, do it now at once if (sourceMediaDeleteSchedule != null) { // Gather a list of directories from which we're removing HashSet <string> affectedDirectories = new HashSet <string>(); foreach (string file in sourceMediaDeleteSchedule) { affectedDirectories.Add(Path.GetDirectoryName(file)); } // Send all the files to the recycle bin RecycleBin.SendSilent(sourceMediaDeleteSchedule); // Remove directories that are now empty foreach (string dir in affectedDirectories) { PathHelper.DeleteEmptyDirectory(dir, true); } } // If required, perform a global rename operation in all existing content if (renameEventBuffer != null) { // Don't do it now - schedule it for the main form event loop so we don't block here. DualityEditorApp.MainForm.BeginInvoke((Action) delegate() { ProcessingBigTaskDialog taskDialog = new ProcessingBigTaskDialog( Properties.GeneralRes.TaskRenameContentRefs_Caption, Properties.GeneralRes.TaskRenameContentRefs_Desc, async_RenameContentRefs, renameEventBuffer); taskDialog.ShowDialog(DualityEditorApp.MainForm); }); } }