/AvalonEdit/ICSharpCode.AvalonEdit/Editing/SelectionMouseHandler.cs
http://github.com/icsharpcode/ILSpy · C# · 676 lines · 557 code · 53 blank · 66 comment · 182 complexity · 74785dd42c340dd81dba214c3ff65cdf MD5 · raw file
- // 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
- {
- /// <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;
- 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
- }
- }