OSDN Git Service

ファイルを分割した
[fooeditengine/FooEditEngine.git] / Core / Automaion / FooTextBoxRangeProvider.cs
1 /*
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.
5
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.
8
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/>.
10  */
11 using System;
12 using System.Collections.Generic;
13 using System.Linq;
14 using System.Text;
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;
24 #if METRO
25 using FooEditEngine.Metro;
26 #else
27 using FooEditEngine.UWP;
28 #endif
29 #endif
30 #if WPF
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;
36 #endif
37
38 namespace FooEditEngine
39 {
40 #if ENABLE_AUTMATION
41     /// <summary>
42     /// A minimal implementation of ITextRangeProvider, used by CustomControl2AutomationPeer
43     /// A real implementation is beyond the scope of this sample
44     /// </summary>
45     sealed class FooTextBoxRangeProvider : ITextRangeProvider
46     {
47         private FooTextBox textbox;
48         private FooTextBoxAutomationPeer _peer;
49         private int start, end;
50
51         public FooTextBoxRangeProvider(FooTextBox textbox, FooTextBoxAutomationPeer peer)
52             : this(textbox, 0, textbox.Document.Length, peer)
53         {
54         }
55         public FooTextBoxRangeProvider(FooTextBox textbox, int start, int length, FooTextBoxAutomationPeer peer)
56         {
57             this.textbox = textbox;
58             this.start = start;
59             this.end = start + length;
60             _peer = peer;
61         }
62
63         public void AddToSelection()
64         {
65             throw new InvalidOperationException();
66         }
67
68         public ITextRangeProvider Clone()
69         {
70             return new FooTextBoxRangeProvider(this.textbox, this.start, this.end - this.start, _peer);
71         }
72
73         public bool Compare(ITextRangeProvider o)
74         {
75             FooTextBoxRangeProvider other = o as FooTextBoxRangeProvider;
76             if (other == null)
77                 throw new ArgumentNullException("null以外の値を指定してください");
78             if (this.start == other.start && this.end == other.end)
79                 return true;
80             else
81                 return false;
82         }
83
84         public int CompareEndpoints(TextPatternRangeEndpoint endpoint, ITextRangeProvider targetRange, TextPatternRangeEndpoint targetEndpoint)
85         {
86             FooTextBoxRangeProvider other = targetRange as FooTextBoxRangeProvider;
87
88             if (other == null)
89                 throw new ArgumentException("");
90
91             if (endpoint == TextPatternRangeEndpoint.Start)
92             {
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);
97             }
98             if (endpoint == TextPatternRangeEndpoint.End)
99             {
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);
104             }
105             throw new ArgumentException("endpointに未知の値が指定されました");
106         }
107
108         int Compare(int self, int other)
109         {
110             if (self < other)
111                 return -1;
112             else if (self > other)
113                 return 1;
114             else
115                 return 0;
116         }
117
118         public void ExpandToEnclosingUnit(TextUnit unit)
119         {
120             Controller ctrl = this.textbox.Controller;
121             Document doc = this.textbox.Document;
122             if (unit == TextUnit.Character)
123             {
124                 return;
125             }
126             if (unit == TextUnit.Format || unit == TextUnit.Word)
127             {
128                 var t = doc.GetSepartor(this.start, (c) => Util.IsWordSeparator(c));
129                 if (t == null)
130                     this.start = this.end = 0;
131                 else
132                 {
133                     this.start = t.Item1;
134                     this.end = t.Item2;
135                 }
136                 return;
137             }
138             if (unit == TextUnit.Line || unit == TextUnit.Paragraph)
139             {
140                 var t = doc.GetSepartor(this.start, (c) => c == Document.NewLine);
141                 if (t == null)
142                     this.start = this.end = 0;
143                 else
144                 {
145                     this.start = t.Item1;
146                     this.end = t.Item2;
147                 }
148                 return;
149             }
150             if (unit == TextUnit.Page || unit == TextUnit.Document)
151             {
152                 this.start = 0;
153                 this.end = this.textbox.Document.Length;
154                 return;
155             }
156             throw new NotImplementedException();
157         }
158
159         public ITextRangeProvider FindAttribute(int attribute, Object value, bool backward)
160         {
161             return null;
162         }
163
164         public ITextRangeProvider FindText(String text, bool backward, bool ignoreCase)
165         {
166             if (backward)
167                 throw new NotImplementedException();
168             textbox.Document.SetFindParam(text, false, ignoreCase ? RegexOptions.IgnoreCase : RegexOptions.None);
169             IEnumerator<SearchResult> it = textbox.Document.Find();
170             if (it.MoveNext())
171             {
172                 SearchResult sr = it.Current;
173                 return new FooTextBoxRangeProvider(this.textbox, sr.Start, sr.End - sr.Start + 1, _peer);
174             }
175             return null;
176         }
177
178         public Object GetAttributeValue(int attribute)
179         {
180             return null;
181         }
182
183 #if METRO || WINDOWS_UWP
184         public void GetBoundingRectangles(out double[] rectangles)
185 #endif
186 #if WPF
187         public double[] GetBoundingRectangles()
188 #endif
189         {
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);
193
194
195 #if METRO || WINDOWS_UWP
196             float dpi;
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);
203 #endif
204 #if WPF
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);
209 #endif
210
211             double width = bottomRightPos.X - topLeftPos.X;
212             if (width == 0)
213                 width = 1;
214             Rectangle rect = new Rectangle(topLeftPos.X, topLeftPos.Y,
215                  width,
216                  bottomRightPos.Y - topLeftPos.Y + layoutLineCollection.GetLineHeight(bottomRight));
217
218 #if METRO || WINDOWS_UWP
219             rectangles = new double[4]{
220                 rect.X,
221                 rect.Y,
222                 rect.Width,
223                 rect.Height
224             };
225 #endif
226 #if WPF
227             return new double[4]{
228                 rect.X,
229                 rect.Y,
230                 rect.Width,
231                 rect.Height
232             };
233 #endif
234         }
235
236         bool IsNewLine(int index)
237         {
238             if (this.textbox.Document.Length > 0)
239                 return this.textbox.Document[index == 0 ? 0 : index - 1] == Document.NewLine;
240             else
241                 return false;
242         }
243
244         public IRawElementProviderSimple[] GetChildren()
245         {
246             return new IRawElementProviderSimple[0];
247         }
248
249         public IRawElementProviderSimple GetEnclosingElement()
250         {
251             return _peer.GetRawElementProviderSimple();
252         }
253
254         public String GetText(int maxLength)
255         {
256             if (this.textbox.Document.Length == 0)
257                 return "";
258             int length = this.end - this.start;
259             if (maxLength < 0)
260                 return this.textbox.Document.ToString(this.start, length);
261             else
262                 return this.textbox.Document.ToString(this.start, (int)Math.Min(length, maxLength));
263         }
264
265         public int Move(TextUnit unit, int count)
266         {
267             if (count == 0)
268                 return 0;
269             Controller ctrl = textbox.Controller;
270             LineToIndexTable layoutLine = textbox.LayoutLineCollection;
271             int moved = this.MoveEndpointByUnit(TextPatternRangeEndpoint.Start, unit, count);
272             this.ExpandToEnclosingUnit(unit);
273             return moved;
274         }
275
276         public void MoveEndpointByRange(TextPatternRangeEndpoint endpoint, ITextRangeProvider targetRange, TextPatternRangeEndpoint targetEndpoint)
277         {
278             FooTextBoxRangeProvider other = targetRange as FooTextBoxRangeProvider;
279
280             if (other == null)
281                 throw new ArgumentException("");
282
283             if (endpoint == TextPatternRangeEndpoint.Start)
284             {
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;
291                 return;
292             }
293             if (endpoint == TextPatternRangeEndpoint.End)
294             {
295                 if (targetEndpoint == TextPatternRangeEndpoint.Start)
296                     this.end = other.start;
297                 if (targetEndpoint == TextPatternRangeEndpoint.End)
298                     this.end = other.end;
299                 return;
300             }
301             throw new ArgumentException("endpointに未知の値が指定されました");
302         }
303
304         public int MoveEndpointByUnit(TextPatternRangeEndpoint endpoint, TextUnit unit, int count)
305         {
306             if (count == 0)
307                 return 0;
308
309             int moved = 0;
310             TextPoint caret = TextPoint.Null, newCaret = TextPoint.Null;
311             Controller ctrl = textbox.Controller;
312             LineToIndexTable layoutLine = textbox.LayoutLineCollection;
313
314             if (endpoint == TextPatternRangeEndpoint.Start)
315                 caret = layoutLine.GetTextPointFromIndex(this.start);
316             else if (endpoint == TextPatternRangeEndpoint.End)
317                 caret = layoutLine.GetTextPointFromIndex(this.end);
318
319             switch (unit)
320             {
321                 case TextUnit.Character:
322                     newCaret = ctrl.GetNextCaret(caret, count, MoveFlow.Character, out moved);
323                     break;
324                 case TextUnit.Format:
325                 case TextUnit.Word:
326                     newCaret = ctrl.GetNextCaret(caret, count, MoveFlow.Word, out moved);
327                     break;
328                 case TextUnit.Line:
329                     newCaret = ctrl.GetNextCaret(caret, count, MoveFlow.Line, out moved);
330                     break;
331                 case TextUnit.Paragraph:
332                     newCaret = ctrl.GetNextCaret(caret, count, MoveFlow.Paragraph, out moved);
333                     break;
334                 case TextUnit.Page:
335                 case TextUnit.Document:
336                     this.start = 0;
337                     this.end = this.textbox.Document.Length - 1;
338                     moved = 1;
339                     break;
340             }
341
342             if (endpoint == TextPatternRangeEndpoint.Start)
343             {
344                 this.start = layoutLine.GetIndexFromTextPoint(newCaret);
345                 if (this.start > this.end)
346                     this.end = this.start;
347             }
348             else if (endpoint == TextPatternRangeEndpoint.End)
349             {
350                 this.end = layoutLine.GetIndexFromTextPoint(newCaret);
351                 if (this.end < this.start)
352                     this.start = this.end;
353             }
354             return moved;
355         }
356
357         public void RemoveFromSelection()
358         {
359             throw new InvalidOperationException();
360         }
361
362         public void ScrollIntoView(bool alignToTop)
363         {
364             int row = this.textbox.LayoutLineCollection.GetLineNumberFromIndex(alignToTop ? this.start : this.end);
365             this.textbox.ScrollIntoView(row, alignToTop);
366         }
367
368         public void Select()
369         {
370             this.textbox.Select(this.start, this.end - this.start + 1);
371         }
372     }
373 #endif
374 }