Browse Source

Refactored the selection API; preparing for improving the rectangular selection.

pull/23/head
Daniel Grunwald 15 years ago committed by Siegfried Pammer
parent
commit
b2a774f77b
  1. 8
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/CaretNavigationCommandHandler.cs
  2. 12
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs
  3. 75
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EmptySelection.cs
  4. 8
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/LineNumberMargin.cs
  5. 167
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/RectangleSelection.cs
  6. 86
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Selection.cs
  7. 15
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/SelectionColorizer.cs
  8. 32
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/SelectionMouseHandler.cs
  9. 63
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/SelectionSegment.cs
  10. 73
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/SimpleSelection.cs
  11. 20
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/TextArea.cs
  12. 4
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj
  13. 11
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/ColorizingTransformer.cs
  14. 2
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/InsertionContext.cs
  15. 2
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/SnippetInputHandler.cs
  16. 4
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextEditor.cs
  17. 14
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextViewPosition.cs

8
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/CaretNavigationCommandHandler.cs

@ -81,7 +81,7 @@ namespace ICSharpCode.AvalonEdit.Editing
if (textArea != null && textArea.Document != null) { if (textArea != null && textArea.Document != null) {
args.Handled = true; args.Handled = true;
textArea.Caret.Offset = textArea.Document.TextLength; textArea.Caret.Offset = textArea.Document.TextLength;
textArea.Selection = new SimpleSelection(0, textArea.Document.TextLength); textArea.Selection = SimpleSelection.Create(textArea, 0, textArea.Document.TextLength);
} }
} }
@ -112,7 +112,7 @@ namespace ICSharpCode.AvalonEdit.Editing
TextArea textArea = GetTextArea(target); TextArea textArea = GetTextArea(target);
if (textArea != null && textArea.Document != null) { if (textArea != null && textArea.Document != null) {
args.Handled = true; args.Handled = true;
textArea.Selection = Selection.Empty; textArea.ClearSelection();
MoveCaret(textArea, direction); MoveCaret(textArea, direction);
textArea.Caret.BringCaretToView(); textArea.Caret.BringCaretToView();
} }
@ -125,9 +125,9 @@ namespace ICSharpCode.AvalonEdit.Editing
TextArea textArea = GetTextArea(target); TextArea textArea = GetTextArea(target);
if (textArea != null && textArea.Document != null) { if (textArea != null && textArea.Document != null) {
args.Handled = true; args.Handled = true;
int oldOffset = textArea.Caret.Offset; TextViewPosition oldPosition = textArea.Caret.Position;
MoveCaret(textArea, direction); MoveCaret(textArea, direction);
textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldOffset, textArea.Caret.Offset); textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldPosition, textArea.Caret.Position);
textArea.Caret.BringCaretToView(); textArea.Caret.BringCaretToView();
} }
}; };

12
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs

@ -143,7 +143,7 @@ namespace ICSharpCode.AvalonEdit.Editing
if (defaultSegmentType == DefaultSegmentType.CurrentLine) { if (defaultSegmentType == DefaultSegmentType.CurrentLine) {
segments = new ISegment[] { textArea.Document.GetLineByNumber(textArea.Caret.Line) }; segments = new ISegment[] { textArea.Document.GetLineByNumber(textArea.Caret.Line) };
} else if (defaultSegmentType == DefaultSegmentType.WholeDocument) { } else if (defaultSegmentType == DefaultSegmentType.WholeDocument) {
segments = textArea.Document.Lines.Cast<ISegment>(); segments = textArea.Document.Lines;
} else { } else {
segments = null; segments = null;
} }
@ -181,7 +181,7 @@ namespace ICSharpCode.AvalonEdit.Editing
TextArea textArea = GetTextArea(target); TextArea textArea = GetTextArea(target);
if (textArea != null && textArea.Document != null) { if (textArea != null && textArea.Document != null) {
using (textArea.Document.RunUpdate()) { using (textArea.Document.RunUpdate()) {
if (textArea.Selection.IsMultiline(textArea.Document)) { if (textArea.Selection.IsMultiline) {
var segment = textArea.Selection.SurroundingSegment; var segment = textArea.Selection.SurroundingSegment;
DocumentLine start = textArea.Document.GetLineByOffset(segment.Offset); DocumentLine start = textArea.Document.GetLineByOffset(segment.Offset);
DocumentLine end = textArea.Document.GetLineByOffset(segment.EndOffset); DocumentLine end = textArea.Document.GetLineByOffset(segment.EndOffset);
@ -246,7 +246,7 @@ namespace ICSharpCode.AvalonEdit.Editing
// If nothing in the selection is deletable; then reset caret+selection // If nothing in the selection is deletable; then reset caret+selection
// to the previous value. This prevents the caret from moving through read-only sections. // to the previous value. This prevents the caret from moving through read-only sections.
textArea.Caret.Position = oldCaretPosition; textArea.Caret.Position = oldCaretPosition;
textArea.Selection = Selection.Empty; textArea.ClearSelection();
} }
} }
textArea.RemoveSelectedText(); textArea.RemoveSelectedText();
@ -325,7 +325,7 @@ namespace ICSharpCode.AvalonEdit.Editing
return; return;
} }
string text = textArea.Selection.GetText(textArea.Document); string text = textArea.Selection.GetText();
text = TextUtilities.NormalizeNewLines(text, Environment.NewLine); text = TextUtilities.NormalizeNewLines(text, Environment.NewLine);
textArea.OnTextCopied(new TextEventArgs(text)); textArea.OnTextCopied(new TextEventArgs(text));
} }
@ -399,7 +399,7 @@ namespace ICSharpCode.AvalonEdit.Editing
textArea.Document.Insert(currentLine.Offset, text); textArea.Document.Insert(currentLine.Offset, text);
} }
} else if (rectangular && textArea.Selection.IsEmpty) { } else if (rectangular && textArea.Selection.IsEmpty) {
if (!RectangleSelection.PerformRectangularPaste(textArea, textArea.Caret.Offset, text, false)) if (!RectangleSelection.PerformRectangularPaste(textArea, textArea.Caret.Position, text, false))
textArea.ReplaceSelectionWithText(text); textArea.ReplaceSelectionWithText(text);
} else { } else {
textArea.ReplaceSelectionWithText(text); textArea.ReplaceSelectionWithText(text);
@ -417,7 +417,7 @@ namespace ICSharpCode.AvalonEdit.Editing
TextArea textArea = GetTextArea(target); TextArea textArea = GetTextArea(target);
if (textArea != null && textArea.Document != null) { if (textArea != null && textArea.Document != null) {
DocumentLine currentLine = textArea.Document.GetLineByNumber(textArea.Caret.Line); DocumentLine currentLine = textArea.Document.GetLineByNumber(textArea.Caret.Line);
textArea.Selection = new SimpleSelection(currentLine.Offset, currentLine.Offset + currentLine.TotalLength); textArea.Selection = Selection.Create(textArea, currentLine.Offset, currentLine.Offset + currentLine.TotalLength);
textArea.RemoveSelectedText(); textArea.RemoveSelectedText();
args.Handled = true; args.Handled = true;
} }

75
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EmptySelection.cs

@ -0,0 +1,75 @@
// 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.Runtime.CompilerServices;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Utils;
namespace ICSharpCode.AvalonEdit.Editing
{
sealed class EmptySelection : Selection
{
public EmptySelection(TextArea textArea) : base(textArea)
{
}
public override Selection UpdateOnDocumentChange(DocumentChangeEventArgs e)
{
return this;
}
public override ISegment SurroundingSegment {
get { return null; }
}
public override Selection SetEndpoint(TextViewPosition endPosition)
{
throw new NotSupportedException();
}
public override Selection StartSelectionOrSetEndpoint(TextViewPosition startPosition, TextViewPosition endPosition)
{
var document = textArea.Document;
if (document == null)
throw ThrowUtil.NoDocumentAssigned();
return Create(textArea, document.GetOffset(startPosition), document.GetOffset(endPosition));
}
public override IEnumerable<SelectionSegment> Segments {
get { return Empty<SelectionSegment>.Array; }
}
public override string GetText()
{
return string.Empty;
}
public override void ReplaceSelectionWithText(string newText)
{
if (newText == null)
throw new ArgumentNullException("newText");
if (newText.Length > 0) {
if (textArea.ReadOnlySectionProvider.CanInsert(textArea.Caret.Offset)) {
textArea.Document.Insert(textArea.Caret.Offset, newText);
}
}
}
public override int Length {
get { return 0; }
}
// Use reference equality because there's only one EmptySelection per text area.
public override int GetHashCode()
{
return RuntimeHelpers.GetHashCode(this);
}
public override bool Equals(object obj)
{
return this == obj;
}
}
}

8
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/LineNumberMargin.cs

@ -158,9 +158,9 @@ namespace ICSharpCode.AvalonEdit.Editing
if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift) { if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift) {
SimpleSelection simpleSelection = textArea.Selection as SimpleSelection; SimpleSelection simpleSelection = textArea.Selection as SimpleSelection;
if (simpleSelection != null) if (simpleSelection != null)
selectionStart = new AnchorSegment(Document, simpleSelection); selectionStart = new AnchorSegment(Document, simpleSelection.SurroundingSegment);
} }
textArea.Selection = new SimpleSelection(selectionStart); textArea.Selection = Selection.Create(textArea, selectionStart);
if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift) { if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift) {
ExtendSelection(currentSeg); ExtendSelection(currentSeg);
} }
@ -191,10 +191,10 @@ namespace ICSharpCode.AvalonEdit.Editing
{ {
if (currentSeg.Offset < selectionStart.Offset) { if (currentSeg.Offset < selectionStart.Offset) {
textArea.Caret.Offset = currentSeg.Offset; textArea.Caret.Offset = currentSeg.Offset;
textArea.Selection = new SimpleSelection(currentSeg.Offset, selectionStart.Offset + selectionStart.Length); textArea.Selection = Selection.Create(textArea, currentSeg.Offset, selectionStart.Offset + selectionStart.Length);
} else { } else {
textArea.Caret.Offset = currentSeg.Offset + currentSeg.Length; textArea.Caret.Offset = currentSeg.Offset + currentSeg.Length;
textArea.Selection = new SimpleSelection(selectionStart.Offset, currentSeg.Offset + currentSeg.Length); textArea.Selection = Selection.Create(textArea, selectionStart.Offset, currentSeg.Offset + currentSeg.Length);
} }
} }

167
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/RectangleSelection.cs

@ -6,8 +6,10 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Windows;
using System.Windows.Media.TextFormatting;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.AvalonEdit.Utils;
namespace ICSharpCode.AvalonEdit.Editing namespace ICSharpCode.AvalonEdit.Editing
@ -18,52 +20,71 @@ namespace ICSharpCode.AvalonEdit.Editing
public sealed class RectangleSelection : Selection public sealed class RectangleSelection : Selection
{ {
TextDocument document; TextDocument document;
readonly int startLine, endLine;
readonly double startXPos, endXPos;
readonly int startOffset, endOffset;
/// <summary> readonly List<SelectionSegment> segments = new List<SelectionSegment>();
/// Gets the start position of the selection.
/// </summary>
public int StartOffset { get; private set; }
/// <summary> void InitDocument()
/// Gets the end position of the selection. {
/// </summary> document = textArea.Document;
public int EndOffset { get; private set; } if (document == null)
throw ThrowUtil.NoDocumentAssigned();
}
/// <summary> /// <summary>
/// Creates a new rectangular selection. /// Creates a new rectangular selection.
/// </summary> /// </summary>
public RectangleSelection(TextDocument document, int start, int end) public RectangleSelection(TextArea textArea, TextViewPosition start, TextViewPosition end)
: base(textArea)
{ {
if (document == null) InitDocument();
throw new ArgumentNullException("document"); this.startLine = start.Line;
this.document = document; this.endLine = end.Line;
this.StartOffset = start; this.startXPos = GetXPos(start);
this.EndOffset = end; this.endXPos = GetXPos(end);
this.startOffset = document.GetOffset(start);
this.endOffset = document.GetOffset(end);
CalculateSegments();
} }
/// <inheritdoc/> private RectangleSelection(TextArea textArea, int startLine, double startXPos, int startOffset, TextViewPosition end)
public override bool IsEmpty { : base(textArea)
get { {
TextLocation start = document.GetLocation(StartOffset); InitDocument();
TextLocation end = document.GetLocation(EndOffset); this.startLine = startLine;
return start.Column == end.Column; this.endLine = end.Line;
} this.startXPos = startXPos;
this.endXPos = GetXPos(end);
this.startOffset = startOffset;
this.endOffset = document.GetOffset(end);
CalculateSegments();
} }
/// <inheritdoc/> private RectangleSelection(TextArea textArea, TextViewPosition start, int endLine, double endXPos, int endOffset)
public override bool Contains(int offset) : base(textArea)
{ {
if (Math.Min(StartOffset, EndOffset) <= offset && offset <= Math.Max(StartOffset, EndOffset)) { InitDocument();
foreach (ISegment s in this.Segments) { this.startLine = start.Line;
if (s.Contains(offset)) this.endLine = endLine;
return true; this.startXPos = GetXPos(start);
} this.endXPos = endXPos;
} this.startOffset = document.GetOffset(start);
return false; this.endOffset = endOffset;
CalculateSegments();
}
double GetXPos(TextViewPosition pos)
{
DocumentLine documentLine = document.GetLineByNumber(pos.Line);
VisualLine visualLine = textArea.TextView.GetOrConstructVisualLine(documentLine);
TextLine textLine = visualLine.GetTextLine(pos.VisualColumn);
return visualLine.GetTextLineVisualXPosition(textLine, pos.VisualColumn);
} }
/// <inheritdoc/> /// <inheritdoc/>
public override string GetText(TextDocument document) public override string GetText()
{ {
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();
foreach (ISegment s in this.Segments) { foreach (ISegment s in this.Segments) {
@ -75,9 +96,9 @@ namespace ICSharpCode.AvalonEdit.Editing
} }
/// <inheritdoc/> /// <inheritdoc/>
public override Selection StartSelectionOrSetEndpoint(int startOffset, int newEndOffset) public override Selection StartSelectionOrSetEndpoint(TextViewPosition startPosition, TextViewPosition endPosition)
{ {
return SetEndpoint(newEndOffset); return SetEndpoint(endPosition);
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -90,61 +111,67 @@ namespace ICSharpCode.AvalonEdit.Editing
/// <inheritdoc/> /// <inheritdoc/>
public override ISegment SurroundingSegment { public override ISegment SurroundingSegment {
get { get {
return new SimpleSegment(Math.Min(StartOffset, EndOffset), Math.Abs(EndOffset - StartOffset)); return new SimpleSegment(Math.Min(startOffset, endOffset), Math.Abs(endOffset - startOffset));
} }
} }
/// <inheritdoc/> /// <inheritdoc/>
public override IEnumerable<ISegment> Segments { public override IEnumerable<SelectionSegment> Segments {
get { get { return segments; }
TextLocation start = document.GetLocation(StartOffset); }
TextLocation end = document.GetLocation(EndOffset);
DocumentLine line = document.GetLineByNumber(Math.Min(start.Line, end.Line)); void CalculateSegments()
int numberOfLines = Math.Abs(start.Line - end.Line); {
int startCol = Math.Min(start.Column, end.Column); DocumentLine nextLine = document.GetLineByNumber(Math.Min(startLine, endLine));
int endCol = Math.Max(start.Column, end.Column); do {
for (int i = 0; i <= numberOfLines; i++) { VisualLine vl = textArea.TextView.GetOrConstructVisualLine(nextLine);
if (line.Length + 1 >= startCol) { int startVC = vl.GetVisualColumn(new Point(startXPos, 0), true);
int thisLineEndCol = Math.Min(endCol, line.Length + 1); int endVC = vl.GetVisualColumn(new Point(endXPos, 0), true);
yield return new SimpleSegment(line.Offset + startCol - 1, thisLineEndCol - startCol);
} int baseOffset = vl.FirstDocumentLine.Offset;
line = line.NextLine; int startOffset = baseOffset + vl.GetRelativeOffset(startVC);
} int endOffset = baseOffset + vl.GetRelativeOffset(endVC);
} segments.Add(new SelectionSegment(startOffset, startVC, endOffset, endVC));
nextLine = vl.LastDocumentLine.NextLine;
} while (nextLine.LineNumber <= Math.Max(startLine, endLine));
} }
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
RectangleSelection r = obj as RectangleSelection; RectangleSelection r = obj as RectangleSelection;
return r != null && r.document == this.document && r.StartOffset == this.StartOffset && r.EndOffset == this.EndOffset; return r != null && r.textArea == this.textArea
&& r.startOffset == this.startOffset && r.endOffset == this.endOffset
&& r.startLine == this.startLine && r.endLine == this.endLine
&& r.startXPos == this.startXPos && r.endXPos == this.endXPos;
} }
/// <inheritdoc/> /// <inheritdoc/>
public override int GetHashCode() public override int GetHashCode()
{ {
return StartOffset ^ EndOffset; return startOffset ^ endOffset;
} }
/// <inheritdoc/> /// <inheritdoc/>
public override Selection SetEndpoint(int newEndOffset) public override Selection SetEndpoint(TextViewPosition endPosition)
{ {
return new RectangleSelection(this.document, this.StartOffset, newEndOffset); return new RectangleSelection(textArea, startLine, startXPos, startOffset, endPosition);
} }
/// <inheritdoc/> /// <inheritdoc/>
public override Selection UpdateOnDocumentChange(DocumentChangeEventArgs e) public override Selection UpdateOnDocumentChange(DocumentChangeEventArgs e)
{ {
return new RectangleSelection(document, throw new NotImplementedException();
e.GetNewOffset(StartOffset, AnchorMovementType.AfterInsertion), // return new RectangleSelection(textArea,
e.GetNewOffset(EndOffset, AnchorMovementType.BeforeInsertion)); // e.GetNewOffset(StartOffset, AnchorMovementType.AfterInsertion),
// e.GetNewOffset(EndOffset, AnchorMovementType.BeforeInsertion));
} }
/// <inheritdoc/> /// <inheritdoc/>
public override void ReplaceSelectionWithText(TextArea textArea, string newText) public override void ReplaceSelectionWithText(string newText)
{ {
if (textArea == null) throw new NotImplementedException(); /*
throw new ArgumentNullException("textArea");
if (newText == null) if (newText == null)
throw new ArgumentNullException("newText"); throw new ArgumentNullException("newText");
using (textArea.Document.RunUpdate()) { using (textArea.Document.RunUpdate()) {
@ -201,7 +228,7 @@ namespace ICSharpCode.AvalonEdit.Editing
textArea.Selection = Selection.Empty; textArea.Selection = Selection.Empty;
} }
} }
} }*/
} }
static void ReplaceSingleLineText(TextArea textArea, ISegment lineSegment, string newText) static void ReplaceSingleLineText(TextArea textArea, ISegment lineSegment, string newText)
@ -225,23 +252,22 @@ namespace ICSharpCode.AvalonEdit.Editing
/// <summary> /// <summary>
/// Performs a rectangular paste operation. /// Performs a rectangular paste operation.
/// </summary> /// </summary>
public static bool PerformRectangularPaste(TextArea textArea, int startOffset, string text, bool selectInsertedText) public static bool PerformRectangularPaste(TextArea textArea, TextViewPosition startPosition, string text, bool selectInsertedText)
{ {
if (textArea == null) if (textArea == null)
throw new ArgumentNullException("textArea"); throw new ArgumentNullException("textArea");
if (text == null) if (text == null)
throw new ArgumentNullException("text"); throw new ArgumentNullException("text");
int newLineCount = text.Count(c => c == '\n'); int newLineCount = text.Count(c => c == '\n');
TextLocation startLocation = textArea.Document.GetLocation(startOffset); TextLocation endLocation = new TextLocation(startPosition.Line + newLineCount, startPosition.Column);
TextLocation endLocation = new TextLocation(startLocation.Line + newLineCount, startLocation.Column);
if (endLocation.Line <= textArea.Document.LineCount) { if (endLocation.Line <= textArea.Document.LineCount) {
int endOffset = textArea.Document.GetOffset(endLocation); int endOffset = textArea.Document.GetOffset(endLocation);
if (textArea.Document.GetLocation(endOffset) == endLocation) { if (textArea.Document.GetLocation(endOffset) == endLocation) {
RectangleSelection rsel = new RectangleSelection(textArea.Document, startOffset, endOffset); RectangleSelection rsel = new RectangleSelection(textArea, startPosition, new TextViewPosition(endLocation));
rsel.ReplaceSelectionWithText(textArea, text); rsel.ReplaceSelectionWithText(text);
if (selectInsertedText && textArea.Selection is RectangleSelection) { if (selectInsertedText && textArea.Selection is RectangleSelection) {
RectangleSelection sel = (RectangleSelection)textArea.Selection; RectangleSelection sel = (RectangleSelection)textArea.Selection;
textArea.Selection = new RectangleSelection(textArea.Document, startOffset, sel.EndOffset); textArea.Selection = new RectangleSelection(textArea, startPosition, sel.endLine, sel.endXPos, sel.endOffset);
} }
return true; return true;
} }
@ -270,10 +296,7 @@ namespace ICSharpCode.AvalonEdit.Editing
{ {
// It's possible that ToString() gets called on old (invalid) selections, e.g. for "change from... to..." debug message // It's possible that ToString() gets called on old (invalid) selections, e.g. for "change from... to..." debug message
// make sure we don't crash even when the desired locations don't exist anymore. // make sure we don't crash even when the desired locations don't exist anymore.
if (StartOffset < document.TextLength && EndOffset < document.TextLength) return "[RectangleSelection " + startOffset + " to " + endOffset + "]";
return "[RectangleSelection " + document.GetLocation(StartOffset) + " to " + document.GetLocation(EndOffset) + "]";
else
return "[RectangleSelection " + StartOffset + " to " + EndOffset + "]";
} }
} }
} }

86
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Selection.cs

@ -5,9 +5,9 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.Windows; using System.Windows;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Utils;
namespace ICSharpCode.AvalonEdit.Editing namespace ICSharpCode.AvalonEdit.Editing
{ {
@ -17,15 +17,44 @@ namespace ICSharpCode.AvalonEdit.Editing
public abstract class Selection public abstract class Selection
{ {
/// <summary> /// <summary>
/// Gets the empty selection. /// Creates a new simple selection that selects the text from startOffset to endOffset.
/// </summary>
public static Selection Create(TextArea textArea, int startOffset, int endOffset)
{
if (textArea == null)
throw new ArgumentNullException("textArea");
if (startOffset == endOffset)
return textArea.emptySelection;
else
return new SimpleSelection(textArea, startOffset, endOffset);
}
/// <summary>
/// Creates a new simple selection that selects the text in the specified segment.
/// </summary>
public static Selection Create(TextArea textArea, ISegment segment)
{
if (segment == null)
throw new ArgumentNullException("segment");
return Create(textArea, segment.Offset, segment.EndOffset);
}
internal readonly TextArea textArea;
/// <summary>
/// Constructor for Selection.
/// </summary> /// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", Justification="Empty selection is immutable")] protected Selection(TextArea textArea)
public static readonly Selection Empty = new SimpleSelection(-1, -1); {
if (textArea == null)
throw new ArgumentNullException("textArea");
this.textArea = textArea;
}
/// <summary> /// <summary>
/// Gets the selected text segments. /// Gets the selected text segments.
/// </summary> /// </summary>
public abstract IEnumerable<ISegment> Segments { get; } public abstract IEnumerable<SelectionSegment> Segments { get; }
/// <summary> /// <summary>
/// Gets the smallest segment that contains all segments in this selection. /// Gets the smallest segment that contains all segments in this selection.
@ -36,7 +65,7 @@ namespace ICSharpCode.AvalonEdit.Editing
/// <summary> /// <summary>
/// Replaces the selection with the specified text. /// Replaces the selection with the specified text.
/// </summary> /// </summary>
public abstract void ReplaceSelectionWithText(TextArea textArea, string newText); public abstract void ReplaceSelectionWithText(string newText);
/// <summary> /// <summary>
/// Updates the selection when the document changes. /// Updates the selection when the document changes.
@ -59,42 +88,39 @@ namespace ICSharpCode.AvalonEdit.Editing
/// Returns a new selection with the changed end point. /// Returns a new selection with the changed end point.
/// </summary> /// </summary>
/// <exception cref="NotSupportedException">Cannot set endpoint for empty selection</exception> /// <exception cref="NotSupportedException">Cannot set endpoint for empty selection</exception>
public abstract Selection SetEndpoint(int newEndOffset); public abstract Selection SetEndpoint(TextViewPosition endPosition);
/// <summary> /// <summary>
/// If this selection is empty, starts a new selection from <paramref name="startOffset"/> to /// If this selection is empty, starts a new selection from <paramref name="startOffset"/> to
/// <paramref name="newEndOffset"/>, otherwise, changes the endpoint of this selection. /// <paramref name="newEndOffset"/>, otherwise, changes the endpoint of this selection.
/// </summary> /// </summary>
public virtual Selection StartSelectionOrSetEndpoint(int startOffset, int newEndOffset) public abstract Selection StartSelectionOrSetEndpoint(TextViewPosition startPosition, TextViewPosition endPosition);
{
if (IsEmpty)
return new SimpleSelection(startOffset, newEndOffset);
else
return SetEndpoint(newEndOffset);
}
/// <summary> /// <summary>
/// Gets whether the selection is multi-line. /// Gets whether the selection is multi-line.
/// </summary> /// </summary>
public virtual bool IsMultiline(TextDocument document) public virtual bool IsMultiline {
{ get {
if (document == null) ISegment surroundingSegment = this.SurroundingSegment;
throw new ArgumentNullException("document"); if (surroundingSegment == null)
ISegment surroundingSegment = this.SurroundingSegment; return false;
if (surroundingSegment == null) int start = surroundingSegment.Offset;
return false; int end = start + surroundingSegment.Length;
int start = surroundingSegment.Offset; var document = textArea.Document;
int end = start + surroundingSegment.Length; if (document == null)
return document.GetLineByOffset(start) != document.GetLineByOffset(end); throw ThrowUtil.NoDocumentAssigned();
return document.GetLineByOffset(start) != document.GetLineByOffset(end);
}
} }
/// <summary> /// <summary>
/// Gets the selected text. /// Gets the selected text.
/// </summary> /// </summary>
public virtual string GetText(TextDocument document) public virtual string GetText()
{ {
var document = textArea.Document;
if (document == null) if (document == null)
throw new ArgumentNullException("document"); throw ThrowUtil.NoDocumentAssigned();
StringBuilder b = null; StringBuilder b = null;
string text = null; string text = null;
foreach (ISegment s in Segments) { foreach (ISegment s in Segments) {
@ -117,10 +143,8 @@ namespace ICSharpCode.AvalonEdit.Editing
/// <summary> /// <summary>
/// Creates a HTML fragment for the selected text. /// Creates a HTML fragment for the selected text.
/// </summary> /// </summary>
public string CreateHtmlFragment(TextArea textArea, HtmlOptions options) public string CreateHtmlFragment(HtmlOptions options)
{ {
if (textArea == null)
throw new ArgumentNullException("textArea");
if (options == null) if (options == null)
throw new ArgumentNullException("options"); throw new ArgumentNullException("options");
IHighlighter highlighter = textArea.GetService(typeof(IHighlighter)) as IHighlighter; IHighlighter highlighter = textArea.GetService(typeof(IHighlighter)) as IHighlighter;
@ -166,7 +190,7 @@ namespace ICSharpCode.AvalonEdit.Editing
/// </summary> /// </summary>
public virtual DataObject CreateDataObject(TextArea textArea) public virtual DataObject CreateDataObject(TextArea textArea)
{ {
string text = GetText(textArea.Document); string text = GetText();
// Ensure we use the appropriate newline sequence for the OS // Ensure we use the appropriate newline sequence for the OS
DataObject data = new DataObject(TextUtilities.NormalizeNewLines(text, Environment.NewLine)); DataObject data = new DataObject(TextUtilities.NormalizeNewLines(text, Environment.NewLine));
// we cannot use DataObject.SetText - then we cannot drag to SciTe // we cannot use DataObject.SetText - then we cannot drag to SciTe
@ -174,7 +198,7 @@ namespace ICSharpCode.AvalonEdit.Editing
// Also copy text in HTML format to clipboard - good for pasting text into Word // Also copy text in HTML format to clipboard - good for pasting text into Word
// or to the SharpDevelop forums. // or to the SharpDevelop forums.
HtmlClipboard.SetHtml(data, CreateHtmlFragment(textArea, new HtmlOptions(textArea.Options))); HtmlClipboard.SetHtml(data, CreateHtmlFragment(new HtmlOptions(textArea.Options)));
return data; return data;
} }
} }

15
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/SelectionColorizer.cs

@ -28,15 +28,20 @@ namespace ICSharpCode.AvalonEdit.Editing
int lineStartOffset = context.VisualLine.FirstDocumentLine.Offset; int lineStartOffset = context.VisualLine.FirstDocumentLine.Offset;
int lineEndOffset = context.VisualLine.LastDocumentLine.Offset + context.VisualLine.LastDocumentLine.TotalLength; int lineEndOffset = context.VisualLine.LastDocumentLine.Offset + context.VisualLine.LastDocumentLine.TotalLength;
foreach (ISegment segment in textArea.Selection.Segments) { foreach (var segment in textArea.Selection.Segments) {
int segmentStart = segment.Offset; int segmentStart = segment.StartOffset;
int segmentEnd = segment.Offset + segment.Length; int segmentEnd = segment.EndOffset;
if (segmentEnd <= lineStartOffset) if (segmentEnd <= lineStartOffset)
continue; continue;
if (segmentStart >= lineEndOffset) if (segmentStart >= lineEndOffset)
continue; continue;
int startColumn = context.VisualLine.GetVisualColumn(Math.Max(0, segmentStart - lineStartOffset)); int startColumn = segment.StartVisualColumn;
int endColumn = context.VisualLine.GetVisualColumn(segmentEnd - lineStartOffset); int endColumn = segment.EndVisualColumn;
if (startColumn < 0)
startColumn = context.VisualLine.GetVisualColumn(Math.Max(0, segmentStart - lineStartOffset));
if (endColumn < 0)
endColumn = context.VisualLine.GetVisualColumn(segmentEnd - lineStartOffset);
ChangeVisualElements( ChangeVisualElements(
startColumn, endColumn, startColumn, endColumn,
element => { element => {

32
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/SelectionMouseHandler.cs

@ -222,11 +222,11 @@ namespace ICSharpCode.AvalonEdit.Editing
// the undo groups when text is moved. // the undo groups when text is moved.
textArea.Document.UndoStack.StartUndoGroup(this.currentDragDescriptor); textArea.Document.UndoStack.StartUndoGroup(this.currentDragDescriptor);
try { try {
if (rectangular && RectangleSelection.PerformRectangularPaste(textArea, start, text, true)) { if (rectangular && RectangleSelection.PerformRectangularPaste(textArea, textArea.Caret.Position, text, true)) {
} else { } else {
textArea.Document.Insert(start, text); textArea.Document.Insert(start, text);
textArea.Selection = new SimpleSelection(start, start + text.Length); textArea.Selection = Selection.Create(textArea, start, start + text.Length);
} }
} finally { } finally {
textArea.Document.UndoStack.EndUndoGroup(); textArea.Document.UndoStack.EndUndoGroup();
@ -391,23 +391,23 @@ namespace ICSharpCode.AvalonEdit.Editing
} }
} }
int oldOffset = textArea.Caret.Offset; var oldPosition = textArea.Caret.Position;
SetCaretOffsetToMousePosition(e); SetCaretOffsetToMousePosition(e);
if (!shift) { if (!shift) {
textArea.Selection = Selection.Empty; textArea.ClearSelection();
} }
if (textArea.CaptureMouse()) { if (textArea.CaptureMouse()) {
if ((modifiers & ModifierKeys.Alt) == ModifierKeys.Alt && textArea.Options.EnableRectangularSelection) { if ((modifiers & ModifierKeys.Alt) == ModifierKeys.Alt && textArea.Options.EnableRectangularSelection) {
mode = SelectionMode.Rectangular; mode = SelectionMode.Rectangular;
if (shift && textArea.Selection is RectangleSelection) { if (shift && textArea.Selection is RectangleSelection) {
textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldOffset, textArea.Caret.Offset); textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldPosition, textArea.Caret.Position);
} }
} else if (e.ClickCount == 1 && ((modifiers & ModifierKeys.Control) == 0)) { } else if (e.ClickCount == 1 && ((modifiers & ModifierKeys.Control) == 0)) {
mode = SelectionMode.Normal; mode = SelectionMode.Normal;
if (shift && !(textArea.Selection is RectangleSelection)) { if (shift && !(textArea.Selection is RectangleSelection)) {
textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldOffset, textArea.Caret.Offset); textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldPosition, textArea.Caret.Position);
} }
} else { } else {
SimpleSegment startWord; SimpleSegment startWord;
@ -425,13 +425,13 @@ namespace ICSharpCode.AvalonEdit.Editing
} }
if (shift && !textArea.Selection.IsEmpty) { if (shift && !textArea.Selection.IsEmpty) {
if (startWord.Offset < textArea.Selection.SurroundingSegment.Offset) { if (startWord.Offset < textArea.Selection.SurroundingSegment.Offset) {
textArea.Selection = textArea.Selection.SetEndpoint(startWord.Offset); textArea.Selection = textArea.Selection.SetEndpoint(new TextViewPosition(textArea.Document.GetLocation(startWord.Offset)));
} else if (startWord.EndOffset > textArea.Selection.SurroundingSegment.EndOffset) { } else if (startWord.EndOffset > textArea.Selection.SurroundingSegment.EndOffset) {
textArea.Selection = textArea.Selection.SetEndpoint(startWord.EndOffset); textArea.Selection = textArea.Selection.SetEndpoint(new TextViewPosition(textArea.Document.GetLocation(startWord.EndOffset)));
} }
this.startWord = new AnchorSegment(textArea.Document, textArea.Selection.SurroundingSegment); this.startWord = new AnchorSegment(textArea.Document, textArea.Selection.SurroundingSegment);
} else { } else {
textArea.Selection = new SimpleSelection(startWord.Offset, startWord.EndOffset); textArea.Selection = Selection.Create(textArea, startWord.Offset, startWord.EndOffset);
this.startWord = new AnchorSegment(textArea.Document, startWord.Offset, startWord.Length); this.startWord = new AnchorSegment(textArea.Document, startWord.Offset, startWord.Length);
} }
} }
@ -561,19 +561,21 @@ namespace ICSharpCode.AvalonEdit.Editing
void ExtendSelectionToMouse(MouseEventArgs e) void ExtendSelectionToMouse(MouseEventArgs e)
{ {
int oldOffset = textArea.Caret.Offset; int oldOffset = textArea.Caret.Offset;
TextViewPosition oldPosition = textArea.Caret.Position;
if (mode == SelectionMode.Normal || mode == SelectionMode.Rectangular) { if (mode == SelectionMode.Normal || mode == SelectionMode.Rectangular) {
SetCaretOffsetToMousePosition(e); SetCaretOffsetToMousePosition(e);
if (mode == SelectionMode.Normal && textArea.Selection is RectangleSelection) if (mode == SelectionMode.Normal && textArea.Selection is RectangleSelection)
textArea.Selection = new SimpleSelection(oldOffset, textArea.Caret.Offset); textArea.Selection = Selection.Create(textArea, oldOffset, textArea.Caret.Offset);
else if (mode == SelectionMode.Rectangular && !(textArea.Selection is RectangleSelection)) else if (mode == SelectionMode.Rectangular && !(textArea.Selection is RectangleSelection))
textArea.Selection = new RectangleSelection(textArea.Document, oldOffset, textArea.Caret.Offset); textArea.Selection = new RectangleSelection(textArea, oldPosition, textArea.Caret.Position);
else else
textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldOffset, textArea.Caret.Offset); textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldPosition, textArea.Caret.Position);
} else if (mode == SelectionMode.WholeWord || mode == SelectionMode.WholeLine) { } else if (mode == SelectionMode.WholeWord || mode == SelectionMode.WholeLine) {
var newWord = (mode == SelectionMode.WholeLine) ? GetLineAtMousePosition(e) : GetWordAtMousePosition(e); var newWord = (mode == SelectionMode.WholeLine) ? GetLineAtMousePosition(e) : GetWordAtMousePosition(e);
if (newWord != SimpleSegment.Invalid) { if (newWord != SimpleSegment.Invalid) {
textArea.Selection = new SimpleSelection(Math.Min(newWord.Offset, startWord.Offset), textArea.Selection = Selection.Create(textArea,
Math.Max(newWord.EndOffset, startWord.EndOffset)); Math.Min(newWord.Offset, startWord.Offset),
Math.Max(newWord.EndOffset, startWord.EndOffset));
// Set caret offset, but limit the caret to stay inside the selection. // Set caret offset, but limit the caret to stay inside the selection.
// in whole-word selection, it's otherwise possible that we get the caret outside the // in whole-word selection, it's otherwise possible that we get the caret outside the
// selection - but the TextArea doesn't like that and will reset the selection, causing // selection - but the TextArea doesn't like that and will reset the selection, causing
@ -594,7 +596,7 @@ namespace ICSharpCode.AvalonEdit.Editing
if (mode == SelectionMode.PossibleDragStart) { if (mode == SelectionMode.PossibleDragStart) {
// -> this was not a drag start (mouse didn't move after mousedown) // -> this was not a drag start (mouse didn't move after mousedown)
SetCaretOffsetToMousePosition(e); SetCaretOffsetToMousePosition(e);
textArea.Selection = Selection.Empty; textArea.ClearSelection();
} else if (mode == SelectionMode.Normal || mode == SelectionMode.WholeWord || mode == SelectionMode.WholeLine || mode == SelectionMode.Rectangular) { } else if (mode == SelectionMode.Normal || mode == SelectionMode.WholeWord || mode == SelectionMode.WholeLine || mode == SelectionMode.Rectangular) {
ExtendSelectionToMouse(e); ExtendSelectionToMouse(e);
} }

63
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/SelectionSegment.cs

@ -0,0 +1,63 @@
// 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 ICSharpCode.AvalonEdit.Document;
namespace ICSharpCode.AvalonEdit.Editing
{
/// <summary>
/// Represents a selected segment.
/// </summary>
public class SelectionSegment : ISegment
{
int startOffset, endOffset;
int startVC, endVC;
public SelectionSegment(int startOffset, int endOffset)
{
this.startOffset = Math.Min(startOffset, endOffset);
this.endOffset = Math.Max(startOffset, endOffset);
}
public SelectionSegment(int startOffset, int startVC, int endOffset, int endVC)
{
if (startOffset <= endOffset) {
this.startOffset = startOffset;
this.startVC = startVC;
this.endOffset = endOffset;
this.endVC = endVC;
} else {
this.startOffset = endOffset;
this.startVC = endVC;
this.endOffset = startOffset;
this.endVC = startVC;
}
}
public int StartOffset {
get { return startOffset; }
}
public int EndOffset {
get { return endOffset; }
}
public int StartVisualColumn {
get { return startVC; }
}
public int EndVisualColumn {
get { return endVC; }
}
int ISegment.Offset {
get { return startOffset; }
}
/// <inheritdoc/>
public int Length {
get { return endOffset - startOffset; }
}
}
}

73
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/SimpleSelection.cs

@ -13,56 +13,37 @@ namespace ICSharpCode.AvalonEdit.Editing
/// <summary> /// <summary>
/// A simple selection. /// A simple selection.
/// </summary> /// </summary>
public sealed class SimpleSelection : Selection, ISegment sealed class SimpleSelection : Selection
{ {
readonly int startOffset, endOffset; readonly int startOffset, endOffset;
/// <summary> /// <summary>
/// Creates a new SimpleSelection instance. /// Creates a new SimpleSelection instance.
/// </summary> /// </summary>
public SimpleSelection(int startOffset, int endOffset) internal SimpleSelection(TextArea textArea, int startOffset, int endOffset)
: base(textArea)
{ {
this.startOffset = startOffset; this.startOffset = startOffset;
this.endOffset = endOffset; this.endOffset = endOffset;
} }
/// <summary>
/// Creates a new SimpleSelection instance.
/// </summary>
public SimpleSelection(ISegment segment)
{
if (segment == null)
throw new ArgumentNullException("segment");
this.startOffset = segment.Offset;
this.endOffset = startOffset + segment.Length;
}
/// <inheritdoc/> /// <inheritdoc/>
public override IEnumerable<ISegment> Segments { public override IEnumerable<SelectionSegment> Segments {
get { get {
if (!IsEmpty) { return ExtensionMethods.Sequence<SelectionSegment>(new SelectionSegment(startOffset, endOffset));
return ExtensionMethods.Sequence<ISegment>(this);
} else {
return Empty<ISegment>.Array;
}
} }
} }
/// <inheritdoc/> /// <inheritdoc/>
public override ISegment SurroundingSegment { public override ISegment SurroundingSegment {
get { get {
if (IsEmpty) return new SelectionSegment(startOffset, endOffset);
return null;
else
return this;
} }
} }
/// <inheritdoc/> /// <inheritdoc/>
public override void ReplaceSelectionWithText(TextArea textArea, string newText) public override void ReplaceSelectionWithText(string newText)
{ {
if (textArea == null)
throw new ArgumentNullException("textArea");
if (newText == null) if (newText == null)
throw new ArgumentNullException("newText"); throw new ArgumentNullException("newText");
using (textArea.Document.RunUpdate()) { using (textArea.Document.RunUpdate()) {
@ -73,7 +54,7 @@ namespace ICSharpCode.AvalonEdit.Editing
} }
} }
} else { } else {
ISegment[] segmentsToDelete = textArea.GetDeletableSegments(this); ISegment[] segmentsToDelete = textArea.GetDeletableSegments(this.SurroundingSegment);
for (int i = segmentsToDelete.Length - 1; i >= 0; i--) { for (int i = segmentsToDelete.Length - 1; i >= 0; i--) {
if (i == segmentsToDelete.Length - 1) { if (i == segmentsToDelete.Length - 1) {
textArea.Caret.Offset = segmentsToDelete[i].EndOffset; textArea.Caret.Offset = segmentsToDelete[i].EndOffset;
@ -83,7 +64,7 @@ namespace ICSharpCode.AvalonEdit.Editing
} }
} }
if (segmentsToDelete.Length != 0) { if (segmentsToDelete.Length != 0) {
textArea.Selection = Selection.Empty; textArea.ClearSelection();
} }
} }
} }
@ -108,7 +89,8 @@ namespace ICSharpCode.AvalonEdit.Editing
{ {
if (e == null) if (e == null)
throw new ArgumentNullException("e"); throw new ArgumentNullException("e");
return new SimpleSelection( return Selection.Create(
textArea,
e.GetNewOffset(startOffset, AnchorMovementType.Default), e.GetNewOffset(startOffset, AnchorMovementType.Default),
e.GetNewOffset(endOffset, AnchorMovementType.Default) e.GetNewOffset(endOffset, AnchorMovementType.Default)
); );
@ -119,16 +101,6 @@ namespace ICSharpCode.AvalonEdit.Editing
get { return startOffset == endOffset; } get { return startOffset == endOffset; }
} }
// For segments, Offset must be less than or equal to EndOffset;
// so we must use Min/Max.
int ISegment.Offset {
get { return Math.Min(startOffset, endOffset); }
}
int ISegment.EndOffset {
get { return Math.Max(startOffset, endOffset); }
}
/// <inheritdoc/> /// <inheritdoc/>
public override int Length { public override int Length {
get { get {
@ -137,18 +109,25 @@ namespace ICSharpCode.AvalonEdit.Editing
} }
/// <inheritdoc/> /// <inheritdoc/>
public override Selection SetEndpoint(int newEndOffset) public override Selection SetEndpoint(TextViewPosition endPosition)
{ {
if (IsEmpty) return Create(textArea, startOffset, textArea.Document.GetOffset(endPosition));
throw new NotSupportedException(); }
else
return new SimpleSelection(startOffset, newEndOffset); public override Selection StartSelectionOrSetEndpoint(TextViewPosition startPosition, TextViewPosition endPosition)
{
var document = textArea.Document;
if (document == null)
throw ThrowUtil.NoDocumentAssigned();
return Create(textArea, startOffset, document.GetOffset(endPosition));
} }
/// <inheritdoc/> /// <inheritdoc/>
public override int GetHashCode() public override int GetHashCode()
{ {
return startOffset ^ endOffset; unchecked {
return startOffset * 27811 + endOffset + textArea.GetHashCode();
}
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -156,9 +135,7 @@ namespace ICSharpCode.AvalonEdit.Editing
{ {
SimpleSelection other = obj as SimpleSelection; SimpleSelection other = obj as SimpleSelection;
if (other == null) return false; if (other == null) return false;
if (IsEmpty && other.IsEmpty) return this.startOffset == other.startOffset && this.endOffset == other.endOffset && this.textArea == other.textArea;
return true;
return this.startOffset == other.startOffset && this.endOffset == other.endOffset;
} }
/// <inheritdoc/> /// <inheritdoc/>

20
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/TextArea.cs

@ -59,6 +59,8 @@ namespace ICSharpCode.AvalonEdit.Editing
this.textView = textView; this.textView = textView;
this.Options = textView.Options; this.Options = textView.Options;
selection = emptySelection = new EmptySelection(this);
textView.Services.AddService(typeof(TextArea), this); textView.Services.AddService(typeof(TextArea), this);
textView.LineTransformers.Add(new SelectionColorizer(this)); textView.LineTransformers.Add(new SelectionColorizer(this));
@ -204,7 +206,7 @@ namespace ICSharpCode.AvalonEdit.Editing
// Reset caret location and selection: this is necessary because the caret/selection might be invalid // Reset caret location and selection: this is necessary because the caret/selection might be invalid
// in the new document (e.g. if new document is shorter than the old document). // in the new document (e.g. if new document is shorter than the old document).
caret.Location = new TextLocation(1, 1); caret.Location = new TextLocation(1, 1);
this.Selection = Selection.Empty; this.ClearSelection();
if (DocumentChanged != null) if (DocumentChanged != null)
DocumentChanged(this, EventArgs.Empty); DocumentChanged(this, EventArgs.Empty);
CommandManager.InvalidateRequerySuggested(); CommandManager.InvalidateRequerySuggested();
@ -365,7 +367,8 @@ namespace ICSharpCode.AvalonEdit.Editing
#endregion #endregion
#region Selection property #region Selection property
Selection selection = Selection.Empty; internal readonly Selection emptySelection;
Selection selection;
/// <summary> /// <summary>
/// Occurs when the selection has changed. /// Occurs when the selection has changed.
@ -380,6 +383,8 @@ namespace ICSharpCode.AvalonEdit.Editing
set { set {
if (value == null) if (value == null)
throw new ArgumentNullException("value"); throw new ArgumentNullException("value");
if (value.textArea != this)
throw new ArgumentException("Cannot use a Selection instance that belongs to another text area.");
if (!object.Equals(selection, value)) { if (!object.Equals(selection, value)) {
//Debug.WriteLine("Selection change from " + selection + " to " + value); //Debug.WriteLine("Selection change from " + selection + " to " + value);
if (textView != null) { if (textView != null) {
@ -416,6 +421,11 @@ namespace ICSharpCode.AvalonEdit.Editing
} }
} }
public void ClearSelection()
{
this.Selection = emptySelection;
}
/// <summary> /// <summary>
/// The <see cref="SelectionBrush"/> property. /// The <see cref="SelectionBrush"/> property.
/// </summary> /// </summary>
@ -504,7 +514,7 @@ namespace ICSharpCode.AvalonEdit.Editing
if (allowCaretOutsideSelection == 0) { if (allowCaretOutsideSelection == 0) {
if (!selection.IsEmpty && !selection.Contains(caret.Offset)) { if (!selection.IsEmpty && !selection.Contains(caret.Offset)) {
Debug.WriteLine("Resetting selection because caret is outside"); Debug.WriteLine("Resetting selection because caret is outside");
this.Selection = Selection.Empty; this.ClearSelection();
} }
} }
} }
@ -861,7 +871,7 @@ namespace ICSharpCode.AvalonEdit.Editing
{ {
if (this.Document == null) if (this.Document == null)
throw ThrowUtil.NoDocumentAssigned(); throw ThrowUtil.NoDocumentAssigned();
selection.ReplaceSelectionWithText(this, string.Empty); selection.ReplaceSelectionWithText(string.Empty);
#if DEBUG #if DEBUG
if (!selection.IsEmpty) { if (!selection.IsEmpty) {
foreach (ISegment s in selection.Segments) { foreach (ISegment s in selection.Segments) {
@ -877,7 +887,7 @@ namespace ICSharpCode.AvalonEdit.Editing
throw new ArgumentNullException("newText"); throw new ArgumentNullException("newText");
if (this.Document == null) if (this.Document == null)
throw ThrowUtil.NoDocumentAssigned(); throw ThrowUtil.NoDocumentAssigned();
selection.ReplaceSelectionWithText(this, newText); selection.ReplaceSelectionWithText(newText);
} }
internal ISegment[] GetDeletableSegments(ISegment segment) internal ISegment[] GetDeletableSegments(ISegment segment)

4
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj

@ -144,6 +144,10 @@
<Compile Include="Editing\DottedLineMargin.cs" /> <Compile Include="Editing\DottedLineMargin.cs" />
<Compile Include="Editing\DragDropException.cs" /> <Compile Include="Editing\DragDropException.cs" />
<Compile Include="Editing\EditingCommandHandler.cs" /> <Compile Include="Editing\EditingCommandHandler.cs" />
<Compile Include="Editing\EmptySelection.cs">
<DependentUpon>Selection.cs</DependentUpon>
</Compile>
<Compile Include="Editing\SelectionSegment.cs" />
<Compile Include="Folding\AbstractFoldingStrategy.cs" /> <Compile Include="Folding\AbstractFoldingStrategy.cs" />
<Compile Include="Folding\FoldingElementGenerator.cs" /> <Compile Include="Folding\FoldingElementGenerator.cs" />
<Compile Include="Folding\FoldingManager.cs" /> <Compile Include="Folding\FoldingManager.cs" />

11
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/ColorizingTransformer.cs

@ -24,9 +24,16 @@ namespace ICSharpCode.AvalonEdit.Rendering
/// </summary> /// </summary>
public void Transform(ITextRunConstructionContext context, IList<VisualLineElement> elements) public void Transform(ITextRunConstructionContext context, IList<VisualLineElement> elements)
{ {
if (elements == null)
throw new ArgumentNullException("elements");
if (this.CurrentElements != null)
throw new InvalidOperationException("Recursive Transform() call");
this.CurrentElements = elements; this.CurrentElements = elements;
Colorize(context); try {
this.CurrentElements = null; Colorize(context);
} finally {
this.CurrentElements = null;
}
} }
/// <summary> /// <summary>

2
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/InsertionContext.cs

@ -37,7 +37,7 @@ namespace ICSharpCode.AvalonEdit.Snippets
throw new ArgumentNullException("textArea"); throw new ArgumentNullException("textArea");
this.TextArea = textArea; this.TextArea = textArea;
this.Document = textArea.Document; this.Document = textArea.Document;
this.SelectedText = textArea.Selection.GetText(textArea.Document); this.SelectedText = textArea.Selection.GetText();
this.InsertionPosition = insertionPosition; this.InsertionPosition = insertionPosition;
this.startPosition = insertionPosition; this.startPosition = insertionPosition;

2
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/SnippetInputHandler.cs

@ -54,7 +54,7 @@ namespace ICSharpCode.AvalonEdit.Snippets
void SelectElement(IActiveElement element) void SelectElement(IActiveElement element)
{ {
if (element != null) { if (element != null) {
TextArea.Selection = new SimpleSelection(element.Segment); TextArea.Selection = Selection.Create(TextArea, element.Segment);
TextArea.Caret.Offset = element.Segment.EndOffset; TextArea.Caret.Offset = element.Segment.EndOffset;
} }
} }

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

@ -818,7 +818,7 @@ namespace ICSharpCode.AvalonEdit
int length = this.SelectionLength; int length = this.SelectionLength;
textArea.Document.Replace(offset, length, value); textArea.Document.Replace(offset, length, value);
// keep inserted text selected // keep inserted text selected
textArea.Selection = new SimpleSelection(offset, offset + value.Length); textArea.Selection = SimpleSelection.Create(textArea, offset, offset + value.Length);
} }
} }
} }
@ -890,7 +890,7 @@ namespace ICSharpCode.AvalonEdit
throw new ArgumentOutOfRangeException("start", start, "Value must be between 0 and " + documentLength); throw new ArgumentOutOfRangeException("start", start, "Value must be between 0 and " + documentLength);
if (length < 0 || start + length > documentLength) if (length < 0 || start + length > documentLength)
throw new ArgumentOutOfRangeException("length", length, "Value must be between 0 and " + (documentLength - length)); throw new ArgumentOutOfRangeException("length", length, "Value must be between 0 and " + (documentLength - length));
textArea.Selection = new SimpleSelection(start, start + length); textArea.Selection = SimpleSelection.Create(textArea, start, start + length);
textArea.Caret.Offset = start + length; textArea.Caret.Offset = start + length;
} }

14
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextViewPosition.cs

@ -14,6 +14,19 @@ namespace ICSharpCode.AvalonEdit
{ {
int line, column, visualColumn; int line, column, visualColumn;
/// <summary>
/// Gets/Sets Location.
/// </summary>
public TextLocation Location {
get {
return new TextLocation(line, column);
}
set {
line = value.Line;
column = value.Column;
}
}
/// <summary> /// <summary>
/// Gets/Sets the line number. /// Gets/Sets the line number.
/// </summary> /// </summary>
@ -86,6 +99,7 @@ namespace ICSharpCode.AvalonEdit
/// <summary> /// <summary>
/// Implicit conversion to <see cref="TextLocation"/>. /// Implicit conversion to <see cref="TextLocation"/>.
/// </summary> /// </summary>
[Obsolete("Avoid")]
public static implicit operator TextLocation(TextViewPosition position) public static implicit operator TextLocation(TextViewPosition position)
{ {
return new TextLocation(position.Line, position.Column); return new TextLocation(position.Line, position.Column);

Loading…
Cancel
Save