Browse Source

Move remainder of the FileService into interface.

newNRvisualizers
Daniel Grunwald 14 years ago
parent
commit
1c61553dd7
  1. 2
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/AvalonEditDisplayBinding.cs
  2. 6
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ChooseEncodingDialog.xaml.cs
  3. 2
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs
  4. 2
      src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlView.cs
  5. 2
      src/AddIns/Misc/StartPage/Project/Src/RecentProjectsControl.xaml.cs
  6. 2
      src/AddIns/VersionControl/GitAddIn/Src/Commands.cs
  7. 2
      src/AddIns/VersionControl/SubversionAddIn/Src/Commands/ProjectBrowserCommands.cs
  8. 6
      src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
  9. 8
      src/Main/Base/Project/Src/Commands/FileCommands.cs
  10. 20
      src/Main/Base/Project/Src/Commands/MenuItemBuilders.cs
  11. 8
      src/Main/Base/Project/Src/Gui/Dialogs/NewProjectDialog.cs
  12. 12
      src/Main/Base/Project/Src/Gui/Dialogs/OptionPanels/IDEOptions/ProjectAndSolutionOptions.xaml.cs
  13. 2
      src/Main/Base/Project/Src/Gui/Pads/ProjectBrowser/TreeNodes/DirectoryNode.cs
  14. 541
      src/Main/Base/Project/Src/Services/File/FileService.cs
  15. 195
      src/Main/Base/Project/Src/Services/File/IFileService.cs
  16. 147
      src/Main/Base/Project/Src/Services/File/OpenedFile.cs
  17. 132
      src/Main/Base/Project/Src/Services/File/RecentOpen.cs
  18. 2
      src/Main/Base/Project/Src/Services/ProjectService/ParseableFileContentFinder.cs
  19. 1
      src/Main/Base/Project/Src/Services/ProjectService/ProjectService.cs
  20. 2
      src/Main/Base/Project/Src/Services/RefactoringService/FindReferencesAndRenameHelper.cs
  21. 2
      src/Main/Base/Project/Src/Util/FakeXmlViewContent.cs
  22. 14
      src/Main/SharpDevelop/OptionPanels/LoadSaveOptions.xaml
  23. 2
      src/Main/SharpDevelop/OptionPanels/LoadSaveOptions.xaml.cs
  24. 8
      src/Main/SharpDevelop/SharpDevelop.csproj
  25. 588
      src/Main/SharpDevelop/Workbench/FileService.cs
  26. 161
      src/Main/SharpDevelop/Workbench/FileServiceOpenedFile.cs
  27. 114
      src/Main/SharpDevelop/Workbench/RecentOpen.cs
  28. 4
      src/Main/SharpDevelop/Workbench/WorkbenchStartup.cs
  29. 10
      src/Main/SharpDevelop/Workbench/WpfWorkbench.cs

2
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/AvalonEditDisplayBinding.cs

@ -65,7 +65,7 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -65,7 +65,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
ChooseEncodingDialog dlg = new ChooseEncodingDialog();
dlg.Owner = WorkbenchSingleton.MainWindow;
using (Stream stream = file.OpenRead()) {
using (StreamReader reader = FileReader.OpenStream(stream, FileService.DefaultFileEncoding.GetEncoding())) {
using (StreamReader reader = FileReader.OpenStream(stream, SD.FileService.DefaultFileEncoding)) {
reader.Peek(); // force reader to auto-detect encoding
dlg.Encoding = reader.CurrentEncoding;
}

6
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ChooseEncodingDialog.xaml.cs

@ -18,13 +18,13 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -18,13 +18,13 @@ namespace ICSharpCode.AvalonEdit.AddIn
public ChooseEncodingDialog()
{
InitializeComponent();
encodingComboBox.ItemsSource = FileService.AllEncodings;
encodingComboBox.SelectedItem = FileService.DefaultFileEncoding;
encodingComboBox.ItemsSource = SD.FileService.AllEncodings;
encodingComboBox.SelectedItem = SD.FileService.DefaultFileEncodingInfo;
}
public Encoding Encoding {
get { return ((EncodingInfo)encodingComboBox.SelectedItem).GetEncoding(); }
set { encodingComboBox.SelectedItem = FileService.AllEncodings.Single(e => e.CodePage == value.CodePage); }
set { encodingComboBox.SelectedItem = SD.FileService.AllEncodings.Single(e => e.CodePage == value.CodePage); }
}
void okButton_Click(object sender, RoutedEventArgs e)

2
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs

@ -293,7 +293,7 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -293,7 +293,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
}
} else {
// do encoding auto-detection
using (StreamReader reader = FileReader.OpenStream(stream, this.Encoding ?? FileService.DefaultFileEncoding.GetEncoding())) {
using (StreamReader reader = FileReader.OpenStream(stream, this.Encoding ?? SD.FileService.DefaultFileEncoding)) {
ReloadDocument(primaryTextEditor.Document, reader.ReadToEnd());
this.Encoding = reader.CurrentEncoding;
}

2
src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlView.cs

@ -56,7 +56,7 @@ namespace ICSharpCode.XmlEditor @@ -56,7 +56,7 @@ namespace ICSharpCode.XmlEditor
public static XmlView ForFileName(string fileName)
{
return ForFile(FileService.GetOpenedFile(fileName));
return ForFile(SD.FileService.GetOpenedFile(fileName));
}
public OpenedFile File { get; set; }

2
src/AddIns/Misc/StartPage/Project/Src/RecentProjectsControl.xaml.cs

@ -45,7 +45,7 @@ namespace ICSharpCode.StartPage @@ -45,7 +45,7 @@ namespace ICSharpCode.StartPage
// When building the project list we access the .sln files (to see if they still exist).
// Because those might be stored on a slow network drive, we do this on a background thread so that
// SharpDevelop startup doesn't have to wait.
ThreadPool.QueueUserWorkItem(AsyncBuildRecentProjectList, FileService.RecentOpen.RecentProject.ToArray());
ThreadPool.QueueUserWorkItem(AsyncBuildRecentProjectList, SD.FileService.RecentOpen.RecentProjects.ToArray());
}
void AsyncBuildRecentProjectList(object state)

2
src/AddIns/VersionControl/GitAddIn/Src/Commands.cs

@ -32,7 +32,7 @@ namespace ICSharpCode.GitAddIn @@ -32,7 +32,7 @@ namespace ICSharpCode.GitAddIn
}
if (nodeFileName != null) {
List<OpenedFile> unsavedFiles = new List<OpenedFile>();
foreach (OpenedFile file in FileService.OpenedFiles) {
foreach (OpenedFile file in SD.FileService.OpenedFiles) {
if (file.IsDirty && !file.IsUntitled) {
if (string.IsNullOrEmpty(file.FileName)) continue;
if (FileUtility.IsUrl(file.FileName)) continue;

2
src/AddIns/VersionControl/SubversionAddIn/Src/Commands/ProjectBrowserCommands.cs

@ -29,7 +29,7 @@ namespace ICSharpCode.Svn.Commands @@ -29,7 +29,7 @@ namespace ICSharpCode.Svn.Commands
}
if (nodeFileName != null) {
List<OpenedFile> unsavedFiles = new List<OpenedFile>();
foreach (OpenedFile file in FileService.OpenedFiles) {
foreach (OpenedFile file in SD.FileService.OpenedFiles) {
if (file.IsDirty && !file.IsUntitled) {
if (string.IsNullOrEmpty(file.FileName)) continue;
if (FileUtility.IsUrl(file.FileName)) continue;

6
src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj

@ -211,10 +211,6 @@ @@ -211,10 +211,6 @@
<Compile Include="Src\Gui\Dialogs\OpenWithDialog.Designer.cs">
<DependentUpon>OpenWithDialog.cs</DependentUpon>
</Compile>
<Compile Include="Src\Gui\Dialogs\OptionPanels\IDEOptions\LoadSaveOptions.xaml.cs">
<DependentUpon>LoadSaveOptions.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="Src\Gui\Dialogs\OptionPanels\IDEOptions\ProjectAndSolutionOptions.xaml.cs">
<DependentUpon>ProjectAndSolutionOptions.xaml</DependentUpon>
<SubType>Code</SubType>
@ -362,7 +358,6 @@ @@ -362,7 +358,6 @@
<Compile Include="Src\Services\File\FileChangeWatcher.cs" />
<Compile Include="Src\Services\File\IFileService.cs" />
<Compile Include="Src\Services\File\OpenedFile.cs" />
<Compile Include="Src\Services\File\RecentOpen.cs" />
<Compile Include="Src\Services\IMessageLoop.cs" />
<Compile Include="Src\Services\LanguageBinding\AggregatedLanguageBinding.cs" />
<Compile Include="Src\Services\LanguageBinding\DefaultLanguageBinding.cs" />
@ -857,7 +852,6 @@ @@ -857,7 +852,6 @@
<Page Include="Src\Gui\Dialogs\GotoDialog.xaml">
<DependentUpon>GotoDialog.cs</DependentUpon>
</Page>
<Page Include="Src\Gui\Dialogs\OptionPanels\IDEOptions\LoadSaveOptions.xaml" />
<Page Include="Src\Gui\Dialogs\OptionPanels\IDEOptions\ProjectAndSolutionOptions.xaml" />
<Page Include="Src\Gui\Dialogs\OptionPanels\IDEOptions\SelectCulturePanel.xaml" />
<Page Include="Src\Gui\Dialogs\TabbedOptionsDialog.xaml" />

8
src/Main/Base/Project/Src/Commands/FileCommands.cs

@ -181,7 +181,7 @@ namespace ICSharpCode.SharpDevelop.Commands @@ -181,7 +181,7 @@ namespace ICSharpCode.SharpDevelop.Commands
return;
}
if (FileUtility.ObservedSave(new NamedFileOperationDelegate(file.SaveToDisk), fileName) == FileOperationResult.OK) {
FileService.RecentOpen.AddLastFile(fileName);
SD.FileService.RecentOpen.AddRecentFile(fileName);
MessageService.ShowMessage(fileName, "${res:ICSharpCode.SharpDevelop.Commands.SaveFile.FileSaved}");
}
}
@ -198,7 +198,7 @@ namespace ICSharpCode.SharpDevelop.Commands @@ -198,7 +198,7 @@ namespace ICSharpCode.SharpDevelop.Commands
((ICustomizedCommands)content).SaveCommand();
}
}
foreach (OpenedFile file in FileService.OpenedFiles) {
foreach (OpenedFile file in SD.FileService.OpenedFiles) {
if (file.IsDirty) {
SaveFile.Save(file);
}
@ -308,7 +308,7 @@ namespace ICSharpCode.SharpDevelop.Commands @@ -308,7 +308,7 @@ namespace ICSharpCode.SharpDevelop.Commands
{
public override void Run()
{
FileService.RecentOpen.ClearRecentFiles();
SD.FileService.RecentOpen.ClearRecentFiles();
}
}
@ -316,7 +316,7 @@ namespace ICSharpCode.SharpDevelop.Commands @@ -316,7 +316,7 @@ namespace ICSharpCode.SharpDevelop.Commands
{
public override void Run()
{
FileService.RecentOpen.ClearRecentProjects();
SD.FileService.RecentOpen.ClearRecentProjects();
}
}
}

20
src/Main/Base/Project/Src/Commands/MenuItemBuilders.cs

@ -136,14 +136,14 @@ namespace ICSharpCode.SharpDevelop.Commands @@ -136,14 +136,14 @@ namespace ICSharpCode.SharpDevelop.Commands
{
public ICollection BuildItems(Codon codon, object owner)
{
RecentOpen recentOpen = FileService.RecentOpen;
IRecentOpen recentOpen = SD.FileService.RecentOpen;
if (recentOpen.RecentFile.Count > 0) {
var items = new System.Windows.Controls.MenuItem[recentOpen.RecentFile.Count];
if (recentOpen.RecentFiles.Count > 0) {
var items = new System.Windows.Controls.MenuItem[recentOpen.RecentFiles.Count];
for (int i = 0; i < recentOpen.RecentFile.Count; ++i) {
for (int i = 0; i < recentOpen.RecentFiles.Count; ++i) {
// variable inside loop, so that anonymous method refers to correct recent file
string recentFile = recentOpen.RecentFile[i];
string recentFile = recentOpen.RecentFiles[i];
string accelaratorKeyPrefix = i < 10 ? "_" + ((i + 1) % 10) + " " : "";
items[i] = new System.Windows.Controls.MenuItem() {
Header = accelaratorKeyPrefix + recentFile
@ -166,14 +166,14 @@ namespace ICSharpCode.SharpDevelop.Commands @@ -166,14 +166,14 @@ namespace ICSharpCode.SharpDevelop.Commands
{
public ICollection BuildItems(Codon codon, object owner)
{
RecentOpen recentOpen = FileService.RecentOpen;
IRecentOpen recentOpen = SD.FileService.RecentOpen;
if (recentOpen.RecentProject.Count > 0) {
var items = new System.Windows.Controls.MenuItem[recentOpen.RecentProject.Count];
if (recentOpen.RecentProjects.Count > 0) {
var items = new System.Windows.Controls.MenuItem[recentOpen.RecentProjects.Count];
for (int i = 0; i < recentOpen.RecentProject.Count; ++i) {
for (int i = 0; i < recentOpen.RecentProjects.Count; ++i) {
// variable inside loop, so that anonymous method refers to correct recent file
string recentProject = recentOpen.RecentProject[i];
string recentProject = recentOpen.RecentProjects[i];
string accelaratorKeyPrefix = i < 10 ? "_" + ((i + 1) % 10) + " " : "";
items[i] = new System.Windows.Controls.MenuItem() {
Header = accelaratorKeyPrefix + recentProject

8
src/Main/Base/Project/Src/Gui/Dialogs/NewProjectDialog.cs

@ -362,11 +362,9 @@ namespace ICSharpCode.SharpDevelop.Project.Dialogs @@ -362,11 +362,9 @@ namespace ICSharpCode.SharpDevelop.Project.Dialogs
void BrowseDirectories(object sender, EventArgs e)
{
using (FolderBrowserDialog fd = FileService.CreateFolderBrowserDialog("${res:Dialog.NewProject.SelectDirectoryForProject}", locationTextBox.Text)) {
if (fd.ShowDialog() == DialogResult.OK) {
locationTextBox.Text = fd.SelectedPath;
}
}
string path = SD.FileService.BrowseForFolder("${res:Dialog.NewProject.SelectDirectoryForProject}", locationTextBox.Text);
if (path != null)
locationTextBox.Text = path;
}
// list view event handlers

12
src/Main/Base/Project/Src/Gui/Dialogs/OptionPanels/IDEOptions/ProjectAndSolutionOptions.xaml.cs

@ -20,7 +20,7 @@ namespace ICSharpCode.SharpDevelop.Gui.OptionPanels @@ -20,7 +20,7 @@ namespace ICSharpCode.SharpDevelop.Gui.OptionPanels
FillComboBoxWithEnumValues(typeof(Project.BuildOnExecuteSetting), onExecuteComboBox);
FillComboBoxWithEnumValues(typeof(Project.BuildOutputVerbosity), verbosityComboBox);
}
void FillComboBoxWithEnumValues(Type type, ComboBox comboBox)
{
foreach (Project.BuildOnExecuteSetting element in Enum.GetValues(type)) {
@ -37,11 +37,11 @@ namespace ICSharpCode.SharpDevelop.Gui.OptionPanels @@ -37,11 +37,11 @@ namespace ICSharpCode.SharpDevelop.Gui.OptionPanels
void defaultProjectLocationButtonClick(object sender, RoutedEventArgs e)
{
using (var fdiag = FileService.CreateFolderBrowserDialog("${res:Dialog.Options.IDEOptions.ProjectAndSolutionOptions.SelectDefaultProjectLocationDialog.Title}", defaultProjectLocationTextBox.Text)) {
if (fdiag.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
defaultProjectLocationTextBox.Text = fdiag.SelectedPath;
}
}
string path = SD.FileService.BrowseForFolder(
"${res:Dialog.Options.IDEOptions.ProjectAndSolutionOptions.SelectDefaultProjectLocationDialog.Title}",
defaultProjectLocationTextBox.Text);
if (path != null)
defaultProjectLocationTextBox.Text = path;
}
public override void LoadOptions()

2
src/Main/Base/Project/Src/Gui/Pads/ProjectBrowser/TreeNodes/DirectoryNode.cs

@ -674,7 +674,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -674,7 +674,7 @@ namespace ICSharpCode.SharpDevelop.Project
RecreateSubNodes();
}
if (performMove) {
foreach (OpenedFile file in FileService.OpenedFiles) {
foreach (OpenedFile file in SD.FileService.OpenedFiles) {
if (file.FileName != null &&
FileUtility.IsEqualFileName(file.FileName, fileName))
{

541
src/Main/Base/Project/Src/Services/File/FileService.cs

@ -20,30 +20,8 @@ namespace ICSharpCode.SharpDevelop @@ -20,30 +20,8 @@ namespace ICSharpCode.SharpDevelop
{
static bool serviceInitialized;
#region RecentOpen
static RecentOpen recentOpen = null;
public static RecentOpen RecentOpen {
get {
if (recentOpen == null) {
recentOpen = RecentOpen.FromXmlElement(PropertyService.NestedProperties("RecentOpen"));
}
return recentOpen;
}
}
static void ProjectServiceSolutionLoaded(object sender, SolutionEventArgs e)
{
RecentOpen.AddLastProject(e.Solution.FileName);
}
#endregion
internal static void Unload()
{
if (recentOpen != null) {
PropertyService.SetNestedProperties("RecentOpen", recentOpen.ToProperties());
}
ProjectService.SolutionLoaded -= ProjectServiceSolutionLoaded;
SD.ParserService.LoadSolutionProjectsThreadEnded -= ParserServiceLoadSolutionProjectsThreadEnded;
serviceInitialized = false;
}
@ -51,134 +29,18 @@ namespace ICSharpCode.SharpDevelop @@ -51,134 +29,18 @@ namespace ICSharpCode.SharpDevelop
internal static void InitializeService()
{
if (!serviceInitialized) {
ProjectService.SolutionLoaded += ProjectServiceSolutionLoaded;
SD.ParserService.LoadSolutionProjectsThreadEnded += ParserServiceLoadSolutionProjectsThreadEnded;
serviceInitialized = true;
}
}
#region OpenedFile
static Dictionary<FileName, OpenedFile> openedFileDict = new Dictionary<FileName, OpenedFile>();
/// <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)
{
return GetOpenedFile(FileName.Create(fileName));
}
/// <summary>
/// Gets an opened file, or returns null if the file is not opened.
/// </summary>
public static OpenedFile GetOpenedFile(FileName fileName)
{
if (fileName == null)
throw new ArgumentNullException("fileName");
WorkbenchSingleton.AssertMainThread();
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)
{
return GetOrCreateOpenedFile(FileName.Create(fileName));
}
/// <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(FileName fileName)
{
if (fileName == null)
throw new ArgumentNullException("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 = new 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, FileName oldName, FileName newName)
{
if (oldName == null) return; // File just created with NewFile where name is being initialized.
LoggingService.Debug("OpenedFileFileNameChange: " + oldName + " => " + newName);
if (openedFileDict[oldName] != file)
throw new ArgumentException("file must be registered as oldName");
if (openedFileDict.ContainsKey(newName)) {
OpenedFile oldFile = openedFileDict[newName];
if (oldFile.CurrentView != null) {
oldFile.CurrentView.WorkbenchWindow.CloseWindow(true);
} else {
throw new ArgumentException("there already is a file with the newName");
}
}
openedFileDict.Remove(oldName);
openedFileDict[newName] = file;
}
/// <summary>Called by OpenedFile.UnregisterView to update the dictionary.</summary>
internal static void OpenedFileClosed(OpenedFile file)
{
if (openedFileDict[file.FileName] != file)
throw new ArgumentException("file must be registered");
openedFileDict.Remove(file.FileName);
LoggingService.Debug("OpenedFileClosed: " + file.FileName);
}
#endregion
/// <summary>
/// Checks if the path 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 path)
{
if (FileUtility.IsValidPath(path))
return true;
MessageService.ShowMessage(StringParser.Parse("${res:ICSharpCode.SharpDevelop.Commands.SaveFile.InvalidFileNameError}", new StringTagPair("FileName", path)));
return false;
return SD.FileService.CheckFileName(path);
}
/// <summary>
@ -188,10 +50,7 @@ namespace ICSharpCode.SharpDevelop @@ -188,10 +50,7 @@ namespace ICSharpCode.SharpDevelop
/// <param name="name">A single file name not the full path</param>
public static bool CheckDirectoryEntryName(string name)
{
if (FileUtility.IsValidDirectoryEntryName(name))
return true;
MessageService.ShowMessage(StringParser.Parse("${res:ICSharpCode.SharpDevelop.Commands.SaveFile.InvalidFileNameError}", new StringTagPair("FileName", name)));
return false;
return SD.FileService.CheckDirectoryEntryName(name);
}
internal sealed class LoadFileWrapper
@ -207,7 +66,7 @@ namespace ICSharpCode.SharpDevelop @@ -207,7 +66,7 @@ namespace ICSharpCode.SharpDevelop
public void Invoke(string fileName)
{
OpenedFile file = FileService.GetOrCreateOpenedFile(FileName.Create(fileName));
OpenedFile file = SD.FileService.GetOrCreateOpenedFile(FileName.Create(fileName));
IViewContent newContent = binding.CreateContentForFile(file);
if (newContent != null) {
DisplayBindingService.AttachSubWindows(newContent, false);
@ -226,7 +85,7 @@ namespace ICSharpCode.SharpDevelop @@ -226,7 +85,7 @@ namespace ICSharpCode.SharpDevelop
public static bool IsOpen(string fileName)
{
return GetOpenFile(fileName) != null;
return SD.FileService.IsOpen(FileName.Create(fileName));
}
/// <summary>
@ -237,7 +96,7 @@ namespace ICSharpCode.SharpDevelop @@ -237,7 +96,7 @@ namespace ICSharpCode.SharpDevelop
/// <returns>The existing or opened <see cref="IViewContent"/> for the specified file.</returns>
public static IViewContent OpenFile(string fileName)
{
return OpenFile(fileName, true);
return SD.FileService.OpenFile(FileName.Create(fileName));
}
/// <summary>
@ -249,26 +108,7 @@ namespace ICSharpCode.SharpDevelop @@ -249,26 +108,7 @@ namespace ICSharpCode.SharpDevelop
/// <returns>The existing or opened <see cref="IViewContent"/> for the specified file.</returns>
public static IViewContent OpenFile(string fileName, bool switchToOpenedView)
{
fileName = FileUtility.NormalizePath(fileName);
LoggingService.Info("Open file " + fileName);
IViewContent viewContent = GetOpenFile(fileName);
if (viewContent != null) {
if (switchToOpenedView) {
viewContent.WorkbenchWindow.SelectWindow();
}
return viewContent;
}
IDisplayBinding binding = DisplayBindingService.GetBindingPerFileName(fileName);
if (binding == null) {
binding = new ErrorFallbackBinding("Could not find any display binding for " + Path.GetFileName(fileName));
}
if (FileUtility.ObservedLoad(new NamedFileOperationDelegate(new LoadFileWrapper(binding, switchToOpenedView).Invoke), fileName) == FileOperationResult.OK) {
FileService.RecentOpen.AddLastFile(fileName);
}
return GetOpenFile(fileName);
return SD.FileService.OpenFile(FileName.Create(fileName), switchToOpenedView);
}
/// <summary>
@ -278,7 +118,7 @@ namespace ICSharpCode.SharpDevelop @@ -278,7 +118,7 @@ namespace ICSharpCode.SharpDevelop
/// <param name="content">Content of the file to create</param>
public static IViewContent NewFile(string defaultName, string content)
{
return NewFile(defaultName, SD.FileService.DefaultFileEncoding.GetBytesWithPreamble(content));
return SD.FileService.NewFile(defaultName, content);
}
/// <summary>
@ -288,29 +128,7 @@ namespace ICSharpCode.SharpDevelop @@ -288,29 +128,7 @@ namespace ICSharpCode.SharpDevelop
/// <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) {
binding = new ErrorFallbackBinding("Can't create display binding for file " + defaultName);
}
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;
return SD.FileService.NewFile(defaultName, content);
}
/// <summary>
@ -319,13 +137,7 @@ namespace ICSharpCode.SharpDevelop @@ -319,13 +137,7 @@ namespace ICSharpCode.SharpDevelop
/// </summary>
public static IList<FileName> GetOpenFiles()
{
List<FileName> fileNames = new List<FileName>();
foreach (IViewContent content in WorkbenchSingleton.Workbench.ViewContentCollection) {
FileName contentName = content.PrimaryFileName;
if (contentName != null && !fileNames.Contains(contentName))
fileNames.Add(contentName);
}
return fileNames;
return SD.FileService.OpenPrimaryFiles.ToArray();
}
/// <summary>
@ -333,55 +145,7 @@ namespace ICSharpCode.SharpDevelop @@ -333,55 +145,7 @@ namespace ICSharpCode.SharpDevelop
/// </summary>
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;
}
public static bool DeleteToRecycleBin {
get {
return PropertyService.Get("SharpDevelop.DeleteToRecycleBin", true);
}
set {
PropertyService.Set("SharpDevelop.DeleteToRecycleBin", value);
}
}
public static bool SaveUsingTemporaryFile {
get {
return PropertyService.Get("SharpDevelop.SaveUsingTemporaryFile", true);
}
set {
PropertyService.Set("SharpDevelop.SaveUsingTemporaryFile", value);
}
}
public static int DefaultFileEncodingCodePage {
get { return PropertyService.Get("SharpDevelop.DefaultFileEncoding", 65001); }
set { PropertyService.Set("SharpDevelop.DefaultFileEncoding", value); }
}
static readonly ReadOnlyCollection<EncodingInfo> allEncodings = Encoding.GetEncodings().OrderBy(e => e.DisplayName).ToArray().AsReadOnly();
public static ReadOnlyCollection<EncodingInfo> AllEncodings {
get { return allEncodings; }
}
public static EncodingInfo DefaultFileEncoding {
get {
int cp = FileService.DefaultFileEncodingCodePage;
return allEncodings.Single(e => e.CodePage == cp);
}
set {
FileService.DefaultFileEncodingCodePage = value.CodePage;
}
return SD.FileService.GetOpenFile(FileName.Create(fileName));
}
/// <summary>
@ -389,36 +153,7 @@ namespace ICSharpCode.SharpDevelop @@ -389,36 +153,7 @@ namespace ICSharpCode.SharpDevelop
/// </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)) {
if (DeleteToRecycleBin)
NativeMethods.DeleteToRecycleBin(fileName);
else
Directory.Delete(fileName, true);
}
} catch (Exception e) {
MessageService.ShowHandledException(e, "Can't remove directory " + fileName);
}
} else {
try {
if (File.Exists(fileName)) {
if (DeleteToRecycleBin)
NativeMethods.DeleteToRecycleBin(fileName);
else
File.Delete(fileName);
}
} catch (Exception e) {
MessageService.ShowHandledException(e, "Can't remove file " + fileName);
}
}
}
OnFileRemoved(new FileEventArgs(fileName, isDirectory));
SD.FileService.RemoveFile(fileName, isDirectory);
}
/// <summary>
@ -426,45 +161,7 @@ namespace ICSharpCode.SharpDevelop @@ -426,45 +161,7 @@ namespace ICSharpCode.SharpDevelop
/// </summary>
public static bool RenameFile(string oldName, string newName, bool isDirectory)
{
if (FileUtility.IsEqualFileName(oldName, newName))
return false;
FileChangeWatcher.DisableAllChangeWatchers();
try {
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.ShowHandledException(e, "Can't rename directory " + oldName);
} else {
MessageService.ShowHandledException(e, "Can't rename file " + oldName);
}
return false;
}
}
OnFileRenamed(new FileRenameEventArgs(oldName, newName, isDirectory));
return true;
} finally {
FileChangeWatcher.EnableAllChangeWatchers();
}
return SD.FileService.RenameFile(oldName, newName, isDirectory);
}
/// <summary>
@ -472,40 +169,7 @@ namespace ICSharpCode.SharpDevelop @@ -472,40 +169,7 @@ namespace ICSharpCode.SharpDevelop
/// </summary>
public static bool CopyFile(string oldName, string newName, bool isDirectory, bool overwrite)
{
if (FileUtility.IsEqualFileName(oldName, newName))
return false;
FileRenamingEventArgs eargs = new FileRenamingEventArgs(oldName, newName, isDirectory);
OnFileCopying(eargs);
if (eargs.Cancel)
return false;
if (!eargs.OperationAlreadyDone) {
try {
if (isDirectory && Directory.Exists(oldName)) {
if (!overwrite && Directory.Exists(newName)) {
MessageService.ShowMessage(StringParser.Parse("${res:Gui.ProjectBrowser.FileInUseError}"));
return false;
}
FileUtility.DeepCopy(oldName, newName, overwrite);
} else if (File.Exists(oldName)) {
if (!overwrite && File.Exists(newName)) {
MessageService.ShowMessage(StringParser.Parse("${res:Gui.ProjectBrowser.FileInUseError}"));
return false;
}
File.Copy(oldName, newName, overwrite);
}
} catch (Exception e) {
if (isDirectory) {
MessageService.ShowHandledException(e, "Can't copy directory " + oldName);
} else {
MessageService.ShowHandledException(e, "Can't copy file " + oldName);
}
return false;
}
}
OnFileCopied(new FileRenameEventArgs(oldName, newName, isDirectory));
return true;
return SD.FileService.CopyFile(oldName, newName, isDirectory, overwrite);
}
/// <summary>
@ -514,38 +178,7 @@ namespace ICSharpCode.SharpDevelop @@ -514,38 +178,7 @@ namespace ICSharpCode.SharpDevelop
/// </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);
if (fileName == null || fileName.Length == 0) {
return null;
}
NavigationService.SuspendLogging();
bool loggingResumed = false;
try {
IViewContent content = OpenFile(fileName);
if (content is IPositionable) {
// TODO: enable jumping to a particular view
content.WorkbenchWindow.ActiveViewContent = content;
NavigationService.ResumeLogging();
loggingResumed = true;
((IPositionable)content).JumpTo(Math.Max(1, line), Math.Max(1, column));
} else {
NavigationService.ResumeLogging();
loggingResumed = true;
NavigationService.Log(content);
}
return content;
} finally {
LoggingService.InfoFormatted("FileService\n\tJumped to File Position: [{0} : {1}x{2}]", fileName, line, column);
if (!loggingResumed) {
NavigationService.ResumeLogging();
}
}
return SD.FileService.JumpToFilePosition(FileName.Create(fileName), line, column);
}
/// <summary>
@ -554,7 +187,8 @@ namespace ICSharpCode.SharpDevelop @@ -554,7 +187,8 @@ namespace ICSharpCode.SharpDevelop
/// behaviour of the FolderBrowserDialog is used where it selects the
/// desktop folder.
/// </summary>
public static FolderBrowserDialog CreateFolderBrowserDialog(string description, string selectedPath)
[Obsolete("Use SD.FileService.BrowseForFolder instead.")]
public static FolderBrowserDialog CreateFolderBrowserDialog(string description, string selectedPath = null)
{
FolderBrowserDialog dialog = new FolderBrowserDialog();
dialog.Description = StringParser.Parse(description);
@ -565,59 +199,6 @@ namespace ICSharpCode.SharpDevelop @@ -565,59 +199,6 @@ namespace ICSharpCode.SharpDevelop
return dialog;
}
/// <summary>
/// Creates a FolderBrowserDialog that will initially select the
/// desktop folder.
/// </summary>
public static FolderBrowserDialog CreateFolderBrowserDialog(string description)
{
return CreateFolderBrowserDialog(description, null);
}
#region Event Handlers
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);
}
}
static void OnFileCopied(FileRenameEventArgs e)
{
if (FileCopied != null) {
FileCopied(null, e);
}
}
static void OnFileCopying(FileRenamingEventArgs e) {
if (FileCopying != null) {
FileCopying(null, e);
}
}
#endregion Event Handlers
#region Static event firing methods
/// <summary>
@ -627,11 +208,7 @@ namespace ICSharpCode.SharpDevelop @@ -627,11 +208,7 @@ namespace ICSharpCode.SharpDevelop
/// <param name="isDirectory">Set to true if this is a directory</param>
public static bool FireFileReplacing(string fileName, bool isDirectory)
{
FileCancelEventArgs e = new FileCancelEventArgs(fileName, isDirectory);
if (FileReplacing != null) {
FileReplacing(null, e);
}
return !e.Cancel;
return SD.FileService.FireFileReplacing(fileName, isDirectory);
}
/// <summary>
@ -641,9 +218,7 @@ namespace ICSharpCode.SharpDevelop @@ -641,9 +218,7 @@ namespace ICSharpCode.SharpDevelop
/// <param name="isDirectory">Set to true if this is a directory</param>
public static void FireFileReplaced(string fileName, bool isDirectory)
{
if (FileReplaced != null) {
FileReplaced(null, new FileEventArgs(fileName, isDirectory));
}
SD.FileService.FireFileReplaced(fileName, isDirectory);
}
/// <summary>
@ -653,59 +228,53 @@ namespace ICSharpCode.SharpDevelop @@ -653,59 +228,53 @@ namespace ICSharpCode.SharpDevelop
/// <param name="isDirectory">Set to true if this is a directory</param>
public static void FireFileCreated(string fileName, bool isDirectory)
{
if (FileCreated != null) {
FileCreated(null, new FileEventArgs(fileName, isDirectory));
}
SD.FileService.FireFileCreated(fileName, isDirectory);
}
#endregion Static event firing methods
#region Events
public static event EventHandler<FileEventArgs> FileCreated;
public static event EventHandler<FileRenamingEventArgs> FileRenaming;
public static event EventHandler<FileRenameEventArgs> FileRenamed;
public static event EventHandler<FileRenamingEventArgs> FileRenaming {
add { SD.FileService.FileRenaming += value; }
remove { SD.FileService.FileRenaming -= value; }
}
public static event EventHandler<FileRenameEventArgs> FileRenamed {
add { SD.FileService.FileRenamed += value; }
remove { SD.FileService.FileRenamed -= value; }
}
public static event EventHandler<FileRenamingEventArgs> FileCopying;
public static event EventHandler<FileRenameEventArgs> FileCopied;
public static event EventHandler<FileRenamingEventArgs> FileCopying {
add { SD.FileService.FileCopying += value; }
remove { SD.FileService.FileCopying -= value; }
}
public static event EventHandler<FileRenameEventArgs> FileCopied {
add { SD.FileService.FileCopied += value; }
remove { SD.FileService.FileCopied -= value; }
}
public static event EventHandler<FileCancelEventArgs> FileRemoving;
public static event EventHandler<FileEventArgs> FileRemoved;
public static event EventHandler<FileCancelEventArgs> FileRemoving {
add { SD.FileService.FileRemoving += value; }
remove { SD.FileService.FileRemoving -= value; }
}
public static event EventHandler<FileEventArgs> FileRemoved {
add { SD.FileService.FileRemoved += value; }
remove { SD.FileService.FileRemoved -= value; }
}
public static event EventHandler<FileCancelEventArgs> FileReplacing;
public static event EventHandler<FileEventArgs> FileReplaced;
public static event EventHandler<FileEventArgs> FileCreated {
add { SD.FileService.FileCreated += value; }
remove { SD.FileService.FileCreated -= value; }
}
public static event EventHandler<FileCancelEventArgs> FileReplacing {
add { SD.FileService.FileReplacing += value; }
remove { SD.FileService.FileReplacing -= value; }
}
public static event EventHandler<FileEventArgs> FileReplaced {
add { SD.FileService.FileReplaced += value; }
remove { SD.FileService.FileReplaced -= value; }
}
#endregion Events
sealed class ErrorFallbackBinding : IDisplayBinding
{
string errorMessage;
public ErrorFallbackBinding(string errorMessage)
{
this.errorMessage = errorMessage;
}
public bool CanCreateContentForFile(string fileName)
{
return true;
}
public IViewContent CreateContentForFile(OpenedFile file)
{
return new SimpleViewContent(errorMessage) { TitleName = Path.GetFileName(file.FileName) };
}
public bool IsPreferredBindingForFile(string fileName)
{
return false;
}
public double AutoDetectFileContent(string fileName, Stream fileContent, string detectedMimeType)
{
return double.NegativeInfinity;
}
}
}
}

195
src/Main/Base/Project/Src/Services/File/IFileService.cs

@ -2,10 +2,13 @@ @@ -2,10 +2,13 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using ICSharpCode.Core;
using ICSharpCode.NRefactory.Editor;
using ICSharpCode.SharpDevelop.Gui;
namespace ICSharpCode.SharpDevelop
{
@ -14,12 +17,24 @@ namespace ICSharpCode.SharpDevelop @@ -14,12 +17,24 @@ namespace ICSharpCode.SharpDevelop
/// </summary>
public interface IFileService
{
#region Options
IRecentOpen RecentOpen { get; }
bool DeleteToRecycleBin { get; set; }
bool SaveUsingTemporaryFile { get; set; }
/// <summary>
/// Gets the default file encoding.
/// This property is thread-safe.
/// </summary>
Encoding DefaultFileEncoding { get; }
EncodingInfo DefaultFileEncodingInfo { get; set; }
IReadOnlyList<EncodingInfo> AllEncodings { get; }
#endregion
#region GetFileContent
/// <summary>
/// Gets the content of the specified file.
/// If the file is currently open in SharpDevelop, retrieves a snapshot
@ -51,5 +66,185 @@ namespace ICSharpCode.SharpDevelop @@ -51,5 +66,185 @@ namespace ICSharpCode.SharpDevelop
/// This method is thread-safe.
/// </remarks>
ITextSource GetFileContentFromDisk(FileName fileName, CancellationToken cancellationToken = default(CancellationToken));
#endregion
#region BrowseForFolder
/// <summary>
/// Shows a 'browse for folder' dialog.
/// </summary>
/// <param name="description">Description shown in the dialog.</param>
/// <param name="selectedPath">Optional: Initially selected folder.</param>
/// <returns>The selected folder; or <c>null</c> if the user cancelled the dialog.</returns>
string BrowseForFolder(string description, string selectedPath = null);
#endregion
#region OpenedFiles
/// <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>
IReadOnlyList<OpenedFile> OpenedFiles { get; }
/// <summary>
/// Gets an opened file, or returns null if the file is not opened.
/// </summary>
OpenedFile GetOpenedFile(string fileName);
/// <summary>
/// Gets an opened file, or returns null if the file is not opened.
/// </summary>
OpenedFile GetOpenedFile(FileName fileName);
/// <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>
OpenedFile GetOrCreateOpenedFile(string fileName);
/// <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>
OpenedFile GetOrCreateOpenedFile(FileName fileName);
/// <summary>
/// Creates a new untitled OpenedFile.
/// </summary>
OpenedFile CreateUntitledOpenedFile(string defaultName, byte[] content);
#endregion
#region CheckFileName
/// <summary>
/// Checks if the path is valid <b>and shows a MessageBox if it is not valid</b>.
/// Do not use in non-UI methods.
/// </summary>
/// <seealso cref="FileUtility.IsValidPath"/>
bool CheckFileName(string path);
/// <summary>
/// Checks that a single directory entry (file or subdirectory) name is valid
/// <b>and shows a MessageBox if it is not valid</b>.
/// </summary>
/// <param name="name">A single file name not the full path</param>
/// <seealso cref="FileUtility.IsValidDirectoryEntryName"/>
bool CheckDirectoryEntryName(string name);
#endregion
#region OpenFile (ViewContent)
/// <summary>
/// Gets whether the file is open in a view content.
/// </summary>
bool IsOpen(FileName fileName);
/// <summary>
/// Opens a view content for the specified file
/// or returns the existing view content for the file if it is already open.
/// </summary>
/// <param name="fileName">The name of the file to open.</param>
/// <param name="switchToOpenedView">Specifies whether to switch to the view for the specified file.</param>
/// <returns>The existing or opened <see cref="IViewContent"/> for the specified file.</returns>
IViewContent OpenFile(FileName fileName, bool switchToOpenedView = true);
/// <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>
IViewContent NewFile(string defaultName, string 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>
IViewContent NewFile(string defaultName, byte[] content);
/// <summary>
/// Gets a list of the names of the files that are open as primary files
/// in view contents.
/// </summary>
IReadOnlyList<FileName> OpenPrimaryFiles { get; }
/// <summary>
/// Gets the IViewContent for a fileName. Returns null if the file is not opened currently.
/// </summary>
IViewContent GetOpenFile(FileName fileName);
/// <summary>
/// Opens the specified file and jumps to the specified file position.
/// Line and column start counting at 1.
/// </summary>
IViewContent JumpToFilePosition(FileName fileName, int line, int column);
#endregion
#region Remove/Rename/Copy
/// <summary>
/// Removes a file, raising the appropriate events. This method may show message boxes.
/// </summary>
void RemoveFile(string fileName, bool isDirectory);
/// <summary>
/// Renames or moves a file, raising the appropriate events. This method may show message boxes.
/// </summary>
bool RenameFile(string oldName, string newName, bool isDirectory);
/// <summary>
/// Copies a file, raising the appropriate events. This method may show message boxes.
/// </summary>
bool CopyFile(string oldName, string newName, bool isDirectory, bool overwrite);
event EventHandler<FileRenamingEventArgs> FileRenaming;
event EventHandler<FileRenameEventArgs> FileRenamed;
event EventHandler<FileRenamingEventArgs> FileCopying;
event EventHandler<FileRenameEventArgs> FileCopied;
event EventHandler<FileCancelEventArgs> FileRemoving;
event EventHandler<FileEventArgs> FileRemoved;
#endregion
#region FileCreated/Replaced
/// <summary>
/// Fires the event handlers for a file being created.
/// </summary>
/// <param name="fileName">The name of the file being created. This should be a fully qualified path.</param>
/// <param name="isDirectory">Set to true if this is a directory</param>
/// <returns>True if the operation can proceed, false if an event handler cancelled the operation.</returns>
bool FireFileReplacing(string fileName, bool isDirectory);
/// <summary>
/// Fires the event handlers for a file being replaced.
/// </summary>
/// <param name="fileName">The name of the file being created. This should be a fully qualified path.</param>
/// <param name="isDirectory">Set to true if this is a directory</param>
void FireFileReplaced(string fileName, bool isDirectory);
/// <summary>
/// Fires the event handlers for a file being created.
/// </summary>
/// <param name="fileName">The name of the file being created. This should be a fully qualified path.</param>
/// <param name="isDirectory">Set to true if this is a directory</param>
void FireFileCreated(string fileName, bool isDirectory);
event EventHandler<FileEventArgs> FileCreated;
event EventHandler<FileCancelEventArgs> FileReplacing;
event EventHandler<FileEventArgs> FileReplaced;
#endregion
}
public interface IRecentOpen
{
IReadOnlyList<string> RecentFiles { get; }
IReadOnlyList<string> RecentProjects { get; }
void ClearRecentFiles();
void ClearRecentProjects();
void AddRecentFile(string fileName);
void AddRecentProject(string fileName);
}
}

147
src/Main/Base/Project/Src/Services/File/OpenedFile.cs

@ -201,7 +201,7 @@ namespace ICSharpCode.SharpDevelop @@ -201,7 +201,7 @@ namespace ICSharpCode.SharpDevelop
throw new InvalidOperationException("Cannot save an untitled file to disk!");
LoggingService.Debug("Save " + FileName);
bool safeSaving = FileService.SaveUsingTemporaryFile && File.Exists(FileName);
bool safeSaving = SD.FileService.SaveUsingTemporaryFile && File.Exists(FileName);
string saveAs = safeSaving ? FileName + ".bak" : FileName;
using (FileStream fs = new FileStream(saveAs, FileMode.Create, FileAccess.Write)) {
if (currentView != null) {
@ -354,149 +354,4 @@ namespace ICSharpCode.SharpDevelop @@ -354,149 +354,4 @@ namespace ICSharpCode.SharpDevelop
}
}
}
sealed class FileServiceOpenedFile : OpenedFile
{
List<IViewContent> registeredViews = new List<IViewContent>();
FileChangeWatcher fileChangeWatcher;
protected override void ChangeFileName(FileName newValue)
{
FileService.OpenedFileFileNameChange(this, this.FileName, newValue);
base.ChangeFileName(newValue);
}
internal FileServiceOpenedFile(FileName fileName)
{
this.FileName = fileName;
IsUntitled = false;
fileChangeWatcher = new FileChangeWatcher(this);
}
internal FileServiceOpenedFile(byte[] fileData)
{
this.FileName = null;
SetData(fileData);
IsUntitled = true;
MakeDirty();
fileChangeWatcher = new FileChangeWatcher(this);
}
/// <summary>
/// Gets the list of view contents registered with this opened file.
/// </summary>
public override IList<IViewContent> RegisteredViewContents {
get { return registeredViews.AsReadOnly(); }
}
public override void ForceInitializeView(IViewContent view)
{
if (view == null)
throw new ArgumentNullException("view");
if (!registeredViews.Contains(view))
throw new ArgumentException("registeredViews must contain view");
base.ForceInitializeView(view);
}
public override void RegisterView(IViewContent view)
{
if (view == null)
throw new ArgumentNullException("view");
if (registeredViews.Contains(view))
throw new ArgumentException("registeredViews already contains view");
registeredViews.Add(view);
if (WorkbenchSingleton.Workbench != null) {
WorkbenchSingleton.Workbench.ActiveViewContentChanged += WorkbenchActiveViewContentChanged;
if (WorkbenchSingleton.Workbench.ActiveViewContent == view) {
SwitchedToView(view);
}
}
#if DEBUG
view.Disposed += ViewDisposed;
#endif
}
public override void UnregisterView(IViewContent view)
{
if (view == null)
throw new ArgumentNullException("view");
Debug.Assert(registeredViews.Contains(view));
if (WorkbenchSingleton.Workbench != null) {
WorkbenchSingleton.Workbench.ActiveViewContentChanged -= WorkbenchActiveViewContentChanged;
}
#if DEBUG
view.Disposed -= ViewDisposed;
#endif
registeredViews.Remove(view);
if (registeredViews.Count > 0) {
if (currentView == view) {
SaveCurrentView();
currentView = null;
}
} else {
// all views to the file were closed
CloseIfAllViewsClosed();
}
}
public override void CloseIfAllViewsClosed()
{
if (registeredViews.Count == 0) {
bool wasDirty = this.IsDirty;
FileService.OpenedFileClosed(this);
FileClosed.RaiseEvent(this, EventArgs.Empty);
if (fileChangeWatcher != null) {
fileChangeWatcher.Dispose();
fileChangeWatcher = null;
}
if (wasDirty) {
// We discarded some information when closing the file,
// so we need to re-parse it.
if (File.Exists(this.FileName))
SD.ParserService.ParseAsync(this.FileName).FireAndForget();
else
SD.ParserService.ClearParseInformation(this.FileName);
}
}
}
#if DEBUG
void ViewDisposed(object sender, EventArgs e)
{
Debug.Fail("View was disposed while still registered with OpenedFile!");
}
#endif
void WorkbenchActiveViewContentChanged(object sender, EventArgs e)
{
IViewContent newView = WorkbenchSingleton.Workbench.ActiveViewContent;
if (!registeredViews.Contains(newView))
return;
SwitchedToView(newView);
}
public override void SaveToDisk()
{
try {
if (fileChangeWatcher != null)
fileChangeWatcher.Enabled = false;
base.SaveToDisk();
} finally {
if (fileChangeWatcher != null)
fileChangeWatcher.Enabled = true;
}
}
public override event EventHandler FileClosed;
}
}

132
src/Main/Base/Project/Src/Services/File/RecentOpen.cs

@ -1,132 +0,0 @@ @@ -1,132 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Windows.Shell;
using ICSharpCode.Core;
namespace ICSharpCode.SharpDevelop
{
/// <summary>
/// This class handles the recent open files and the recent open project files of SharpDevelop
/// it checks, if the files exists at every creation, and if not it doesn't list them in the
/// recent files, and they'll not be saved during the next option save.
/// </summary>
public sealed class RecentOpen
{
/// <summary>
/// This variable is the maximal length of lastfile/lastopen entries
/// must be > 0
/// </summary>
int MAX_LENGTH = 10;
readonly ObservableCollection<string> lastfile = new ObservableCollection<string>();
readonly ObservableCollection<string> lastproject = new ObservableCollection<string>();
public IList<string> RecentFile {
get {
return lastfile;
}
}
public IList<string> RecentProject {
get {
return lastproject;
}
}
public RecentOpen()
{
}
public RecentOpen(Properties p)
{
// don't check whether files exist because that might be slow (e.g. if file is on network
// drive that's unavailable)
lastfile.AddRange(p.GetList<string>("Files"));
lastproject.AddRange(p.GetList<string>("Projects"));
}
public void AddLastFile(string name)
{
for (int i = 0; i < lastfile.Count; ++i) {
if (lastfile[i].Equals(name, StringComparison.OrdinalIgnoreCase)) {
lastfile.RemoveAt(i);
}
}
while (lastfile.Count >= MAX_LENGTH) {
lastfile.RemoveAt(lastfile.Count - 1);
}
lastfile.Insert(0, name);
}
public void ClearRecentFiles()
{
lastfile.Clear();
}
public void ClearRecentProjects()
{
lastproject.Clear();
}
public void AddLastProject(string name)
{
for (int i = 0; i < lastproject.Count; ++i) {
if (lastproject[i].ToString().Equals(name, StringComparison.OrdinalIgnoreCase)) {
lastproject.RemoveAt(i);
}
}
while (lastproject.Count >= MAX_LENGTH) {
lastproject.RemoveAt(lastproject.Count - 1);
}
lastproject.Insert(0, name);
JumpList.AddToRecentCategory(name);
}
public static RecentOpen FromXmlElement(Properties properties)
{
return new RecentOpen(properties);
}
public Properties ToProperties()
{
Properties p = new Properties();
p.SetList("Files", lastfile);
p.SetList("Projects", lastproject);
return p;
}
internal void FileRemoved(object sender, FileEventArgs e)
{
for (int i = 0; i < lastfile.Count; ++i) {
string file = lastfile[i].ToString();
if (e.FileName == file) {
lastfile.RemoveAt(i);
break;
}
}
}
internal void FileRenamed(object sender, FileRenameEventArgs e)
{
for (int i = 0; i < lastfile.Count; ++i) {
string file = lastfile[i].ToString();
if (e.SourceFile == file) {
lastfile.RemoveAt(i);
lastfile.Insert(i, e.TargetFile);
break;
}
}
}
}
}

2
src/Main/Base/Project/Src/Services/ProjectService/ParseableFileContentFinder.cs

@ -17,7 +17,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -17,7 +17,7 @@ namespace ICSharpCode.SharpDevelop.Project
/// </summary>
public class ParseableFileContentFinder
{
FileName[] viewContentFileNamesCollection = SD.MainThread.InvokeIfRequired(() => FileService.OpenedFiles.Select(f => f.FileName).ToArray());
FileName[] viewContentFileNamesCollection = SD.MainThread.InvokeIfRequired(() => SD.FileService.OpenedFiles.Select(f => f.FileName).ToArray());
/// <summary>
/// Retrieves the file contents for the specified project items.

1
src/Main/Base/Project/Src/Services/ProjectService/ProjectService.cs

@ -301,6 +301,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -301,6 +301,7 @@ namespace ICSharpCode.SharpDevelop.Project
MessageService.ShowException(ex);
}
SD.ParserService.InvalidateCurrentSolutionSnapshot();
SD.FileService.RecentOpen.AddRecentProject(openSolution.FileName);
Project.Converter.UpgradeViewContent.ShowIfRequired(openSolution);

2
src/Main/Base/Project/Src/Services/RefactoringService/FindReferencesAndRenameHelper.cs

@ -237,7 +237,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring @@ -237,7 +237,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring
[Obsolete]
public static ProvidedDocumentInformation GetDocumentInformation(string fileName)
{
OpenedFile file = FileService.GetOpenedFile(fileName);
OpenedFile file = SD.FileService.GetOpenedFile(fileName);
if (file != null) {
IFileDocumentProvider documentProvider = file.CurrentView as IFileDocumentProvider;
if (documentProvider != null) {

2
src/Main/Base/Project/Src/Util/FakeXmlViewContent.cs

@ -17,7 +17,7 @@ namespace ICSharpCode.SharpDevelop.Util @@ -17,7 +17,7 @@ namespace ICSharpCode.SharpDevelop.Util
{
public FakeXmlViewContent(string fileName)
{
this.PrimaryFile = FileService.GetOrCreateOpenedFile(fileName);
this.PrimaryFile = SD.FileService.GetOrCreateOpenedFile(fileName);
this.oldView = this.PrimaryFile.CurrentView;
this.PrimaryFile.RegisterView(this);
this.PrimaryFile.SwitchedToView(this);

14
src/Main/Base/Project/Src/Gui/Dialogs/OptionPanels/IDEOptions/LoadSaveOptions.xaml → src/Main/SharpDevelop/OptionPanels/LoadSaveOptions.xaml

@ -1,8 +1,10 @@ @@ -1,8 +1,10 @@
<gui:OptionPanel x:Class="ICSharpCode.SharpDevelop.Gui.OptionPanels.LoadSaveOptions"
x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:core="http://icsharpcode.net/sharpdevelop/core"
xmlns:sd="clr-namespace:ICSharpCode.SharpDevelop"
xmlns:gui="clr-namespace:ICSharpCode.SharpDevelop.Gui"
xmlns:sd="clr-namespace:ICSharpCode.SharpDevelop;assembly=ICSharpCode.SharpDevelop"
xmlns:gui="clr-namespace:ICSharpCode.SharpDevelop.Gui;assembly=ICSharpCode.SharpDevelop"
xmlns:wb="clr-namespace:ICSharpCode.SharpDevelop.Workbench"
xmlns:widgets="http://icsharpcode.net/sharpdevelop/widgets"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ICSharpCode.SharpDevelop.Gui.OptionPanels">
@ -10,7 +12,7 @@ @@ -10,7 +12,7 @@
<GroupBox Header="{core:Localize Dialog.Options.IDEOptions.LoadSaveOptions.LoadLabel}">
<widgets:StackPanelWithSpacing SpaceBetweenItems="5">
<CheckBox Content="{core:Localize Dialog.Options.IDEOptions.LoadSaveOptions.LoadUserDataCheckBox}"
IsChecked="{core:OptionBinding gui:WpfWorkbench.LoadDocumentProperties}" />
IsChecked="{core:OptionBinding wb:WpfWorkbench.LoadDocumentProperties}" />
<CheckBox Name="detectExternalChanges"
Content="{core:Localize Dialog.Options.IDEOptions.LoadSaveOptions.DetectExternalChanges}"
IsChecked="{core:OptionBinding sd:FileChangeWatcher.DetectExternalChangesOption}" />
@ -23,15 +25,15 @@ @@ -23,15 +25,15 @@
<GroupBox Header="{core:Localize Dialog.Options.IDEOptions.LoadSaveOptions.SaveLabel}">
<StackPanel>
<CheckBox Content="{core:Localize Dialog.Options.IDEOptions.LoadSaveOptions.CreateBackupCopyCheckBox}"
IsChecked="{core:OptionBinding sd:FileService.SaveUsingTemporaryFile}" />
IsChecked="{core:OptionBinding wb:FileService.SaveUsingTemporaryFile}" />
<Label
Content="{core:Localize Dialog.Options.IDEOptions.LoadSaveOptions.DefaultFileEncoding}"
Target="{Binding ElementName=encodingComboBox}" />
<ComboBox
Name="encodingComboBox"
Margin="18,0,4,0"
ItemsSource="{x:Static sd:FileService.AllEncodings}"
SelectedItem="{core:OptionBinding sd:FileService.DefaultFileEncoding}">
ItemsSource="{Binding AllEncodings, Source={x:Static sd:SD.FileService}}"
SelectedItem="{core:OptionBinding wb:FileService.DefaultFileEncodingInfo}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}" />

2
src/Main/Base/Project/Src/Gui/Dialogs/OptionPanels/IDEOptions/LoadSaveOptions.xaml.cs → src/Main/SharpDevelop/OptionPanels/LoadSaveOptions.xaml.cs

@ -15,7 +15,7 @@ using ICSharpCode.Core; @@ -15,7 +15,7 @@ using ICSharpCode.Core;
namespace ICSharpCode.SharpDevelop.Gui.OptionPanels
{
public partial class LoadSaveOptions : OptionPanel
partial class LoadSaveOptions : OptionPanel
{
public LoadSaveOptions()
{

8
src/Main/SharpDevelop/SharpDevelop.csproj

@ -77,6 +77,10 @@ @@ -77,6 +77,10 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="OptionPanels\LoadSaveOptions.xaml.cs">
<DependentUpon>LoadSaveOptions.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="Startup\App.xaml.cs" />
<Compile Include="Startup\SharpDevelopMain.cs" />
<Compile Include="Startup\SplashScreen.cs" />
@ -86,8 +90,10 @@ @@ -86,8 +90,10 @@
<Compile Include="Workbench\ChooseLayoutCommand.cs" />
<Compile Include="Workbench\DispatcherMessageLoop.cs" />
<Compile Include="Workbench\FileService.cs" />
<Compile Include="Workbench\FileServiceOpenedFile.cs" />
<Compile Include="Workbench\FullScreenEnabledWindow.cs" />
<Compile Include="Workbench\LayoutConfiguration.cs" />
<Compile Include="Workbench\RecentOpen.cs" />
<Compile Include="Workbench\SDStatusBar.cs" />
<Compile Include="Workbench\SingleInstanceHelper.cs" />
<Compile Include="Workbench\StatusBarService.cs" />
@ -182,6 +188,7 @@ @@ -182,6 +188,7 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Folder Include="OptionPanels" />
<Folder Include="Startup" />
<Folder Include="Workbench" />
<Folder Include="Logging" />
@ -189,6 +196,7 @@ @@ -189,6 +196,7 @@
<Folder Include="Sda" />
</ItemGroup>
<ItemGroup>
<Page Include="OptionPanels\LoadSaveOptions.xaml" />
<Page Include="Startup\App.xaml" />
<Page Include="Workbench\WpfWorkbench.xaml" />
</ItemGroup>

588
src/Main/SharpDevelop/Workbench/FileService.cs

@ -2,24 +2,87 @@ @@ -2,24 +2,87 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using ICSharpCode.AvalonEdit.Utils;
using ICSharpCode.Core;
using ICSharpCode.NRefactory.Editor;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Gui;
namespace ICSharpCode.SharpDevelop.Workbench
{
sealed class FileService : IFileService
{
#region Options
/// <summary>used for OptionBinding</summary>
public static FileService Instance {
get { return (FileService)SD.FileService; }
}
IRecentOpen recentOpen;
public IRecentOpen RecentOpen {
get {
return LazyInitializer.EnsureInitialized(
ref recentOpen, () => new RecentOpen(PropertyService.NestedProperties("RecentOpen")));
}
}
public bool DeleteToRecycleBin {
get {
return PropertyService.Get("SharpDevelop.DeleteToRecycleBin", true);
}
set {
PropertyService.Set("SharpDevelop.DeleteToRecycleBin", value);
}
}
public bool SaveUsingTemporaryFile {
get {
return PropertyService.Get("SharpDevelop.SaveUsingTemporaryFile", true);
}
set {
PropertyService.Set("SharpDevelop.SaveUsingTemporaryFile", value);
}
}
#endregion
#region DefaultFileEncoding
public int DefaultFileEncodingCodePage {
get { return PropertyService.Get("SharpDevelop.DefaultFileEncoding", 65001); }
set { PropertyService.Set("SharpDevelop.DefaultFileEncoding", value); }
}
public Encoding DefaultFileEncoding {
get {
return Encoding.GetEncoding(SharpDevelop.FileService.DefaultFileEncodingCodePage);
return Encoding.GetEncoding(DefaultFileEncodingCodePage);
}
}
readonly EncodingInfo[] allEncodings = Encoding.GetEncodings().OrderBy(e => e.DisplayName).ToArray();
public IReadOnlyList<EncodingInfo> AllEncodings {
get { return allEncodings; }
}
public EncodingInfo DefaultFileEncodingInfo {
get {
int cp = DefaultFileEncodingCodePage;
return allEncodings.Single(e => e.CodePage == cp);
}
set {
DefaultFileEncodingCodePage = value.CodePage;
}
}
#endregion
#region GetFileContent
public ITextSource GetFileContent(FileName fileName)
{
return GetFileContentForOpenFile(fileName) ?? GetFileContentFromDisk(fileName, CancellationToken.None);
@ -34,7 +97,7 @@ namespace ICSharpCode.SharpDevelop.Workbench @@ -34,7 +97,7 @@ namespace ICSharpCode.SharpDevelop.Workbench
{
return SD.MainThread.InvokeIfRequired(
delegate {
OpenedFile file = SharpDevelop.FileService.GetOpenedFile(fileName);
OpenedFile file = this.GetOpenedFile(fileName);
if (file != null) {
IFileDocumentProvider p = file.CurrentView as IFileDocumentProvider;
if (p != null) {
@ -57,5 +120,526 @@ namespace ICSharpCode.SharpDevelop.Workbench @@ -57,5 +120,526 @@ namespace ICSharpCode.SharpDevelop.Workbench
{
return new StringTextSource(FileReader.ReadFileContent(fileName, DefaultFileEncoding));
}
#endregion
#region BrowseForFolder
public string BrowseForFolder(string description, string selectedPath)
{
using (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;
}
if (dialog.ShowDialog() == DialogResult.OK) {
return dialog.SelectedPath;
} else {
return null;
}
}
}
#endregion
#region OpenedFile
Dictionary<FileName, OpenedFile> openedFileDict = new Dictionary<FileName, OpenedFile>();
/// <inheritdoc/>
public IReadOnlyList<OpenedFile> OpenedFiles {
get {
SD.MainThread.VerifyAccess();
return openedFileDict.Values.ToArray();
}
}
/// <inheritdoc/>
public OpenedFile GetOpenedFile(string fileName)
{
return GetOpenedFile(FileName.Create(fileName));
}
/// <inheritdoc/>
public OpenedFile GetOpenedFile(FileName fileName)
{
if (fileName == null)
throw new ArgumentNullException("fileName");
SD.MainThread.VerifyAccess();
OpenedFile file;
openedFileDict.TryGetValue(fileName, out file);
return file;
}
/// <inheritdoc/>
public OpenedFile GetOrCreateOpenedFile(string fileName)
{
return GetOrCreateOpenedFile(FileName.Create(fileName));
}
/// <inheritdoc/>
public OpenedFile GetOrCreateOpenedFile(FileName fileName)
{
if (fileName == null)
throw new ArgumentNullException("fileName");
OpenedFile file;
if (!openedFileDict.TryGetValue(fileName, out file)) {
openedFileDict[fileName] = file = new FileServiceOpenedFile(this, fileName);
}
return file;
}
/// <inheritdoc/>
public OpenedFile CreateUntitledOpenedFile(string defaultName, byte[] content)
{
if (defaultName == null)
throw new ArgumentNullException("defaultName");
OpenedFile file = new FileServiceOpenedFile(this, content);
file.FileName = new FileName(file.GetHashCode() + "/" + defaultName);
openedFileDict[file.FileName] = file;
return file;
}
/// <summary>Called by OpenedFile.set_FileName to update the dictionary.</summary>
internal void OpenedFileFileNameChange(OpenedFile file, FileName oldName, FileName newName)
{
if (oldName == null) return; // File just created with NewFile where name is being initialized.
LoggingService.Debug("OpenedFileFileNameChange: " + oldName + " => " + newName);
if (openedFileDict[oldName] != file)
throw new ArgumentException("file must be registered as oldName");
if (openedFileDict.ContainsKey(newName)) {
OpenedFile oldFile = openedFileDict[newName];
if (oldFile.CurrentView != null) {
oldFile.CurrentView.WorkbenchWindow.CloseWindow(true);
} else {
throw new ArgumentException("there already is a file with the newName");
}
}
openedFileDict.Remove(oldName);
openedFileDict[newName] = file;
}
/// <summary>Called by OpenedFile.UnregisterView to update the dictionary.</summary>
internal void OpenedFileClosed(OpenedFile file)
{
if (openedFileDict[file.FileName] != file)
throw new ArgumentException("file must be registered");
openedFileDict.Remove(file.FileName);
LoggingService.Debug("OpenedFileClosed: " + file.FileName);
}
#endregion
#region CheckFileName
/// <inheritdoc/>
public bool CheckFileName(string path)
{
if (FileUtility.IsValidPath(path))
return true;
MessageService.ShowMessage(StringParser.Parse("${res:ICSharpCode.SharpDevelop.Commands.SaveFile.InvalidFileNameError}", new StringTagPair("FileName", path)));
return false;
}
/// <inheritdoc/>
public bool CheckDirectoryEntryName(string name)
{
if (FileUtility.IsValidDirectoryEntryName(name))
return true;
MessageService.ShowMessage(StringParser.Parse("${res:ICSharpCode.SharpDevelop.Commands.SaveFile.InvalidFileNameError}", new StringTagPair("FileName", name)));
return false;
}
#endregion
#region OpenFile (ViewContent)
/// <inheritdoc/>
public bool IsOpen(FileName fileName)
{
return GetOpenFile(fileName) != null;
}
/// <inheritdoc/>
public IViewContent OpenFile(FileName fileName)
{
return OpenFile(fileName, true);
}
/// <inheritdoc/>
public IViewContent OpenFile(FileName fileName, bool switchToOpenedView)
{
LoggingService.Info("Open file " + fileName);
IViewContent viewContent = GetOpenFile(fileName);
if (viewContent != null) {
if (switchToOpenedView) {
viewContent.WorkbenchWindow.SelectWindow();
}
return viewContent;
}
IDisplayBinding binding = DisplayBindingService.GetBindingPerFileName(fileName);
if (binding == null) {
binding = new ErrorFallbackBinding("Could not find any display binding for " + Path.GetFileName(fileName));
}
if (FileUtility.ObservedLoad(new NamedFileOperationDelegate(new SharpDevelop.FileService.LoadFileWrapper(binding, switchToOpenedView).Invoke), fileName) == FileOperationResult.OK) {
SD.FileService.RecentOpen.AddRecentFile(fileName);
}
return GetOpenFile(fileName);
}
/// <inheritdoc/>
public IViewContent NewFile(string defaultName, string content)
{
return NewFile(defaultName, DefaultFileEncoding.GetBytesWithPreamble(content));
}
/// <inheritdoc/>
public 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) {
binding = new ErrorFallbackBinding("Can't create display binding for file " + defaultName);
}
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;
}
/// <inheritdoc/>
public IReadOnlyList<FileName> OpenPrimaryFiles {
get {
List<FileName> fileNames = new List<FileName>();
foreach (IViewContent content in WorkbenchSingleton.Workbench.ViewContentCollection) {
FileName contentName = content.PrimaryFileName;
if (contentName != null && !fileNames.Contains(contentName))
fileNames.Add(contentName);
}
return fileNames;
}
}
/// <inheritdoc/>
public IViewContent GetOpenFile(FileName fileName)
{
if (fileName != null) {
foreach (IViewContent content in WorkbenchSingleton.Workbench.ViewContentCollection) {
string contentName = content.PrimaryFileName;
if (contentName != null) {
if (FileUtility.IsEqualFileName(fileName, contentName))
return content;
}
}
}
return null;
}
sealed class ErrorFallbackBinding : IDisplayBinding
{
string errorMessage;
public ErrorFallbackBinding(string errorMessage)
{
this.errorMessage = errorMessage;
}
public bool CanCreateContentForFile(string fileName)
{
return true;
}
public IViewContent CreateContentForFile(OpenedFile file)
{
return new SimpleViewContent(errorMessage) { TitleName = Path.GetFileName(file.FileName) };
}
public bool IsPreferredBindingForFile(string fileName)
{
return false;
}
public double AutoDetectFileContent(string fileName, Stream fileContent, string detectedMimeType)
{
return double.NegativeInfinity;
}
}
/// <inheritdoc/>
public IViewContent JumpToFilePosition(FileName fileName, int line, int column)
{
LoggingService.InfoFormatted("FileService\n\tJumping to File Position: [{0} : {1}x{2}]", fileName, line, column);
if (fileName == null) {
return null;
}
NavigationService.SuspendLogging();
bool loggingResumed = false;
try {
IViewContent content = OpenFile(fileName);
if (content is IPositionable) {
// TODO: enable jumping to a particular view
content.WorkbenchWindow.ActiveViewContent = content;
NavigationService.ResumeLogging();
loggingResumed = true;
((IPositionable)content).JumpTo(Math.Max(1, line), Math.Max(1, column));
} else {
NavigationService.ResumeLogging();
loggingResumed = true;
NavigationService.Log(content);
}
return content;
} finally {
LoggingService.InfoFormatted("FileService\n\tJumped to File Position: [{0} : {1}x{2}]", fileName, line, column);
if (!loggingResumed) {
NavigationService.ResumeLogging();
}
}
}
#endregion
#region Remove/Rename/Copy
/// <summary>
/// Removes a file, raising the appropriate events. This method may show message boxes.
/// </summary>
public 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)) {
if (SD.FileService.DeleteToRecycleBin)
NativeMethods.DeleteToRecycleBin(fileName);
else
Directory.Delete(fileName, true);
}
} catch (Exception e) {
MessageService.ShowHandledException(e, "Can't remove directory " + fileName);
}
} else {
try {
if (File.Exists(fileName)) {
if (SD.FileService.DeleteToRecycleBin)
NativeMethods.DeleteToRecycleBin(fileName);
else
File.Delete(fileName);
}
} catch (Exception e) {
MessageService.ShowHandledException(e, "Can't remove file " + fileName);
}
}
}
OnFileRemoved(new FileEventArgs(fileName, isDirectory));
}
/// <summary>
/// Renames or moves a file, raising the appropriate events. This method may show message boxes.
/// </summary>
public bool RenameFile(string oldName, string newName, bool isDirectory)
{
if (FileUtility.IsEqualFileName(oldName, newName))
return false;
FileChangeWatcher.DisableAllChangeWatchers();
try {
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.ShowHandledException(e, "Can't rename directory " + oldName);
} else {
MessageService.ShowHandledException(e, "Can't rename file " + oldName);
}
return false;
}
}
OnFileRenamed(new FileRenameEventArgs(oldName, newName, isDirectory));
return true;
} finally {
FileChangeWatcher.EnableAllChangeWatchers();
}
}
/// <summary>
/// Copies a file, raising the appropriate events. This method may show message boxes.
/// </summary>
public bool CopyFile(string oldName, string newName, bool isDirectory, bool overwrite)
{
if (FileUtility.IsEqualFileName(oldName, newName))
return false;
FileRenamingEventArgs eargs = new FileRenamingEventArgs(oldName, newName, isDirectory);
OnFileCopying(eargs);
if (eargs.Cancel)
return false;
if (!eargs.OperationAlreadyDone) {
try {
if (isDirectory && Directory.Exists(oldName)) {
if (!overwrite && Directory.Exists(newName)) {
MessageService.ShowMessage(StringParser.Parse("${res:Gui.ProjectBrowser.FileInUseError}"));
return false;
}
FileUtility.DeepCopy(oldName, newName, overwrite);
} else if (File.Exists(oldName)) {
if (!overwrite && File.Exists(newName)) {
MessageService.ShowMessage(StringParser.Parse("${res:Gui.ProjectBrowser.FileInUseError}"));
return false;
}
File.Copy(oldName, newName, overwrite);
}
} catch (Exception e) {
if (isDirectory) {
MessageService.ShowHandledException(e, "Can't copy directory " + oldName);
} else {
MessageService.ShowHandledException(e, "Can't copy file " + oldName);
}
return false;
}
}
OnFileCopied(new FileRenameEventArgs(oldName, newName, isDirectory));
return true;
}
void OnFileRemoved(FileEventArgs e)
{
if (FileRemoved != null) {
FileRemoved(this, e);
}
}
void OnFileRemoving(FileCancelEventArgs e)
{
if (FileRemoving != null) {
FileRemoving(this, e);
}
}
void OnFileRenamed(FileRenameEventArgs e)
{
if (FileRenamed != null) {
FileRenamed(this, e);
}
}
void OnFileRenaming(FileRenamingEventArgs e)
{
if (FileRenaming != null) {
FileRenaming(this, e);
}
}
void OnFileCopied(FileRenameEventArgs e)
{
if (FileCopied != null) {
FileCopied(this, e);
}
}
void OnFileCopying(FileRenamingEventArgs e)
{
if (FileCopying != null) {
FileCopying(this, e);
}
}
public event EventHandler<FileRenamingEventArgs> FileRenaming;
public event EventHandler<FileRenameEventArgs> FileRenamed;
public event EventHandler<FileRenamingEventArgs> FileCopying;
public event EventHandler<FileRenameEventArgs> FileCopied;
public event EventHandler<FileCancelEventArgs> FileRemoving;
public event EventHandler<FileEventArgs> FileRemoved;
#endregion
#region FileCreated/Replaced
/// <summary>
/// Fires the event handlers for a file being created.
/// </summary>
/// <param name="fileName">The name of the file being created. This should be a fully qualified path.</param>
/// <param name="isDirectory">Set to true if this is a directory</param>
/// <returns>True if the operation can proceed, false if an event handler cancelled the operation.</returns>
public bool FireFileReplacing(string fileName, bool isDirectory)
{
FileCancelEventArgs e = new FileCancelEventArgs(fileName, isDirectory);
if (FileReplacing != null) {
FileReplacing(this, e);
}
return !e.Cancel;
}
/// <summary>
/// Fires the event handlers for a file being replaced.
/// </summary>
/// <param name="fileName">The name of the file being created. This should be a fully qualified path.</param>
/// <param name="isDirectory">Set to true if this is a directory</param>
public void FireFileReplaced(string fileName, bool isDirectory)
{
if (FileReplaced != null) {
FileReplaced(this, new FileEventArgs(fileName, isDirectory));
}
}
/// <summary>
/// Fires the event handlers for a file being created.
/// </summary>
/// <param name="fileName">The name of the file being created. This should be a fully qualified path.</param>
/// <param name="isDirectory">Set to true if this is a directory</param>
public void FireFileCreated(string fileName, bool isDirectory)
{
if (FileCreated != null) {
FileCreated(this, new FileEventArgs(fileName, isDirectory));
}
}
public event EventHandler<FileEventArgs> FileCreated;
public event EventHandler<FileCancelEventArgs> FileReplacing;
public event EventHandler<FileEventArgs> FileReplaced;
#endregion
}
}

161
src/Main/SharpDevelop/Workbench/FileServiceOpenedFile.cs

@ -0,0 +1,161 @@ @@ -0,0 +1,161 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Gui;
namespace ICSharpCode.SharpDevelop.Workbench
{
sealed class FileServiceOpenedFile : OpenedFile
{
readonly FileService fileService;
List<IViewContent> registeredViews = new List<IViewContent>();
FileChangeWatcher fileChangeWatcher;
protected override void ChangeFileName(FileName newValue)
{
fileService.OpenedFileFileNameChange(this, this.FileName, newValue);
base.ChangeFileName(newValue);
}
internal FileServiceOpenedFile(FileService fileService, FileName fileName)
{
this.fileService = fileService;
this.FileName = fileName;
IsUntitled = false;
fileChangeWatcher = new FileChangeWatcher(this);
}
internal FileServiceOpenedFile(FileService fileService, byte[] fileData)
{
this.fileService = fileService;
this.FileName = null;
SetData(fileData);
IsUntitled = true;
MakeDirty();
fileChangeWatcher = new FileChangeWatcher(this);
}
/// <summary>
/// Gets the list of view contents registered with this opened file.
/// </summary>
public override IList<IViewContent> RegisteredViewContents {
get { return registeredViews.AsReadOnly(); }
}
public override void ForceInitializeView(IViewContent view)
{
if (view == null)
throw new ArgumentNullException("view");
if (!registeredViews.Contains(view))
throw new ArgumentException("registeredViews must contain view");
base.ForceInitializeView(view);
}
public override void RegisterView(IViewContent view)
{
if (view == null)
throw new ArgumentNullException("view");
if (registeredViews.Contains(view))
throw new ArgumentException("registeredViews already contains view");
registeredViews.Add(view);
if (WorkbenchSingleton.Workbench != null) {
WorkbenchSingleton.Workbench.ActiveViewContentChanged += WorkbenchActiveViewContentChanged;
if (WorkbenchSingleton.Workbench.ActiveViewContent == view) {
SwitchedToView(view);
}
}
#if DEBUG
view.Disposed += ViewDisposed;
#endif
}
public override void UnregisterView(IViewContent view)
{
if (view == null)
throw new ArgumentNullException("view");
Debug.Assert(registeredViews.Contains(view));
if (WorkbenchSingleton.Workbench != null) {
WorkbenchSingleton.Workbench.ActiveViewContentChanged -= WorkbenchActiveViewContentChanged;
}
#if DEBUG
view.Disposed -= ViewDisposed;
#endif
registeredViews.Remove(view);
if (registeredViews.Count > 0) {
if (currentView == view) {
SaveCurrentView();
currentView = null;
}
} else {
// all views to the file were closed
CloseIfAllViewsClosed();
}
}
public override void CloseIfAllViewsClosed()
{
if (registeredViews.Count == 0) {
bool wasDirty = this.IsDirty;
fileService.OpenedFileClosed(this);
FileClosed.RaiseEvent(this, EventArgs.Empty);
if (fileChangeWatcher != null) {
fileChangeWatcher.Dispose();
fileChangeWatcher = null;
}
if (wasDirty) {
// We discarded some information when closing the file,
// so we need to re-parse it.
if (File.Exists(this.FileName))
SD.ParserService.ParseAsync(this.FileName).FireAndForget();
else
SD.ParserService.ClearParseInformation(this.FileName);
}
}
}
#if DEBUG
void ViewDisposed(object sender, EventArgs e)
{
Debug.Fail("View was disposed while still registered with OpenedFile!");
}
#endif
void WorkbenchActiveViewContentChanged(object sender, EventArgs e)
{
IViewContent newView = WorkbenchSingleton.Workbench.ActiveViewContent;
if (!registeredViews.Contains(newView))
return;
SwitchedToView(newView);
}
public override void SaveToDisk()
{
try {
if (fileChangeWatcher != null)
fileChangeWatcher.Enabled = false;
base.SaveToDisk();
} finally {
if (fileChangeWatcher != null)
fileChangeWatcher.Enabled = true;
}
}
public override event EventHandler FileClosed;
}
}

114
src/Main/SharpDevelop/Workbench/RecentOpen.cs

@ -0,0 +1,114 @@ @@ -0,0 +1,114 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Windows.Shell;
using ICSharpCode.Core;
namespace ICSharpCode.SharpDevelop
{
/// <summary>
/// This class handles the recent open files and the recent open project files of SharpDevelop
/// </summary>
sealed class RecentOpen : IRecentOpen
{
/// <summary>
/// This variable is the maximal length of lastfile/lastopen entries
/// must be > 0
/// </summary>
int MAX_LENGTH = 10;
ObservableCollection<string> recentFiles = new ObservableCollection<string>();
ObservableCollection<string> recentProjects = new ObservableCollection<string>();
Properties properties;
public IReadOnlyList<string> RecentFiles {
get { return recentFiles; }
}
public IReadOnlyList<string> RecentProjects {
get { return recentProjects; }
}
public RecentOpen(Properties p)
{
// don't check whether files exist because that might be slow (e.g. if file is on network
// drive that's unavailable)
this.properties = p;
recentFiles.AddRange(p.GetList<string>("Files"));
recentProjects.AddRange(p.GetList<string>("Projects"));
}
public void AddRecentFile(string name)
{
for (int i = 0; i < recentFiles.Count; ++i) {
if (recentFiles[i].Equals(name, StringComparison.OrdinalIgnoreCase)) {
recentFiles.RemoveAt(i);
}
}
while (recentFiles.Count >= MAX_LENGTH) {
recentFiles.RemoveAt(recentFiles.Count - 1);
}
recentFiles.Insert(0, name);
properties.SetList("Files", recentFiles);
}
public void ClearRecentFiles()
{
recentFiles.Clear();
properties.SetList("Files", recentFiles);
}
public void ClearRecentProjects()
{
recentProjects.Clear();
properties.SetList("Projects", recentProjects);
}
public void AddRecentProject(string name)
{
for (int i = 0; i < recentProjects.Count; ++i) {
if (recentProjects[i].ToString().Equals(name, StringComparison.OrdinalIgnoreCase)) {
recentProjects.RemoveAt(i);
}
}
while (recentProjects.Count >= MAX_LENGTH) {
recentProjects.RemoveAt(recentProjects.Count - 1);
}
recentProjects.Insert(0, name);
JumpList.AddToRecentCategory(name);
properties.SetList("Projects", recentProjects);
}
internal void FileRemoved(object sender, FileEventArgs e)
{
for (int i = 0; i < recentFiles.Count; ++i) {
string file = recentFiles[i].ToString();
if (e.FileName == file) {
recentFiles.RemoveAt(i);
break;
}
}
}
internal void FileRenamed(object sender, FileRenameEventArgs e)
{
for (int i = 0; i < recentFiles.Count; ++i) {
string file = recentFiles[i].ToString();
if (e.SourceFile == file) {
recentFiles.RemoveAt(i);
recentFiles.Insert(i, e.TargetFile);
break;
}
}
}
}
}

4
src/Main/SharpDevelop/Workbench/WorkbenchStartup.cs

@ -65,8 +65,8 @@ namespace ICSharpCode.SharpDevelop.Workbench @@ -65,8 +65,8 @@ namespace ICSharpCode.SharpDevelop.Workbench
// load previous solution
if (!didLoadSolutionOrFile && PropertyService.Get("SharpDevelop.LoadPrevProjectOnStartup", false)) {
if (SharpDevelop.FileService.RecentOpen.RecentProject.Count > 0) {
ProjectService.LoadSolution(SharpDevelop.FileService.RecentOpen.RecentProject[0]);
if (SD.FileService.RecentOpen.RecentProjects.Count > 0) {
ProjectService.LoadSolution(SD.FileService.RecentOpen.RecentProjects[0]);
didLoadSolutionOrFile = true;
}
}

10
src/Main/SharpDevelop/Workbench/WpfWorkbench.cs

@ -128,8 +128,8 @@ namespace ICSharpCode.SharpDevelop.Workbench @@ -128,8 +128,8 @@ namespace ICSharpCode.SharpDevelop.Workbench
SharpDevelop.FileService.FileReplaced += CheckRemovedOrReplacedFile;
SharpDevelop.FileService.FileRenamed += CheckRenamedFile;
SharpDevelop.FileService.FileRemoved += SharpDevelop.FileService.RecentOpen.FileRemoved;
SharpDevelop.FileService.FileRenamed += SharpDevelop.FileService.RecentOpen.FileRenamed;
SharpDevelop.FileService.FileRemoved += ((RecentOpen)SD.FileService.RecentOpen).FileRemoved;
SharpDevelop.FileService.FileRenamed += ((RecentOpen)SD.FileService.RecentOpen).FileRenamed;
requerySuggestedEventHandler = new EventHandler(CommandManager_RequerySuggested);
CommandManager.RequerySuggested += requerySuggestedEventHandler;
@ -172,7 +172,7 @@ namespace ICSharpCode.SharpDevelop.Workbench @@ -172,7 +172,7 @@ namespace ICSharpCode.SharpDevelop.Workbench
void CheckRemovedOrReplacedFile(object sender, FileEventArgs e)
{
foreach (OpenedFile file in SharpDevelop.FileService.OpenedFiles) {
foreach (OpenedFile file in SD.FileService.OpenedFiles) {
if (FileUtility.IsBaseDirectory(e.FileName, file.FileName)) {
foreach (IViewContent content in file.RegisteredViewContents.ToArray()) {
// content.WorkbenchWindow can be null if multiple view contents
@ -190,13 +190,13 @@ namespace ICSharpCode.SharpDevelop.Workbench @@ -190,13 +190,13 @@ namespace ICSharpCode.SharpDevelop.Workbench
void CheckRenamedFile(object sender, FileRenameEventArgs e)
{
if (e.IsDirectory) {
foreach (OpenedFile file in SharpDevelop.FileService.OpenedFiles) {
foreach (OpenedFile file in SD.FileService.OpenedFiles) {
if (file.FileName != null && FileUtility.IsBaseDirectory(e.SourceFile, file.FileName)) {
file.FileName = new FileName(FileUtility.RenameBaseDirectory(file.FileName, e.SourceFile, e.TargetFile));
}
}
} else {
OpenedFile file = SharpDevelop.FileService.GetOpenedFile(e.SourceFile);
OpenedFile file = SD.FileService.GetOpenedFile(e.SourceFile);
if (file != null) {
file.FileName = new FileName(e.TargetFile);
}

Loading…
Cancel
Save