aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Editing/SelectionMouseHandler.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Editing/SelectionMouseHandler.cs')
-rw-r--r--Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Editing/SelectionMouseHandler.cs631
1 files changed, 631 insertions, 0 deletions
diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Editing/SelectionMouseHandler.cs b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Editing/SelectionMouseHandler.cs
new file mode 100644
index 000000000..3c5ec51dc
--- /dev/null
+++ b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Editing/SelectionMouseHandler.cs
@@ -0,0 +1,631 @@
+// 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.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 Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Rendering;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Editing
+{
+ /// <summary>
+ /// Handles selection of text using the mouse.
+ /// </summary>
+ sealed class SelectionMouseHandler : ITextAreaInputHandler
+ {
+ #region enum SelectionMode
+ enum SelectionMode
+ {
+ /// <summary>
+ /// no selection (no mouse button down)
+ /// </summary>
+ None,
+ /// <summary>
+ /// left mouse button down on selection, might be normal click
+ /// or might be drag'n'drop
+ /// </summary>
+ PossibleDragStart,
+ /// <summary>
+ /// dragging text
+ /// </summary>
+ Drag,
+ /// <summary>
+ /// normal selection (click+drag)
+ /// </summary>
+ Normal,
+ /// <summary>
+ /// whole-word selection (double click+drag or ctrl+click+drag)
+ /// </summary>
+ WholeWord,
+ /// <summary>
+ /// whole-line selection (triple click+drag)
+ /// </summary>
+ WholeLine,
+ /// <summary>
+ /// rectangular selection (alt+click+drag)
+ /// </summary>
+ 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;
+ int offset = GetOffsetFromMousePosition(e.GetPosition(textArea.TextView), out visualColumn);
+ if (offset >= 0) {
+ textArea.Caret.Position = new TextViewPosition(textArea.Document.GetLocation(offset), visualColumn);
+ 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);
+
+ // 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.Normal,
+ 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;
+ }
+ }
+
+ 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;
+ int offset = GetOffsetFromMousePosition(e, out visualColumn);
+ 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;
+ int offset = GetOffsetFromMousePosition(e, out visualColumn);
+ 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)
+ {
+ return GetOffsetFromMousePosition(e.GetPosition(textArea.TextView), out visualColumn);
+ }
+
+ int GetOffsetFromMousePosition(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(pos, textArea.Selection.EnableVirtualSpace);
+ return line.GetRelativeOffset(visualColumn) + line.FirstDocumentLine.Offset;
+ }
+ 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;
+ int offset;
+ if (mode == SelectionMode.Rectangular)
+ offset = GetOffsetFromMousePositionFirstTextLineOnly(e.GetPosition(textArea.TextView), out visualColumn);
+ else
+ offset = GetOffsetFromMousePosition(e, out visualColumn);
+ if (allowedSegment != null) {
+ offset = offset.CoerceValue(allowedSegment.Offset, allowedSegment.EndOffset);
+ }
+ if (offset >= 0) {
+ textArea.Caret.Position = new TextViewPosition(textArea.Document.GetLocation(offset), visualColumn);
+ 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
+ }
+}