Browse Source

AvalonEdit: add TextEditor.IsModified property

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@5760 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
pull/1/head
Daniel Grunwald 16 years ago
parent
commit
67d1aa96d3
  1. 61
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/UndoStack.cs
  2. 75
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextEditor.cs

61
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/UndoStack.cs

@ -39,6 +39,60 @@ namespace ICSharpCode.AvalonEdit.Document
object lastGroupDescriptor; object lastGroupDescriptor;
bool allowContinue; bool allowContinue;
#region IsOriginalFile implementation
// implements feature request SD2-784 - File still considered dirty after undoing all changes
/// <summary>
/// Number of times undo must be executed until the original state is reached.
/// Negative: number of times redo must be executed until the original state is reached.
/// Special case: int.MinValue == original state is unreachable
/// </summary>
int elementsOnUndoUntilOriginalFile;
/// <summary>
/// Gets whether the document is currently in its original state (no modifications).
/// </summary>
public bool IsOriginalFile {
get { return elementsOnUndoUntilOriginalFile == 0; }
}
/// <summary>
/// Marks the current state as original. Discards any previous "original" markers.
/// </summary>
public void MarkAsOriginalFile()
{
bool oldIsOriginalFile = IsOriginalFile;
elementsOnUndoUntilOriginalFile = 0;
if (!oldIsOriginalFile)
NotifyPropertyChanged("IsOriginalFile");
}
/// <summary>
/// Discards the current "original" marker.
/// </summary>
public void DiscardOriginalFileMarker()
{
bool oldIsOriginalFile = IsOriginalFile;
elementsOnUndoUntilOriginalFile = int.MinValue;
if (oldIsOriginalFile)
NotifyPropertyChanged("IsOriginalFile");
}
void FileModified(int newElementsOnUndoStack)
{
if (newElementsOnUndoStack == 0 || elementsOnUndoUntilOriginalFile == int.MinValue)
return;
bool oldIsOriginalFile = IsOriginalFile;
elementsOnUndoUntilOriginalFile += newElementsOnUndoStack;
if (elementsOnUndoUntilOriginalFile > undostack.Count)
elementsOnUndoUntilOriginalFile = int.MinValue;
if (oldIsOriginalFile != IsOriginalFile)
NotifyPropertyChanged("IsOriginalFile");
}
#endregion
/// <summary> /// <summary>
/// Gets if the undo stack currently accepts changes. /// Gets if the undo stack currently accepts changes.
/// Is false while an undo action is running. /// Is false while an undo action is running.
@ -166,9 +220,11 @@ namespace ICSharpCode.AvalonEdit.Document
} else if (actionCountInUndoGroup > 1) { } else if (actionCountInUndoGroup > 1) {
// combine all actions within the group into a single grouped action // combine all actions within the group into a single grouped action
undostack.PushBack(new UndoOperationGroup(undostack, actionCountInUndoGroup)); undostack.PushBack(new UndoOperationGroup(undostack, actionCountInUndoGroup));
actionCountInUndoGroup = 1; // actions were combined
} }
EnforceSizeLimit(); EnforceSizeLimit();
allowContinue = true; allowContinue = true;
FileModified(actionCountInUndoGroup);
} }
} }
@ -204,6 +260,7 @@ namespace ICSharpCode.AvalonEdit.Document
} finally { } finally {
state = StateListen; state = StateListen;
} }
FileModified(-1);
if (undostack.Count == 0) if (undostack.Count == 0)
NotifyPropertyChanged("CanUndo"); NotifyPropertyChanged("CanUndo");
if (redostack.Count == 1) if (redostack.Count == 1)
@ -236,6 +293,7 @@ namespace ICSharpCode.AvalonEdit.Document
} finally { } finally {
state = StateListen; state = StateListen;
} }
FileModified(1);
if (redostack.Count == 0) if (redostack.Count == 0)
NotifyPropertyChanged("CanRedo"); NotifyPropertyChanged("CanRedo");
if (undostack.Count == 1) if (undostack.Count == 1)
@ -304,6 +362,9 @@ namespace ICSharpCode.AvalonEdit.Document
if (redostack.Count != 0) { if (redostack.Count != 0) {
redostack.Clear(); redostack.Clear();
NotifyPropertyChanged("CanRedo"); NotifyPropertyChanged("CanRedo");
// if the "orginal file" marker is on the redo stack: remove it
if (elementsOnUndoUntilOriginalFile < 0)
elementsOnUndoUntilOriginalFile = int.MinValue;
} }
} }

75
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextEditor.cs

@ -121,10 +121,12 @@ namespace ICSharpCode.AvalonEdit
{ {
if (oldValue != null) { if (oldValue != null) {
TextDocumentWeakEventManager.TextChanged.RemoveListener(oldValue, this); TextDocumentWeakEventManager.TextChanged.RemoveListener(oldValue, this);
PropertyChangedEventManager.RemoveListener(oldValue.UndoStack, this, "IsOriginalFile");
} }
textArea.Document = newValue; textArea.Document = newValue;
if (newValue != null) { if (newValue != null) {
TextDocumentWeakEventManager.TextChanged.AddListener(newValue, this); TextDocumentWeakEventManager.TextChanged.AddListener(newValue, this);
PropertyChangedEventManager.AddListener(newValue.UndoStack, this, "IsOriginalFile");
} }
OnDocumentChanged(EventArgs.Empty); OnDocumentChanged(EventArgs.Empty);
OnTextChanged(EventArgs.Empty); OnTextChanged(EventArgs.Empty);
@ -187,6 +189,8 @@ namespace ICSharpCode.AvalonEdit
} else if (managerType == typeof(TextDocumentWeakEventManager.TextChanged)) { } else if (managerType == typeof(TextDocumentWeakEventManager.TextChanged)) {
OnTextChanged(e); OnTextChanged(e);
return true; return true;
} else if (managerType == typeof(PropertyChangedEventManager)) {
return HandleIsOriginalChanged((PropertyChangedEventArgs)e);
} }
return false; return false;
} }
@ -208,10 +212,8 @@ namespace ICSharpCode.AvalonEdit
return document != null ? document.Text : string.Empty; return document != null ? document.Text : string.Empty;
} }
set { set {
if (value == null)
value = string.Empty;
TextDocument document = GetDocument(); TextDocument document = GetDocument();
document.Text = value; document.Text = value ?? string.Empty;
// after replacing the full text, the caret is positioned at the end of the document // after replacing the full text, the caret is positioned at the end of the document
// - reset it to the beginning. // - reset it to the beginning.
this.CaretOffset = 0; this.CaretOffset = 0;
@ -391,6 +393,53 @@ namespace ICSharpCode.AvalonEdit
} }
#endregion #endregion
#region IsModified
/// <summary>
/// Dependency property for <see cref="IsModified"/>
/// </summary>
public static readonly DependencyProperty IsModifiedProperty =
DependencyProperty.Register("IsModified", typeof(bool), typeof(TextEditor),
new FrameworkPropertyMetadata(Boxes.False, OnIsModifiedChanged));
/// <summary>
/// Gets/Sets the 'modified' flag.
/// </summary>
public bool IsModified {
get { return (bool)GetValue(IsModifiedProperty); }
set { SetValue(IsModifiedProperty, value); }
}
static void OnIsModifiedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TextEditor editor = d as TextEditor;
if (editor != null) {
TextDocument document = editor.Document;
if (document != null) {
UndoStack undoStack = document.UndoStack;
if ((bool)e.NewValue) {
if (undoStack.IsOriginalFile)
undoStack.DiscardOriginalFileMarker();
} else {
undoStack.MarkAsOriginalFile();
}
}
}
}
bool HandleIsOriginalChanged(PropertyChangedEventArgs e)
{
if (e.PropertyName == "IsOriginalFile") {
TextDocument document = this.Document;
if (document != null) {
this.IsModified = !document.UndoStack.IsOriginalFile;
}
return true;
} else {
return false;
}
}
#endregion
#region ShowLineNumbers #region ShowLineNumbers
/// <summary> /// <summary>
/// IsReadOnly dependency property. /// IsReadOnly dependency property.
@ -825,12 +874,16 @@ namespace ICSharpCode.AvalonEdit
/// <summary> /// <summary>
/// Loads the text from the stream, auto-detecting the encoding. /// Loads the text from the stream, auto-detecting the encoding.
/// </summary> /// </summary>
/// <remarks>
/// This method sets <see cref="IsModified"/> to false.
/// </remarks>
public void Load(Stream stream) public void Load(Stream stream)
{ {
using (StreamReader reader = FileReader.OpenStream(stream, Encoding ?? Encoding.UTF8)) { using (StreamReader reader = FileReader.OpenStream(stream, this.Encoding ?? Encoding.UTF8)) {
Text = reader.ReadToEnd(); this.Text = reader.ReadToEnd();
Encoding = reader.CurrentEncoding; // assign encoding after ReadToEnd() so that the StreamReader can autodetect the encoding this.Encoding = reader.CurrentEncoding; // assign encoding after ReadToEnd() so that the StreamReader can autodetect the encoding
} }
this.IsModified = false;
} }
/// <summary> /// <summary>
@ -854,18 +907,22 @@ namespace ICSharpCode.AvalonEdit
/// <summary> /// <summary>
/// Saves the text to the stream. /// Saves the text to the stream.
/// </summary> /// </summary>
/// <remarks>
/// This method sets <see cref="IsModified"/> to false.
/// </remarks>
public void Save(Stream stream) public void Save(Stream stream)
{ {
if (stream == null) if (stream == null)
throw new ArgumentNullException("stream"); throw new ArgumentNullException("stream");
StreamWriter writer = new StreamWriter(stream, Encoding ?? Encoding.UTF8); StreamWriter writer = new StreamWriter(stream, this.Encoding ?? Encoding.UTF8);
writer.Write(Text); writer.Write(this.Text);
writer.Flush(); writer.Flush();
// do not close the stream // do not close the stream
this.IsModified = false;
} }
/// <summary> /// <summary>
/// Loads the text from the stream, auto-detecting the encoding. /// Saves the text to the file.
/// </summary> /// </summary>
public void Save(string fileName) public void Save(string fileName)
{ {

Loading…
Cancel
Save