OSDN Git Service

自動補完を行うかどうか指定できるようにした
[fooeditengine/FooEditEngine.git] / Core / AutoCompleteBoxBase.cs
1 using System;
2 using System.Linq;
3
4 namespace FooEditEngine
5 {
6     /// <summary>
7     /// イベントパラメーター
8     /// </summary>
9     public sealed class ShowingCompleteBoxEventArgs : EventArgs
10     {
11         /// <summary>
12         /// 入力された文字
13         /// </summary>
14         public string KeyChar;
15         /// <summary>
16         /// 入力した単語と一致したコレクションのインデックス。一致しないなら-1をセットする
17         /// </summary>
18         public int foundIndex;
19         /// <summary>
20         /// 入力しようとした単語を設定する
21         /// </summary>
22         public string inputedWord;
23         /// <summary>
24         /// 補完対象のテキストボックス
25         /// </summary>
26         public Document textbox;
27         /// <summary>
28         /// キャレット座標
29         /// </summary>
30         public Point CaretPostion;
31         /// <summary>
32         /// コンストラクター
33         /// </summary>
34         /// <param name="keyChar"></param>
35         /// <param name="textbox"></param>
36         /// <param name="caret_pos"></param>
37         public ShowingCompleteBoxEventArgs(string keyChar, Document textbox, Point caret_pos)
38         {
39             this.inputedWord = null;
40             this.KeyChar = keyChar;
41             this.foundIndex = -1;
42             this.textbox = textbox;
43             this.CaretPostion = caret_pos;
44         }
45     }
46
47     /// <summary>
48     /// イベントパラメーター
49     /// </summary>
50     public sealed class SelectItemEventArgs : EventArgs
51     {
52         /// <summary>
53         /// 補完候補
54         /// </summary>
55         public string word;
56         /// <summary>
57         /// 入力中の単語
58         /// </summary>
59         public string inputing_word;
60         /// <summary>
61         /// 補完対象のテキストボックス
62         /// </summary>
63         public Document textbox;
64         /// <summary>
65         /// コンストラクター
66         /// </summary>
67         /// <param name="word"></param>
68         /// <param name="inputing_word"></param>
69         /// <param name="textbox"></param>
70         public SelectItemEventArgs(string word, string inputing_word, Document textbox)
71         {
72             this.word = word;
73             this.inputing_word = inputing_word;
74             this.textbox = textbox;
75         }
76     }
77
78     /// <summary>
79     /// イベントパンドラーの定義
80     /// </summary>
81     /// <param name="sender"></param>
82     /// <param name="e"></param>
83     public delegate void SelectItemEventHandler(object sender,SelectItemEventArgs e);
84     /// <summary>
85     /// イベントパンドラーの定義
86     /// </summary>
87     /// <param name="sender"></param>
88     /// <param name="e"></param>
89     public delegate void ShowingCompleteBoxEnventHandler(object sender, ShowingCompleteBoxEventArgs e);
90
91     /// <summary>
92     /// 自動補完のベースクラス
93     /// </summary>
94     public class AutoCompleteBoxBase
95     {
96         const int InputLength = 2;  //補完を開始する文字の長さ
97
98         /// <summary>
99         /// 対象となるドキュメント
100         /// </summary>
101         protected Document Document
102         {
103             get;
104             private set;
105         }
106
107         /// <summary>
108         /// コンストラクター
109         /// </summary>
110         /// <param name="document">対象となるDocumentWindow</param>
111         public AutoCompleteBoxBase(Document document)
112         {
113             this.SelectItem = (s, e) => {
114                 string inputing_word = e.inputing_word;
115                 string word = e.word;
116
117                 var doc = e.textbox;
118                 //キャレットは入力された文字の後ろにあるので、一致する分だけ選択して置き換える
119                 int caretIndex = doc.LayoutLines.GetIndexFromTextPoint(e.textbox.CaretPostion);
120                 int start = caretIndex - inputing_word.Length;
121                 if (start < 0)
122                     start = 0;
123                 doc.Replace(start, inputing_word.Length, word);
124                 doc.RequestRedraw();
125             };
126             this.ShowingCompleteBox = (s, e) => {
127                 AutoCompleteBoxBase box = (AutoCompleteBoxBase)s;
128
129                 var doc = e.textbox;
130                 int caretIndex = doc.LayoutLines.GetIndexFromTextPoint(e.textbox.CaretPostion);
131                 int inputingIndex = caretIndex - 1;
132                 if (inputingIndex < 0)
133                     inputingIndex = 0;
134
135                 e.inputedWord = CompleteHelper.GetWord(doc, inputingIndex, box.Operators) + e.KeyChar;
136
137                 if (e.inputedWord == null)
138                     return;
139
140                 for (int i = 0; i < box.Items.Count; i++)
141                 {
142                     CompleteWord item = (CompleteWord)box.Items[i];
143                     if (item.word.StartsWith(e.inputedWord))
144                     {
145                         e.foundIndex = i;
146                         break;
147                     }
148                 }
149             };
150             this.Operators = new char[] { ' ', '\t', Document.NewLine };
151             this.Document = document;
152         }
153
154         internal void ParseInput(string input_text)
155         {
156             if (this.Operators == null ||
157                 input_text == "\r" ||
158                 input_text == "\n" ||
159                 this.ShowingCompleteBox == null ||
160                 (this.IsCloseCompleteBox == false && input_text == "\b"))
161                 return;
162
163             this.OpenCompleteBox(input_text);
164         }
165
166         /// <summary>
167         /// 補完すべき単語が選択されたときに発生するイベント
168         /// </summary>
169         public SelectItemEventHandler SelectItem;
170         /// <summary>
171         /// UI表示前のイベント
172         /// </summary>
173         public ShowingCompleteBoxEnventHandler ShowingCompleteBox;
174
175         /// <summary>
176         /// 区切り文字のリスト
177         /// </summary>
178         public char[] Operators
179         {
180             get;
181             set;
182         }
183
184         /// <summary>
185         /// オートコンプリートの対象となる単語のリスト
186         /// </summary>
187         public virtual CompleteCollection<ICompleteItem> Items
188         {
189             get;
190             set;
191         }
192
193         internal Func<TextPoint,Document, Point> GetPostion;
194
195         /// <summary>
196         /// 自動補完リストが表示されているかどうか
197         /// </summary>
198         protected virtual bool IsCloseCompleteBox
199         {
200             get;
201         }
202
203         /// <summary>
204         /// 自動補完を行うかどうか。行うなら真
205         /// </summary>
206         public bool Enabled
207         {
208             get;
209             set;
210         }
211
212         /// <summary>
213         /// 補完候補の表示要求を処理する
214         /// </summary>
215         /// <param name="ev"></param>
216         protected virtual void RequestShowCompleteBox(ShowingCompleteBoxEventArgs ev)
217         {
218         }
219
220         /// <summary>
221         /// 補完候補の非表示要求を処理する
222         /// </summary>
223         protected virtual void RequestCloseCompleteBox()
224         {
225         }
226
227         /// <summary>
228         /// 補完候補を表示する
229         /// </summary>
230         /// <param name="key_char">入力しようとしていた文字列</param>
231         /// <param name="force">補完候補がなくても表示するなら真。そうでないなら偽</param>
232         public void OpenCompleteBox(string key_char, bool force = false)
233         {
234             if (!this.Enabled)
235                 return;
236
237             if (this.GetPostion == null)
238                 throw new InvalidOperationException("GetPostionがnullです");
239             Point p = this.GetPostion(this.Document.CaretPostion,this.Document);
240
241             ShowingCompleteBoxEventArgs ev = new ShowingCompleteBoxEventArgs(key_char, this.Document, p);
242             ShowingCompleteBox(this, ev);
243
244             bool hasCompleteItem = ev.foundIndex != -1 && ev.inputedWord != null && ev.inputedWord != string.Empty && ev.inputedWord.Length >= InputLength;
245             System.Diagnostics.Debug.WriteLine("hasCompleteItem:{0}", hasCompleteItem);
246             if (force || hasCompleteItem)
247             {
248                 RequestShowCompleteBox(ev);
249             }
250             else
251             {
252                 RequestCloseCompleteBox();
253             }
254         }
255
256     }
257 }