// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media.TextFormatting;
using System.Windows.Threading;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.AvalonEdit.Utils;
#if NREFACTORY
using ICSharpCode.NRefactory.Editor;
#endif
namespace ICSharpCode.AvalonEdit.Editing
{
///
/// Handles selection of text using the mouse.
///
sealed class SelectionMouseHandler : ITextAreaInputHandler
{
#region enum SelectionMode
enum SelectionMode
{
///
/// no selection (no mouse button down)
///
None,
///
/// left mouse button down on selection, might be normal click
/// or might be drag'n'drop
///
PossibleDragStart,
///
/// dragging text
///
Drag,
///
/// normal selection (click+drag)
///
Normal,
///
/// whole-word selection (double click+drag or ctrl+click+drag)
///
WholeWord,
///
/// whole-line selection (triple click+drag)
///
WholeLine,
///
/// rectangular selection (alt+click+drag)
///
Rectangular
}
#endregion
readonly TextArea textArea;
SelectionMode mode;
AnchorSegment startWord;
Point possibleDragStartMousePos;
#region Constructor + Attach + Detach
public SelectionMouseHandler(TextArea textArea)
{
if (textArea == null)
throw new ArgumentNullException("textArea");
this.textArea = textArea;
}
public TextArea TextArea {
get { return textArea; }
}
public void Attach()
{
textArea.MouseLeftButtonDown += textArea_MouseLeftButtonDown;
textArea.MouseMove += textArea_MouseMove;
textArea.MouseLeftButtonUp += textArea_MouseLeftButtonUp;
textArea.QueryCursor += textArea_QueryCursor;
textArea.OptionChanged += textArea_OptionChanged;
enableTextDragDrop = textArea.Options.EnableTextDragDrop;
if (enableTextDragDrop) {
AttachDragDrop();
}
}
public void Detach()
{
mode = SelectionMode.None;
textArea.MouseLeftButtonDown -= textArea_MouseLeftButtonDown;
textArea.MouseMove -= textArea_MouseMove;
textArea.MouseLeftButtonUp -= textArea_MouseLeftButtonUp;
textArea.QueryCursor -= textArea_QueryCursor;
textArea.OptionChanged -= textArea_OptionChanged;
if (enableTextDragDrop) {
DetachDragDrop();
}
}
void AttachDragDrop()
{
textArea.AllowDrop = true;
textArea.GiveFeedback += textArea_GiveFeedback;
textArea.QueryContinueDrag += textArea_QueryContinueDrag;
textArea.DragEnter += textArea_DragEnter;
textArea.DragOver += textArea_DragOver;
textArea.DragLeave += textArea_DragLeave;
textArea.Drop += textArea_Drop;
}
void DetachDragDrop()
{
textArea.AllowDrop = false;
textArea.GiveFeedback -= textArea_GiveFeedback;
textArea.QueryContinueDrag -= textArea_QueryContinueDrag;
textArea.DragEnter -= textArea_DragEnter;
textArea.DragOver -= textArea_DragOver;
textArea.DragLeave -= textArea_DragLeave;
textArea.Drop -= textArea_Drop;
}
bool enableTextDragDrop;
void textArea_OptionChanged(object sender, PropertyChangedEventArgs e)
{
bool newEnableTextDragDrop = textArea.Options.EnableTextDragDrop;
if (newEnableTextDragDrop != enableTextDragDrop) {
enableTextDragDrop = newEnableTextDragDrop;
if (newEnableTextDragDrop)
AttachDragDrop();
else
DetachDragDrop();
}
}
#endregion
#region Dropping text
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
void textArea_DragEnter(object sender, DragEventArgs e)
{
try {
e.Effects = GetEffect(e);
textArea.Caret.Show();
} catch (Exception ex) {
OnDragException(ex);
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
void textArea_DragOver(object sender, DragEventArgs e)
{
try {
e.Effects = GetEffect(e);
} catch (Exception ex) {
OnDragException(ex);
}
}
DragDropEffects GetEffect(DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.UnicodeText, true)) {
e.Handled = true;
int visualColumn;
bool isAtEndOfLine;
int offset = GetOffsetFromMousePosition(e.GetPosition(textArea.TextView), out visualColumn, out isAtEndOfLine);
if (offset >= 0) {
textArea.Caret.Position = new TextViewPosition(textArea.Document.GetLocation(offset), visualColumn) { IsAtEndOfLine = isAtEndOfLine };
textArea.Caret.DesiredXPos = double.NaN;
if (textArea.ReadOnlySectionProvider.CanInsert(offset)) {
if ((e.AllowedEffects & DragDropEffects.Move) == DragDropEffects.Move
&& (e.KeyStates & DragDropKeyStates.ControlKey) != DragDropKeyStates.ControlKey)
{
return DragDropEffects.Move;
} else {
return e.AllowedEffects & DragDropEffects.Copy;
}
}
}
}
return DragDropEffects.None;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
void textArea_DragLeave(object sender, DragEventArgs e)
{
try {
e.Handled = true;
if (!textArea.IsKeyboardFocusWithin)
textArea.Caret.Hide();
} catch (Exception ex) {
OnDragException(ex);
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
void textArea_Drop(object sender, DragEventArgs e)
{
try {
DragDropEffects effect = GetEffect(e);
e.Effects = effect;
if (effect != DragDropEffects.None) {
string text = e.Data.GetData(DataFormats.UnicodeText, true) as string;
if (text != null) {
int start = textArea.Caret.Offset;
if (mode == SelectionMode.Drag && textArea.Selection.Contains(start)) {
Debug.WriteLine("Drop: did not drop: drop target is inside selection");
e.Effects = DragDropEffects.None;
} else {
Debug.WriteLine("Drop: insert at " + start);
bool rectangular = e.Data.GetDataPresent(RectangleSelection.RectangularSelectionDataType);
string newLine = TextUtilities.GetNewLineFromDocument(textArea.Document, textArea.Caret.Line);
text = TextUtilities.NormalizeNewLines(text, newLine);
string pasteFormat;
// fill the suggested DataFormat used for the paste action:
if (rectangular)
pasteFormat = RectangleSelection.RectangularSelectionDataType;
else
pasteFormat = DataFormats.UnicodeText;
var pastingEventArgs = new DataObjectPastingEventArgs(e.Data, true, pasteFormat);
textArea.RaiseEvent(pastingEventArgs);
if (pastingEventArgs.CommandCancelled)
return;
// DataObject.PastingEvent handlers might have changed the format to apply.
rectangular = pastingEventArgs.FormatToApply == RectangleSelection.RectangularSelectionDataType;
// Mark the undo group with the currentDragDescriptor, if the drag
// is originating from the same control. This allows combining
// the undo groups when text is moved.
textArea.Document.UndoStack.StartUndoGroup(this.currentDragDescriptor);
try {
if (rectangular && RectangleSelection.PerformRectangularPaste(textArea, textArea.Caret.Position, text, true)) {
} else {
textArea.Document.Insert(start, text);
textArea.Selection = Selection.Create(textArea, start, start + text.Length);
}
} finally {
textArea.Document.UndoStack.EndUndoGroup();
}
}
e.Handled = true;
}
}
} catch (Exception ex) {
OnDragException(ex);
}
}
void OnDragException(Exception ex)
{
// WPF swallows exceptions during drag'n'drop or reports them incorrectly, so
// we re-throw them later to allow the application's unhandled exception handler
// to catch them
textArea.Dispatcher.BeginInvoke(
DispatcherPriority.Send,
new Action(delegate {
throw new DragDropException("Exception during drag'n'drop", ex);
}));
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
void textArea_GiveFeedback(object sender, GiveFeedbackEventArgs e)
{
try {
e.UseDefaultCursors = true;
e.Handled = true;
} catch (Exception ex) {
OnDragException(ex);
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
void textArea_QueryContinueDrag(object sender, QueryContinueDragEventArgs e)
{
try {
if (e.EscapePressed) {
e.Action = DragAction.Cancel;
} else if ((e.KeyStates & DragDropKeyStates.LeftMouseButton) != DragDropKeyStates.LeftMouseButton) {
e.Action = DragAction.Drop;
} else {
e.Action = DragAction.Continue;
}
e.Handled = true;
} catch (Exception ex) {
OnDragException(ex);
}
}
#endregion
#region Start Drag
object currentDragDescriptor;
void StartDrag()
{
// prevent nested StartDrag calls
mode = SelectionMode.Drag;
// mouse capture and Drag'n'Drop doesn't mix
textArea.ReleaseMouseCapture();
DataObject dataObject = textArea.Selection.CreateDataObject(textArea);
DragDropEffects allowedEffects = DragDropEffects.All;
var deleteOnMove = textArea.Selection.Segments.Select(s => new AnchorSegment(textArea.Document, s)).ToList();
foreach (ISegment s in deleteOnMove) {
ISegment[] result = textArea.GetDeletableSegments(s);
if (result.Length != 1 || result[0].Offset != s.Offset || result[0].EndOffset != s.EndOffset) {
allowedEffects &= ~DragDropEffects.Move;
}
}
var copyingEventArgs = new DataObjectCopyingEventArgs(dataObject, true);
textArea.RaiseEvent(copyingEventArgs);
if (copyingEventArgs.CommandCancelled)
return;
object dragDescriptor = new object();
this.currentDragDescriptor = dragDescriptor;
DragDropEffects resultEffect;
using (textArea.AllowCaretOutsideSelection()) {
var oldCaretPosition = textArea.Caret.Position;
try {
Debug.WriteLine("DoDragDrop with allowedEffects=" + allowedEffects);
resultEffect = DragDrop.DoDragDrop(textArea, dataObject, allowedEffects);
Debug.WriteLine("DoDragDrop done, resultEffect=" + resultEffect);
} catch (COMException ex) {
// ignore COM errors - don't crash on badly implemented drop targets
Debug.WriteLine("DoDragDrop failed: " + ex.ToString());
return;
}
if (resultEffect == DragDropEffects.None) {
// reset caret if drag was aborted
textArea.Caret.Position = oldCaretPosition;
}
}
this.currentDragDescriptor = null;
if (deleteOnMove != null && resultEffect == DragDropEffects.Move && (allowedEffects & DragDropEffects.Move) == DragDropEffects.Move) {
bool draggedInsideSingleDocument = (dragDescriptor == textArea.Document.UndoStack.LastGroupDescriptor);
if (draggedInsideSingleDocument)
textArea.Document.UndoStack.StartContinuedUndoGroup(null);
textArea.Document.BeginUpdate();
try {
foreach (ISegment s in deleteOnMove) {
textArea.Document.Remove(s.Offset, s.Length);
}
} finally {
textArea.Document.EndUpdate();
if (draggedInsideSingleDocument)
textArea.Document.UndoStack.EndUndoGroup();
}
}
}
#endregion
#region QueryCursor
// provide the IBeam Cursor for the text area
void textArea_QueryCursor(object sender, QueryCursorEventArgs e)
{
if (!e.Handled) {
if (mode != SelectionMode.None || !enableTextDragDrop) {
e.Cursor = Cursors.IBeam;
e.Handled = true;
} else if (textArea.TextView.VisualLinesValid) {
// Only query the cursor if the visual lines are valid.
// If they are invalid, the cursor will get re-queried when the visual lines
// get refreshed.
Point p = e.GetPosition(textArea.TextView);
if (p.X >= 0 && p.Y >= 0 && p.X <= textArea.TextView.ActualWidth && p.Y <= textArea.TextView.ActualHeight) {
int visualColumn;
bool isAtEndOfLine;
int offset = GetOffsetFromMousePosition(e, out visualColumn, out isAtEndOfLine);
if (textArea.Selection.Contains(offset))
e.Cursor = Cursors.Arrow;
else
e.Cursor = Cursors.IBeam;
e.Handled = true;
}
}
}
}
#endregion
#region LeftButtonDown
void textArea_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
mode = SelectionMode.None;
if (!e.Handled && e.ChangedButton == MouseButton.Left) {
ModifierKeys modifiers = Keyboard.Modifiers;
bool shift = (modifiers & ModifierKeys.Shift) == ModifierKeys.Shift;
if (enableTextDragDrop && e.ClickCount == 1 && !shift) {
int visualColumn;
bool isAtEndOfLine;
int offset = GetOffsetFromMousePosition(e, out visualColumn, out isAtEndOfLine);
if (textArea.Selection.Contains(offset)) {
if (textArea.CaptureMouse()) {
mode = SelectionMode.PossibleDragStart;
possibleDragStartMousePos = e.GetPosition(textArea);
}
e.Handled = true;
return;
}
}
var oldPosition = textArea.Caret.Position;
SetCaretOffsetToMousePosition(e);
if (!shift) {
textArea.ClearSelection();
}
if (textArea.CaptureMouse()) {
if ((modifiers & ModifierKeys.Alt) == ModifierKeys.Alt && textArea.Options.EnableRectangularSelection) {
mode = SelectionMode.Rectangular;
if (shift && textArea.Selection is RectangleSelection) {
textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldPosition, textArea.Caret.Position);
}
} else if (e.ClickCount == 1 && ((modifiers & ModifierKeys.Control) == 0)) {
mode = SelectionMode.Normal;
if (shift && !(textArea.Selection is RectangleSelection)) {
textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldPosition, textArea.Caret.Position);
}
} else {
SimpleSegment startWord;
if (e.ClickCount == 3) {
mode = SelectionMode.WholeLine;
startWord = GetLineAtMousePosition(e);
} else {
mode = SelectionMode.WholeWord;
startWord = GetWordAtMousePosition(e);
}
if (startWord == SimpleSegment.Invalid) {
mode = SelectionMode.None;
textArea.ReleaseMouseCapture();
return;
}
if (shift && !textArea.Selection.IsEmpty) {
if (startWord.Offset < textArea.Selection.SurroundingSegment.Offset) {
textArea.Selection = textArea.Selection.SetEndpoint(new TextViewPosition(textArea.Document.GetLocation(startWord.Offset)));
} else if (startWord.EndOffset > textArea.Selection.SurroundingSegment.EndOffset) {
textArea.Selection = textArea.Selection.SetEndpoint(new TextViewPosition(textArea.Document.GetLocation(startWord.EndOffset)));
}
this.startWord = new AnchorSegment(textArea.Document, textArea.Selection.SurroundingSegment);
} else {
textArea.Selection = Selection.Create(textArea, startWord.Offset, startWord.EndOffset);
this.startWord = new AnchorSegment(textArea.Document, startWord.Offset, startWord.Length);
}
}
}
}
e.Handled = true;
}
#endregion
#region Mouse Position <-> Text coordinates
SimpleSegment GetWordAtMousePosition(MouseEventArgs e)
{
TextView textView = textArea.TextView;
if (textView == null) return SimpleSegment.Invalid;
Point pos = e.GetPosition(textView);
if (pos.Y < 0)
pos.Y = 0;
if (pos.Y > textView.ActualHeight)
pos.Y = textView.ActualHeight;
pos += textView.ScrollOffset;
VisualLine line = textView.GetVisualLineFromVisualTop(pos.Y);
if (line != null) {
int visualColumn = line.GetVisualColumn(pos, textArea.Selection.EnableVirtualSpace);
int wordStartVC = line.GetNextCaretPosition(visualColumn + 1, LogicalDirection.Backward, CaretPositioningMode.WordStartOrSymbol, textArea.Selection.EnableVirtualSpace);
if (wordStartVC == -1)
wordStartVC = 0;
int wordEndVC = line.GetNextCaretPosition(wordStartVC, LogicalDirection.Forward, CaretPositioningMode.WordBorderOrSymbol, textArea.Selection.EnableVirtualSpace);
if (wordEndVC == -1)
wordEndVC = line.VisualLength;
int relOffset = line.FirstDocumentLine.Offset;
int wordStartOffset = line.GetRelativeOffset(wordStartVC) + relOffset;
int wordEndOffset = line.GetRelativeOffset(wordEndVC) + relOffset;
return new SimpleSegment(wordStartOffset, wordEndOffset - wordStartOffset);
} else {
return SimpleSegment.Invalid;
}
}
SimpleSegment GetLineAtMousePosition(MouseEventArgs e)
{
TextView textView = textArea.TextView;
if (textView == null) return SimpleSegment.Invalid;
Point pos = e.GetPosition(textView);
if (pos.Y < 0)
pos.Y = 0;
if (pos.Y > textView.ActualHeight)
pos.Y = textView.ActualHeight;
pos += textView.ScrollOffset;
VisualLine line = textView.GetVisualLineFromVisualTop(pos.Y);
if (line != null) {
return new SimpleSegment(line.StartOffset, line.LastDocumentLine.EndOffset - line.StartOffset);
} else {
return SimpleSegment.Invalid;
}
}
int GetOffsetFromMousePosition(MouseEventArgs e, out int visualColumn, out bool isAtEndOfLine)
{
return GetOffsetFromMousePosition(e.GetPosition(textArea.TextView), out visualColumn, out isAtEndOfLine);
}
int GetOffsetFromMousePosition(Point positionRelativeToTextView, out int visualColumn, out bool isAtEndOfLine)
{
visualColumn = 0;
TextView textView = textArea.TextView;
Point pos = positionRelativeToTextView;
if (pos.Y < 0)
pos.Y = 0;
if (pos.Y > textView.ActualHeight)
pos.Y = textView.ActualHeight;
pos += textView.ScrollOffset;
if (pos.Y > textView.DocumentHeight)
pos.Y = textView.DocumentHeight - ExtensionMethods.Epsilon;
VisualLine line = textView.GetVisualLineFromVisualTop(pos.Y);
if (line != null) {
visualColumn = line.GetVisualColumn(pos, textArea.Selection.EnableVirtualSpace, out isAtEndOfLine);
return line.GetRelativeOffset(visualColumn) + line.FirstDocumentLine.Offset;
}
isAtEndOfLine = false;
return -1;
}
int GetOffsetFromMousePositionFirstTextLineOnly(Point positionRelativeToTextView, out int visualColumn)
{
visualColumn = 0;
TextView textView = textArea.TextView;
Point pos = positionRelativeToTextView;
if (pos.Y < 0)
pos.Y = 0;
if (pos.Y > textView.ActualHeight)
pos.Y = textView.ActualHeight;
pos += textView.ScrollOffset;
if (pos.Y > textView.DocumentHeight)
pos.Y = textView.DocumentHeight - ExtensionMethods.Epsilon;
VisualLine line = textView.GetVisualLineFromVisualTop(pos.Y);
if (line != null) {
visualColumn = line.GetVisualColumn(line.TextLines.First(), pos.X, textArea.Selection.EnableVirtualSpace);
return line.GetRelativeOffset(visualColumn) + line.FirstDocumentLine.Offset;
}
return -1;
}
#endregion
#region MouseMove
void textArea_MouseMove(object sender, MouseEventArgs e)
{
if (e.Handled)
return;
if (mode == SelectionMode.Normal || mode == SelectionMode.WholeWord || mode == SelectionMode.WholeLine || mode == SelectionMode.Rectangular) {
e.Handled = true;
if (textArea.TextView.VisualLinesValid) {
// If the visual lines are not valid, don't extend the selection.
// Extending the selection forces a VisualLine refresh, and it is sufficient
// to do that on MouseUp, we don't have to do it every MouseMove.
ExtendSelectionToMouse(e);
}
} else if (mode == SelectionMode.PossibleDragStart) {
e.Handled = true;
Vector mouseMovement = e.GetPosition(textArea) - possibleDragStartMousePos;
if (Math.Abs(mouseMovement.X) > SystemParameters.MinimumHorizontalDragDistance
|| Math.Abs(mouseMovement.Y) > SystemParameters.MinimumVerticalDragDistance)
{
StartDrag();
}
}
}
#endregion
#region ExtendSelection
void SetCaretOffsetToMousePosition(MouseEventArgs e)
{
SetCaretOffsetToMousePosition(e, null);
}
void SetCaretOffsetToMousePosition(MouseEventArgs e, ISegment allowedSegment)
{
int visualColumn;
bool isAtEndOfLine;
int offset;
if (mode == SelectionMode.Rectangular) {
offset = GetOffsetFromMousePositionFirstTextLineOnly(e.GetPosition(textArea.TextView), out visualColumn);
isAtEndOfLine = true;
} else {
offset = GetOffsetFromMousePosition(e, out visualColumn, out isAtEndOfLine);
}
if (allowedSegment != null) {
offset = offset.CoerceValue(allowedSegment.Offset, allowedSegment.EndOffset);
}
if (offset >= 0) {
textArea.Caret.Position = new TextViewPosition(textArea.Document.GetLocation(offset), visualColumn) { IsAtEndOfLine = isAtEndOfLine };
textArea.Caret.DesiredXPos = double.NaN;
}
}
void ExtendSelectionToMouse(MouseEventArgs e)
{
TextViewPosition oldPosition = textArea.Caret.Position;
if (mode == SelectionMode.Normal || mode == SelectionMode.Rectangular) {
SetCaretOffsetToMousePosition(e);
if (mode == SelectionMode.Normal && textArea.Selection is RectangleSelection)
textArea.Selection = new SimpleSelection(textArea, oldPosition, textArea.Caret.Position);
else if (mode == SelectionMode.Rectangular && !(textArea.Selection is RectangleSelection))
textArea.Selection = new RectangleSelection(textArea, oldPosition, textArea.Caret.Position);
else
textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldPosition, textArea.Caret.Position);
} else if (mode == SelectionMode.WholeWord || mode == SelectionMode.WholeLine) {
var newWord = (mode == SelectionMode.WholeLine) ? GetLineAtMousePosition(e) : GetWordAtMousePosition(e);
if (newWord != SimpleSegment.Invalid) {
textArea.Selection = Selection.Create(textArea,
Math.Min(newWord.Offset, startWord.Offset),
Math.Max(newWord.EndOffset, startWord.EndOffset));
// 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
// selection - but the TextArea doesn't like that and will reset the selection, causing
// flickering.
SetCaretOffsetToMousePosition(e, textArea.Selection.SurroundingSegment);
}
}
textArea.Caret.BringCaretToView(5.0);
}
#endregion
#region MouseLeftButtonUp
void textArea_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (mode == SelectionMode.None || e.Handled)
return;
e.Handled = true;
if (mode == SelectionMode.PossibleDragStart) {
// -> this was not a drag start (mouse didn't move after mousedown)
SetCaretOffsetToMousePosition(e);
textArea.ClearSelection();
} else if (mode == SelectionMode.Normal || mode == SelectionMode.WholeWord || mode == SelectionMode.WholeLine || mode == SelectionMode.Rectangular) {
ExtendSelectionToMouse(e);
}
mode = SelectionMode.None;
textArea.ReleaseMouseCapture();
}
#endregion
}
}