Browse Source

When moving text using drag'n'drop, combine text removal on drag and text insertion on drop into a single undo group.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@3839 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 17 years ago
parent
commit
0c8e66a620
  1. 8
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit.Tests/WeakReferenceTests.cs
  2. 48
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/UndoStack.cs
  3. 1
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/FoldingMarginMarker.cs
  4. 27
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/SelectionMouseHandler.cs

8
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit.Tests/WeakReferenceTests.cs

@ -31,21 +31,21 @@ namespace ICSharpCode.AvalonEdit.Tests
{ {
bool collectedTextView = false; bool collectedTextView = false;
TextDocument textDocument = new TextDocument(); TextDocument textDocument = new TextDocument();
Assert.AreEqual(0, textDocument.LineTracker.Count); Assert.AreEqual(0, textDocument.LineTrackers.Count);
TextView textView = new TextViewWithGCCallback(delegate { collectedTextView = true; }); TextView textView = new TextViewWithGCCallback(delegate { collectedTextView = true; });
textView.Document = textDocument; textView.Document = textDocument;
Assert.AreEqual(1, textDocument.LineTracker.Count); Assert.AreEqual(1, textDocument.LineTrackers.Count);
textView = null; textView = null;
GarbageCollect(); GarbageCollect();
Assert.IsTrue(collectedTextView); Assert.IsTrue(collectedTextView);
// document cannot immediately clear the line tracker // document cannot immediately clear the line tracker
Assert.AreEqual(1, textDocument.LineTracker.Count); Assert.AreEqual(1, textDocument.LineTrackers.Count);
// but it should clear it on the next change // but it should clear it on the next change
textDocument.Insert(0, "a"); textDocument.Insert(0, "a");
Assert.AreEqual(0, textDocument.LineTracker.Count); Assert.AreEqual(0, textDocument.LineTrackers.Count);
} }
[Test] [Test]

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

@ -49,21 +49,66 @@ namespace ICSharpCode.AvalonEdit.Document
int undoGroupDepth; int undoGroupDepth;
int actionCountInUndoGroup; int actionCountInUndoGroup;
int optionalActionCount; int optionalActionCount;
object lastGroupDescriptor;
/// <summary>
/// If an undo group is open, gets the group descriptor of the current top-level
/// undo group.
/// If no undo group is open, gets the group descriptor from the previous undo group.
/// </summary>
/// <remarks>The group descriptor can be used to join adjacent undo groups:
/// use a group descriptor to mark your changes, and on the second action,
/// compare LastGroupDescriptor and use <see cref="StartContinuedUndoGroup"/> if you
/// want to join the undo groups.</remarks>
public object LastGroupDescriptor {
get { return lastGroupDescriptor; }
}
/// <summary> /// <summary>
/// Starts grouping changes. /// Starts grouping changes.
/// Maintains a counter so that nested calls are possible. /// Maintains a counter so that nested calls are possible.
/// </summary> /// </summary>
public void StartUndoGroup() public void StartUndoGroup()
{
StartUndoGroup(null);
}
/// <summary>
/// Starts grouping changes.
/// Maintains a counter so that nested calls are possible.
/// </summary>
/// <param name="groupDescriptor">An object that is stored with the undo group.
/// If this is not a top-level undo group, the parameter is ignored.</param>
public void StartUndoGroup(object groupDescriptor)
{ {
if (undoGroupDepth == 0) { if (undoGroupDepth == 0) {
actionCountInUndoGroup = 0; actionCountInUndoGroup = 0;
optionalActionCount = 0; optionalActionCount = 0;
lastGroupDescriptor = groupDescriptor;
} }
undoGroupDepth++; undoGroupDepth++;
//Util.LoggingService.Debug("Open undo group (new depth=" + undoGroupDepth + ")"); //Util.LoggingService.Debug("Open undo group (new depth=" + undoGroupDepth + ")");
} }
/// <summary>
/// Starts grouping changes, continuing with the previously closed undo group.
/// Maintains a counter so that nested calls are possible.
/// If the call to StartContinuedUndoGroup is a nested call, it behaves exactly
/// as <see cref="StartUndoGroup()"/>, only top-level calls can continue existing undo groups.
/// </summary>
/// <param name="groupDescriptor">An object that is stored with the undo group.
/// If this is not a top-level undo group, the parameter is ignored.</param>
public void StartContinuedUndoGroup(object groupDescriptor)
{
if (undoGroupDepth == 0) {
actionCountInUndoGroup = undostack.Count > 0 ? 1 : 0;
optionalActionCount = 0;
lastGroupDescriptor = groupDescriptor;
}
undoGroupDepth++;
//Util.LoggingService.Debug("Continue undo group (new depth=" + undoGroupDepth + ")");
}
/// <summary> /// <summary>
/// Stops grouping changes. /// Stops grouping changes.
/// </summary> /// </summary>
@ -102,6 +147,7 @@ namespace ICSharpCode.AvalonEdit.Document
{ {
VerifyNoUndoGroupOpen(); VerifyNoUndoGroupOpen();
if (undostack.Count > 0) { if (undostack.Count > 0) {
lastGroupDescriptor = null;
acceptChanges = false; acceptChanges = false;
IUndoableOperation uedit = undostack.Pop(); IUndoableOperation uedit = undostack.Pop();
redostack.Push(uedit); redostack.Push(uedit);
@ -121,6 +167,7 @@ namespace ICSharpCode.AvalonEdit.Document
{ {
VerifyNoUndoGroupOpen(); VerifyNoUndoGroupOpen();
if (redostack.Count > 0) { if (redostack.Count > 0) {
lastGroupDescriptor = null;
acceptChanges = false; acceptChanges = false;
IUndoableOperation uedit = redostack.Pop(); IUndoableOperation uedit = redostack.Pop();
undostack.Push(uedit); undostack.Push(uedit);
@ -195,6 +242,7 @@ namespace ICSharpCode.AvalonEdit.Document
{ {
VerifyNoUndoGroupOpen(); VerifyNoUndoGroupOpen();
if (undostack.Count != 0) { if (undostack.Count != 0) {
lastGroupDescriptor = null;
undostack.Clear(); undostack.Clear();
NotifyPropertyChanged("CanUndo"); NotifyPropertyChanged("CanUndo");
} }

1
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/FoldingMarginMarker.cs

@ -38,6 +38,7 @@ namespace ICSharpCode.AvalonEdit.Gui
if (!e.Handled) { if (!e.Handled) {
if (e.ChangedButton == MouseButton.Left) { if (e.ChangedButton == MouseButton.Left) {
IsExpanded = !IsExpanded; IsExpanded = !IsExpanded;
e.Handled = true;
} }
} }
} }

27
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/SelectionMouseHandler.cs

@ -164,8 +164,16 @@ namespace ICSharpCode.AvalonEdit.Gui
e.Effects = DragDropEffects.None; e.Effects = DragDropEffects.None;
} else { } else {
Debug.WriteLine("Drop: insert at " + start); Debug.WriteLine("Drop: insert at " + start);
textArea.Document.Insert(start, text); // Mark the undo group with the currentDragDescriptor, if the drag
textArea.Selection = new SimpleSelection(start, start + text.Length); // is originating from the same control. This allows combining
// the undo groups when text is moved.
textArea.Document.UndoStack.StartUndoGroup(this.currentDragDescriptor);
try {
textArea.Document.Insert(start, text);
textArea.Selection = new SimpleSelection(start, start + text.Length);
} finally {
textArea.Document.UndoStack.EndUndoGroup();
}
} }
} }
} }
@ -206,6 +214,8 @@ namespace ICSharpCode.AvalonEdit.Gui
#endregion #endregion
#region Start Drag #region Start Drag
object currentDragDescriptor;
void StartDrag() void StartDrag()
{ {
// prevent nested StartDrag calls // prevent nested StartDrag calls
@ -219,14 +229,21 @@ namespace ICSharpCode.AvalonEdit.Gui
dataObject.SetText(text); dataObject.SetText(text);
DragDropEffects allowedEffects = DragDropEffects.All; DragDropEffects allowedEffects = DragDropEffects.All;
List<AnchorSegment> deleteOnMove; var deleteOnMove = textArea.Selection.Segments.Select(s => new AnchorSegment(textArea.Document, s)).ToList();
deleteOnMove = textArea.Selection.Segments.Select(s => new AnchorSegment(textArea.Document, s)).ToList();
object dragDescriptor = new object();
this.currentDragDescriptor = dragDescriptor;
Debug.WriteLine("DoDragDrop with allowedEffects=" + allowedEffects); Debug.WriteLine("DoDragDrop with allowedEffects=" + allowedEffects);
DragDropEffects resultEffect = DragDrop.DoDragDrop(textArea, dataObject, allowedEffects); DragDropEffects resultEffect = DragDrop.DoDragDrop(textArea, dataObject, allowedEffects);
Debug.WriteLine("DoDragDrop done, resultEffect=" + resultEffect); Debug.WriteLine("DoDragDrop done, resultEffect=" + resultEffect);
this.currentDragDescriptor = null;
if (deleteOnMove != null && resultEffect == DragDropEffects.Move) { if (deleteOnMove != null && resultEffect == DragDropEffects.Move) {
bool draggedInsideSingleDocument = (dragDescriptor == textArea.Document.UndoStack.LastGroupDescriptor);
if (draggedInsideSingleDocument)
textArea.Document.UndoStack.StartContinuedUndoGroup(null);
textArea.Document.BeginUpdate(); textArea.Document.BeginUpdate();
try { try {
foreach (ISegment s in deleteOnMove) { foreach (ISegment s in deleteOnMove) {
@ -234,6 +251,8 @@ namespace ICSharpCode.AvalonEdit.Gui
} }
} finally { } finally {
textArea.Document.EndUpdate(); textArea.Document.EndUpdate();
if (draggedInsideSingleDocument)
textArea.Document.UndoStack.EndUndoGroup();
} }
} }
} }

Loading…
Cancel
Save