2 * Copyright (C) 2013 FooProject
3 * * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
6 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
7 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
9 You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
12 using System.Collections.Generic;
15 using System.Text.RegularExpressions;
16 using System.Threading.Tasks;
17 #if METRO || WINDOWS_UWP
18 using Windows.UI.Xaml;
19 using Windows.UI.Xaml.Media;
20 using Windows.UI.Xaml.Automation;
21 using Windows.UI.Xaml.Automation.Peers;
22 using Windows.UI.Xaml.Automation.Provider;
23 using Windows.UI.Xaml.Automation.Text;
25 using FooEditEngine.Metro;
27 using FooEditEngine.UWP;
31 using System.Windows.Automation;
32 using System.Windows.Automation.Peers;
33 using System.Windows.Automation.Provider;
34 using System.Windows.Automation.Text;
35 using FooEditEngine.WPF;
38 namespace FooEditEngine
42 /// A minimal implementation of ITextRangeProvider, used by CustomControl2AutomationPeer
43 /// A real implementation is beyond the scope of this sample
45 sealed class FooTextBoxRangeProvider : ITextRangeProvider
47 private FooTextBox textbox;
48 private FooTextBoxAutomationPeer _peer;
49 private int start, end;
51 public FooTextBoxRangeProvider(FooTextBox textbox, FooTextBoxAutomationPeer peer)
52 : this(textbox, 0, textbox.Document.Length, peer)
55 public FooTextBoxRangeProvider(FooTextBox textbox, int start, int length, FooTextBoxAutomationPeer peer)
57 this.textbox = textbox;
59 this.end = start + length;
63 public void AddToSelection()
65 throw new InvalidOperationException();
68 public ITextRangeProvider Clone()
70 return new FooTextBoxRangeProvider(this.textbox, this.start, this.end - this.start, _peer);
73 public bool Compare(ITextRangeProvider o)
75 FooTextBoxRangeProvider other = o as FooTextBoxRangeProvider;
77 throw new ArgumentNullException("null以外の値を指定してください");
78 if (this.start == other.start && this.end == other.end)
84 public int CompareEndpoints(TextPatternRangeEndpoint endpoint, ITextRangeProvider targetRange, TextPatternRangeEndpoint targetEndpoint)
86 FooTextBoxRangeProvider other = targetRange as FooTextBoxRangeProvider;
89 throw new ArgumentException("");
91 if (endpoint == TextPatternRangeEndpoint.Start)
93 if (targetEndpoint == TextPatternRangeEndpoint.Start)
94 return this.Compare(this.start, other.start);
95 if (targetEndpoint == TextPatternRangeEndpoint.End)
96 return this.Compare(this.start, other.end);
98 if (endpoint == TextPatternRangeEndpoint.End)
100 if (targetEndpoint == TextPatternRangeEndpoint.Start)
101 return this.Compare(this.start, other.end);
102 if (targetEndpoint == TextPatternRangeEndpoint.End)
103 return this.Compare(this.end, other.end);
105 throw new ArgumentException("endpointに未知の値が指定されました");
108 int Compare(int self, int other)
112 else if (self > other)
118 public void ExpandToEnclosingUnit(TextUnit unit)
120 Controller ctrl = this.textbox.Controller;
121 Document doc = this.textbox.Document;
122 if (unit == TextUnit.Character)
126 if (unit == TextUnit.Format || unit == TextUnit.Word)
128 var t = doc.GetSepartor(this.start, (c) => Util.IsWordSeparator(c));
130 this.start = this.end = 0;
133 this.start = t.Item1;
138 if (unit == TextUnit.Line || unit == TextUnit.Paragraph)
140 var t = doc.GetSepartor(this.start, (c) => c == Document.NewLine);
142 this.start = this.end = 0;
145 this.start = t.Item1;
150 if (unit == TextUnit.Page || unit == TextUnit.Document)
153 this.end = this.textbox.Document.Length;
156 throw new NotImplementedException();
159 public ITextRangeProvider FindAttribute(int attribute, Object value, bool backward)
164 public ITextRangeProvider FindText(String text, bool backward, bool ignoreCase)
167 throw new NotImplementedException();
168 textbox.Document.SetFindParam(text, false, ignoreCase ? RegexOptions.IgnoreCase : RegexOptions.None);
169 IEnumerator<SearchResult> it = textbox.Document.Find();
172 SearchResult sr = it.Current;
173 return new FooTextBoxRangeProvider(this.textbox, sr.Start, sr.End - sr.Start + 1, _peer);
178 public Object GetAttributeValue(int attribute)
183 #if METRO || WINDOWS_UWP
184 public void GetBoundingRectangles(out double[] rectangles)
187 public double[] GetBoundingRectangles()
190 LineToIndexTable layoutLineCollection = this.textbox.LayoutLineCollection;
191 TextPoint topLeft = layoutLineCollection.GetTextPointFromIndex(this.start);
192 TextPoint bottomRight = this.textbox.LayoutLineCollection.GetTextPointFromIndex(IsNewLine(this.end) ? this.end - 1 : this.end);
195 #if METRO || WINDOWS_UWP
197 Util.GetDpi(out dpi, out dpi);
198 double scale = dpi / 96;
199 Point topLeftPos = this.textbox.GetPostionFromTextPoint(topLeft);
200 Point bottomRightPos = this.textbox.GetPostionFromTextPoint(bottomRight);
201 topLeftPos = Util.GetPointInWindow(topLeftPos.Scale(scale), textbox);
202 bottomRightPos = Util.GetPointInWindow(bottomRightPos.Scale(scale), textbox);
205 Point topLeftPos = this.textbox.GetPostionFromTextPoint(topLeft);
206 Point bottomRightPos = this.textbox.GetPostionFromTextPoint(bottomRight);
207 topLeftPos = this.textbox.PointToScreen(topLeftPos);
208 bottomRightPos = this.textbox.PointToScreen(bottomRightPos);
211 double width = bottomRightPos.X - topLeftPos.X;
214 Rectangle rect = new Rectangle(topLeftPos.X, topLeftPos.Y,
216 bottomRightPos.Y - topLeftPos.Y + layoutLineCollection.GetLineHeight(bottomRight));
218 #if METRO || WINDOWS_UWP
219 rectangles = new double[4]{
227 return new double[4]{
236 bool IsNewLine(int index)
238 if (this.textbox.Document.Length > 0)
239 return this.textbox.Document[index == 0 ? 0 : index - 1] == Document.NewLine;
244 public IRawElementProviderSimple[] GetChildren()
246 return new IRawElementProviderSimple[0];
249 public IRawElementProviderSimple GetEnclosingElement()
251 return _peer.GetRawElementProviderSimple();
254 public String GetText(int maxLength)
256 if (this.textbox.Document.Length == 0)
258 int length = this.end - this.start;
260 return this.textbox.Document.ToString(this.start, length);
262 return this.textbox.Document.ToString(this.start, (int)Math.Min(length, maxLength));
265 public int Move(TextUnit unit, int count)
269 Controller ctrl = textbox.Controller;
270 LineToIndexTable layoutLine = textbox.LayoutLineCollection;
271 int moved = this.MoveEndpointByUnit(TextPatternRangeEndpoint.Start, unit, count);
272 this.ExpandToEnclosingUnit(unit);
276 public void MoveEndpointByRange(TextPatternRangeEndpoint endpoint, ITextRangeProvider targetRange, TextPatternRangeEndpoint targetEndpoint)
278 FooTextBoxRangeProvider other = targetRange as FooTextBoxRangeProvider;
281 throw new ArgumentException("");
283 if (endpoint == TextPatternRangeEndpoint.Start)
285 if (targetEndpoint == TextPatternRangeEndpoint.Start)
286 this.start = other.start;
287 if (targetEndpoint == TextPatternRangeEndpoint.End)
288 this.start = other.end;
289 if (this.start > this.end)
290 this.end = this.start;
293 if (endpoint == TextPatternRangeEndpoint.End)
295 if (targetEndpoint == TextPatternRangeEndpoint.Start)
296 this.end = other.start;
297 if (targetEndpoint == TextPatternRangeEndpoint.End)
298 this.end = other.end;
301 throw new ArgumentException("endpointに未知の値が指定されました");
304 public int MoveEndpointByUnit(TextPatternRangeEndpoint endpoint, TextUnit unit, int count)
310 TextPoint caret = TextPoint.Null, newCaret = TextPoint.Null;
311 Controller ctrl = textbox.Controller;
312 LineToIndexTable layoutLine = textbox.LayoutLineCollection;
314 if (endpoint == TextPatternRangeEndpoint.Start)
315 caret = layoutLine.GetTextPointFromIndex(this.start);
316 else if (endpoint == TextPatternRangeEndpoint.End)
317 caret = layoutLine.GetTextPointFromIndex(this.end);
321 case TextUnit.Character:
322 newCaret = ctrl.GetNextCaret(caret, count, MoveFlow.Character, out moved);
324 case TextUnit.Format:
326 newCaret = ctrl.GetNextCaret(caret, count, MoveFlow.Word, out moved);
329 newCaret = ctrl.GetNextCaret(caret, count, MoveFlow.Line, out moved);
331 case TextUnit.Paragraph:
332 newCaret = ctrl.GetNextCaret(caret, count, MoveFlow.Paragraph, out moved);
335 case TextUnit.Document:
337 this.end = this.textbox.Document.Length - 1;
342 if (endpoint == TextPatternRangeEndpoint.Start)
344 this.start = layoutLine.GetIndexFromTextPoint(newCaret);
345 if (this.start > this.end)
346 this.end = this.start;
348 else if (endpoint == TextPatternRangeEndpoint.End)
350 this.end = layoutLine.GetIndexFromTextPoint(newCaret);
351 if (this.end < this.start)
352 this.start = this.end;
357 public void RemoveFromSelection()
359 throw new InvalidOperationException();
362 public void ScrollIntoView(bool alignToTop)
364 int row = this.textbox.LayoutLineCollection.GetLineNumberFromIndex(alignToTop ? this.start : this.end);
365 this.textbox.ScrollIntoView(row, alignToTop);
370 this.textbox.Select(this.start, this.end - this.start + 1);