Free cookie consent management tool by TermsFeed Policy Generator

source: branches/PersistenceOverhaul/HeuristicLab.ExtLibs/HeuristicLab.AvalonEdit/5.0.1/AvalonEdit-5.0.1/Editing/SelectionMouseHandler.cs @ 18242

Last change on this file since 18242 was 11700, checked in by jkarder, 10 years ago

#2077: created branch and added first version

File size: 24.0 KB
Line 
1// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy of this
4// software and associated documentation files (the "Software"), to deal in the Software
5// without restriction, including without limitation the rights to use, copy, modify, merge,
6// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7// to whom the Software is furnished to do so, subject to the following conditions:
8//
9// The above copyright notice and this permission notice shall be included in all copies or
10// substantial portions of the Software.
11//
12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17// DEALINGS IN THE SOFTWARE.
18
19using System;
20using System.ComponentModel;
21using System.Diagnostics;
22using System.Linq;
23using System.Runtime.InteropServices;
24using System.Windows;
25using System.Windows.Documents;
26using System.Windows.Input;
27using System.Windows.Media.TextFormatting;
28using System.Windows.Threading;
29using ICSharpCode.AvalonEdit.Document;
30using ICSharpCode.AvalonEdit.Rendering;
31using ICSharpCode.AvalonEdit.Utils;
32#if NREFACTORY
33using ICSharpCode.NRefactory.Editor;
34#endif
35
36namespace ICSharpCode.AvalonEdit.Editing
37{
38  /// <summary>
39  /// Handles selection of text using the mouse.
40  /// </summary>
41  sealed class SelectionMouseHandler : ITextAreaInputHandler
42  {
43    #region enum SelectionMode
44    enum SelectionMode
45    {
46      /// <summary>
47      /// no selection (no mouse button down)
48      /// </summary>
49      None,
50      /// <summary>
51      /// left mouse button down on selection, might be normal click
52      /// or might be drag'n'drop
53      /// </summary>
54      PossibleDragStart,
55      /// <summary>
56      /// dragging text
57      /// </summary>
58      Drag,
59      /// <summary>
60      /// normal selection (click+drag)
61      /// </summary>
62      Normal,
63      /// <summary>
64      /// whole-word selection (double click+drag or ctrl+click+drag)
65      /// </summary>
66      WholeWord,
67      /// <summary>
68      /// whole-line selection (triple click+drag)
69      /// </summary>
70      WholeLine,
71      /// <summary>
72      /// rectangular selection (alt+click+drag)
73      /// </summary>
74      Rectangular
75    }
76    #endregion
77   
78    readonly TextArea textArea;
79   
80    SelectionMode mode;
81    AnchorSegment startWord;
82    Point possibleDragStartMousePos;
83   
84    #region Constructor + Attach + Detach
85    public SelectionMouseHandler(TextArea textArea)
86    {
87      if (textArea == null)
88        throw new ArgumentNullException("textArea");
89      this.textArea = textArea;
90    }
91   
92    public TextArea TextArea {
93      get { return textArea; }
94    }
95   
96    public void Attach()
97    {
98      textArea.MouseLeftButtonDown += textArea_MouseLeftButtonDown;
99      textArea.MouseMove += textArea_MouseMove;
100      textArea.MouseLeftButtonUp += textArea_MouseLeftButtonUp;
101      textArea.QueryCursor += textArea_QueryCursor;
102      textArea.OptionChanged += textArea_OptionChanged;
103     
104      enableTextDragDrop = textArea.Options.EnableTextDragDrop;
105      if (enableTextDragDrop) {
106        AttachDragDrop();
107      }
108    }
109   
110    public void Detach()
111    {
112      mode = SelectionMode.None;
113      textArea.MouseLeftButtonDown -= textArea_MouseLeftButtonDown;
114      textArea.MouseMove -= textArea_MouseMove;
115      textArea.MouseLeftButtonUp -= textArea_MouseLeftButtonUp;
116      textArea.QueryCursor -= textArea_QueryCursor;
117      textArea.OptionChanged -= textArea_OptionChanged;
118      if (enableTextDragDrop) {
119        DetachDragDrop();
120      }
121    }
122   
123    void AttachDragDrop()
124    {
125      textArea.AllowDrop = true;
126      textArea.GiveFeedback += textArea_GiveFeedback;
127      textArea.QueryContinueDrag += textArea_QueryContinueDrag;
128      textArea.DragEnter += textArea_DragEnter;
129      textArea.DragOver += textArea_DragOver;
130      textArea.DragLeave += textArea_DragLeave;
131      textArea.Drop += textArea_Drop;
132    }
133   
134    void DetachDragDrop()
135    {
136      textArea.AllowDrop = false;
137      textArea.GiveFeedback -= textArea_GiveFeedback;
138      textArea.QueryContinueDrag -= textArea_QueryContinueDrag;
139      textArea.DragEnter -= textArea_DragEnter;
140      textArea.DragOver -= textArea_DragOver;
141      textArea.DragLeave -= textArea_DragLeave;
142      textArea.Drop -= textArea_Drop;
143    }
144   
145    bool enableTextDragDrop;
146   
147    void textArea_OptionChanged(object sender, PropertyChangedEventArgs e)
148    {
149      bool newEnableTextDragDrop = textArea.Options.EnableTextDragDrop;
150      if (newEnableTextDragDrop != enableTextDragDrop) {
151        enableTextDragDrop = newEnableTextDragDrop;
152        if (newEnableTextDragDrop)
153          AttachDragDrop();
154        else
155          DetachDragDrop();
156      }
157    }
158    #endregion
159   
160    #region Dropping text
161    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
162    void textArea_DragEnter(object sender, DragEventArgs e)
163    {
164      try {
165        e.Effects = GetEffect(e);
166        textArea.Caret.Show();
167      } catch (Exception ex) {
168        OnDragException(ex);
169      }
170    }
171
172    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
173    void textArea_DragOver(object sender, DragEventArgs e)
174    {
175      try {
176        e.Effects = GetEffect(e);
177      } catch (Exception ex) {
178        OnDragException(ex);
179      }
180    }
181   
182    DragDropEffects GetEffect(DragEventArgs e)
183    {
184      if (e.Data.GetDataPresent(DataFormats.UnicodeText, true)) {
185        e.Handled = true;
186        int visualColumn;
187        bool isAtEndOfLine;
188        int offset = GetOffsetFromMousePosition(e.GetPosition(textArea.TextView), out visualColumn, out isAtEndOfLine);
189        if (offset >= 0) {
190          textArea.Caret.Position = new TextViewPosition(textArea.Document.GetLocation(offset), visualColumn) { IsAtEndOfLine = isAtEndOfLine };
191          textArea.Caret.DesiredXPos = double.NaN;
192          if (textArea.ReadOnlySectionProvider.CanInsert(offset)) {
193            if ((e.AllowedEffects & DragDropEffects.Move) == DragDropEffects.Move
194                && (e.KeyStates & DragDropKeyStates.ControlKey) != DragDropKeyStates.ControlKey)
195            {
196              return DragDropEffects.Move;
197            } else {
198              return e.AllowedEffects & DragDropEffects.Copy;
199            }
200          }
201        }
202      }
203      return DragDropEffects.None;
204    }
205   
206    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
207    void textArea_DragLeave(object sender, DragEventArgs e)
208    {
209      try {
210        e.Handled = true;
211        if (!textArea.IsKeyboardFocusWithin)
212          textArea.Caret.Hide();
213      } catch (Exception ex) {
214        OnDragException(ex);
215      }
216    }
217   
218    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
219    void textArea_Drop(object sender, DragEventArgs e)
220    {
221      try {
222        DragDropEffects effect = GetEffect(e);
223        e.Effects = effect;
224        if (effect != DragDropEffects.None) {
225          string text = e.Data.GetData(DataFormats.UnicodeText, true) as string;
226          if (text != null) {
227            int start = textArea.Caret.Offset;
228            if (mode == SelectionMode.Drag && textArea.Selection.Contains(start)) {
229              Debug.WriteLine("Drop: did not drop: drop target is inside selection");
230              e.Effects = DragDropEffects.None;
231            } else {
232              Debug.WriteLine("Drop: insert at " + start);
233             
234              bool rectangular = e.Data.GetDataPresent(RectangleSelection.RectangularSelectionDataType);
235             
236              string newLine = TextUtilities.GetNewLineFromDocument(textArea.Document, textArea.Caret.Line);
237              text = TextUtilities.NormalizeNewLines(text, newLine);
238             
239              string pasteFormat;
240              // fill the suggested DataFormat used for the paste action:
241              if (rectangular)
242                pasteFormat = RectangleSelection.RectangularSelectionDataType;
243              else
244                pasteFormat = DataFormats.UnicodeText;
245             
246              var pastingEventArgs = new DataObjectPastingEventArgs(e.Data, true, pasteFormat);
247              textArea.RaiseEvent(pastingEventArgs);
248              if (pastingEventArgs.CommandCancelled)
249                return;
250             
251              // DataObject.PastingEvent handlers might have changed the format to apply.
252              rectangular = pastingEventArgs.FormatToApply == RectangleSelection.RectangularSelectionDataType;
253             
254              // Mark the undo group with the currentDragDescriptor, if the drag
255              // is originating from the same control. This allows combining
256              // the undo groups when text is moved.
257              textArea.Document.UndoStack.StartUndoGroup(this.currentDragDescriptor);
258              try {
259                if (rectangular && RectangleSelection.PerformRectangularPaste(textArea, textArea.Caret.Position, text, true)) {
260                 
261                } else {
262                  textArea.Document.Insert(start, text);
263                  textArea.Selection = Selection.Create(textArea, start, start + text.Length);
264                }
265              } finally {
266                textArea.Document.UndoStack.EndUndoGroup();
267              }
268            }
269            e.Handled = true;
270          }
271        }
272      } catch (Exception ex) {
273        OnDragException(ex);
274      }
275    }
276   
277    void OnDragException(Exception ex)
278    {
279      // WPF swallows exceptions during drag'n'drop or reports them incorrectly, so
280      // we re-throw them later to allow the application's unhandled exception handler
281      // to catch them
282      textArea.Dispatcher.BeginInvoke(
283        DispatcherPriority.Send,
284        new Action(delegate {
285                    throw new DragDropException("Exception during drag'n'drop", ex);
286                   }));
287    }
288   
289    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
290    void textArea_GiveFeedback(object sender, GiveFeedbackEventArgs e)
291    {
292      try {
293        e.UseDefaultCursors = true;
294        e.Handled = true;
295      } catch (Exception ex) {
296        OnDragException(ex);
297      }
298    }
299   
300    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
301    void textArea_QueryContinueDrag(object sender, QueryContinueDragEventArgs e)
302    {
303      try {
304        if (e.EscapePressed) {
305          e.Action = DragAction.Cancel;
306        } else if ((e.KeyStates & DragDropKeyStates.LeftMouseButton) != DragDropKeyStates.LeftMouseButton) {
307          e.Action = DragAction.Drop;
308        } else {
309          e.Action = DragAction.Continue;
310        }
311        e.Handled = true;
312      } catch (Exception ex) {
313        OnDragException(ex);
314      }
315    }
316    #endregion
317   
318    #region Start Drag
319    object currentDragDescriptor;
320   
321    void StartDrag()
322    {
323      // prevent nested StartDrag calls
324      mode = SelectionMode.Drag;
325     
326      // mouse capture and Drag'n'Drop doesn't mix
327      textArea.ReleaseMouseCapture();
328     
329      DataObject dataObject = textArea.Selection.CreateDataObject(textArea);
330     
331      DragDropEffects allowedEffects = DragDropEffects.All;
332      var deleteOnMove = textArea.Selection.Segments.Select(s => new AnchorSegment(textArea.Document, s)).ToList();
333      foreach (ISegment s in deleteOnMove) {
334        ISegment[] result = textArea.GetDeletableSegments(s);
335        if (result.Length != 1 || result[0].Offset != s.Offset || result[0].EndOffset != s.EndOffset) {
336          allowedEffects &= ~DragDropEffects.Move;
337        }
338      }
339     
340      var copyingEventArgs = new DataObjectCopyingEventArgs(dataObject, true);
341      textArea.RaiseEvent(copyingEventArgs);
342      if (copyingEventArgs.CommandCancelled)
343        return;
344     
345      object dragDescriptor = new object();
346      this.currentDragDescriptor = dragDescriptor;
347     
348      DragDropEffects resultEffect;
349      using (textArea.AllowCaretOutsideSelection()) {
350        var oldCaretPosition = textArea.Caret.Position;
351        try {
352          Debug.WriteLine("DoDragDrop with allowedEffects=" + allowedEffects);
353          resultEffect = DragDrop.DoDragDrop(textArea, dataObject, allowedEffects);
354          Debug.WriteLine("DoDragDrop done, resultEffect=" + resultEffect);
355        } catch (COMException ex) {
356          // ignore COM errors - don't crash on badly implemented drop targets
357          Debug.WriteLine("DoDragDrop failed: " + ex.ToString());
358          return;
359        }
360        if (resultEffect == DragDropEffects.None) {
361          // reset caret if drag was aborted
362          textArea.Caret.Position = oldCaretPosition;
363        }
364      }
365     
366      this.currentDragDescriptor = null;
367     
368      if (deleteOnMove != null && resultEffect == DragDropEffects.Move && (allowedEffects & DragDropEffects.Move) == DragDropEffects.Move) {
369        bool draggedInsideSingleDocument = (dragDescriptor == textArea.Document.UndoStack.LastGroupDescriptor);
370        if (draggedInsideSingleDocument)
371          textArea.Document.UndoStack.StartContinuedUndoGroup(null);
372        textArea.Document.BeginUpdate();
373        try {
374          foreach (ISegment s in deleteOnMove) {
375            textArea.Document.Remove(s.Offset, s.Length);
376          }
377        } finally {
378          textArea.Document.EndUpdate();
379          if (draggedInsideSingleDocument)
380            textArea.Document.UndoStack.EndUndoGroup();
381        }
382      }
383    }
384    #endregion
385   
386    #region QueryCursor
387    // provide the IBeam Cursor for the text area
388    void textArea_QueryCursor(object sender, QueryCursorEventArgs e)
389    {
390      if (!e.Handled) {
391        if (mode != SelectionMode.None || !enableTextDragDrop) {
392          e.Cursor = Cursors.IBeam;
393          e.Handled = true;
394        } else if (textArea.TextView.VisualLinesValid) {
395          // Only query the cursor if the visual lines are valid.
396          // If they are invalid, the cursor will get re-queried when the visual lines
397          // get refreshed.
398          Point p = e.GetPosition(textArea.TextView);
399          if (p.X >= 0 && p.Y >= 0 && p.X <= textArea.TextView.ActualWidth && p.Y <= textArea.TextView.ActualHeight) {
400            int visualColumn;
401            bool isAtEndOfLine;
402            int offset = GetOffsetFromMousePosition(e, out visualColumn, out isAtEndOfLine);
403            if (textArea.Selection.Contains(offset))
404              e.Cursor = Cursors.Arrow;
405            else
406              e.Cursor = Cursors.IBeam;
407            e.Handled = true;
408          }
409        }
410      }
411    }
412    #endregion
413   
414    #region LeftButtonDown
415    void textArea_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
416    {
417      mode = SelectionMode.None;
418      if (!e.Handled && e.ChangedButton == MouseButton.Left) {
419        ModifierKeys modifiers = Keyboard.Modifiers;
420        bool shift = (modifiers & ModifierKeys.Shift) == ModifierKeys.Shift;
421        if (enableTextDragDrop && e.ClickCount == 1 && !shift) {
422          int visualColumn;
423          bool isAtEndOfLine;
424          int offset = GetOffsetFromMousePosition(e, out visualColumn, out isAtEndOfLine);
425          if (textArea.Selection.Contains(offset)) {
426            if (textArea.CaptureMouse()) {
427              mode = SelectionMode.PossibleDragStart;
428              possibleDragStartMousePos = e.GetPosition(textArea);
429            }
430            e.Handled = true;
431            return;
432          }
433        }
434       
435        var oldPosition = textArea.Caret.Position;
436        SetCaretOffsetToMousePosition(e);
437       
438       
439        if (!shift) {
440          textArea.ClearSelection();
441        }
442        if (textArea.CaptureMouse()) {
443          if ((modifiers & ModifierKeys.Alt) == ModifierKeys.Alt && textArea.Options.EnableRectangularSelection) {
444            mode = SelectionMode.Rectangular;
445            if (shift && textArea.Selection is RectangleSelection) {
446              textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldPosition, textArea.Caret.Position);
447            }
448          } else if (e.ClickCount == 1 && ((modifiers & ModifierKeys.Control) == 0)) {
449            mode = SelectionMode.Normal;
450            if (shift && !(textArea.Selection is RectangleSelection)) {
451              textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldPosition, textArea.Caret.Position);
452            }
453          } else {
454            SimpleSegment startWord;
455            if (e.ClickCount == 3) {
456              mode = SelectionMode.WholeLine;
457              startWord = GetLineAtMousePosition(e);
458            } else {
459              mode = SelectionMode.WholeWord;
460              startWord = GetWordAtMousePosition(e);
461            }
462            if (startWord == SimpleSegment.Invalid) {
463              mode = SelectionMode.None;
464              textArea.ReleaseMouseCapture();
465              return;
466            }
467            if (shift && !textArea.Selection.IsEmpty) {
468              if (startWord.Offset < textArea.Selection.SurroundingSegment.Offset) {
469                textArea.Selection = textArea.Selection.SetEndpoint(new TextViewPosition(textArea.Document.GetLocation(startWord.Offset)));
470              } else if (startWord.EndOffset > textArea.Selection.SurroundingSegment.EndOffset) {
471                textArea.Selection = textArea.Selection.SetEndpoint(new TextViewPosition(textArea.Document.GetLocation(startWord.EndOffset)));
472              }
473              this.startWord = new AnchorSegment(textArea.Document, textArea.Selection.SurroundingSegment);
474            } else {
475              textArea.Selection = Selection.Create(textArea, startWord.Offset, startWord.EndOffset);
476              this.startWord = new AnchorSegment(textArea.Document, startWord.Offset, startWord.Length);
477            }
478          }
479        }
480      }
481      e.Handled = true;
482    }
483    #endregion
484   
485    #region Mouse Position <-> Text coordinates
486    SimpleSegment GetWordAtMousePosition(MouseEventArgs e)
487    {
488      TextView textView = textArea.TextView;
489      if (textView == null) return SimpleSegment.Invalid;
490      Point pos = e.GetPosition(textView);
491      if (pos.Y < 0)
492        pos.Y = 0;
493      if (pos.Y > textView.ActualHeight)
494        pos.Y = textView.ActualHeight;
495      pos += textView.ScrollOffset;
496      VisualLine line = textView.GetVisualLineFromVisualTop(pos.Y);
497      if (line != null) {
498        int visualColumn = line.GetVisualColumn(pos, textArea.Selection.EnableVirtualSpace);
499        int wordStartVC = line.GetNextCaretPosition(visualColumn + 1, LogicalDirection.Backward, CaretPositioningMode.WordStartOrSymbol, textArea.Selection.EnableVirtualSpace);
500        if (wordStartVC == -1)
501          wordStartVC = 0;
502        int wordEndVC = line.GetNextCaretPosition(wordStartVC, LogicalDirection.Forward, CaretPositioningMode.WordBorderOrSymbol, textArea.Selection.EnableVirtualSpace);
503        if (wordEndVC == -1)
504          wordEndVC = line.VisualLength;
505        int relOffset = line.FirstDocumentLine.Offset;
506        int wordStartOffset = line.GetRelativeOffset(wordStartVC) + relOffset;
507        int wordEndOffset = line.GetRelativeOffset(wordEndVC) + relOffset;
508        return new SimpleSegment(wordStartOffset, wordEndOffset - wordStartOffset);
509      } else {
510        return SimpleSegment.Invalid;
511      }
512    }
513   
514    SimpleSegment GetLineAtMousePosition(MouseEventArgs e)
515    {
516      TextView textView = textArea.TextView;
517      if (textView == null) return SimpleSegment.Invalid;
518      Point pos = e.GetPosition(textView);
519      if (pos.Y < 0)
520        pos.Y = 0;
521      if (pos.Y > textView.ActualHeight)
522        pos.Y = textView.ActualHeight;
523      pos += textView.ScrollOffset;
524      VisualLine line = textView.GetVisualLineFromVisualTop(pos.Y);
525      if (line != null) {
526        return new SimpleSegment(line.StartOffset, line.LastDocumentLine.EndOffset - line.StartOffset);
527      } else {
528        return SimpleSegment.Invalid;
529      }
530    }
531   
532    int GetOffsetFromMousePosition(MouseEventArgs e, out int visualColumn, out bool isAtEndOfLine)
533    {
534      return GetOffsetFromMousePosition(e.GetPosition(textArea.TextView), out visualColumn, out isAtEndOfLine);
535    }
536   
537    int GetOffsetFromMousePosition(Point positionRelativeToTextView, out int visualColumn, out bool isAtEndOfLine)
538    {
539      visualColumn = 0;
540      TextView textView = textArea.TextView;
541      Point pos = positionRelativeToTextView;
542      if (pos.Y < 0)
543        pos.Y = 0;
544      if (pos.Y > textView.ActualHeight)
545        pos.Y = textView.ActualHeight;
546      pos += textView.ScrollOffset;
547      if (pos.Y > textView.DocumentHeight)
548        pos.Y = textView.DocumentHeight - ExtensionMethods.Epsilon;
549      VisualLine line = textView.GetVisualLineFromVisualTop(pos.Y);
550      if (line != null) {
551        visualColumn = line.GetVisualColumn(pos, textArea.Selection.EnableVirtualSpace, out isAtEndOfLine);
552        return line.GetRelativeOffset(visualColumn) + line.FirstDocumentLine.Offset;
553      }
554      isAtEndOfLine = false;
555      return -1;
556    }
557   
558    int GetOffsetFromMousePositionFirstTextLineOnly(Point positionRelativeToTextView, out int visualColumn)
559    {
560      visualColumn = 0;
561      TextView textView = textArea.TextView;
562      Point pos = positionRelativeToTextView;
563      if (pos.Y < 0)
564        pos.Y = 0;
565      if (pos.Y > textView.ActualHeight)
566        pos.Y = textView.ActualHeight;
567      pos += textView.ScrollOffset;
568      if (pos.Y > textView.DocumentHeight)
569        pos.Y = textView.DocumentHeight - ExtensionMethods.Epsilon;
570      VisualLine line = textView.GetVisualLineFromVisualTop(pos.Y);
571      if (line != null) {
572        visualColumn = line.GetVisualColumn(line.TextLines.First(), pos.X, textArea.Selection.EnableVirtualSpace);
573        return line.GetRelativeOffset(visualColumn) + line.FirstDocumentLine.Offset;
574      }
575      return -1;
576    }
577    #endregion
578   
579    #region MouseMove
580    void textArea_MouseMove(object sender, MouseEventArgs e)
581    {
582      if (e.Handled)
583        return;
584      if (mode == SelectionMode.Normal || mode == SelectionMode.WholeWord || mode == SelectionMode.WholeLine || mode == SelectionMode.Rectangular) {
585        e.Handled = true;
586        if (textArea.TextView.VisualLinesValid) {
587          // If the visual lines are not valid, don't extend the selection.
588          // Extending the selection forces a VisualLine refresh, and it is sufficient
589          // to do that on MouseUp, we don't have to do it every MouseMove.
590          ExtendSelectionToMouse(e);
591        }
592      } else if (mode == SelectionMode.PossibleDragStart) {
593        e.Handled = true;
594        Vector mouseMovement = e.GetPosition(textArea) - possibleDragStartMousePos;
595        if (Math.Abs(mouseMovement.X) > SystemParameters.MinimumHorizontalDragDistance
596            || Math.Abs(mouseMovement.Y) > SystemParameters.MinimumVerticalDragDistance)
597        {
598          StartDrag();
599        }
600      }
601    }
602    #endregion
603   
604    #region ExtendSelection
605    void SetCaretOffsetToMousePosition(MouseEventArgs e)
606    {
607      SetCaretOffsetToMousePosition(e, null);
608    }
609   
610    void SetCaretOffsetToMousePosition(MouseEventArgs e, ISegment allowedSegment)
611    {
612      int visualColumn;
613      bool isAtEndOfLine;
614      int offset;
615      if (mode == SelectionMode.Rectangular) {
616        offset = GetOffsetFromMousePositionFirstTextLineOnly(e.GetPosition(textArea.TextView), out visualColumn);
617        isAtEndOfLine = true;
618      } else {
619        offset = GetOffsetFromMousePosition(e, out visualColumn, out isAtEndOfLine);
620      }
621      if (allowedSegment != null) {
622        offset = offset.CoerceValue(allowedSegment.Offset, allowedSegment.EndOffset);
623      }
624      if (offset >= 0) {
625        textArea.Caret.Position = new TextViewPosition(textArea.Document.GetLocation(offset), visualColumn) { IsAtEndOfLine = isAtEndOfLine };
626        textArea.Caret.DesiredXPos = double.NaN;
627      }
628    }
629   
630    void ExtendSelectionToMouse(MouseEventArgs e)
631    {
632      TextViewPosition oldPosition = textArea.Caret.Position;
633      if (mode == SelectionMode.Normal || mode == SelectionMode.Rectangular) {
634        SetCaretOffsetToMousePosition(e);
635        if (mode == SelectionMode.Normal && textArea.Selection is RectangleSelection)
636          textArea.Selection = new SimpleSelection(textArea, oldPosition, textArea.Caret.Position);
637        else if (mode == SelectionMode.Rectangular && !(textArea.Selection is RectangleSelection))
638          textArea.Selection = new RectangleSelection(textArea, oldPosition, textArea.Caret.Position);
639        else
640          textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldPosition, textArea.Caret.Position);
641      } else if (mode == SelectionMode.WholeWord || mode == SelectionMode.WholeLine) {
642        var newWord = (mode == SelectionMode.WholeLine) ? GetLineAtMousePosition(e) : GetWordAtMousePosition(e);
643        if (newWord != SimpleSegment.Invalid) {
644          textArea.Selection = Selection.Create(textArea,
645                                                Math.Min(newWord.Offset, startWord.Offset),
646                                                Math.Max(newWord.EndOffset, startWord.EndOffset));
647          // Set caret offset, but limit the caret to stay inside the selection.
648          // in whole-word selection, it's otherwise possible that we get the caret outside the
649          // selection - but the TextArea doesn't like that and will reset the selection, causing
650          // flickering.
651          SetCaretOffsetToMousePosition(e, textArea.Selection.SurroundingSegment);
652        }
653      }
654      textArea.Caret.BringCaretToView(5.0);
655    }
656    #endregion
657   
658    #region MouseLeftButtonUp
659    void textArea_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
660    {
661      if (mode == SelectionMode.None || e.Handled)
662        return;
663      e.Handled = true;
664      if (mode == SelectionMode.PossibleDragStart) {
665        // -> this was not a drag start (mouse didn't move after mousedown)
666        SetCaretOffsetToMousePosition(e);
667        textArea.ClearSelection();
668      } else if (mode == SelectionMode.Normal || mode == SelectionMode.WholeWord || mode == SelectionMode.WholeLine || mode == SelectionMode.Rectangular) {
669        ExtendSelectionToMouse(e);
670      }
671      mode = SelectionMode.None;
672      textArea.ReleaseMouseCapture();
673    }
674    #endregion
675  }
676}
Note: See TracBrowser for help on using the repository browser.