OSDN Git Service

矩形編集中にIMEで入力後に確定するとキャレット位置がおかしくなってしまう点を修正した
[fooeditengine/FooEditEngine.git] / Common / FoldingCollection.cs
1 /*\r
2  * Copyright (C) 2013 FooProject\r
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\r
4  * the Free Software Foundation; either version 3 of the License, or (at your option) any later version.\r
5 \r
6  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of \r
7  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\r
8 \r
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/>.\r
10  */\r
11 using System;\r
12 using System.Collections.Generic;\r
13 using System.Linq;\r
14 using System.Text;\r
15 using System.Threading.Tasks;\r
16 \r
17 namespace FooEditEngine\r
18 {\r
19     /// <summary>\r
20     /// 新しく作成されるフォールティングアイテムを表す\r
21     /// </summary>\r
22     public class FoldingItem : IRangeProvider<int>\r
23     {\r
24         /// <summary>\r
25         /// 開始インデックス\r
26         /// </summary>\r
27         public int Start\r
28         {\r
29             get\r
30             {\r
31                 return this.Range.From;\r
32             }\r
33         }\r
34 \r
35         /// <summary>\r
36         /// 終了インデックス\r
37         /// </summary>\r
38         public int End\r
39         {\r
40             get\r
41             {\r
42                 return this.Range.To;\r
43             }\r
44         }\r
45 \r
46         /// <summary>\r
47         /// 展開されているなら真。そうでないなら偽\r
48         /// </summary>\r
49         public bool Expand\r
50         {\r
51             get;\r
52             internal set;\r
53         }\r
54 \r
55         /// <summary>\r
56         /// 内部で使用しているメンバーです。外部から参照しないでください\r
57         /// </summary>\r
58         public Range<int> Range\r
59         {\r
60             get;\r
61             set;\r
62         }\r
63 \r
64         /// <summary>\r
65         /// コンストラクター\r
66         /// </summary>\r
67         /// <param name="start">開始インデックス</param>\r
68         /// <param name="end">終了インデックス</param>\r
69         /// <param name="expand">展開フラグ</param>\r
70         public FoldingItem(int start, int end,bool expand = true)\r
71         {\r
72             if (start >= end)\r
73                 throw new ArgumentException("start < endである必要があります");\r
74             this.Range = new Range<int>(start, end);\r
75             this.Expand = expand;\r
76         }\r
77 \r
78         internal bool IsFirstLine(LineToIndexTable layoutLines, int row)\r
79         {\r
80             int firstRow = layoutLines.GetLineNumberFromIndex(this.Start);\r
81             return row == firstRow;\r
82         }\r
83     }\r
84 \r
85     sealed class RangeItemComparer : IComparer<FoldingItem>\r
86     {\r
87         public int Compare(FoldingItem x, FoldingItem y)\r
88         {\r
89             return x.Range.CompareTo(y.Range);\r
90         }\r
91     }\r
92 \r
93     /// <summary>\r
94     /// イベントデーター\r
95     /// </summary>\r
96     public sealed class FoldingItemStatusChangedEventArgs : EventArgs\r
97     {\r
98         /// <summary>\r
99         /// 状態に変化があったアイテム\r
100         /// </summary>\r
101         public FoldingItem Item;\r
102         /// <summary>\r
103         /// コンストラクター\r
104         /// </summary>\r
105         /// <param name="item">FoldingItemオブジェクト</param>\r
106         public FoldingItemStatusChangedEventArgs(FoldingItem item)\r
107         {\r
108             this.Item = item;\r
109         }\r
110     }\r
111 \r
112     /// <summary>\r
113     /// 折り畳み関係のコレクションを表す\r
114     /// </summary>\r
115     public sealed class FoldingCollection\r
116     {\r
117         RangeTree<int, FoldingItem> collection = new RangeTree<int, FoldingItem>(new RangeItemComparer());\r
118 \r
119         internal FoldingCollection()\r
120         {\r
121             this.collection.AutoRebuild = false;\r
122             this.StatusChanged += (s, e) => { };\r
123         }\r
124 \r
125         internal void UpdateData(Document doc,int startIndex,int insertLength,int removeLength)\r
126         {\r
127             if (this.collection.Count == 0)\r
128                 return;\r
129             int delta = insertLength - removeLength;\r
130             foreach (FoldingItem item in this.collection.Items)\r
131             {\r
132                 int endIndex = startIndex + removeLength - 1;\r
133                 if (startIndex <= item.Start)\r
134                 {\r
135                     if ((endIndex >= item.Start && endIndex <= item.End) || endIndex > item.End)\r
136                         item.Range = new Range<int>(item.Start, item.Start);    //ここで削除すると例外が発生する\r
137                     else\r
138                         item.Range = new Range<int>(item.Start + delta, item.End + delta);\r
139                 }\r
140                 else if (startIndex >= item.Start && startIndex <= item.End)\r
141                 {\r
142                     if (endIndex > item.End)\r
143                         item.Range = new Range<int>(item.Start, item.Start);    //ここで削除すると例外が発生する\r
144                     else\r
145                         item.Range = new Range<int>(item.Start, item.End + delta);\r
146                 }\r
147             }\r
148             this.collection.Rebuild();\r
149         }\r
150 \r
151         internal void CollectEmptyFolding(int startIndex,int endIndex)\r
152         {\r
153             foreach (FoldingItem foldingData in this.GetRange(startIndex, endIndex - startIndex + 1))\r
154                 if (foldingData.Start == foldingData.End)\r
155                     this.Remove(foldingData);\r
156         }\r
157 \r
158         /// <summary>\r
159         /// 状態が変わったことを表す\r
160         /// </summary>\r
161         public event EventHandler<FoldingItemStatusChangedEventArgs> StatusChanged;\r
162 \r
163         /// <summary>\r
164         /// 折り畳みを追加する\r
165         /// </summary>\r
166         /// <param name="data">FoldingItemオブジェクト</param>\r
167         public void Add(FoldingItem data)\r
168         {\r
169             foreach (FoldingItem item in this.collection.Items)\r
170             {\r
171                 if (item.Start == data.Start && item.End == data.End)\r
172                     return;\r
173             }\r
174             this.collection.Add(data);\r
175         }\r
176 \r
177         /// <summary>\r
178         /// 折り畳みを追加する\r
179         /// </summary>\r
180         /// <param name="collection">FoldingItemのコレクション</param>\r
181         public void AddRange(IEnumerable<FoldingItem> collection)\r
182         {\r
183             foreach (FoldingItem data in collection)\r
184             {\r
185                 this.Add(data);\r
186             }\r
187         }\r
188         \r
189         /// <summary>\r
190         /// 折り畳みを削除する\r
191         /// </summary>\r
192         /// <param name="data">FoldingItemオブジェクト</param>\r
193         public void Remove(FoldingItem data)\r
194         {\r
195             this.collection.Remove(data);\r
196         }\r
197 \r
198         /// <summary>\r
199         /// 指定した範囲の折り畳みを取得する\r
200         /// </summary>\r
201         /// <param name="index">開始インデックス</param>\r
202         /// <param name="length">長さ</param>\r
203         /// <returns>FoldingItemイテレーター</returns>\r
204         public IEnumerable<FoldingItem> GetRange(int index, int length)\r
205         {\r
206             if (this.collection.Count == 0)\r
207                 yield break;\r
208 \r
209             this.collection.Rebuild();\r
210 \r
211             List<FoldingItem> items = this.collection.Query(new Range<int>(index, index + length - 1));\r
212             foreach (FoldingItem item in items)\r
213                 yield return item;\r
214         }\r
215 \r
216         /// <summary>\r
217         /// 指定した範囲に最も近い折り畳みを取得する\r
218         /// </summary>\r
219         /// <param name="index">開始インデックス</param>\r
220         /// <param name="length">長さ</param>\r
221         /// <returns>FoldingItemオブジェクト</returns>\r
222         public FoldingItem Get(int index, int length)\r
223         {\r
224             if (this.collection.Count == 0)\r
225                 return null;\r
226             \r
227             this.collection.Rebuild();\r
228 \r
229             List<FoldingItem> items = this.collection.Query(new Range<int>(index, index + length - 1));\r
230 \r
231             int minLength = Int32.MaxValue;\r
232             FoldingItem minItem = null;\r
233             foreach (FoldingItem item in items)\r
234                 if (index - item.Start < minLength)\r
235                     minItem = item;\r
236             return minItem;\r
237         }\r
238 \r
239         /// <summary>\r
240         /// すべて削除する\r
241         /// </summary>\r
242         public void Clear()\r
243         {\r
244             this.collection.Clear();\r
245         }\r
246 \r
247         /// <summary>\r
248         /// 展開する\r
249         /// </summary>\r
250         /// <param name="foldingData">foldingItemオブジェクト</param>\r
251         /// <remarks>親ノードも含めてすべて展開されます</remarks>\r
252         public void Expand(FoldingItem foldingData)\r
253         {\r
254             if (this.collection.Count == 0)\r
255                 return;\r
256             this.collection.Rebuild();\r
257             List<FoldingItem> items = this.collection.Query(foldingData.Range);\r
258             foreach (FoldingItem item in items)\r
259                 item.Expand = true;\r
260             this.StatusChanged(this, new FoldingItemStatusChangedEventArgs(foldingData));\r
261         }\r
262 \r
263         /// <summary>\r
264         /// 折りたたむ\r
265         /// </summary>\r
266         /// <param name="foldingData">foldingItemオブジェクト</param>\r
267         /// <remarks>全ての子ノードは折りたたまれます</remarks>\r
268         public void Collapse(FoldingItem foldingData)\r
269         {\r
270             if (foldingData == null)\r
271                 return;\r
272             this.collection.Rebuild();\r
273             List<FoldingItem> items = this.collection.Query(foldingData.Range);\r
274             foldingData.Expand = false;\r
275             foreach (FoldingItem item in items)\r
276                 if (item.Start > foldingData.Start && item.End <= foldingData.End)\r
277                     item.Expand = false;\r
278             this.StatusChanged(this, new FoldingItemStatusChangedEventArgs(foldingData));\r
279         }\r
280 \r
281         /// <summary>\r
282         /// インデックスを含むノードが折りたたまれているかを判定する\r
283         /// </summary>\r
284         /// <param name="index">インデックス</param>\r
285         /// <returns>折りたたまれていれば真を返す。そうでない場合・ノードが存在しない場合は偽を返す</returns>\r
286         public bool IsHidden(int index)\r
287         {\r
288             this.collection.Rebuild();\r
289             List<FoldingItem> items = this.collection.Query(index);\r
290             if (items.Count == 0)\r
291                 return false;\r
292             int hiddenCount = items.Count((item) =>{\r
293                 return !item.Expand && index > item.Start && index <= item.End;\r
294             });\r
295             return hiddenCount > 0;\r
296         }\r
297 \r
298         /// <summary>\r
299         /// 親ノードが隠されているかどうかを判定する\r
300         /// </summary>\r
301         /// <param name="foldingItem">判定したいノード</param>\r
302         /// <returns>隠されていれば真を返す</returns>\r
303         public bool IsParentHidden(FoldingItem foldingItem)\r
304         {\r
305             if (foldingItem == null)\r
306                 return false;\r
307             this.collection.Rebuild();\r
308             List<FoldingItem> items = this.collection.Query(foldingItem.Range);\r
309             if (items.Count == 0)\r
310                 return false;\r
311             int hiddenCount = items.Count((item) =>\r
312             {\r
313                 //自分自身ノードか\r
314                 if (foldingItem.Range.Equals(item.Range))\r
315                     return false;\r
316                 //ノードが親かつ隠されているかどうか\r
317                 return !item.Expand && item.Start < foldingItem.Start && item.End > foldingItem.End;\r
318             });\r
319             return hiddenCount > 0;\r
320         }\r
321 \r
322         /// <summary>\r
323         /// 親を持っているか判定する\r
324         /// </summary>\r
325         /// <param name="foldingItem">判定したいノード</param>\r
326         /// <returns>親を持っていれば真を返す</returns>\r
327         public bool IsHasParent(FoldingItem foldingItem)\r
328         {\r
329             if (foldingItem == null)\r
330                 return false;\r
331             this.collection.Rebuild();\r
332             List<FoldingItem> items = this.collection.Query(foldingItem.Range);\r
333             if (items.Count == 0 || items.Count == 1)\r
334                 return false;\r
335             int parentItemCount = items.Count((item) => item.Start < foldingItem.Start && item.End > foldingItem.End);\r
336             return parentItemCount > 0;\r
337         }\r
338 \r
339         /// <summary>\r
340         /// 指定した範囲に属する親ノードを取得する\r
341         /// </summary>\r
342         /// <param name="index">開始インデックス</param>\r
343         /// <param name="length">長さ</param>\r
344         /// <returns>FoldingItemオブジェクト</returns>\r
345         /// <remarks>指定した範囲には属する中で隠された親ノードだけが取得される</remarks>\r
346         public FoldingItem GetFarestHiddenFoldingData(int index, int length)\r
347         {\r
348             if (this.collection.Count == 0)\r
349                 return null;\r
350             this.collection.Rebuild();\r
351             List<FoldingItem> items = this.collection.Query(new Range<int>(index, index + length - 1));\r
352 \r
353             //もっとも範囲の広いアイテムが親を表す\r
354             FoldingItem parentItem = null;\r
355             int max = 0;\r
356             foreach(FoldingItem item in items)\r
357             {\r
358                 int item_length = item.End -item.Start + 1;\r
359                 if(item_length > max)\r
360                 {\r
361                     max = item_length;\r
362                     parentItem = item;\r
363                 }\r
364             }\r
365 \r
366             return parentItem;\r
367         }\r
368     }\r
369 }\r