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 | |
---|
19 | using System; |
---|
20 | using System.Collections.Generic; |
---|
21 | using System.Diagnostics; |
---|
22 | using System.Linq; |
---|
23 | using System.Windows; |
---|
24 | using System.Windows.Documents; |
---|
25 | using System.Windows.Input; |
---|
26 | using System.Windows.Media.TextFormatting; |
---|
27 | using ICSharpCode.AvalonEdit.Document; |
---|
28 | using ICSharpCode.AvalonEdit.Rendering; |
---|
29 | using ICSharpCode.AvalonEdit.Utils; |
---|
30 | |
---|
31 | namespace ICSharpCode.AvalonEdit.Editing |
---|
32 | { |
---|
33 | enum CaretMovementType |
---|
34 | { |
---|
35 | None, |
---|
36 | CharLeft, |
---|
37 | CharRight, |
---|
38 | Backspace, |
---|
39 | WordLeft, |
---|
40 | WordRight, |
---|
41 | LineUp, |
---|
42 | LineDown, |
---|
43 | PageUp, |
---|
44 | PageDown, |
---|
45 | LineStart, |
---|
46 | LineEnd, |
---|
47 | DocumentStart, |
---|
48 | DocumentEnd |
---|
49 | } |
---|
50 | |
---|
51 | static class CaretNavigationCommandHandler |
---|
52 | { |
---|
53 | /// <summary> |
---|
54 | /// Creates a new <see cref="TextAreaInputHandler"/> for the text area. |
---|
55 | /// </summary> |
---|
56 | public static TextAreaInputHandler Create(TextArea textArea) |
---|
57 | { |
---|
58 | TextAreaInputHandler handler = new TextAreaInputHandler(textArea); |
---|
59 | handler.CommandBindings.AddRange(CommandBindings); |
---|
60 | handler.InputBindings.AddRange(InputBindings); |
---|
61 | return handler; |
---|
62 | } |
---|
63 | |
---|
64 | static readonly List<CommandBinding> CommandBindings = new List<CommandBinding>(); |
---|
65 | static readonly List<InputBinding> InputBindings = new List<InputBinding>(); |
---|
66 | |
---|
67 | static void AddBinding(ICommand command, ModifierKeys modifiers, Key key, ExecutedRoutedEventHandler handler) |
---|
68 | { |
---|
69 | CommandBindings.Add(new CommandBinding(command, handler)); |
---|
70 | InputBindings.Add(TextAreaDefaultInputHandler.CreateFrozenKeyBinding(command, modifiers, key)); |
---|
71 | } |
---|
72 | |
---|
73 | static CaretNavigationCommandHandler() |
---|
74 | { |
---|
75 | const ModifierKeys None = ModifierKeys.None; |
---|
76 | const ModifierKeys Ctrl = ModifierKeys.Control; |
---|
77 | const ModifierKeys Shift = ModifierKeys.Shift; |
---|
78 | const ModifierKeys Alt = ModifierKeys.Alt; |
---|
79 | |
---|
80 | AddBinding(EditingCommands.MoveLeftByCharacter, None, Key.Left, OnMoveCaret(CaretMovementType.CharLeft)); |
---|
81 | AddBinding(EditingCommands.SelectLeftByCharacter, Shift, Key.Left, OnMoveCaretExtendSelection(CaretMovementType.CharLeft)); |
---|
82 | AddBinding(RectangleSelection.BoxSelectLeftByCharacter, Alt | Shift, Key.Left, OnMoveCaretBoxSelection(CaretMovementType.CharLeft)); |
---|
83 | AddBinding(EditingCommands.MoveRightByCharacter, None, Key.Right, OnMoveCaret(CaretMovementType.CharRight)); |
---|
84 | AddBinding(EditingCommands.SelectRightByCharacter, Shift, Key.Right, OnMoveCaretExtendSelection(CaretMovementType.CharRight)); |
---|
85 | AddBinding(RectangleSelection.BoxSelectRightByCharacter, Alt | Shift, Key.Right, OnMoveCaretBoxSelection(CaretMovementType.CharRight)); |
---|
86 | |
---|
87 | AddBinding(EditingCommands.MoveLeftByWord, Ctrl, Key.Left, OnMoveCaret(CaretMovementType.WordLeft)); |
---|
88 | AddBinding(EditingCommands.SelectLeftByWord, Ctrl | Shift, Key.Left, OnMoveCaretExtendSelection(CaretMovementType.WordLeft)); |
---|
89 | AddBinding(RectangleSelection.BoxSelectLeftByWord, Ctrl | Alt | Shift, Key.Left, OnMoveCaretBoxSelection(CaretMovementType.WordLeft)); |
---|
90 | AddBinding(EditingCommands.MoveRightByWord, Ctrl, Key.Right, OnMoveCaret(CaretMovementType.WordRight)); |
---|
91 | AddBinding(EditingCommands.SelectRightByWord, Ctrl | Shift, Key.Right, OnMoveCaretExtendSelection(CaretMovementType.WordRight)); |
---|
92 | AddBinding(RectangleSelection.BoxSelectRightByWord, Ctrl | Alt | Shift, Key.Right, OnMoveCaretBoxSelection(CaretMovementType.WordRight)); |
---|
93 | |
---|
94 | AddBinding(EditingCommands.MoveUpByLine, None, Key.Up, OnMoveCaret(CaretMovementType.LineUp)); |
---|
95 | AddBinding(EditingCommands.SelectUpByLine, Shift, Key.Up, OnMoveCaretExtendSelection(CaretMovementType.LineUp)); |
---|
96 | AddBinding(RectangleSelection.BoxSelectUpByLine, Alt | Shift, Key.Up, OnMoveCaretBoxSelection(CaretMovementType.LineUp)); |
---|
97 | AddBinding(EditingCommands.MoveDownByLine, None, Key.Down, OnMoveCaret(CaretMovementType.LineDown)); |
---|
98 | AddBinding(EditingCommands.SelectDownByLine, Shift, Key.Down, OnMoveCaretExtendSelection(CaretMovementType.LineDown)); |
---|
99 | AddBinding(RectangleSelection.BoxSelectDownByLine, Alt | Shift, Key.Down, OnMoveCaretBoxSelection(CaretMovementType.LineDown)); |
---|
100 | |
---|
101 | AddBinding(EditingCommands.MoveDownByPage, None, Key.PageDown, OnMoveCaret(CaretMovementType.PageDown)); |
---|
102 | AddBinding(EditingCommands.SelectDownByPage, Shift, Key.PageDown, OnMoveCaretExtendSelection(CaretMovementType.PageDown)); |
---|
103 | AddBinding(EditingCommands.MoveUpByPage, None, Key.PageUp, OnMoveCaret(CaretMovementType.PageUp)); |
---|
104 | AddBinding(EditingCommands.SelectUpByPage, Shift, Key.PageUp, OnMoveCaretExtendSelection(CaretMovementType.PageUp)); |
---|
105 | |
---|
106 | AddBinding(EditingCommands.MoveToLineStart, None, Key.Home, OnMoveCaret(CaretMovementType.LineStart)); |
---|
107 | AddBinding(EditingCommands.SelectToLineStart, Shift, Key.Home, OnMoveCaretExtendSelection(CaretMovementType.LineStart)); |
---|
108 | AddBinding(RectangleSelection.BoxSelectToLineStart, Alt | Shift, Key.Home, OnMoveCaretBoxSelection(CaretMovementType.LineStart)); |
---|
109 | AddBinding(EditingCommands.MoveToLineEnd, None, Key.End, OnMoveCaret(CaretMovementType.LineEnd)); |
---|
110 | AddBinding(EditingCommands.SelectToLineEnd, Shift, Key.End, OnMoveCaretExtendSelection(CaretMovementType.LineEnd)); |
---|
111 | AddBinding(RectangleSelection.BoxSelectToLineEnd, Alt | Shift, Key.End, OnMoveCaretBoxSelection(CaretMovementType.LineEnd)); |
---|
112 | |
---|
113 | AddBinding(EditingCommands.MoveToDocumentStart, Ctrl, Key.Home, OnMoveCaret(CaretMovementType.DocumentStart)); |
---|
114 | AddBinding(EditingCommands.SelectToDocumentStart, Ctrl | Shift, Key.Home, OnMoveCaretExtendSelection(CaretMovementType.DocumentStart)); |
---|
115 | AddBinding(EditingCommands.MoveToDocumentEnd, Ctrl, Key.End, OnMoveCaret(CaretMovementType.DocumentEnd)); |
---|
116 | AddBinding(EditingCommands.SelectToDocumentEnd, Ctrl | Shift, Key.End, OnMoveCaretExtendSelection(CaretMovementType.DocumentEnd)); |
---|
117 | |
---|
118 | CommandBindings.Add(new CommandBinding(ApplicationCommands.SelectAll, OnSelectAll)); |
---|
119 | |
---|
120 | TextAreaDefaultInputHandler.WorkaroundWPFMemoryLeak(InputBindings); |
---|
121 | } |
---|
122 | |
---|
123 | static void OnSelectAll(object target, ExecutedRoutedEventArgs args) |
---|
124 | { |
---|
125 | TextArea textArea = GetTextArea(target); |
---|
126 | if (textArea != null && textArea.Document != null) { |
---|
127 | args.Handled = true; |
---|
128 | textArea.Caret.Offset = textArea.Document.TextLength; |
---|
129 | textArea.Selection = SimpleSelection.Create(textArea, 0, textArea.Document.TextLength); |
---|
130 | } |
---|
131 | } |
---|
132 | |
---|
133 | static TextArea GetTextArea(object target) |
---|
134 | { |
---|
135 | return target as TextArea; |
---|
136 | } |
---|
137 | |
---|
138 | static ExecutedRoutedEventHandler OnMoveCaret(CaretMovementType direction) |
---|
139 | { |
---|
140 | return (target, args) => { |
---|
141 | TextArea textArea = GetTextArea(target); |
---|
142 | if (textArea != null && textArea.Document != null) { |
---|
143 | args.Handled = true; |
---|
144 | textArea.ClearSelection(); |
---|
145 | MoveCaret(textArea, direction); |
---|
146 | textArea.Caret.BringCaretToView(); |
---|
147 | } |
---|
148 | }; |
---|
149 | } |
---|
150 | |
---|
151 | static ExecutedRoutedEventHandler OnMoveCaretExtendSelection(CaretMovementType direction) |
---|
152 | { |
---|
153 | return (target, args) => { |
---|
154 | TextArea textArea = GetTextArea(target); |
---|
155 | if (textArea != null && textArea.Document != null) { |
---|
156 | args.Handled = true; |
---|
157 | TextViewPosition oldPosition = textArea.Caret.Position; |
---|
158 | MoveCaret(textArea, direction); |
---|
159 | textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldPosition, textArea.Caret.Position); |
---|
160 | textArea.Caret.BringCaretToView(); |
---|
161 | } |
---|
162 | }; |
---|
163 | } |
---|
164 | |
---|
165 | static ExecutedRoutedEventHandler OnMoveCaretBoxSelection(CaretMovementType direction) |
---|
166 | { |
---|
167 | return (target, args) => { |
---|
168 | TextArea textArea = GetTextArea(target); |
---|
169 | if (textArea != null && textArea.Document != null) { |
---|
170 | args.Handled = true; |
---|
171 | // First, convert the selection into a rectangle selection |
---|
172 | // (this is required so that virtual space gets enabled for the caret movement) |
---|
173 | if (textArea.Options.EnableRectangularSelection && !(textArea.Selection is RectangleSelection)) { |
---|
174 | if (textArea.Selection.IsEmpty) { |
---|
175 | textArea.Selection = new RectangleSelection(textArea, textArea.Caret.Position, textArea.Caret.Position); |
---|
176 | } else { |
---|
177 | // Convert normal selection to rectangle selection |
---|
178 | textArea.Selection = new RectangleSelection(textArea, textArea.Selection.StartPosition, textArea.Caret.Position); |
---|
179 | } |
---|
180 | } |
---|
181 | // Now move the caret and extend the selection |
---|
182 | TextViewPosition oldPosition = textArea.Caret.Position; |
---|
183 | MoveCaret(textArea, direction); |
---|
184 | textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldPosition, textArea.Caret.Position); |
---|
185 | textArea.Caret.BringCaretToView(); |
---|
186 | } |
---|
187 | }; |
---|
188 | } |
---|
189 | |
---|
190 | #region Caret movement |
---|
191 | internal static void MoveCaret(TextArea textArea, CaretMovementType direction) |
---|
192 | { |
---|
193 | double desiredXPos = textArea.Caret.DesiredXPos; |
---|
194 | textArea.Caret.Position = GetNewCaretPosition(textArea.TextView, textArea.Caret.Position, direction, textArea.Selection.EnableVirtualSpace, ref desiredXPos); |
---|
195 | textArea.Caret.DesiredXPos = desiredXPos; |
---|
196 | } |
---|
197 | |
---|
198 | internal static TextViewPosition GetNewCaretPosition(TextView textView, TextViewPosition caretPosition, CaretMovementType direction, bool enableVirtualSpace, ref double desiredXPos) |
---|
199 | { |
---|
200 | switch (direction) { |
---|
201 | case CaretMovementType.None: |
---|
202 | return caretPosition; |
---|
203 | case CaretMovementType.DocumentStart: |
---|
204 | desiredXPos = double.NaN; |
---|
205 | return new TextViewPosition(0, 0); |
---|
206 | case CaretMovementType.DocumentEnd: |
---|
207 | desiredXPos = double.NaN; |
---|
208 | return new TextViewPosition(textView.Document.GetLocation(textView.Document.TextLength)); |
---|
209 | } |
---|
210 | DocumentLine caretLine = textView.Document.GetLineByNumber(caretPosition.Line); |
---|
211 | VisualLine visualLine = textView.GetOrConstructVisualLine(caretLine); |
---|
212 | TextLine textLine = visualLine.GetTextLine(caretPosition.VisualColumn, caretPosition.IsAtEndOfLine); |
---|
213 | switch (direction) { |
---|
214 | case CaretMovementType.CharLeft: |
---|
215 | desiredXPos = double.NaN; |
---|
216 | return GetPrevCaretPosition(textView, caretPosition, visualLine, CaretPositioningMode.Normal, enableVirtualSpace); |
---|
217 | case CaretMovementType.Backspace: |
---|
218 | desiredXPos = double.NaN; |
---|
219 | return GetPrevCaretPosition(textView, caretPosition, visualLine, CaretPositioningMode.EveryCodepoint, enableVirtualSpace); |
---|
220 | case CaretMovementType.CharRight: |
---|
221 | desiredXPos = double.NaN; |
---|
222 | return GetNextCaretPosition(textView, caretPosition, visualLine, CaretPositioningMode.Normal, enableVirtualSpace); |
---|
223 | case CaretMovementType.WordLeft: |
---|
224 | desiredXPos = double.NaN; |
---|
225 | return GetPrevCaretPosition(textView, caretPosition, visualLine, CaretPositioningMode.WordStart, enableVirtualSpace); |
---|
226 | case CaretMovementType.WordRight: |
---|
227 | desiredXPos = double.NaN; |
---|
228 | return GetNextCaretPosition(textView, caretPosition, visualLine, CaretPositioningMode.WordStart, enableVirtualSpace); |
---|
229 | case CaretMovementType.LineUp: |
---|
230 | case CaretMovementType.LineDown: |
---|
231 | case CaretMovementType.PageUp: |
---|
232 | case CaretMovementType.PageDown: |
---|
233 | return GetUpDownCaretPosition(textView, caretPosition, direction, visualLine, textLine, enableVirtualSpace, ref desiredXPos); |
---|
234 | case CaretMovementType.LineStart: |
---|
235 | desiredXPos = double.NaN; |
---|
236 | return GetStartOfLineCaretPosition(caretPosition.VisualColumn, visualLine, textLine, enableVirtualSpace); |
---|
237 | case CaretMovementType.LineEnd: |
---|
238 | desiredXPos = double.NaN; |
---|
239 | return GetEndOfLineCaretPosition(visualLine, textLine); |
---|
240 | default: |
---|
241 | throw new NotSupportedException(direction.ToString()); |
---|
242 | } |
---|
243 | } |
---|
244 | #endregion |
---|
245 | |
---|
246 | #region Home/End |
---|
247 | static TextViewPosition GetStartOfLineCaretPosition(int oldVC, VisualLine visualLine, TextLine textLine, bool enableVirtualSpace) |
---|
248 | { |
---|
249 | int newVC = visualLine.GetTextLineVisualStartColumn(textLine); |
---|
250 | if (newVC == 0) |
---|
251 | newVC = visualLine.GetNextCaretPosition(newVC - 1, LogicalDirection.Forward, CaretPositioningMode.WordStart, enableVirtualSpace); |
---|
252 | if (newVC < 0) |
---|
253 | throw ThrowUtil.NoValidCaretPosition(); |
---|
254 | // when the caret is already at the start of the text, jump to start before whitespace |
---|
255 | if (newVC == oldVC) |
---|
256 | newVC = 0; |
---|
257 | return visualLine.GetTextViewPosition(newVC); |
---|
258 | } |
---|
259 | |
---|
260 | static TextViewPosition GetEndOfLineCaretPosition(VisualLine visualLine, TextLine textLine) |
---|
261 | { |
---|
262 | int newVC = visualLine.GetTextLineVisualStartColumn(textLine) + textLine.Length - textLine.TrailingWhitespaceLength; |
---|
263 | TextViewPosition pos = visualLine.GetTextViewPosition(newVC); |
---|
264 | pos.IsAtEndOfLine = true; |
---|
265 | return pos; |
---|
266 | } |
---|
267 | #endregion |
---|
268 | |
---|
269 | #region By-character / By-word movement |
---|
270 | static TextViewPosition GetNextCaretPosition(TextView textView, TextViewPosition caretPosition, VisualLine visualLine, CaretPositioningMode mode, bool enableVirtualSpace) |
---|
271 | { |
---|
272 | int pos = visualLine.GetNextCaretPosition(caretPosition.VisualColumn, LogicalDirection.Forward, mode, enableVirtualSpace); |
---|
273 | if (pos >= 0) { |
---|
274 | return visualLine.GetTextViewPosition(pos); |
---|
275 | } else { |
---|
276 | // move to start of next line |
---|
277 | DocumentLine nextDocumentLine = visualLine.LastDocumentLine.NextLine; |
---|
278 | if (nextDocumentLine != null) { |
---|
279 | VisualLine nextLine = textView.GetOrConstructVisualLine(nextDocumentLine); |
---|
280 | pos = nextLine.GetNextCaretPosition(-1, LogicalDirection.Forward, mode, enableVirtualSpace); |
---|
281 | if (pos < 0) |
---|
282 | throw ThrowUtil.NoValidCaretPosition(); |
---|
283 | return nextLine.GetTextViewPosition(pos); |
---|
284 | } else { |
---|
285 | // at end of document |
---|
286 | Debug.Assert(visualLine.LastDocumentLine.Offset + visualLine.LastDocumentLine.TotalLength == textView.Document.TextLength); |
---|
287 | return new TextViewPosition(textView.Document.GetLocation(textView.Document.TextLength)); |
---|
288 | } |
---|
289 | } |
---|
290 | } |
---|
291 | |
---|
292 | static TextViewPosition GetPrevCaretPosition(TextView textView, TextViewPosition caretPosition, VisualLine visualLine, CaretPositioningMode mode, bool enableVirtualSpace) |
---|
293 | { |
---|
294 | int pos = visualLine.GetNextCaretPosition(caretPosition.VisualColumn, LogicalDirection.Backward, mode, enableVirtualSpace); |
---|
295 | if (pos >= 0) { |
---|
296 | return visualLine.GetTextViewPosition(pos); |
---|
297 | } else { |
---|
298 | // move to end of previous line |
---|
299 | DocumentLine previousDocumentLine = visualLine.FirstDocumentLine.PreviousLine; |
---|
300 | if (previousDocumentLine != null) { |
---|
301 | VisualLine previousLine = textView.GetOrConstructVisualLine(previousDocumentLine); |
---|
302 | pos = previousLine.GetNextCaretPosition(previousLine.VisualLength + 1, LogicalDirection.Backward, mode, enableVirtualSpace); |
---|
303 | if (pos < 0) |
---|
304 | throw ThrowUtil.NoValidCaretPosition(); |
---|
305 | return previousLine.GetTextViewPosition(pos); |
---|
306 | } else { |
---|
307 | // at start of document |
---|
308 | Debug.Assert(visualLine.FirstDocumentLine.Offset == 0); |
---|
309 | return new TextViewPosition(0, 0); |
---|
310 | } |
---|
311 | } |
---|
312 | } |
---|
313 | #endregion |
---|
314 | |
---|
315 | #region Line+Page up/down |
---|
316 | static TextViewPosition GetUpDownCaretPosition(TextView textView, TextViewPosition caretPosition, CaretMovementType direction, VisualLine visualLine, TextLine textLine, bool enableVirtualSpace, ref double xPos) |
---|
317 | { |
---|
318 | // moving up/down happens using the desired visual X position |
---|
319 | if (double.IsNaN(xPos)) |
---|
320 | xPos = visualLine.GetTextLineVisualXPosition(textLine, caretPosition.VisualColumn); |
---|
321 | // now find the TextLine+VisualLine where the caret will end up in |
---|
322 | VisualLine targetVisualLine = visualLine; |
---|
323 | TextLine targetLine; |
---|
324 | int textLineIndex = visualLine.TextLines.IndexOf(textLine); |
---|
325 | switch (direction) { |
---|
326 | case CaretMovementType.LineUp: |
---|
327 | { |
---|
328 | // Move up: move to the previous TextLine in the same visual line |
---|
329 | // or move to the last TextLine of the previous visual line |
---|
330 | int prevLineNumber = visualLine.FirstDocumentLine.LineNumber - 1; |
---|
331 | if (textLineIndex > 0) { |
---|
332 | targetLine = visualLine.TextLines[textLineIndex - 1]; |
---|
333 | } else if (prevLineNumber >= 1) { |
---|
334 | DocumentLine prevLine = textView.Document.GetLineByNumber(prevLineNumber); |
---|
335 | targetVisualLine = textView.GetOrConstructVisualLine(prevLine); |
---|
336 | targetLine = targetVisualLine.TextLines[targetVisualLine.TextLines.Count - 1]; |
---|
337 | } else { |
---|
338 | targetLine = null; |
---|
339 | } |
---|
340 | break; |
---|
341 | } |
---|
342 | case CaretMovementType.LineDown: |
---|
343 | { |
---|
344 | // Move down: move to the next TextLine in the same visual line |
---|
345 | // or move to the first TextLine of the next visual line |
---|
346 | int nextLineNumber = visualLine.LastDocumentLine.LineNumber + 1; |
---|
347 | if (textLineIndex < visualLine.TextLines.Count - 1) { |
---|
348 | targetLine = visualLine.TextLines[textLineIndex + 1]; |
---|
349 | } else if (nextLineNumber <= textView.Document.LineCount) { |
---|
350 | DocumentLine nextLine = textView.Document.GetLineByNumber(nextLineNumber); |
---|
351 | targetVisualLine = textView.GetOrConstructVisualLine(nextLine); |
---|
352 | targetLine = targetVisualLine.TextLines[0]; |
---|
353 | } else { |
---|
354 | targetLine = null; |
---|
355 | } |
---|
356 | break; |
---|
357 | } |
---|
358 | case CaretMovementType.PageUp: |
---|
359 | case CaretMovementType.PageDown: |
---|
360 | { |
---|
361 | // Page up/down: find the target line using its visual position |
---|
362 | double yPos = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.LineMiddle); |
---|
363 | if (direction == CaretMovementType.PageUp) |
---|
364 | yPos -= textView.RenderSize.Height; |
---|
365 | else |
---|
366 | yPos += textView.RenderSize.Height; |
---|
367 | DocumentLine newLine = textView.GetDocumentLineByVisualTop(yPos); |
---|
368 | targetVisualLine = textView.GetOrConstructVisualLine(newLine); |
---|
369 | targetLine = targetVisualLine.GetTextLineByVisualYPosition(yPos); |
---|
370 | break; |
---|
371 | } |
---|
372 | default: |
---|
373 | throw new NotSupportedException(direction.ToString()); |
---|
374 | } |
---|
375 | if (targetLine != null) { |
---|
376 | double yPos = targetVisualLine.GetTextLineVisualYPosition(targetLine, VisualYPosition.LineMiddle); |
---|
377 | int newVisualColumn = targetVisualLine.GetVisualColumn(new Point(xPos, yPos), enableVirtualSpace); |
---|
378 | |
---|
379 | // prevent wrapping to the next line; TODO: could 'IsAtEnd' help here? |
---|
380 | int targetLineStartCol = targetVisualLine.GetTextLineVisualStartColumn(targetLine); |
---|
381 | if (newVisualColumn >= targetLineStartCol + targetLine.Length) { |
---|
382 | if (newVisualColumn <= targetVisualLine.VisualLength) |
---|
383 | newVisualColumn = targetLineStartCol + targetLine.Length - 1; |
---|
384 | } |
---|
385 | return targetVisualLine.GetTextViewPosition(newVisualColumn); |
---|
386 | } else { |
---|
387 | return caretPosition; |
---|
388 | } |
---|
389 | } |
---|
390 | #endregion |
---|
391 | } |
---|
392 | } |
---|