OSDN Git Service

Selection~系列のプロパティをSelectionプロパティに統合した
[fooeditengine/FooEditEngine.git] / Common / Automaion / FooTextBoxAutomationPeer.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
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 using FooEditEngine.Metro;
25 #endif
26 #if WPF
27 using System.Windows.Automation;
28 using System.Windows.Automation.Peers;
29 using System.Windows.Automation.Provider;
30 using System.Windows.Automation.Text;
31 using FooEditEngine.WPF;
32 #endif
33
34 namespace FooEditEngine
35 {
36     /// <summary>
37     /// Automation Peer class for CustomInputBox2.  
38     /// 
39     /// Note: The difference between this and CustomControl1AutomationPeer is that this one implements
40     /// Text Pattern (ITextProvider) and Value Pattern (IValuePattern) interfaces.  So Touch keyboard shows 
41     /// automatically when user taps on the control with Touch or Pen.
42     /// </summary>
43     sealed class FooTextBoxAutomationPeer : FrameworkElementAutomationPeer, ITextProvider, IValueProvider
44     {
45         private FooTextBox fooTextBox;
46         private string accClass = "FooTextBox";
47
48         /// <summary>
49         /// 
50         /// </summary>
51         /// <param name="owner"></param>
52         public FooTextBoxAutomationPeer(FooTextBox owner)
53             : base(owner)
54         {
55             this.fooTextBox = owner;
56             this.fooTextBox.Document.Update += Document_Update;
57         }
58
59         void Document_Update(object sender, DocumentUpdateEventArgs e)
60         {
61             this.RaiseAutomationEvent(AutomationEvents.TextPatternOnTextChanged);
62         }
63
64         public void OnNotifyCaretChanged()
65         {
66             this.RaiseAutomationEvent(AutomationEvents.TextPatternOnTextSelectionChanged);
67         }
68
69 #if METRO
70         /// <summary>
71         /// Override GetPatternCore to return the object that supports the specified pattern.  In this case the Value pattern, Text
72         /// patter and any base class patterns.
73         /// </summary>
74         /// <param name="patternInterface"></param>
75         /// <returns>the object that supports the specified pattern</returns>
76         protected override object GetPatternCore(PatternInterface patternInterface)
77         {
78             if (patternInterface == PatternInterface.Value)
79             {
80                 return this;
81             }
82             else if (patternInterface == PatternInterface.Text)
83             {
84                 return this;
85             }
86             return base.GetPatternCore(patternInterface);
87         }
88 #endif
89 #if WPF
90         public override object GetPattern(PatternInterface patternInterface)
91         {
92             if (patternInterface == PatternInterface.Value)
93             {
94                 return this;
95             }
96             else if (patternInterface == PatternInterface.Text)
97             {
98                 return this;
99             }
100             return base.GetPattern(patternInterface);
101         }
102 #endif
103
104         /// <summary>
105         /// Override GetClassNameCore and set the name of the class that defines the type associated with this control.
106         /// </summary>
107         /// <returns>The name of the control class</returns>
108         protected override string GetClassNameCore()
109         {
110             return this.accClass;
111         }
112
113         protected override AutomationControlType GetAutomationControlTypeCore()
114         {
115             return AutomationControlType.Edit;
116         }
117
118         protected override bool IsContentElementCore()
119         {
120             return true;
121         }
122
123 #if METRO
124         protected override Windows.Foundation.Rect GetBoundingRectangleCore()
125         {
126             return Util.GetScreentRect(new Windows.Foundation.Rect(0, 0, this.fooTextBox.ActualWidth, this.fooTextBox.ActualHeight),this.fooTextBox);
127         }
128 #endif
129 #if WPF
130         protected override System.Windows.Rect GetBoundingRectangleCore()
131         {
132             System.Windows.Point left = this.fooTextBox.PointToScreen(new System.Windows.Point(0, 0));
133             System.Windows.Point bottom = this.fooTextBox.PointToScreen(new System.Windows.Point(this.fooTextBox.ActualWidth, this.fooTextBox.ActualHeight));
134             return new System.Windows.Rect(left, bottom);
135         }
136 #endif
137
138         #region Implementation for ITextPattern interface
139         // Complete implementation of the ITextPattern is beyond the scope of this sample.  The implementation provided
140         // is specific to this sample's custom control, so it is unlikely that they are directly transferable to other 
141         // custom control.
142
143         ITextRangeProvider ITextProvider.DocumentRange
144         {
145             // A real implementation of this method is beyond the scope of this sample.
146             // If your custom control has complex text involving both readonly and non-readonly ranges, 
147             // it will need a smarter implementation than just returning a fixed range
148             get
149             {
150                 return new FooTextBoxRangeProvider(this.fooTextBox, this);
151             }
152         }
153
154         ITextRangeProvider[] ITextProvider.GetSelection()
155         {
156             ITextRangeProvider[] ret = new ITextRangeProvider[1];
157             int selStart = this.fooTextBox.Selection.Index;
158             int selLength = this.fooTextBox.Selection.Length;
159             ret[0] = new FooTextBoxRangeProvider(this.fooTextBox, selStart, selLength, this);
160             return ret;
161         }
162
163         ITextRangeProvider[] ITextProvider.GetVisibleRanges()
164         {
165             ITextRangeProvider[] ret = new ITextRangeProvider[1];
166             if (this.fooTextBox.LayoutLineCollection.Count == 0)
167             {
168                 ret[0] = new FooTextBoxRangeProvider(this.fooTextBox, 0, 0, this);
169             }
170             else
171             {
172 #if METRO
173                 int startIndex = this.fooTextBox.GetIndexFromPostion(new Windows.Foundation.Point(0,0));
174                 int endIndex = this.fooTextBox.GetIndexFromPostion(new Windows.Foundation.Point(this.fooTextBox.ActualWidth, this.fooTextBox.ActualHeight));
175 #endif
176 #if WPF
177                 int startIndex = this.fooTextBox.GetIndexFromPostion(new System.Windows.Point(0, 0));
178                 int endIndex = this.fooTextBox.GetIndexFromPostion(new System.Windows.Point(this.fooTextBox.ActualWidth, this.fooTextBox.ActualHeight));
179 #endif
180                 ret[0] = new FooTextBoxRangeProvider(this.fooTextBox, startIndex, endIndex - startIndex, this);
181             }
182             return ret;
183         }
184
185         ITextRangeProvider ITextProvider.RangeFromChild(IRawElementProviderSimple childElement)
186         {
187             return new FooTextBoxRangeProvider(this.fooTextBox,0,0, this);
188         }
189
190 #if METRO
191         ITextRangeProvider ITextProvider.RangeFromPoint(Windows.Foundation.Point screenLocation)
192         {
193             Windows.Foundation.Point pt = Util.GetClientPoint(screenLocation, this.fooTextBox);
194
195             int index = this.fooTextBox.GetIndexFromPostion(pt);
196             int length = 1;
197             if (index == this.fooTextBox.Document.Length)
198                 length = 0;
199             
200             return new FooTextBoxRangeProvider(this.fooTextBox, index, length, this);
201         }
202 #endif
203 #if WPF
204         ITextRangeProvider ITextProvider.RangeFromPoint(System.Windows.Point screenLocation)
205         {
206             System.Windows.Point pt = this.fooTextBox.PointFromScreen(screenLocation);
207
208             int index = this.fooTextBox.GetIndexFromPostion(pt);
209             int length = 1;
210             if (index == this.fooTextBox.Document.Length)
211                 length = 0;
212
213             return new FooTextBoxRangeProvider(this.fooTextBox, index, length, this);
214         }
215 #endif
216
217         SupportedTextSelection ITextProvider.SupportedTextSelection
218         {
219             get { return SupportedTextSelection.Single; }
220         }
221
222         #endregion
223
224         #region Implementation for IValueProvider interface
225         // Complete implementation of the IValueProvider is beyond the scope of this sample.  The implementation provided
226         // is specific to this sample's custom control, so it is unlikely that they are directly transferable to other 
227         // custom control.
228
229         /// <summary>
230         /// The value needs to be false for the Touch keyboard to be launched automatically because Touch keyboard
231         /// does not appear when the input focus is in a readonly UI control.
232         /// </summary>
233         bool IValueProvider.IsReadOnly
234         {
235             get { return false; }
236         }
237
238         void IValueProvider.SetValue(string value)
239         {
240             this.fooTextBox.Document.Replace(0,this.fooTextBox.Document.Length,value);
241         }
242
243         string IValueProvider.Value
244         {
245             get
246             {
247                 return this.fooTextBox.Document.ToString(0,this.fooTextBox.Document.Length);
248             }
249         }
250
251         #endregion //Implementation for IValueProvider interface
252
253         public IRawElementProviderSimple GetRawElementProviderSimple()
254         {
255             return ProviderFromPeer(this);
256         }
257     }
258
259     /// <summary>
260     /// A minimal implementation of ITextRangeProvider, used by CustomControl2AutomationPeer
261     /// A real implementation is beyond the scope of this sample
262     /// </summary>
263     sealed class FooTextBoxRangeProvider : ITextRangeProvider
264     {
265         private FooTextBox textbox;
266         private Document document;
267         private FooTextBoxAutomationPeer _peer;
268         private int start, end;
269
270         public FooTextBoxRangeProvider(FooTextBox textbox, FooTextBoxAutomationPeer peer)
271             : this(textbox,0,textbox.Document.Length,peer)
272         {
273         }
274         public FooTextBoxRangeProvider(FooTextBox textbox, int start, int length, FooTextBoxAutomationPeer peer)
275         {
276             this.textbox = textbox;
277             this.document = textbox.Document;
278             this.start = start;
279             this.end = start + length;
280             _peer = peer;
281         }
282
283         public void AddToSelection()
284         {
285
286         }
287
288         public ITextRangeProvider Clone()
289         {
290             return new FooTextBoxRangeProvider(this.textbox,this.start,this.end - this.start, _peer);
291         }
292
293         public bool Compare(ITextRangeProvider o)
294         {
295             FooTextBoxRangeProvider other = o as FooTextBoxRangeProvider;
296             if (other == null)
297                 throw new ArgumentNullException("null以外の値を指定してください");
298             if (this.start == other.start && this.end == other.end)
299                 return true;
300             else
301                 return false;
302         }
303
304         public int CompareEndpoints(TextPatternRangeEndpoint endpoint, ITextRangeProvider targetRange, TextPatternRangeEndpoint targetEndpoint)
305         {
306             FooTextBoxRangeProvider other = targetRange as FooTextBoxRangeProvider;
307             
308             if (other == null)
309                 throw new ArgumentException("");
310
311             if (endpoint == TextPatternRangeEndpoint.Start)
312             {
313                 if (targetEndpoint == TextPatternRangeEndpoint.Start)
314                     return this.Compare(this.start,other.start);
315                 if(targetEndpoint == TextPatternRangeEndpoint.End)
316                     return this.Compare(this.start,other.end);
317             }
318             if (endpoint == TextPatternRangeEndpoint.End)
319             {
320                 if (targetEndpoint == TextPatternRangeEndpoint.Start)
321                     return this.Compare(this.start, other.end);
322                 if (targetEndpoint == TextPatternRangeEndpoint.End)
323                     return this.Compare(this.end, other.end);
324             }
325             throw new ArgumentException("endpointに未知の値が指定されました");
326         }
327
328         int Compare(int self, int other)
329         {
330             if (self < other)
331                 return -1;
332             else if (self > other)
333                 return 1;
334             else
335                 return 0;
336         }
337
338         public void ExpandToEnclosingUnit(TextUnit unit)
339         {
340             if (unit == TextUnit.Character)
341             {
342                 if (this.start == this.end)
343                 {
344                     if (this.start < this.document.Length)
345                         this.end += 1;
346                 }
347                 return;
348             }
349             if (unit == TextUnit.Format || unit == TextUnit.Word || unit == TextUnit.Line)
350             {
351                 LineToIndexTable layoutLineCollection = this.textbox.LayoutLineCollection;
352                 int row = layoutLineCollection.GetLineNumberFromIndex(this.start);
353                 this.start = layoutLineCollection.GetIndexFromLineNumber(row);
354                 this.end = this.start + layoutLineCollection.GetLengthFromLineNumber(row);
355                 return;
356             }
357             if (unit == TextUnit.Paragraph || unit == TextUnit.Page || unit == TextUnit.Document)
358             {
359                 this.start = 0;
360                 this.end = this.document.Length;
361                 return;
362             }
363             throw new NotImplementedException();
364         }
365
366         public ITextRangeProvider FindAttribute(int attribute, Object value, bool backward)
367         {
368             return null;
369         }
370
371         public ITextRangeProvider FindText(String text, bool backward, bool ignoreCase)
372         {
373             if (backward)
374                 throw new NotImplementedException();
375             document.SetFindParam(text, false, ignoreCase ? RegexOptions.IgnoreCase : RegexOptions.None);
376             IEnumerator<SearchResult> it = document.Find();
377             if (it.MoveNext())
378             {
379                 SearchResult sr = it.Current;
380                 return new FooTextBoxRangeProvider(this.textbox, sr.Start, sr.End - sr.Start + 1, _peer);
381             }
382             return null;
383         }
384
385         public Object GetAttributeValue(int attribute)
386         {
387             return null;
388         }
389
390 #if METRO
391         public void GetBoundingRectangles(out double[] rectangles)
392 #endif
393 #if WPF
394         public double[] GetBoundingRectangles()
395 #endif
396         {
397             LineToIndexTable layoutLineCollection = this.textbox.LayoutLineCollection;
398             TextPoint topLeft = layoutLineCollection.GetTextPointFromIndex(this.start);
399             TextPoint bottomRight = this.textbox.LayoutLineCollection.GetTextPointFromIndex(IsNewLine(this.end) ? this.end - 1 : this.end);
400
401
402 #if METRO
403             Windows.Foundation.Point topLeftPos = this.textbox.GetPostionFromTextPoint(topLeft);
404             Windows.Foundation.Point bottomRightPos = this.textbox.GetPostionFromTextPoint(bottomRight);
405             topLeftPos = Util.GetScreentPoint(topLeftPos, this.textbox);
406             bottomRightPos = Util.GetScreentPoint(bottomRightPos, this.textbox);
407 #endif
408 #if WPF
409             System.Windows.Point topLeftPos = this.textbox.GetPostionFromTextPoint(topLeft);
410             System.Windows.Point bottomRightPos = this.textbox.GetPostionFromTextPoint(bottomRight);
411             topLeftPos = this.textbox.PointToScreen(topLeftPos);
412             bottomRightPos = this.textbox.PointToScreen(bottomRightPos);
413 #endif
414
415             double width = bottomRightPos.X - topLeftPos.X;
416             if (width == 0)
417                 width = 1;
418             Rectangle rect = new Rectangle(topLeftPos.X, topLeftPos.Y,
419                  width,
420                  bottomRightPos.Y - topLeftPos.Y + layoutLineCollection.GetLayout(bottomRight.row).Height);
421
422 #if METRO
423             rectangles = new double[4]{
424                 rect.X,
425                 rect.Y,
426                 rect.Width,
427                 rect.Height
428             };
429 #endif
430 #if WPF
431             return new double[4]{
432                 rect.X,
433                 rect.Y,
434                 rect.Width,
435                 rect.Height
436             };
437 #endif
438         }
439
440         bool IsNewLine(int index)
441         {
442             if (this.document.Length > 0)
443                 return this.document[index == 0 ? 0 : index - 1] == Document.NewLine;
444             else
445                 return false;
446         }
447
448         public IRawElementProviderSimple[] GetChildren()
449         {
450             return new IRawElementProviderSimple[0];
451         }
452
453         public IRawElementProviderSimple GetEnclosingElement()
454         {
455             return _peer.GetRawElementProviderSimple();
456         }
457
458         public String GetText(int maxLength)
459         {
460             if (this.document.Length == 0)
461                 return "";
462             int length = this.end - this.start;
463             if (maxLength < 0)
464                 return this.document.ToString(this.start, length);
465             else
466                 return this.document.ToString(this.start, (int)Math.Min(length, maxLength));
467         }
468
469         public int Move(TextUnit unit, int count)
470         {
471             if (count == 0)
472                 return 0;
473             switch (unit)
474             {
475                 case TextUnit.Character:
476                     {
477                         int moved;
478                         this.start = this.MoveEndpointByCharacter(this.start,count,out moved);
479                         this.end = this.start + 1;
480                         return moved;
481                     }
482                 case TextUnit.Format:
483                 case TextUnit.Word:
484                 case TextUnit.Line:
485                     {
486                         return this.MoveEndPointByLine(this.start, count, out this.start, out this.end);
487                     }
488                 case TextUnit.Paragraph:
489                 case TextUnit.Page:
490                 case TextUnit.Document:
491                     {
492                         this.start = 0;
493                         this.end = this.document.Length;
494                         return 1;
495                     }
496             }
497             throw new NotImplementedException();
498         }
499
500         public void MoveEndpointByRange(TextPatternRangeEndpoint endpoint, ITextRangeProvider targetRange, TextPatternRangeEndpoint targetEndpoint)
501         {
502             FooTextBoxRangeProvider other = targetRange as FooTextBoxRangeProvider;
503
504             if (other == null)
505                 throw new ArgumentException("");
506
507             if (endpoint == TextPatternRangeEndpoint.Start)
508             {
509                 if (targetEndpoint == TextPatternRangeEndpoint.Start)
510                     this.start = other.start;
511                 if (targetEndpoint == TextPatternRangeEndpoint.End)
512                     this.start = other.end;
513                 if (this.start > this.end)
514                     this.end = this.start;
515                 return;
516             }
517             if (endpoint == TextPatternRangeEndpoint.End)
518             {
519                 if (targetEndpoint == TextPatternRangeEndpoint.Start)
520                     this.end = other.start;
521                 if (targetEndpoint == TextPatternRangeEndpoint.End)
522                     this.end = other.end;
523                 return;
524             }
525             throw new ArgumentException("endpointに未知の値が指定されました");
526         }
527
528         public int MoveEndpointByUnit(TextPatternRangeEndpoint endpoint, TextUnit unit, int count)
529         {
530             if (count == 0)
531                 return 0;
532             int moved = 0;
533             if (unit == TextUnit.Character)
534             {
535                 if (endpoint == TextPatternRangeEndpoint.Start)
536                 {
537                     this.start = this.MoveEndpointByCharacter(this.start, count, out moved);
538                     if(this.start > this.end)
539                         this.end = this.start;
540                 }
541                 else if (endpoint == TextPatternRangeEndpoint.End)
542                 {
543                     this.end = this.MoveEndpointByCharacter(this.end, count, out moved);
544                     if (this.end < this.start)
545                         this.start = this.end;
546                 }
547             }
548             if (unit == TextUnit.Format || unit == TextUnit.Word || unit == TextUnit.Line)
549             {
550                 if (endpoint == TextPatternRangeEndpoint.Start)
551                     return this.MoveEndPointByLine(this.start, count, out this.start, out this.end);
552                 else if (endpoint == TextPatternRangeEndpoint.End)
553                     return this.MoveEndPointByLine(this.end, count, out this.start, out this.end);
554             }
555             if (unit == TextUnit.Paragraph || unit == TextUnit.Page || unit == TextUnit.Document)
556             {
557                 this.start = 0;
558                 this.end = this.document.Length - 1;
559                 moved = 1;
560             }
561             return moved;
562         }
563
564         int MoveEndpointByCharacter(int index, int count,out int moved)
565         {
566             int newIndex = index + count - 1;
567             if (newIndex > this.document.Length)
568                 newIndex = this.document.Length;
569             if (newIndex < 0)
570                 newIndex = 0;
571             moved = newIndex - index;
572             return newIndex;
573         }
574
575         int MoveEndPointByLine(int index, int count,out int newStartInex,out int newEndInex)
576         {
577             LineToIndexTable layoutLineCollection = this.textbox.LayoutLineCollection;
578             Controller controller = this.textbox.Controller;
579             TextPoint currentPoint = layoutLineCollection.GetTextPointFromIndex(index);
580             TextPoint newPoint = controller.GetTextPointAfterMoveLine(count, currentPoint);
581             int lineLength = layoutLineCollection.GetLengthFromLineNumber(newPoint.row);
582             newStartInex = layoutLineCollection.GetIndexFromLineNumber(newPoint.row);
583             newEndInex = newStartInex + lineLength;
584             return newPoint.row - currentPoint.row;
585         }
586
587         public void RemoveFromSelection()
588         {
589         }
590
591         public void ScrollIntoView(bool alignToTop)
592         {
593         }
594
595         public void Select()
596         {
597             this.textbox.Select(this.start, this.end - this.start + 1);
598         }
599     }
600 }