diff --git a/ICSharpCode.AvalonEdit/Editing/Caret.cs b/ICSharpCode.AvalonEdit/Editing/Caret.cs
index 894031d0..1039f896 100644
--- a/ICSharpCode.AvalonEdit/Editing/Caret.cs
+++ b/ICSharpCode.AvalonEdit/Editing/Caret.cs
@@ -1,14 +1,14 @@
-// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
-//
+// 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
@@ -20,9 +20,6 @@
using System.Diagnostics;
using System.Windows;
using System.Windows.Documents;
-using System.Windows.Media;
-using System.Windows.Media.TextFormatting;
-using System.Windows.Threading;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Rendering;
@@ -33,11 +30,10 @@ namespace ICSharpCode.AvalonEdit.Editing
///
/// Helper class with caret-related methods.
///
- public sealed class Caret
+ public sealed partial class Caret
{
readonly TextArea textArea;
readonly TextView textView;
- readonly CaretLayer caretAdorner;
bool visible;
internal Caret(TextArea textArea)
@@ -46,12 +42,14 @@ internal Caret(TextArea textArea)
this.textView = textArea.TextView;
position = new TextViewPosition(1, 1, 0);
- caretAdorner = new CaretLayer(textArea);
- textView.InsertLayer(caretAdorner, KnownLayer.Caret, LayerInsertionPosition.Replace);
textView.VisualLinesChanged += TextView_VisualLinesChanged;
textView.ScrollOffsetChanged += TextView_ScrollOffsetChanged;
+
+ Initialize();
}
+ partial void Initialize();
+
internal void UpdateIfVisible()
{
if (visible) {
@@ -72,11 +70,11 @@ void TextView_VisualLinesChanged(object sender, EventArgs e)
void TextView_ScrollOffsetChanged(object sender, EventArgs e)
{
- if (caretAdorner != null) {
- caretAdorner.InvalidateVisual();
- }
+ InvalidateCaretVisual();
}
+ partial void InvalidateCaretVisual();
+
double desiredXPos = double.NaN;
TextViewPosition position;
@@ -371,56 +369,20 @@ void RevalidateVisualColumn(VisualLine visualLine)
isInVirtualSpace = (position.VisualColumn > visualLine.VisualLength);
}
- Rect CalcCaretRectangle(VisualLine visualLine)
- {
- if (!visualColumnValid) {
- RevalidateVisualColumn(visualLine);
- }
-
- TextLine textLine = visualLine.GetTextLine(position.VisualColumn, position.IsAtEndOfLine);
- double xPos = visualLine.GetTextLineVisualXPosition(textLine, position.VisualColumn);
- double lineTop = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.TextTop);
- double lineBottom = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.TextBottom);
-
- return new Rect(xPos,
- lineTop,
- SystemParameters.CaretWidth,
- lineBottom - lineTop);
- }
+ partial void GetCaretWidthCore(ref double width);
- Rect CalcCaretOverstrikeRectangle(VisualLine visualLine)
+ double GetCaretWidth()
{
- if (!visualColumnValid) {
- RevalidateVisualColumn(visualLine);
- }
-
- int currentPos = position.VisualColumn;
- // The text being overwritten in overstrike mode is everything up to the next normal caret stop
- int nextPos = visualLine.GetNextCaretPosition(currentPos, LogicalDirection.Forward, CaretPositioningMode.Normal, true);
- TextLine textLine = visualLine.GetTextLine(currentPos);
-
- Rect r;
- if (currentPos < visualLine.VisualLength) {
- // If the caret is within the text, use GetTextBounds() for the text being overwritten.
- // This is necessary to ensure the rectangle is calculated correctly in bidirectional text.
- var textBounds = textLine.GetTextBounds(currentPos, nextPos - currentPos)[0];
- r = textBounds.Rectangle;
- r.Y += visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.LineTop);
- } else {
- // If the caret is at the end of the line (or in virtual space),
- // use the visual X position of currentPos and nextPos (one or more of which will be in virtual space)
- double xPos = visualLine.GetTextLineVisualXPosition(textLine, currentPos);
- double xPos2 = visualLine.GetTextLineVisualXPosition(textLine, nextPos);
- double lineTop = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.TextTop);
- double lineBottom = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.TextBottom);
- r = new Rect(xPos, lineTop, xPos2 - xPos, lineBottom - lineTop);
- }
- // If the caret is too small (e.g. in front of zero-width character), ensure it's still visible
- if (r.Width < SystemParameters.CaretWidth)
- r.Width = SystemParameters.CaretWidth;
- return r;
+ double width = 1.0;
+ GetCaretWidthCore(ref width);
+ return width;
}
+ // These methods use System.Windows.Media.TextFormatting.TextLine (WPF TextFormatting API).
+ // Implementations live in Caret.wpf.cs (WPF) and Caret.uno.cs (Uno).
+ private partial Rect CalcCaretRectangle(VisualLine visualLine);
+ private partial Rect CalcCaretOverstrikeRectangle(VisualLine visualLine);
+
///
/// Returns the caret rectangle. The coordinate system is in device-independent pixels from the top of the document.
///
@@ -465,10 +427,18 @@ public void Show()
visible = true;
if (!showScheduled) {
showScheduled = true;
- textArea.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(ShowInternal));
+ // ShowCaretAsync (wpf) clears showScheduled and posts a dispatcher call to ShowInternal.
+ // If ShowCaretAsync has no implementation (Uno), showScheduled stays true and we fall
+ // through to call ShowInternal synchronously.
+ ShowCaretAsync();
+ if (showScheduled) {
+ ShowInternal();
+ }
}
}
+ partial void ShowCaretAsync();
+
bool showScheduled;
bool hasWin32Caret;
@@ -480,26 +450,20 @@ void ShowInternal()
if (!visible)
return;
- if (caretAdorner != null && textView != null) {
+ if (textView != null) {
VisualLine visualLine = textView.GetVisualLine(position.Line);
if (visualLine != null) {
Rect caretRect = this.textArea.OverstrikeMode ? CalcCaretOverstrikeRectangle(visualLine) : CalcCaretRectangle(visualLine);
- // Create Win32 caret so that Windows knows where our managed caret is. This is necessary for
- // features like 'Follow text editing' in the Windows Magnifier.
- if (!hasWin32Caret) {
- hasWin32Caret = Win32.CreateCaret(textView, caretRect.Size);
- }
- if (hasWin32Caret) {
- Win32.SetCaretPosition(textView, caretRect.Location - textView.ScrollOffset);
- }
- caretAdorner.Show(caretRect);
- textArea.ime.UpdateCompositionWindow();
+ ShowCaretInternal(caretRect);
} else {
- caretAdorner.Hide();
+ HideCaretInternal();
}
}
}
+ partial void ShowCaretInternal(Rect caretRect);
+ partial void HideCaretInternal();
+
///
/// Makes the caret invisible.
///
@@ -507,28 +471,17 @@ public void Hide()
{
Log("Caret.Hide()");
visible = false;
- if (hasWin32Caret) {
- Win32.DestroyCaret();
- hasWin32Caret = false;
- }
- if (caretAdorner != null) {
- caretAdorner.Hide();
- }
+ DestroyWin32Caret();
+ HideCaretInternal();
}
+ partial void DestroyWin32Caret();
+
[Conditional("DEBUG")]
static void Log(string text)
{
// commented out to make debug output less noisy - add back if there are any problems with the caret
//Debug.WriteLine(text);
}
-
- ///
- /// Gets/Sets the color of the caret.
- ///
- public Brush CaretBrush {
- get { return caretAdorner.CaretBrush; }
- set { caretAdorner.CaretBrush = value; }
- }
}
}
diff --git a/ICSharpCode.AvalonEdit/Editing/Caret.wpf.cs b/ICSharpCode.AvalonEdit/Editing/Caret.wpf.cs
new file mode 100644
index 00000000..015f2432
--- /dev/null
+++ b/ICSharpCode.AvalonEdit/Editing/Caret.wpf.cs
@@ -0,0 +1,137 @@
+// 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.Windows;
+using System.Windows.Documents;
+using System.Windows.Media;
+using System.Windows.Media.TextFormatting;
+using System.Windows.Threading;
+
+using ICSharpCode.AvalonEdit.Document;
+using ICSharpCode.AvalonEdit.Rendering;
+using ICSharpCode.AvalonEdit.Utils;
+
+namespace ICSharpCode.AvalonEdit.Editing
+{
+ public sealed partial class Caret
+ {
+ CaretLayer caretAdorner;
+
+ partial void Initialize()
+ {
+ caretAdorner = new CaretLayer(textArea);
+ textView.InsertLayer(caretAdorner, KnownLayer.Caret, LayerInsertionPosition.Replace);
+ }
+
+ partial void InvalidateCaretVisual()
+ {
+ if (caretAdorner != null) {
+ caretAdorner.InvalidateVisual();
+ }
+ }
+
+ partial void ShowCaretAsync()
+ {
+ // Clear showScheduled now so Show()'s fallback guard does not fire synchronously.
+ // The dispatcher callback will call ShowInternal() when it runs.
+ showScheduled = false;
+ textArea.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(ShowInternal));
+ }
+
+ partial void ShowCaretInternal(Rect caretRect)
+ {
+ if (caretAdorner != null) {
+ if (!hasWin32Caret) {
+ hasWin32Caret = Win32.CreateCaret(textView, caretRect.Size);
+ }
+ if (hasWin32Caret) {
+ Win32.SetCaretPosition(textView, caretRect.Location - textView.ScrollOffset);
+ }
+ caretAdorner.Show(caretRect);
+ textArea.ime.UpdateCompositionWindow();
+ }
+ }
+
+ partial void HideCaretInternal()
+ {
+ if (caretAdorner != null) {
+ caretAdorner.Hide();
+ }
+ }
+
+ partial void DestroyWin32Caret()
+ {
+ if (hasWin32Caret) {
+ Win32.DestroyCaret();
+ hasWin32Caret = false;
+ }
+ }
+
+ private partial Rect CalcCaretRectangle(VisualLine visualLine)
+ {
+ if (!visualColumnValid) {
+ RevalidateVisualColumn(visualLine);
+ }
+ TextLine textLine = visualLine.GetTextLine(position.VisualColumn, position.IsAtEndOfLine);
+ double xPos = visualLine.GetTextLineVisualXPosition(textLine, position.VisualColumn);
+ double lineTop = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.TextTop);
+ double lineBottom = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.TextBottom);
+ return new Rect(xPos, lineTop, GetCaretWidth(), lineBottom - lineTop);
+ }
+
+ partial void GetCaretWidthCore(ref double width)
+ {
+ width = SystemParameters.CaretWidth;
+ }
+
+ private partial Rect CalcCaretOverstrikeRectangle(VisualLine visualLine)
+ {
+ if (!visualColumnValid) {
+ RevalidateVisualColumn(visualLine);
+ }
+ int currentPos = position.VisualColumn;
+ int nextPos = visualLine.GetNextCaretPosition(currentPos, LogicalDirection.Forward, CaretPositioningMode.Normal, true);
+ TextLine textLine = visualLine.GetTextLine(currentPos);
+ Rect r;
+ if (currentPos < visualLine.VisualLength) {
+ var textBounds = textLine.GetTextBounds(currentPos, nextPos - currentPos)[0];
+ r = textBounds.Rectangle;
+ r.Y += visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.LineTop);
+ } else {
+ double xPos = visualLine.GetTextLineVisualXPosition(textLine, currentPos);
+ double xPos2 = visualLine.GetTextLineVisualXPosition(textLine, nextPos);
+ double lineTop = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.TextTop);
+ double lineBottom = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.TextBottom);
+ r = new Rect(xPos, lineTop, xPos2 - xPos, lineBottom - lineTop);
+ }
+ double caretWidth = GetCaretWidth();
+ if (r.Width < caretWidth)
+ r.Width = caretWidth;
+ return r;
+ }
+
+ ///
+ /// Gets/Sets the color of the caret.
+ ///
+ public Brush CaretBrush {
+ get { return caretAdorner.CaretBrush; }
+ set { caretAdorner.CaretBrush = value; }
+ }
+ }
+}
diff --git a/ICSharpCode.AvalonEdit/Editing/CaretNavigationCommandHandler.cs b/ICSharpCode.AvalonEdit/Editing/CaretNavigationCommandHandler.cs
index 90cc8fbe..cdd811d1 100644
--- a/ICSharpCode.AvalonEdit/Editing/CaretNavigationCommandHandler.cs
+++ b/ICSharpCode.AvalonEdit/Editing/CaretNavigationCommandHandler.cs
@@ -1,14 +1,14 @@
-// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
-//
+// 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
@@ -17,12 +17,9 @@
// DEALINGS IN THE SOFTWARE.
using System;
-using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using System.Windows.Documents;
-using System.Windows.Input;
-using System.Windows.Media.TextFormatting;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Rendering;
@@ -48,146 +45,10 @@ enum CaretMovementType
DocumentEnd
}
- static class CaretNavigationCommandHandler
+ static partial class CaretNavigationCommandHandler
{
- ///
- /// Creates a new for the text area.
- ///
- public static TextAreaInputHandler Create(TextArea textArea)
- {
- TextAreaInputHandler handler = new TextAreaInputHandler(textArea);
- handler.CommandBindings.AddRange(CommandBindings);
- handler.InputBindings.AddRange(InputBindings);
- return handler;
- }
-
- static readonly List CommandBindings = new List();
- static readonly List InputBindings = new List();
-
- static void AddBinding(ICommand command, ModifierKeys modifiers, Key key, ExecutedRoutedEventHandler handler)
- {
- CommandBindings.Add(new CommandBinding(command, handler));
- InputBindings.Add(TextAreaDefaultInputHandler.CreateFrozenKeyBinding(command, modifiers, key));
- }
-
- static CaretNavigationCommandHandler()
- {
- const ModifierKeys None = ModifierKeys.None;
- const ModifierKeys Ctrl = ModifierKeys.Control;
- const ModifierKeys Shift = ModifierKeys.Shift;
- const ModifierKeys Alt = ModifierKeys.Alt;
-
- AddBinding(EditingCommands.MoveLeftByCharacter, None, Key.Left, OnMoveCaret(CaretMovementType.CharLeft));
- AddBinding(EditingCommands.SelectLeftByCharacter, Shift, Key.Left, OnMoveCaretExtendSelection(CaretMovementType.CharLeft));
- AddBinding(RectangleSelection.BoxSelectLeftByCharacter, Alt | Shift, Key.Left, OnMoveCaretBoxSelection(CaretMovementType.CharLeft));
- AddBinding(EditingCommands.MoveRightByCharacter, None, Key.Right, OnMoveCaret(CaretMovementType.CharRight));
- AddBinding(EditingCommands.SelectRightByCharacter, Shift, Key.Right, OnMoveCaretExtendSelection(CaretMovementType.CharRight));
- AddBinding(RectangleSelection.BoxSelectRightByCharacter, Alt | Shift, Key.Right, OnMoveCaretBoxSelection(CaretMovementType.CharRight));
-
- AddBinding(EditingCommands.MoveLeftByWord, Ctrl, Key.Left, OnMoveCaret(CaretMovementType.WordLeft));
- AddBinding(EditingCommands.SelectLeftByWord, Ctrl | Shift, Key.Left, OnMoveCaretExtendSelection(CaretMovementType.WordLeft));
- AddBinding(RectangleSelection.BoxSelectLeftByWord, Ctrl | Alt | Shift, Key.Left, OnMoveCaretBoxSelection(CaretMovementType.WordLeft));
- AddBinding(EditingCommands.MoveRightByWord, Ctrl, Key.Right, OnMoveCaret(CaretMovementType.WordRight));
- AddBinding(EditingCommands.SelectRightByWord, Ctrl | Shift, Key.Right, OnMoveCaretExtendSelection(CaretMovementType.WordRight));
- AddBinding(RectangleSelection.BoxSelectRightByWord, Ctrl | Alt | Shift, Key.Right, OnMoveCaretBoxSelection(CaretMovementType.WordRight));
-
- AddBinding(EditingCommands.MoveUpByLine, None, Key.Up, OnMoveCaret(CaretMovementType.LineUp));
- AddBinding(EditingCommands.SelectUpByLine, Shift, Key.Up, OnMoveCaretExtendSelection(CaretMovementType.LineUp));
- AddBinding(RectangleSelection.BoxSelectUpByLine, Alt | Shift, Key.Up, OnMoveCaretBoxSelection(CaretMovementType.LineUp));
- AddBinding(EditingCommands.MoveDownByLine, None, Key.Down, OnMoveCaret(CaretMovementType.LineDown));
- AddBinding(EditingCommands.SelectDownByLine, Shift, Key.Down, OnMoveCaretExtendSelection(CaretMovementType.LineDown));
- AddBinding(RectangleSelection.BoxSelectDownByLine, Alt | Shift, Key.Down, OnMoveCaretBoxSelection(CaretMovementType.LineDown));
-
- AddBinding(EditingCommands.MoveDownByPage, None, Key.PageDown, OnMoveCaret(CaretMovementType.PageDown));
- AddBinding(EditingCommands.SelectDownByPage, Shift, Key.PageDown, OnMoveCaretExtendSelection(CaretMovementType.PageDown));
- AddBinding(EditingCommands.MoveUpByPage, None, Key.PageUp, OnMoveCaret(CaretMovementType.PageUp));
- AddBinding(EditingCommands.SelectUpByPage, Shift, Key.PageUp, OnMoveCaretExtendSelection(CaretMovementType.PageUp));
-
- AddBinding(EditingCommands.MoveToLineStart, None, Key.Home, OnMoveCaret(CaretMovementType.LineStart));
- AddBinding(EditingCommands.SelectToLineStart, Shift, Key.Home, OnMoveCaretExtendSelection(CaretMovementType.LineStart));
- AddBinding(RectangleSelection.BoxSelectToLineStart, Alt | Shift, Key.Home, OnMoveCaretBoxSelection(CaretMovementType.LineStart));
- AddBinding(EditingCommands.MoveToLineEnd, None, Key.End, OnMoveCaret(CaretMovementType.LineEnd));
- AddBinding(EditingCommands.SelectToLineEnd, Shift, Key.End, OnMoveCaretExtendSelection(CaretMovementType.LineEnd));
- AddBinding(RectangleSelection.BoxSelectToLineEnd, Alt | Shift, Key.End, OnMoveCaretBoxSelection(CaretMovementType.LineEnd));
-
- AddBinding(EditingCommands.MoveToDocumentStart, Ctrl, Key.Home, OnMoveCaret(CaretMovementType.DocumentStart));
- AddBinding(EditingCommands.SelectToDocumentStart, Ctrl | Shift, Key.Home, OnMoveCaretExtendSelection(CaretMovementType.DocumentStart));
- AddBinding(EditingCommands.MoveToDocumentEnd, Ctrl, Key.End, OnMoveCaret(CaretMovementType.DocumentEnd));
- AddBinding(EditingCommands.SelectToDocumentEnd, Ctrl | Shift, Key.End, OnMoveCaretExtendSelection(CaretMovementType.DocumentEnd));
-
- CommandBindings.Add(new CommandBinding(ApplicationCommands.SelectAll, OnSelectAll));
-
- TextAreaDefaultInputHandler.WorkaroundWPFMemoryLeak(InputBindings);
- }
-
- static void OnSelectAll(object target, ExecutedRoutedEventArgs args)
- {
- TextArea textArea = GetTextArea(target);
- if (textArea != null && textArea.Document != null) {
- args.Handled = true;
- textArea.Caret.Offset = textArea.Document.TextLength;
- textArea.Selection = SimpleSelection.Create(textArea, 0, textArea.Document.TextLength);
- }
- }
-
- static TextArea GetTextArea(object target)
- {
- return target as TextArea;
- }
-
- static ExecutedRoutedEventHandler OnMoveCaret(CaretMovementType direction)
- {
- return (target, args) => {
- TextArea textArea = GetTextArea(target);
- if (textArea != null && textArea.Document != null) {
- args.Handled = true;
- textArea.ClearSelection();
- MoveCaret(textArea, direction);
- textArea.Caret.BringCaretToView();
- }
- };
- }
-
- static ExecutedRoutedEventHandler OnMoveCaretExtendSelection(CaretMovementType direction)
- {
- return (target, args) => {
- TextArea textArea = GetTextArea(target);
- if (textArea != null && textArea.Document != null) {
- args.Handled = true;
- TextViewPosition oldPosition = textArea.Caret.Position;
- MoveCaret(textArea, direction);
- textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldPosition, textArea.Caret.Position);
- textArea.Caret.BringCaretToView();
- }
- };
- }
-
- static ExecutedRoutedEventHandler OnMoveCaretBoxSelection(CaretMovementType direction)
- {
- return (target, args) => {
- TextArea textArea = GetTextArea(target);
- if (textArea != null && textArea.Document != null) {
- args.Handled = true;
- // First, convert the selection into a rectangle selection
- // (this is required so that virtual space gets enabled for the caret movement)
- if (textArea.Options.EnableRectangularSelection && !(textArea.Selection is RectangleSelection)) {
- if (textArea.Selection.IsEmpty) {
- textArea.Selection = new RectangleSelection(textArea, textArea.Caret.Position, textArea.Caret.Position);
- } else {
- // Convert normal selection to rectangle selection
- textArea.Selection = new RectangleSelection(textArea, textArea.Selection.StartPosition, textArea.Caret.Position);
- }
- }
- // Now move the caret and extend the selection
- TextViewPosition oldPosition = textArea.Caret.Position;
- MoveCaret(textArea, direction);
- textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldPosition, textArea.Caret.Position);
- textArea.Caret.BringCaretToView();
- }
- };
- }
-
#region Caret movement
+ /// Moves the caret in the given direction, accounting for FlowDirection.
internal static void MoveCaret(TextArea textArea, CaretMovementType direction)
{
double desiredXPos = textArea.Caret.DesiredXPos;
@@ -207,82 +68,9 @@ internal static void MoveCaret(TextArea textArea, CaretMovementType direction)
textArea.Caret.Position = GetNewCaretPosition(textArea.TextView, textArea.Caret.Position, direction, textArea.Selection.EnableVirtualSpace, ref desiredXPos);
textArea.Caret.DesiredXPos = desiredXPos;
}
-
- internal static TextViewPosition GetNewCaretPosition(TextView textView, TextViewPosition caretPosition, CaretMovementType direction, bool enableVirtualSpace, ref double desiredXPos)
- {
- switch (direction) {
- case CaretMovementType.None:
- return caretPosition;
- case CaretMovementType.DocumentStart:
- desiredXPos = double.NaN;
- return new TextViewPosition(0, 0);
- case CaretMovementType.DocumentEnd:
- desiredXPos = double.NaN;
- return new TextViewPosition(textView.Document.GetLocation(textView.Document.TextLength));
- }
- DocumentLine caretLine = textView.Document.GetLineByNumber(caretPosition.Line);
- VisualLine visualLine = textView.GetOrConstructVisualLine(caretLine);
- TextLine textLine = visualLine.GetTextLine(caretPosition.VisualColumn, caretPosition.IsAtEndOfLine);
- switch (direction) {
- case CaretMovementType.CharLeft:
- desiredXPos = double.NaN;
- // do not move caret to previous line in virtual space
- if (caretPosition.VisualColumn == 0 && enableVirtualSpace)
- return caretPosition;
- return GetPrevCaretPosition(textView, caretPosition, visualLine, CaretPositioningMode.Normal, enableVirtualSpace);
- case CaretMovementType.Backspace:
- desiredXPos = double.NaN;
- return GetPrevCaretPosition(textView, caretPosition, visualLine, CaretPositioningMode.EveryCodepoint, enableVirtualSpace);
- case CaretMovementType.CharRight:
- desiredXPos = double.NaN;
- return GetNextCaretPosition(textView, caretPosition, visualLine, CaretPositioningMode.Normal, enableVirtualSpace);
- case CaretMovementType.WordLeft:
- desiredXPos = double.NaN;
- return GetPrevCaretPosition(textView, caretPosition, visualLine, CaretPositioningMode.WordStart, enableVirtualSpace);
- case CaretMovementType.WordRight:
- desiredXPos = double.NaN;
- return GetNextCaretPosition(textView, caretPosition, visualLine, CaretPositioningMode.WordStart, enableVirtualSpace);
- case CaretMovementType.LineUp:
- case CaretMovementType.LineDown:
- case CaretMovementType.PageUp:
- case CaretMovementType.PageDown:
- return GetUpDownCaretPosition(textView, caretPosition, direction, visualLine, textLine, enableVirtualSpace, ref desiredXPos);
- case CaretMovementType.LineStart:
- desiredXPos = double.NaN;
- return GetStartOfLineCaretPosition(caretPosition.VisualColumn, visualLine, textLine, enableVirtualSpace);
- case CaretMovementType.LineEnd:
- desiredXPos = double.NaN;
- return GetEndOfLineCaretPosition(visualLine, textLine);
- default:
- throw new NotSupportedException(direction.ToString());
- }
- }
#endregion
- #region Home/End
- static TextViewPosition GetStartOfLineCaretPosition(int oldVC, VisualLine visualLine, TextLine textLine, bool enableVirtualSpace)
- {
- int newVC = visualLine.GetTextLineVisualStartColumn(textLine);
- if (newVC == 0)
- newVC = visualLine.GetNextCaretPosition(newVC - 1, LogicalDirection.Forward, CaretPositioningMode.WordStart, enableVirtualSpace);
- if (newVC < 0)
- throw ThrowUtil.NoValidCaretPosition();
- // when the caret is already at the start of the text, jump to start before whitespace
- if (newVC == oldVC)
- newVC = 0;
- return visualLine.GetTextViewPosition(newVC);
- }
-
- static TextViewPosition GetEndOfLineCaretPosition(VisualLine visualLine, TextLine textLine)
- {
- int newVC = visualLine.GetTextLineVisualStartColumn(textLine) + textLine.Length - textLine.NewlineLength;
- TextViewPosition pos = visualLine.GetTextViewPosition(newVC);
- pos.IsAtEndOfLine = true;
- return pos;
- }
- #endregion
-
- #region By-character / By-word movement
+ #region By-character / By-word movement (platform-independent)
static TextViewPosition GetNextCaretPosition(TextView textView, TextViewPosition caretPosition, VisualLine visualLine, CaretPositioningMode mode, bool enableVirtualSpace)
{
int pos = visualLine.GetNextCaretPosition(caretPosition.VisualColumn, LogicalDirection.Forward, mode, enableVirtualSpace);
@@ -327,79 +115,5 @@ static TextViewPosition GetPrevCaretPosition(TextView textView, TextViewPosition
}
}
#endregion
-
- #region Line+Page up/down
- static TextViewPosition GetUpDownCaretPosition(TextView textView, TextViewPosition caretPosition, CaretMovementType direction, VisualLine visualLine, TextLine textLine, bool enableVirtualSpace, ref double xPos)
- {
- // moving up/down happens using the desired visual X position
- if (double.IsNaN(xPos))
- xPos = visualLine.GetTextLineVisualXPosition(textLine, caretPosition.VisualColumn);
- // now find the TextLine+VisualLine where the caret will end up in
- VisualLine targetVisualLine = visualLine;
- TextLine targetLine;
- int textLineIndex = visualLine.TextLines.IndexOf(textLine);
- switch (direction) {
- case CaretMovementType.LineUp: {
- // Move up: move to the previous TextLine in the same visual line
- // or move to the last TextLine of the previous visual line
- int prevLineNumber = visualLine.FirstDocumentLine.LineNumber - 1;
- if (textLineIndex > 0) {
- targetLine = visualLine.TextLines[textLineIndex - 1];
- } else if (prevLineNumber >= 1) {
- DocumentLine prevLine = textView.Document.GetLineByNumber(prevLineNumber);
- targetVisualLine = textView.GetOrConstructVisualLine(prevLine);
- targetLine = targetVisualLine.TextLines[targetVisualLine.TextLines.Count - 1];
- } else {
- targetLine = null;
- }
- break;
- }
- case CaretMovementType.LineDown: {
- // Move down: move to the next TextLine in the same visual line
- // or move to the first TextLine of the next visual line
- int nextLineNumber = visualLine.LastDocumentLine.LineNumber + 1;
- if (textLineIndex < visualLine.TextLines.Count - 1) {
- targetLine = visualLine.TextLines[textLineIndex + 1];
- } else if (nextLineNumber <= textView.Document.LineCount) {
- DocumentLine nextLine = textView.Document.GetLineByNumber(nextLineNumber);
- targetVisualLine = textView.GetOrConstructVisualLine(nextLine);
- targetLine = targetVisualLine.TextLines[0];
- } else {
- targetLine = null;
- }
- break;
- }
- case CaretMovementType.PageUp:
- case CaretMovementType.PageDown: {
- // Page up/down: find the target line using its visual position
- double yPos = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.LineMiddle);
- if (direction == CaretMovementType.PageUp)
- yPos -= textView.RenderSize.Height;
- else
- yPos += textView.RenderSize.Height;
- DocumentLine newLine = textView.GetDocumentLineByVisualTop(yPos);
- targetVisualLine = textView.GetOrConstructVisualLine(newLine);
- targetLine = targetVisualLine.GetTextLineByVisualYPosition(yPos);
- break;
- }
- default:
- throw new NotSupportedException(direction.ToString());
- }
- if (targetLine != null) {
- double yPos = targetVisualLine.GetTextLineVisualYPosition(targetLine, VisualYPosition.LineMiddle);
- int newVisualColumn = targetVisualLine.GetVisualColumn(new Point(xPos, yPos), enableVirtualSpace);
-
- // prevent wrapping to the next line; TODO: could 'IsAtEnd' help here?
- int targetLineStartCol = targetVisualLine.GetTextLineVisualStartColumn(targetLine);
- if (newVisualColumn >= targetLineStartCol + targetLine.Length) {
- if (newVisualColumn <= targetVisualLine.VisualLength)
- newVisualColumn = targetLineStartCol + targetLine.Length - 1;
- }
- return targetVisualLine.GetTextViewPosition(newVisualColumn);
- } else {
- return caretPosition;
- }
- }
- #endregion
}
}
diff --git a/ICSharpCode.AvalonEdit/Editing/CaretNavigationCommandHandler.wpf.cs b/ICSharpCode.AvalonEdit/Editing/CaretNavigationCommandHandler.wpf.cs
new file mode 100644
index 00000000..7ea9948f
--- /dev/null
+++ b/ICSharpCode.AvalonEdit/Editing/CaretNavigationCommandHandler.wpf.cs
@@ -0,0 +1,321 @@
+// 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.Collections.Generic;
+using System.Diagnostics;
+using System.Windows;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media.TextFormatting;
+
+using ICSharpCode.AvalonEdit.Document;
+using ICSharpCode.AvalonEdit.Rendering;
+using ICSharpCode.AvalonEdit.Utils;
+
+namespace ICSharpCode.AvalonEdit.Editing
+{
+ static partial class CaretNavigationCommandHandler
+ {
+ ///
+ /// Creates a new for the text area.
+ ///
+ public static TextAreaInputHandler Create(TextArea textArea)
+ {
+ TextAreaInputHandler handler = new TextAreaInputHandler(textArea);
+ handler.CommandBindings.AddRange(CommandBindings);
+ handler.InputBindings.AddRange(InputBindings);
+ return handler;
+ }
+
+ static readonly List CommandBindings = new List();
+ static readonly List InputBindings = new List();
+
+ static void AddBinding(ICommand command, ModifierKeys modifiers, Key key, ExecutedRoutedEventHandler handler)
+ {
+ CommandBindings.Add(new CommandBinding(command, handler));
+ InputBindings.Add(TextAreaDefaultInputHandler.CreateFrozenKeyBinding(command, modifiers, key));
+ }
+
+ static CaretNavigationCommandHandler()
+ {
+ const ModifierKeys None = ModifierKeys.None;
+ const ModifierKeys Ctrl = ModifierKeys.Control;
+ const ModifierKeys Shift = ModifierKeys.Shift;
+ const ModifierKeys Alt = ModifierKeys.Alt;
+
+ AddBinding(EditingCommands.MoveLeftByCharacter, None, Key.Left, OnMoveCaret(CaretMovementType.CharLeft));
+ AddBinding(EditingCommands.SelectLeftByCharacter, Shift, Key.Left, OnMoveCaretExtendSelection(CaretMovementType.CharLeft));
+ AddBinding(RectangleSelection.BoxSelectLeftByCharacter, Alt | Shift, Key.Left, OnMoveCaretBoxSelection(CaretMovementType.CharLeft));
+ AddBinding(EditingCommands.MoveRightByCharacter, None, Key.Right, OnMoveCaret(CaretMovementType.CharRight));
+ AddBinding(EditingCommands.SelectRightByCharacter, Shift, Key.Right, OnMoveCaretExtendSelection(CaretMovementType.CharRight));
+ AddBinding(RectangleSelection.BoxSelectRightByCharacter, Alt | Shift, Key.Right, OnMoveCaretBoxSelection(CaretMovementType.CharRight));
+
+ AddBinding(EditingCommands.MoveLeftByWord, Ctrl, Key.Left, OnMoveCaret(CaretMovementType.WordLeft));
+ AddBinding(EditingCommands.SelectLeftByWord, Ctrl | Shift, Key.Left, OnMoveCaretExtendSelection(CaretMovementType.WordLeft));
+ AddBinding(RectangleSelection.BoxSelectLeftByWord, Ctrl | Alt | Shift, Key.Left, OnMoveCaretBoxSelection(CaretMovementType.WordLeft));
+ AddBinding(EditingCommands.MoveRightByWord, Ctrl, Key.Right, OnMoveCaret(CaretMovementType.WordRight));
+ AddBinding(EditingCommands.SelectRightByWord, Ctrl | Shift, Key.Right, OnMoveCaretExtendSelection(CaretMovementType.WordRight));
+ AddBinding(RectangleSelection.BoxSelectRightByWord, Ctrl | Alt | Shift, Key.Right, OnMoveCaretBoxSelection(CaretMovementType.WordRight));
+
+ AddBinding(EditingCommands.MoveUpByLine, None, Key.Up, OnMoveCaret(CaretMovementType.LineUp));
+ AddBinding(EditingCommands.SelectUpByLine, Shift, Key.Up, OnMoveCaretExtendSelection(CaretMovementType.LineUp));
+ AddBinding(RectangleSelection.BoxSelectUpByLine, Alt | Shift, Key.Up, OnMoveCaretBoxSelection(CaretMovementType.LineUp));
+ AddBinding(EditingCommands.MoveDownByLine, None, Key.Down, OnMoveCaret(CaretMovementType.LineDown));
+ AddBinding(EditingCommands.SelectDownByLine, Shift, Key.Down, OnMoveCaretExtendSelection(CaretMovementType.LineDown));
+ AddBinding(RectangleSelection.BoxSelectDownByLine, Alt | Shift, Key.Down, OnMoveCaretBoxSelection(CaretMovementType.LineDown));
+
+ AddBinding(EditingCommands.MoveDownByPage, None, Key.PageDown, OnMoveCaret(CaretMovementType.PageDown));
+ AddBinding(EditingCommands.SelectDownByPage, Shift, Key.PageDown, OnMoveCaretExtendSelection(CaretMovementType.PageDown));
+ AddBinding(EditingCommands.MoveUpByPage, None, Key.PageUp, OnMoveCaret(CaretMovementType.PageUp));
+ AddBinding(EditingCommands.SelectUpByPage, Shift, Key.PageUp, OnMoveCaretExtendSelection(CaretMovementType.PageUp));
+
+ AddBinding(EditingCommands.MoveToLineStart, None, Key.Home, OnMoveCaret(CaretMovementType.LineStart));
+ AddBinding(EditingCommands.SelectToLineStart, Shift, Key.Home, OnMoveCaretExtendSelection(CaretMovementType.LineStart));
+ AddBinding(RectangleSelection.BoxSelectToLineStart, Alt | Shift, Key.Home, OnMoveCaretBoxSelection(CaretMovementType.LineStart));
+ AddBinding(EditingCommands.MoveToLineEnd, None, Key.End, OnMoveCaret(CaretMovementType.LineEnd));
+ AddBinding(EditingCommands.SelectToLineEnd, Shift, Key.End, OnMoveCaretExtendSelection(CaretMovementType.LineEnd));
+ AddBinding(RectangleSelection.BoxSelectToLineEnd, Alt | Shift, Key.End, OnMoveCaretBoxSelection(CaretMovementType.LineEnd));
+
+ AddBinding(EditingCommands.MoveToDocumentStart, Ctrl, Key.Home, OnMoveCaret(CaretMovementType.DocumentStart));
+ AddBinding(EditingCommands.SelectToDocumentStart, Ctrl | Shift, Key.Home, OnMoveCaretExtendSelection(CaretMovementType.DocumentStart));
+ AddBinding(EditingCommands.MoveToDocumentEnd, Ctrl, Key.End, OnMoveCaret(CaretMovementType.DocumentEnd));
+ AddBinding(EditingCommands.SelectToDocumentEnd, Ctrl | Shift, Key.End, OnMoveCaretExtendSelection(CaretMovementType.DocumentEnd));
+
+ CommandBindings.Add(new CommandBinding(ApplicationCommands.SelectAll, OnSelectAll));
+
+ TextAreaDefaultInputHandler.WorkaroundWPFMemoryLeak(InputBindings);
+ }
+
+ static void OnSelectAll(object target, ExecutedRoutedEventArgs args)
+ {
+ TextArea textArea = GetTextArea(target);
+ if (textArea != null && textArea.Document != null) {
+ args.Handled = true;
+ textArea.Caret.Offset = textArea.Document.TextLength;
+ textArea.Selection = SimpleSelection.Create(textArea, 0, textArea.Document.TextLength);
+ }
+ }
+
+ static TextArea GetTextArea(object target)
+ {
+ return target as TextArea;
+ }
+
+ static ExecutedRoutedEventHandler OnMoveCaret(CaretMovementType direction)
+ {
+ return (target, args) => {
+ TextArea textArea = GetTextArea(target);
+ if (textArea != null && textArea.Document != null) {
+ args.Handled = true;
+ textArea.ClearSelection();
+ MoveCaret(textArea, direction);
+ textArea.Caret.BringCaretToView();
+ }
+ };
+ }
+
+ static ExecutedRoutedEventHandler OnMoveCaretExtendSelection(CaretMovementType direction)
+ {
+ return (target, args) => {
+ TextArea textArea = GetTextArea(target);
+ if (textArea != null && textArea.Document != null) {
+ args.Handled = true;
+ TextViewPosition oldPosition = textArea.Caret.Position;
+ MoveCaret(textArea, direction);
+ textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldPosition, textArea.Caret.Position);
+ textArea.Caret.BringCaretToView();
+ }
+ };
+ }
+
+ static ExecutedRoutedEventHandler OnMoveCaretBoxSelection(CaretMovementType direction)
+ {
+ return (target, args) => {
+ TextArea textArea = GetTextArea(target);
+ if (textArea != null && textArea.Document != null) {
+ args.Handled = true;
+ // First, convert the selection into a rectangle selection
+ // (this is required so that virtual space gets enabled for the caret movement)
+ if (textArea.Options.EnableRectangularSelection && !(textArea.Selection is RectangleSelection)) {
+ if (textArea.Selection.IsEmpty) {
+ textArea.Selection = new RectangleSelection(textArea, textArea.Caret.Position, textArea.Caret.Position);
+ } else {
+ // Convert normal selection to rectangle selection
+ textArea.Selection = new RectangleSelection(textArea, textArea.Selection.StartPosition, textArea.Caret.Position);
+ }
+ }
+ // Now move the caret and extend the selection
+ TextViewPosition oldPosition = textArea.Caret.Position;
+ MoveCaret(textArea, direction);
+ textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldPosition, textArea.Caret.Position);
+ textArea.Caret.BringCaretToView();
+ }
+ };
+ }
+
+ // GetNewCaretPosition — uses System.Windows.Media.TextFormatting.TextLine.
+ // Implementations live here (WPF). For Uno, this will be in CaretNavigationCommandHandler.uno.cs.
+ internal static TextViewPosition GetNewCaretPosition(TextView textView, TextViewPosition caretPosition, CaretMovementType direction, bool enableVirtualSpace, ref double desiredXPos)
+ {
+ switch (direction) {
+ case CaretMovementType.None:
+ return caretPosition;
+ case CaretMovementType.DocumentStart:
+ desiredXPos = double.NaN;
+ return new TextViewPosition(0, 0);
+ case CaretMovementType.DocumentEnd:
+ desiredXPos = double.NaN;
+ return new TextViewPosition(textView.Document.GetLocation(textView.Document.TextLength));
+ }
+ DocumentLine caretLine = textView.Document.GetLineByNumber(caretPosition.Line);
+ VisualLine visualLine = textView.GetOrConstructVisualLine(caretLine);
+ TextLine textLine = visualLine.GetTextLine(caretPosition.VisualColumn, caretPosition.IsAtEndOfLine);
+ switch (direction) {
+ case CaretMovementType.CharLeft:
+ desiredXPos = double.NaN;
+ // do not move caret to previous line in virtual space
+ if (caretPosition.VisualColumn == 0 && enableVirtualSpace)
+ return caretPosition;
+ return GetPrevCaretPosition(textView, caretPosition, visualLine, CaretPositioningMode.Normal, enableVirtualSpace);
+ case CaretMovementType.Backspace:
+ desiredXPos = double.NaN;
+ return GetPrevCaretPosition(textView, caretPosition, visualLine, CaretPositioningMode.EveryCodepoint, enableVirtualSpace);
+ case CaretMovementType.CharRight:
+ desiredXPos = double.NaN;
+ return GetNextCaretPosition(textView, caretPosition, visualLine, CaretPositioningMode.Normal, enableVirtualSpace);
+ case CaretMovementType.WordLeft:
+ desiredXPos = double.NaN;
+ return GetPrevCaretPosition(textView, caretPosition, visualLine, CaretPositioningMode.WordStart, enableVirtualSpace);
+ case CaretMovementType.WordRight:
+ desiredXPos = double.NaN;
+ return GetNextCaretPosition(textView, caretPosition, visualLine, CaretPositioningMode.WordStart, enableVirtualSpace);
+ case CaretMovementType.LineUp:
+ case CaretMovementType.LineDown:
+ case CaretMovementType.PageUp:
+ case CaretMovementType.PageDown:
+ return GetUpDownCaretPosition(textView, caretPosition, direction, visualLine, textLine, enableVirtualSpace, ref desiredXPos);
+ case CaretMovementType.LineStart:
+ desiredXPos = double.NaN;
+ return GetStartOfLineCaretPosition(caretPosition.VisualColumn, visualLine, textLine, enableVirtualSpace);
+ case CaretMovementType.LineEnd:
+ desiredXPos = double.NaN;
+ return GetEndOfLineCaretPosition(visualLine, textLine);
+ default:
+ throw new NotSupportedException(direction.ToString());
+ }
+ }
+
+ #region Home/End
+ static TextViewPosition GetStartOfLineCaretPosition(int oldVC, VisualLine visualLine, TextLine textLine, bool enableVirtualSpace)
+ {
+ int newVC = visualLine.GetTextLineVisualStartColumn(textLine);
+ if (newVC == 0)
+ newVC = visualLine.GetNextCaretPosition(newVC - 1, LogicalDirection.Forward, CaretPositioningMode.WordStart, enableVirtualSpace);
+ if (newVC < 0)
+ throw ThrowUtil.NoValidCaretPosition();
+ // when the caret is already at the start of the text, jump to start before whitespace
+ if (newVC == oldVC)
+ newVC = 0;
+ return visualLine.GetTextViewPosition(newVC);
+ }
+
+ static TextViewPosition GetEndOfLineCaretPosition(VisualLine visualLine, TextLine textLine)
+ {
+ int newVC = visualLine.GetTextLineVisualStartColumn(textLine) + textLine.Length - textLine.NewlineLength;
+ TextViewPosition pos = visualLine.GetTextViewPosition(newVC);
+ pos.IsAtEndOfLine = true;
+ return pos;
+ }
+ #endregion
+
+ #region Line+Page up/down
+ static TextViewPosition GetUpDownCaretPosition(TextView textView, TextViewPosition caretPosition, CaretMovementType direction, VisualLine visualLine, TextLine textLine, bool enableVirtualSpace, ref double xPos)
+ {
+ // moving up/down happens using the desired visual X position
+ if (double.IsNaN(xPos))
+ xPos = visualLine.GetTextLineVisualXPosition(textLine, caretPosition.VisualColumn);
+ // now find the TextLine+VisualLine where the caret will end up in
+ VisualLine targetVisualLine = visualLine;
+ TextLine targetLine;
+ int textLineIndex = visualLine.TextLines.IndexOf(textLine);
+ switch (direction) {
+ case CaretMovementType.LineUp: {
+ // Move up: move to the previous TextLine in the same visual line
+ // or move to the last TextLine of the previous visual line
+ int prevLineNumber = visualLine.FirstDocumentLine.LineNumber - 1;
+ if (textLineIndex > 0) {
+ targetLine = visualLine.TextLines[textLineIndex - 1];
+ } else if (prevLineNumber >= 1) {
+ DocumentLine prevLine = textView.Document.GetLineByNumber(prevLineNumber);
+ targetVisualLine = textView.GetOrConstructVisualLine(prevLine);
+ targetLine = targetVisualLine.TextLines[targetVisualLine.TextLines.Count - 1];
+ } else {
+ targetLine = null;
+ }
+ break;
+ }
+ case CaretMovementType.LineDown: {
+ // Move down: move to the next TextLine in the same visual line
+ // or move to the first TextLine of the next visual line
+ int nextLineNumber = visualLine.LastDocumentLine.LineNumber + 1;
+ if (textLineIndex < visualLine.TextLines.Count - 1) {
+ targetLine = visualLine.TextLines[textLineIndex + 1];
+ } else if (nextLineNumber <= textView.Document.LineCount) {
+ DocumentLine nextLine = textView.Document.GetLineByNumber(nextLineNumber);
+ targetVisualLine = textView.GetOrConstructVisualLine(nextLine);
+ targetLine = targetVisualLine.TextLines[0];
+ } else {
+ targetLine = null;
+ }
+ break;
+ }
+ case CaretMovementType.PageUp:
+ case CaretMovementType.PageDown: {
+ // Page up/down: find the target line using its visual position
+ double yPos = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.LineMiddle);
+ if (direction == CaretMovementType.PageUp)
+ yPos -= textView.RenderSize.Height;
+ else
+ yPos += textView.RenderSize.Height;
+ DocumentLine newLine = textView.GetDocumentLineByVisualTop(yPos);
+ targetVisualLine = textView.GetOrConstructVisualLine(newLine);
+ targetLine = targetVisualLine.GetTextLineByVisualYPosition(yPos);
+ break;
+ }
+ default:
+ throw new NotSupportedException(direction.ToString());
+ }
+ if (targetLine != null) {
+ double yPos = targetVisualLine.GetTextLineVisualYPosition(targetLine, VisualYPosition.LineMiddle);
+ int newVisualColumn = targetVisualLine.GetVisualColumn(new Point(xPos, yPos), enableVirtualSpace);
+
+ // prevent wrapping to the next line; TODO: could 'IsAtEnd' help here?
+ int targetLineStartCol = targetVisualLine.GetTextLineVisualStartColumn(targetLine);
+ if (newVisualColumn >= targetLineStartCol + targetLine.Length) {
+ if (newVisualColumn <= targetVisualLine.VisualLength)
+ newVisualColumn = targetLineStartCol + targetLine.Length - 1;
+ }
+ return targetVisualLine.GetTextViewPosition(newVisualColumn);
+ } else {
+ return caretPosition;
+ }
+ }
+ #endregion
+ }
+}
diff --git a/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs b/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs
index fcdda52c..8e0a63d9 100644
--- a/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs
+++ b/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs
@@ -1,14 +1,14 @@
-// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
-//
+// 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
@@ -17,18 +17,9 @@
// DEALINGS IN THE SOFTWARE.
using System;
-using System.Collections.Generic;
using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Runtime.InteropServices;
-using System.Windows;
-using System.Windows.Documents;
-using System.Windows.Input;
using ICSharpCode.AvalonEdit.Document;
-using ICSharpCode.AvalonEdit.Highlighting;
-using ICSharpCode.AvalonEdit.Utils;
namespace ICSharpCode.AvalonEdit.Editing
{
@@ -36,68 +27,8 @@ namespace ICSharpCode.AvalonEdit.Editing
/// We re-use the CommandBinding and InputBinding instances between multiple text areas,
/// so this class is static.
///
- static class EditingCommandHandler
+ static partial class EditingCommandHandler
{
- ///
- /// Creates a new for the text area.
- ///
- public static TextAreaInputHandler Create(TextArea textArea)
- {
- TextAreaInputHandler handler = new TextAreaInputHandler(textArea);
- handler.CommandBindings.AddRange(CommandBindings);
- handler.InputBindings.AddRange(InputBindings);
- return handler;
- }
-
- static readonly List CommandBindings = new List();
- static readonly List InputBindings = new List();
-
- static void AddBinding(ICommand command, ModifierKeys modifiers, Key key, ExecutedRoutedEventHandler handler)
- {
- CommandBindings.Add(new CommandBinding(command, handler));
- InputBindings.Add(TextAreaDefaultInputHandler.CreateFrozenKeyBinding(command, modifiers, key));
- }
-
- static EditingCommandHandler()
- {
- CommandBindings.Add(new CommandBinding(ApplicationCommands.Delete, OnDelete(CaretMovementType.None), CanDelete));
- AddBinding(EditingCommands.Delete, ModifierKeys.None, Key.Delete, OnDelete(CaretMovementType.CharRight));
- AddBinding(EditingCommands.DeleteNextWord, ModifierKeys.Control, Key.Delete, OnDelete(CaretMovementType.WordRight));
- AddBinding(EditingCommands.Backspace, ModifierKeys.None, Key.Back, OnDelete(CaretMovementType.Backspace));
- InputBindings.Add(TextAreaDefaultInputHandler.CreateFrozenKeyBinding(EditingCommands.Backspace, ModifierKeys.Shift, Key.Back)); // make Shift-Backspace do the same as plain backspace
- AddBinding(EditingCommands.DeletePreviousWord, ModifierKeys.Control, Key.Back, OnDelete(CaretMovementType.WordLeft));
- AddBinding(EditingCommands.EnterParagraphBreak, ModifierKeys.None, Key.Enter, OnEnter);
- AddBinding(EditingCommands.EnterLineBreak, ModifierKeys.Shift, Key.Enter, OnEnter);
- AddBinding(EditingCommands.TabForward, ModifierKeys.None, Key.Tab, OnTab);
- AddBinding(EditingCommands.TabBackward, ModifierKeys.Shift, Key.Tab, OnShiftTab);
-
- CommandBindings.Add(new CommandBinding(ApplicationCommands.Copy, OnCopy, CanCutOrCopy));
- CommandBindings.Add(new CommandBinding(ApplicationCommands.Cut, OnCut, CanCutOrCopy));
- CommandBindings.Add(new CommandBinding(ApplicationCommands.Paste, OnPaste, CanPaste));
-
- CommandBindings.Add(new CommandBinding(AvalonEditCommands.ToggleOverstrike, OnToggleOverstrike));
- CommandBindings.Add(new CommandBinding(AvalonEditCommands.DeleteLine, OnDeleteLine));
-
- CommandBindings.Add(new CommandBinding(AvalonEditCommands.RemoveLeadingWhitespace, OnRemoveLeadingWhitespace));
- CommandBindings.Add(new CommandBinding(AvalonEditCommands.RemoveTrailingWhitespace, OnRemoveTrailingWhitespace));
- CommandBindings.Add(new CommandBinding(AvalonEditCommands.ConvertToUppercase, OnConvertToUpperCase));
- CommandBindings.Add(new CommandBinding(AvalonEditCommands.ConvertToLowercase, OnConvertToLowerCase));
- CommandBindings.Add(new CommandBinding(AvalonEditCommands.ConvertToTitleCase, OnConvertToTitleCase));
- CommandBindings.Add(new CommandBinding(AvalonEditCommands.InvertCase, OnInvertCase));
- CommandBindings.Add(new CommandBinding(AvalonEditCommands.ConvertTabsToSpaces, OnConvertTabsToSpaces));
- CommandBindings.Add(new CommandBinding(AvalonEditCommands.ConvertSpacesToTabs, OnConvertSpacesToTabs));
- CommandBindings.Add(new CommandBinding(AvalonEditCommands.ConvertLeadingTabsToSpaces, OnConvertLeadingTabsToSpaces));
- CommandBindings.Add(new CommandBinding(AvalonEditCommands.ConvertLeadingSpacesToTabs, OnConvertLeadingSpacesToTabs));
- CommandBindings.Add(new CommandBinding(AvalonEditCommands.IndentSelection, OnIndentSelection));
-
- TextAreaDefaultInputHandler.WorkaroundWPFMemoryLeak(InputBindings);
- }
-
- static TextArea GetTextArea(object target)
- {
- return target as TextArea;
- }
-
#region Text Transformation Helpers
enum DefaultSegmentType
{
@@ -106,451 +37,6 @@ enum DefaultSegmentType
CurrentLine
}
- ///
- /// Calls transformLine on all lines in the selected range.
- /// transformLine needs to handle read-only segments!
- ///
- static void TransformSelectedLines(Action