using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Windows; using System.Xml.Serialization; using RainmeterStudio.Core.Model; using RainmeterStudio.Core.Storage; using RainmeterStudio.Core.Utils; using RainmeterStudio.Editor.ProjectEditor; using RainmeterStudio.Storage; namespace RainmeterStudio.Business { public class ProjectManager { private List _projectTemplates = new List(); #region Properties /// /// Gets the currently opened project /// public Project ActiveProject { get; protected set; } #endregion #region Events /// /// Called when a project is opened or the active project closes. /// public event EventHandler ActiveProjectChanged; #endregion #region Constructor /// /// Initializes the project manager /// /// Project storage public ProjectManager() { ActiveProject = null; } #endregion #region Project operations /// /// Creates a new project /// /// Name of project /// Path of project file public void CreateProject(string name, string path, IProjectTemplate template) { // If there is an opened project, close it if (ActiveProject != null) Close(); // Create project object ActiveProject = template.CreateProject(); ActiveProject.Name = name; ActiveProject.Path = path; // Save to file string directory = Path.GetDirectoryName(path); if (!String.IsNullOrEmpty(directory)) Directory.CreateDirectory(Path.GetDirectoryName(path)); SaveActiveProject(); // Raise event if (ActiveProjectChanged != null) ActiveProjectChanged(this, new EventArgs()); } /// /// Opens a project from disk /// /// public void OpenProject(string path) { // If there is an opened project, close it if (ActiveProject != null) Close(); // Open using storage ActiveProject = ProjectStorage.Read(path); ActiveProject.Path = path; // Raise event if (ActiveProjectChanged != null) ActiveProjectChanged(this, new EventArgs()); } /// /// Saves the changes to the current project to disk /// public void SaveActiveProject() { // Safety check if (ActiveProject == null) throw new InvalidOperationException("Cannot save a project that is not opened."); // Save ProjectStorage.Write(ActiveProject); } /// /// Closes an opened project /// public void Close() { ActiveProject = null; // Raise event if (ActiveProjectChanged != null) ActiveProjectChanged(this, new EventArgs()); } #endregion #region Document templates /// /// Registers a project template /// /// Project template public void RegisterProjectTemplate(IProjectTemplate template) { _projectTemplates.Add(template); } /// /// Gets a list of existing project templates /// public IEnumerable ProjectTemplates { get { return _projectTemplates; } } #endregion #region Project item operations [Serializable] protected struct ClipboardData { public bool Cut; public string QualifiedName; } /// /// Places a project item in the clipboard, and marks it for deletion /// /// Project item to cut public void ProjectItemCutClipboard(Reference @ref) { var dataFormat = DataFormats.GetDataFormat(typeof(ClipboardData).FullName); ClipboardData data = new ClipboardData(); data.Cut = true; data.QualifiedName = @ref.QualifiedName; Clipboard.SetData(dataFormat.Name, data); } /// /// Places a project item in the clipboard /// /// Project item to copy public void ProjectItemCopyClipboard(Reference @ref) { var dataFormat = DataFormats.GetDataFormat(typeof(ClipboardData).FullName); ClipboardData data = new ClipboardData(); data.Cut = false; data.QualifiedName = @ref.QualifiedName; Clipboard.SetData(dataFormat.Name, data); } /// /// Pastes a project item from clipboard /// /// Destination public void ProjectItemPasteClipboard(Reference dest) { var dataFormat = DataFormats.GetDataFormat(typeof(ClipboardData).FullName); if (Clipboard.ContainsData(dataFormat.Name)) { ClipboardData data = (ClipboardData)Clipboard.GetData(dataFormat.Name); var reference = ActiveProject.Root.GetReference(data.QualifiedName); if (data.Cut) { ProjectItemMove(reference, dest); Clipboard.Clear(); } else { ProjectItemCopy(reference, dest); } } } /// /// Moves a project item to another folder /// /// Project item to move /// Destination folder public void ProjectItemMove(Reference @ref, Reference dest) { // Move storage file string refPath = Path.GetFileName(@ref.StoragePath.TrimEnd('\\')); string destinationPath = (dest.TargetKind == ReferenceTargetKind.Directory) ? dest.StoragePath : Path.GetDirectoryName(dest.StoragePath); string newPath = Path.Combine(destinationPath, refPath); if (@ref.TargetKind == ReferenceTargetKind.Directory) { Directory.Move(@ref.StoragePath, newPath); // Update children UpdateRenameChildren(@ref, @ref.StoragePath, newPath); } else { File.Move(@ref.StoragePath, newPath); } // Set up reference object @ref.Unparent(); @ref.StoragePath = newPath; dest.Add(@ref); } private void UpdateRenameChildren(Reference root, string oldPath, string newPath) { foreach (var pair in root.ChildrenDictionary) { pair.Value.StoragePath = pair.Value.StoragePath.Replace(oldPath, newPath); UpdateRenameChildren(pair.Value, oldPath, newPath); } } /// /// Creates a copy of a project item to another folder /// /// Project item to copy /// Destination folder /// Reference to the copy public Reference ProjectItemCopy(Reference @ref, Reference dest) { // Create a clone reference var copyRef = (Reference)@ref.Clone(); // Copy storage file string refPath = Path.GetFileName(@ref.StoragePath.TrimEnd('\\')); string destinationPath = (dest.TargetKind == ReferenceTargetKind.Directory) ? dest.StoragePath : Path.GetDirectoryName(dest.StoragePath); string newPath = Path.Combine(destinationPath, refPath); if (@ref.TargetKind == ReferenceTargetKind.Directory) { DirectoryHelper.CopyDirectory(@ref.StoragePath, newPath); // Update children UpdateRenameChildren(copyRef, copyRef.StoragePath, newPath); } else { // Find a nonconflicting file name newPath = GetNonConflictingPath(refPath, destinationPath); // Copy File.Copy(@ref.StoragePath, newPath); } // Parent reference copyRef.Name = Path.GetFileName(newPath); copyRef.StoragePath = newPath; dest.Add(copyRef); return copyRef; } private static string GetNonConflictingPath(string filename, string destinationPath) { // Initial path - destination path + file name string newPath = Path.Combine(destinationPath, filename); // Initial number int i = 1; // Try to find if there already is a number var match = Regex.Match(newPath, "_([0-9])$"); if (match.Success) { i = Int32.Parse(match.Groups[1].Value); } // Find non-conflicting number while (File.Exists(newPath)) { ++i; newPath = Path.Combine(destinationPath, Path.GetFileNameWithoutExtension(filename) + "_" + i.ToString() + Path.GetExtension(filename)); } return newPath; } public void ProjectItemRename(Reference @ref, string newName) { // Rename on disk string refPath = @ref.StoragePath.TrimEnd('\\'); string refDir = Path.GetDirectoryName(refPath); string newPath = Path.Combine(refDir, newName); if (@ref.TargetKind == ReferenceTargetKind.Directory) { Directory.Move(refPath, newPath); newPath += '\\'; } else { File.Move(refPath, newPath); } // Set reference @ref.Name = newName; @ref.StoragePath = newPath; } /// /// Deletes a project item /// /// public void ProjectItemDelete(Reference @ref, bool fromDisk) { if (fromDisk) { if (@ref.TargetKind == ReferenceTargetKind.File) File.Delete(@ref.StoragePath); else Directory.Delete(@ref.StoragePath, true); } @ref.Unparent(); } /// /// Checks if there is a project item in the clipboard /// /// True if there is a project item in the clipboard public bool HaveProjectItemInClipboard() { var dataFormat = DataFormats.GetDataFormat(typeof(ClipboardData).FullName); return Clipboard.ContainsData(dataFormat.Name); } /// /// Creates a new folder with given name /// /// Name of folder /// Parent folder public void CreateFolder(string name, Reference parent) { string dir = (parent.TargetKind == ReferenceTargetKind.Directory) ? parent.StoragePath : Path.GetDirectoryName(parent.StoragePath); string newDirPath = Path.Combine(dir, name); Directory.CreateDirectory(newDirPath); parent.Add(new Reference(name, newDirPath, ReferenceTargetKind.Directory)); } #endregion } }