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.
238 lines
6.4 KiB
238 lines
6.4 KiB
// 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 System.Windows.Forms; |
|
|
|
using ICSharpCode.Core; |
|
using ICSharpCode.SharpDevelop.Gui; |
|
|
|
namespace ICSharpCode.SharpDevelop |
|
{ |
|
public sealed class FileChangeWatcher : IDisposable |
|
{ |
|
public static bool DetectExternalChangesOption { |
|
get { |
|
return PropertyService.Get("SharpDevelop.FileChangeWatcher.DetectExternalChanges", true); |
|
} |
|
set { |
|
WorkbenchSingleton.AssertMainThread(); |
|
PropertyService.Set("SharpDevelop.FileChangeWatcher.DetectExternalChanges", value); |
|
foreach (FileChangeWatcher watcher in activeWatchers) { |
|
watcher.SetWatcher(); |
|
} |
|
} |
|
} |
|
|
|
public static bool AutoLoadExternalChangesOption { |
|
get { |
|
return PropertyService.Get("SharpDevelop.FileChangeWatcher.AutoLoadExternalChanges", true); |
|
} |
|
set { |
|
PropertyService.Set("SharpDevelop.FileChangeWatcher.AutoLoadExternalChanges", value); |
|
} |
|
} |
|
|
|
static HashSet<FileChangeWatcher> activeWatchers = new HashSet<FileChangeWatcher>(); |
|
|
|
public static HashSet<FileChangeWatcher> ActiveWatchers { |
|
get { return activeWatchers; } |
|
} |
|
|
|
static int globalDisableCount; |
|
|
|
public static void DisableAllChangeWatchers() |
|
{ |
|
WorkbenchSingleton.AssertMainThread(); |
|
globalDisableCount++; |
|
foreach (FileChangeWatcher w in activeWatchers) |
|
w.SetWatcher(); |
|
} |
|
|
|
public static void EnableAllChangeWatchers() |
|
{ |
|
WorkbenchSingleton.AssertMainThread(); |
|
if (globalDisableCount == 0) |
|
throw new InvalidOperationException(); |
|
globalDisableCount--; |
|
foreach (FileChangeWatcher w in activeWatchers) |
|
w.SetWatcher(); |
|
} |
|
|
|
public event EventHandler FileChanged; |
|
|
|
void OnFileChanged(EventArgs e) |
|
{ |
|
if (FileChanged != null) { |
|
FileChanged(this, e); |
|
} |
|
} |
|
|
|
FileSystemWatcher watcher; |
|
bool wasChangedExternally = false; |
|
OpenedFile file; |
|
bool isFileReadOnly; |
|
|
|
public OpenedFile File { |
|
get { return file; } |
|
} |
|
|
|
public FileChangeWatcher(OpenedFile file) |
|
{ |
|
if (file == null) |
|
throw new ArgumentNullException("file"); |
|
this.file = file; |
|
WorkbenchSingleton.MainWindow.Activated += MainForm_Activated; |
|
file.FileNameChanged += file_FileNameChanged; |
|
activeWatchers.Add(this); |
|
SetWatcher(); |
|
|
|
if (System.IO.File.Exists(this.file.FileName)) { |
|
isFileReadOnly = (System.IO.File.GetAttributes(this.file.FileName) & FileAttributes.ReadOnly) == FileAttributes.ReadOnly; |
|
} |
|
} |
|
|
|
void file_FileNameChanged(object sender, EventArgs e) |
|
{ |
|
SetWatcher(); |
|
} |
|
|
|
public void Dispose() |
|
{ |
|
WorkbenchSingleton.AssertMainThread(); |
|
activeWatchers.Remove(this); |
|
if (file != null) { |
|
WorkbenchSingleton.MainWindow.Activated -= MainForm_Activated; |
|
file.FileNameChanged -= file_FileNameChanged; |
|
file = null; |
|
} |
|
if (watcher != null) { |
|
watcher.Dispose(); |
|
watcher = null; |
|
} |
|
} |
|
|
|
bool enabled = true; |
|
|
|
public bool Enabled { |
|
get { return enabled; } |
|
set { |
|
enabled = value; |
|
SetWatcher(); |
|
} |
|
} |
|
|
|
void SetWatcher() |
|
{ |
|
WorkbenchSingleton.AssertMainThread(); |
|
|
|
if (watcher != null) { |
|
watcher.EnableRaisingEvents = false; |
|
} |
|
|
|
if (!enabled) |
|
return; |
|
if (globalDisableCount > 0) |
|
return; |
|
if (DetectExternalChangesOption == false) |
|
return; |
|
|
|
string fileName = file.FileName; |
|
if (string.IsNullOrEmpty(fileName)) |
|
return; |
|
if (FileUtility.IsUrl(fileName)) |
|
return; |
|
if (!Path.IsPathRooted(fileName)) |
|
return; |
|
|
|
try { |
|
if (watcher == null) { |
|
watcher = new FileSystemWatcher(); |
|
if (WorkbenchSingleton.Workbench != null) |
|
watcher.SynchronizingObject = WorkbenchSingleton.Workbench.SynchronizingObject; |
|
watcher.Changed += OnFileChangedEvent; |
|
watcher.Created += OnFileChangedEvent; |
|
watcher.Renamed += OnFileChangedEvent; |
|
} |
|
watcher.Path = Path.GetDirectoryName(fileName); |
|
watcher.Filter = Path.GetFileName(fileName); |
|
watcher.EnableRaisingEvents = true; |
|
watcher.NotifyFilter |= NotifyFilters.Attributes; |
|
} catch (PlatformNotSupportedException) { |
|
if (watcher != null) { |
|
watcher.Dispose(); |
|
} |
|
watcher = null; |
|
} catch (FileNotFoundException) { |
|
// can occur if directory was deleted externally |
|
if (watcher != null) { |
|
watcher.Dispose(); |
|
} |
|
watcher = null; |
|
} catch (ArgumentException) { |
|
// can occur if parent directory was deleted externally |
|
if (watcher != null) { |
|
watcher.Dispose(); |
|
} |
|
watcher = null; |
|
} |
|
} |
|
|
|
void OnFileChangedEvent(object sender, FileSystemEventArgs e) |
|
{ |
|
if (file == null) |
|
return; |
|
LoggingService.Debug("File " + file.FileName + " was changed externally: " + e.ChangeType); |
|
if (!wasChangedExternally) { |
|
wasChangedExternally = true; |
|
OnFileChanged(EventArgs.Empty); |
|
|
|
// if the file was only made readonly, don't prevent reloading it from disk |
|
bool readOnly = (System.IO.File.GetAttributes(this.file.FileName) & FileAttributes.ReadOnly) == FileAttributes.ReadOnly; |
|
if (readOnly != isFileReadOnly) |
|
wasChangedExternally = false; |
|
isFileReadOnly = readOnly; |
|
|
|
if (WorkbenchSingleton.Workbench.IsActiveWindow) { |
|
// delay reloading message a bit, prevents showing two messages |
|
// when the file changes twice in quick succession; and prevents |
|
// trying to reload the file while it is still being written |
|
WorkbenchSingleton.CallLater( |
|
TimeSpan.FromSeconds(0.5), |
|
delegate { MainForm_Activated(this, EventArgs.Empty); } ); |
|
} |
|
} |
|
} |
|
|
|
void MainForm_Activated(object sender, EventArgs e) |
|
{ |
|
if (wasChangedExternally) { |
|
wasChangedExternally = false; |
|
|
|
if (file == null) |
|
return; |
|
|
|
string fileName = file.FileName; |
|
if (!System.IO.File.Exists(fileName)) |
|
return; |
|
|
|
string message = StringParser.Parse( |
|
"${res:ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor.TextEditorDisplayBinding.FileAlteredMessage}", |
|
new StringTagPair("File", Path.GetFullPath(fileName)) |
|
); |
|
if ((AutoLoadExternalChangesOption && file.IsDirty == false) |
|
|| MessageService.AskQuestion(message, StringParser.Parse("${res:MainWindow.DialogName}"))) |
|
{ |
|
if (System.IO.File.Exists(fileName)) { |
|
file.ReloadFromDisk(); |
|
} |
|
} else { |
|
file.MakeDirty(); |
|
} |
|
} |
|
} |
|
} |
|
}
|
|
|