You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
484 lines
15 KiB
484 lines
15 KiB
// <file> |
|
// <copyright see="prj:///doc/copyright.txt"/> |
|
// <license see="prj:///doc/license.txt"/> |
|
// <owner name="Mike Krüger" email="mike@icsharpcode.net"/> |
|
// <version>$Revision$</version> |
|
// </file> |
|
|
|
using System; |
|
using System.Collections.Generic; |
|
using System.Diagnostics; |
|
using System.Linq; |
|
using System.IO; |
|
using System.Windows.Forms; |
|
|
|
using ICSharpCode.Core; |
|
using ICSharpCode.SharpDevelop.Gui; |
|
using ICSharpCode.SharpDevelop.Project; |
|
|
|
namespace ICSharpCode.SharpDevelop |
|
{ |
|
public static class FileService |
|
{ |
|
static bool serviceInitialized; |
|
|
|
#region RecentOpen |
|
static RecentOpen recentOpen = null; |
|
|
|
public static RecentOpen RecentOpen { |
|
get { |
|
if (recentOpen == null) { |
|
recentOpen = RecentOpen.FromXmlElement(PropertyService.Get("RecentOpen", new Properties())); |
|
} |
|
return recentOpen; |
|
} |
|
} |
|
|
|
static void ProjectServiceSolutionLoaded(object sender, SolutionEventArgs e) |
|
{ |
|
RecentOpen.AddLastProject(e.Solution.FileName); |
|
} |
|
#endregion |
|
|
|
public static void Unload() |
|
{ |
|
if (recentOpen != null) { |
|
PropertyService.Set("RecentOpen", recentOpen.ToProperties()); |
|
} |
|
ProjectService.SolutionLoaded -= ProjectServiceSolutionLoaded; |
|
ParserService.LoadSolutionProjectsThreadEnded -= ParserServiceLoadSolutionProjectsThreadEnded; |
|
serviceInitialized = false; |
|
} |
|
|
|
internal static void InitializeService() |
|
{ |
|
if (!serviceInitialized) { |
|
ProjectService.SolutionLoaded += ProjectServiceSolutionLoaded; |
|
ParserService.LoadSolutionProjectsThreadEnded += ParserServiceLoadSolutionProjectsThreadEnded; |
|
serviceInitialized = true; |
|
} |
|
} |
|
|
|
#region OpenedFile |
|
static Dictionary<string, OpenedFile> openedFileDict = new Dictionary<string, OpenedFile>(StringComparer.InvariantCultureIgnoreCase); |
|
|
|
/// <summary> |
|
/// Gets a collection containing all currently opened files. |
|
/// The returned collection is a read-only copy of the currently opened files - |
|
/// it will not reflect future changes of the list of opened files. |
|
/// </summary> |
|
public static ICollection<OpenedFile> OpenedFiles { |
|
get { |
|
return openedFileDict.Values.ToArray(); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets an opened file, or returns null if the file is not opened. |
|
/// </summary> |
|
public static OpenedFile GetOpenedFile(string fileName) |
|
{ |
|
if (fileName == null) |
|
throw new ArgumentNullException("fileName"); |
|
|
|
fileName = FileUtility.NormalizePath(fileName); |
|
OpenedFile file; |
|
openedFileDict.TryGetValue(fileName, out file); |
|
return file; |
|
} |
|
|
|
/// <summary> |
|
/// Gets or creates an opened file. |
|
/// Warning: the opened file will be a file without any views attached. |
|
/// Make sure to attach a view to it, or call CloseIfAllViewsClosed on the OpenedFile to |
|
/// unload the OpenedFile instance if no views were attached to it. |
|
/// </summary> |
|
public static OpenedFile GetOrCreateOpenedFile(string fileName) |
|
{ |
|
if (fileName == null) |
|
throw new ArgumentNullException("fileName"); |
|
|
|
fileName = FileUtility.NormalizePath(fileName); |
|
OpenedFile file; |
|
if (!openedFileDict.TryGetValue(fileName, out file)) { |
|
openedFileDict[fileName] = file = new FileServiceOpenedFile(fileName); |
|
} |
|
return file; |
|
} |
|
|
|
/// <summary> |
|
/// Creates a new untitled OpenedFile. |
|
/// </summary> |
|
public static OpenedFile CreateUntitledOpenedFile(string defaultName, byte[] content) |
|
{ |
|
if (defaultName == null) |
|
throw new ArgumentNullException("defaultName"); |
|
|
|
OpenedFile file = new FileServiceOpenedFile(content); |
|
file.FileName = file.GetHashCode() + "/" + defaultName; |
|
openedFileDict[file.FileName] = file; |
|
return file; |
|
} |
|
|
|
/// <summary>Called by OpenedFile.set_FileName to update the dictionary.</summary> |
|
internal static void OpenedFileFileNameChange(OpenedFile file, string oldName, string newName) |
|
{ |
|
if (oldName == null) return; // File just created with NewFile where name is being initialized. |
|
|
|
LoggingService.Debug("OpenedFileFileNameChange: " + oldName + " => " + newName); |
|
|
|
Debug.Assert(openedFileDict[oldName] == file); |
|
Debug.Assert(!openedFileDict.ContainsKey(newName)); |
|
|
|
openedFileDict.Remove(oldName); |
|
openedFileDict[newName] = file; |
|
} |
|
|
|
/// <summary>Called by OpenedFile.UnregisterView to update the dictionary.</summary> |
|
internal static void OpenedFileClosed(OpenedFile file) |
|
{ |
|
Debug.Assert(openedFileDict[file.FileName] == file); |
|
openedFileDict.Remove(file.FileName); |
|
LoggingService.Debug("OpenedFileClosed: " + file.FileName); |
|
} |
|
#endregion |
|
|
|
/// <summary> |
|
/// Checks if the file name is valid <b>and shows a MessageBox if it is not valid</b>. |
|
/// Do not use in non-UI methods. |
|
/// </summary> |
|
public static bool CheckFileName(string fileName) |
|
{ |
|
if (FileUtility.IsValidPath(fileName)) |
|
return true; |
|
MessageService.ShowMessage(StringParser.Parse("${res:ICSharpCode.SharpDevelop.Commands.SaveFile.InvalidFileNameError}", new string[,] {{"FileName", fileName}})); |
|
return false; |
|
} |
|
|
|
/// <summary> |
|
/// Checks that a single directory name is valid. |
|
/// </summary> |
|
/// <param name="name">A single directory name not the full path</param> |
|
public static bool CheckDirectoryName(string name) |
|
{ |
|
if (FileUtility.IsValidDirectoryName(name)) |
|
return true; |
|
MessageService.ShowMessage(StringParser.Parse("${res:ICSharpCode.SharpDevelop.Commands.SaveFile.InvalidFileNameError}", new string[,] {{"FileName", name}})); |
|
return false; |
|
} |
|
|
|
internal sealed class LoadFileWrapper |
|
{ |
|
IDisplayBinding binding; |
|
|
|
public LoadFileWrapper(IDisplayBinding binding) |
|
{ |
|
this.binding = binding; |
|
} |
|
|
|
public void Invoke(string fileName) |
|
{ |
|
OpenedFile file = FileService.GetOrCreateOpenedFile(fileName); |
|
IViewContent newContent = binding.CreateContentForFile(file); |
|
if (newContent != null) { |
|
DisplayBindingService.AttachSubWindows(newContent, false); |
|
WorkbenchSingleton.Workbench.ShowView(newContent); |
|
} |
|
file.CloseIfAllViewsClosed(); |
|
} |
|
} |
|
|
|
static void ParserServiceLoadSolutionProjectsThreadEnded(object sender, EventArgs e) |
|
{ |
|
WorkbenchSingleton.SafeThreadAsyncCall( |
|
delegate { |
|
foreach (IViewContent content in WorkbenchSingleton.Workbench.ViewContentCollection.ToArray()) { |
|
DisplayBindingService.AttachSubWindows(content, true); |
|
} |
|
}); |
|
} |
|
|
|
public static bool IsOpen(string fileName) |
|
{ |
|
return GetOpenFile(fileName) != null; |
|
} |
|
|
|
public static IViewContent OpenFile(string fileName) |
|
{ |
|
fileName = FileUtility.NormalizePath(fileName); |
|
LoggingService.Info("Open file " + fileName); |
|
|
|
IViewContent viewContent = GetOpenFile(fileName); |
|
if (viewContent != null) { |
|
viewContent.WorkbenchWindow.SelectWindow(); |
|
return viewContent; |
|
} |
|
|
|
IDisplayBinding binding = DisplayBindingService.GetBindingPerFileName(fileName); |
|
|
|
if (binding != null) { |
|
if (FileUtility.ObservedLoad(new NamedFileOperationDelegate(new LoadFileWrapper(binding).Invoke), fileName) == FileOperationResult.OK) { |
|
FileService.RecentOpen.AddLastFile(fileName); |
|
} |
|
} else { |
|
throw new ApplicationException("Cannot open " + fileName + ", no display codon found."); |
|
} |
|
return GetOpenFile(fileName); |
|
} |
|
|
|
/// <summary> |
|
/// Opens a new unsaved file. |
|
/// </summary> |
|
/// <param name="defaultName">The (unsaved) name of the to open</param> |
|
/// <param name="content">Content of the file to create</param> |
|
public static IViewContent NewFile(string defaultName, string content) |
|
{ |
|
return NewFile(defaultName, ParserService.DefaultFileEncoding.GetBytes(content)); |
|
} |
|
|
|
/// <summary> |
|
/// Opens a new unsaved file. |
|
/// </summary> |
|
/// <param name="defaultName">The (unsaved) name of the to open</param> |
|
/// <param name="content">Content of the file to create</param> |
|
public static IViewContent NewFile(string defaultName, byte[] content) |
|
{ |
|
if (defaultName == null) |
|
throw new ArgumentNullException("defaultName"); |
|
if (content == null) |
|
throw new ArgumentNullException("content"); |
|
|
|
IDisplayBinding binding = DisplayBindingService.GetBindingPerFileName(defaultName); |
|
|
|
if (binding != null) { |
|
OpenedFile file = CreateUntitledOpenedFile(defaultName, content); |
|
|
|
IViewContent newContent = binding.CreateContentForFile(file); |
|
if (newContent == null) { |
|
LoggingService.Warn("Created view content was null - DefaultName:" + defaultName); |
|
file.CloseIfAllViewsClosed(); |
|
return null; |
|
} |
|
|
|
DisplayBindingService.AttachSubWindows(newContent, false); |
|
|
|
WorkbenchSingleton.Workbench.ShowView(newContent); |
|
return newContent; |
|
} else { |
|
throw new ApplicationException("Can't create display binding for file " + defaultName); |
|
} |
|
} |
|
|
|
public static IList<string> GetOpenFiles() |
|
{ |
|
List<string> fileNames = new List<string>(); |
|
foreach (IViewContent content in WorkbenchSingleton.Workbench.ViewContentCollection) { |
|
string contentName = content.PrimaryFileName; |
|
if (contentName != null) |
|
fileNames.Add(contentName); |
|
} |
|
return fileNames; |
|
} |
|
|
|
public static IViewContent GetOpenFile(string fileName) |
|
{ |
|
if (fileName != null && fileName.Length > 0) { |
|
foreach (IViewContent content in WorkbenchSingleton.Workbench.ViewContentCollection) { |
|
string contentName = content.PrimaryFileName; |
|
if (contentName != null) { |
|
if (FileUtility.IsEqualFileName(fileName, contentName)) |
|
return content; |
|
} |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
/// <summary> |
|
/// Removes a file, raising the appropriate events. This method may show message boxes. |
|
/// </summary> |
|
public static void RemoveFile(string fileName, bool isDirectory) |
|
{ |
|
FileCancelEventArgs eargs = new FileCancelEventArgs(fileName, isDirectory); |
|
OnFileRemoving(eargs); |
|
if (eargs.Cancel) |
|
return; |
|
if (!eargs.OperationAlreadyDone) { |
|
if (isDirectory) { |
|
try { |
|
if (Directory.Exists(fileName)) { |
|
Directory.Delete(fileName, true); |
|
} |
|
} catch (Exception e) { |
|
MessageService.ShowError(e, "Can't remove directory " + fileName); |
|
// return; |
|
} |
|
} else { |
|
try { |
|
if (File.Exists(fileName)) { |
|
File.Delete(fileName); |
|
} |
|
} catch (Exception e) { |
|
MessageService.ShowError(e, "Can't remove file " + fileName); |
|
// return; |
|
} |
|
} |
|
} |
|
OnFileRemoved(new FileEventArgs(fileName, isDirectory)); |
|
} |
|
|
|
/// <summary> |
|
/// Renames or moves a file, raising the appropriate events. This method may show message boxes. |
|
/// </summary> |
|
public static bool RenameFile(string oldName, string newName, bool isDirectory) |
|
{ |
|
if (FileUtility.IsEqualFileName(oldName, newName)) |
|
return false; |
|
FileRenamingEventArgs eargs = new FileRenamingEventArgs(oldName, newName, isDirectory); |
|
OnFileRenaming(eargs); |
|
if (eargs.Cancel) |
|
return false; |
|
if (!eargs.OperationAlreadyDone) { |
|
try { |
|
if (isDirectory && Directory.Exists(oldName)) { |
|
|
|
if (Directory.Exists(newName)) { |
|
MessageService.ShowMessage(StringParser.Parse("${res:Gui.ProjectBrowser.FileInUseError}")); |
|
return false; |
|
} |
|
Directory.Move(oldName, newName); |
|
|
|
} else if (File.Exists(oldName)) { |
|
if (File.Exists(newName)) { |
|
MessageService.ShowMessage(StringParser.Parse("${res:Gui.ProjectBrowser.FileInUseError}")); |
|
return false; |
|
} |
|
File.Move(oldName, newName); |
|
} |
|
} catch (Exception e) { |
|
if (isDirectory) { |
|
MessageService.ShowError(e, "Can't rename directory " + oldName); |
|
} else { |
|
MessageService.ShowError(e, "Can't rename file " + oldName); |
|
} |
|
return false; |
|
} |
|
} |
|
OnFileRenamed(new FileRenameEventArgs(oldName, newName, isDirectory)); |
|
return true; |
|
} |
|
|
|
/// <summary> |
|
/// Opens the specified file and jumps to the specified file position. |
|
/// Warning: Unlike parser coordinates, line and column are 0-based. |
|
/// </summary> |
|
public static IViewContent JumpToFilePosition(string fileName, int line, int column) |
|
{ |
|
LoggingService.InfoFormatted("FileService\n\tJumping to File Position: [{0} : {1}x{2}]", fileName, line, column); |
|
NavigationService.SuspendLogging(); |
|
|
|
if (fileName == null || fileName.Length == 0) { |
|
return null; |
|
} |
|
IViewContent content = OpenFile(fileName); |
|
if (content is IPositionable) { |
|
// TODO: enable jumping to a particular view |
|
((IPositionable)content).JumpTo(Math.Max(0, line), Math.Max(0, column)); |
|
} |
|
|
|
LoggingService.InfoFormatted("FileService\n\tJumped to File Position: [{0} : {1}x{2}]", fileName, line, column); |
|
NavigationService.ResumeLogging(); |
|
|
|
return content; |
|
} |
|
|
|
/// <summary> |
|
/// Creates a FolderBrowserDialog that will initially select the |
|
/// specified folder. If the folder does not exist then the default |
|
/// behaviour of the FolderBrowserDialog is used where it selects the |
|
/// desktop folder. |
|
/// </summary> |
|
public static FolderBrowserDialog CreateFolderBrowserDialog(string description, string selectedPath) |
|
{ |
|
FolderBrowserDialog dialog = new FolderBrowserDialog(); |
|
dialog.Description = StringParser.Parse(description); |
|
if (selectedPath != null && selectedPath.Length > 0 && Directory.Exists(selectedPath)) { |
|
dialog.RootFolder = Environment.SpecialFolder.MyComputer; |
|
dialog.SelectedPath = selectedPath; |
|
} |
|
return dialog; |
|
} |
|
|
|
/// <summary> |
|
/// Creates a FolderBrowserDialog that will initially select the |
|
/// desktop folder. |
|
/// </summary> |
|
public static FolderBrowserDialog CreateFolderBrowserDialog(string description) |
|
{ |
|
return CreateFolderBrowserDialog(description, null); |
|
} |
|
|
|
static void OnFileRemoved(FileEventArgs e) |
|
{ |
|
if (FileRemoved != null) { |
|
FileRemoved(null, e); |
|
} |
|
} |
|
|
|
static void OnFileRemoving(FileCancelEventArgs e) |
|
{ |
|
if (FileRemoving != null) { |
|
FileRemoving(null, e); |
|
} |
|
} |
|
|
|
static void OnFileRenamed(FileRenameEventArgs e) |
|
{ |
|
if (FileRenamed != null) { |
|
FileRenamed(null, e); |
|
} |
|
} |
|
|
|
static void OnFileRenaming(FileRenamingEventArgs e) { |
|
if (FileRenaming != null) { |
|
FileRenaming(null, e); |
|
} |
|
} |
|
|
|
public static bool FireFileReplacing(string fileName, bool isDirectory) |
|
{ |
|
FileCancelEventArgs e = new FileCancelEventArgs(fileName, isDirectory); |
|
if (FileReplacing != null) { |
|
FileReplacing(null, e); |
|
} |
|
return !e.Cancel; |
|
} |
|
|
|
public static void FireFileReplaced(string fileName, bool isDirectory) |
|
{ |
|
if (FileReplaced != null) { |
|
FileReplaced(null, new FileEventArgs(fileName, isDirectory)); |
|
} |
|
} |
|
|
|
public static void FireFileCreated(string fileName) |
|
{ |
|
if (FileCreated != null) { |
|
FileCreated(null, new FileEventArgs(fileName, false)); |
|
} |
|
} |
|
|
|
public static event EventHandler<FileEventArgs> FileCreated; |
|
|
|
public static event EventHandler<FileRenamingEventArgs> FileRenaming; |
|
public static event EventHandler<FileRenameEventArgs> FileRenamed; |
|
|
|
public static event EventHandler<FileCancelEventArgs> FileRemoving; |
|
public static event EventHandler<FileEventArgs> FileRemoved; |
|
|
|
public static event EventHandler<FileCancelEventArgs> FileReplacing; |
|
public static event EventHandler<FileEventArgs> FileReplaced; |
|
} |
|
} |
|
|
|
|
|
|