OSDN Git Service

初コミット
[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         internal FoldingItem Parent;\r
65 \r
66         /// <summary>\r
67         /// コンストラクター\r
68         /// </summary>\r
69         /// <param name="start">開始インデックス</param>\r
70         /// <param name="end">終了インデックス</param>\r
71         public FoldingItem(int start, int end)\r
72         {\r
73             if (start >= end)\r
74                 throw new ArgumentException("start < endである必要があります");\r
75             this.Range = new Range<int>(start, end);\r
76             this.Expand = true;\r
77         }\r
78 \r
79         internal bool IsParentHidden()\r
80         {\r
81             if (this.Parent != null && !this.Parent.Expand)\r
82                 return true;\r
83             else\r
84                 return false;\r
85         }\r
86 \r
87         internal bool IsHidden(int index)\r
88         {\r
89             if (this.Parent != null && !this.Parent.Expand)\r
90                 return true;\r
91             if (!this.Expand && index > this.Start && index <= this.End)\r
92                 return true;\r
93             return false;\r
94         }\r
95 \r
96         internal bool IsFirstLine(LineToIndexTable layoutLines, int row)\r
97         {\r
98             int firstRow = layoutLines.GetLineNumberFromIndex(this.Start);\r
99             return row == firstRow;\r
100         }\r
101     }\r
102 \r
103     sealed class RangeItemComparer : IComparer<FoldingItem>\r
104     {\r
105         public int Compare(FoldingItem x, FoldingItem y)\r
106         {\r
107             return x.Range.CompareTo(y.Range);\r
108         }\r
109     }\r
110 \r
111     /// <summary>\r
112     /// イベントデーター\r
113     /// </summary>\r
114     public sealed class FoldingItemStatusChangedEventArgs : EventArgs\r
115     {\r
116         /// <summary>\r
117         /// 状態に変化があったアイテム\r
118         /// </summary>\r
119         public FoldingItem Item;\r
120         /// <summary>\r
121         /// コンストラクター\r
122         /// </summary>\r
123         /// <param name="item">FoldingItemオブジェクト</param>\r
124         public FoldingItemStatusChangedEventArgs(FoldingItem item)\r
125         {\r
126             this.Item = item;\r
127         }\r
128     }\r
129 \r
130     /// <summary>\r
131     /// 折り畳み関係のコレクションを表す\r
132     /// </summary>\r
133     public sealed class FoldingCollection\r
134     {\r
135         RangeTree<int, FoldingItem> collection = new RangeTree<int, FoldingItem>(new RangeItemComparer());\r
136 \r
137         internal FoldingCollection()\r
138         {\r
139             this.collection.AutoRebuild = false;\r
140             this.StatusChanged += (s, e) => { };\r
141         }\r
142 \r
143         internal void UpdateData(Document doc,int startIndex,int insertLength,int removeLength)\r
144         {\r
145             if (this.collection.Count == 0)\r
146                 return;\r
147             int delta = insertLength - removeLength;\r
148             foreach (FoldingItem item in this.collection.Items)\r
149             {\r
150                 int endIndex = startIndex + removeLength - 1;\r
151                 if (startIndex <= item.Start)\r
152                 {\r
153                     if ((endIndex >= item.Start && endIndex <= item.End) || endIndex > item.End)\r
154                         item.Range = new Range<int>(item.Start, item.Start);    //ここで削除すると例外が発生する\r
155                     else\r
156                         item.Range = new Range<int>(item.Start + delta, item.End + delta);\r
157                 }\r
158                 else if (startIndex >= item.Start && startIndex <= item.End)\r
159                 {\r
160                     if (endIndex > item.End)\r
161                         item.Range = new Range<int>(item.Start, item.Start);    //ここで削除すると例外が発生する\r
162                     else\r
163                         item.Range = new Range<int>(item.Start, item.End + delta);\r
164                 }\r
165             }\r
166             this.collection.Rebuild();\r
167         }\r
168 \r
169         /// <summary>\r
170         /// 状態が変わったことを表す\r
171         /// </summary>\r
172         public event EventHandler<FoldingItemStatusChangedEventArgs> StatusChanged;\r
173 \r
174         /// <summary>\r
175         /// 折り畳みを追加する\r
176         /// </summary>\r
177         /// <param name="data">FoldingItemオブジェクト</param>\r
178         public void Add(FoldingItem data)\r
179         {\r
180             foreach (FoldingItem item in this.collection.Items)\r
181             {\r
182                 if (item.Start == data.Start && item.End == data.End)\r
183                     return;\r
184                 if (item.Parent != null && data.Start < item.Parent.Start && data.End >= item.Parent.End)\r
185                     continue;\r
186                 else if (item.Start < data.Start && item.End > data.End)\r
187                     data.Parent = item;\r
188                 else if (item.Start > data.Start && item.End <= data.End)\r
189                     item.Parent = data;\r
190             }\r
191             this.collection.Add(data);\r
192         }\r
193 \r
194         /// <summary>\r
195         /// 折り畳みを追加する\r
196         /// </summary>\r
197         /// <param name="collection">FoldingItemのコレクション</param>\r
198         public void AddRange(IEnumerable<FoldingItem> collection)\r
199         {\r
200             foreach (FoldingItem data in collection)\r
201             {\r
202                 this.Add(data);\r
203             }\r
204         }\r
205         \r
206         /// <summary>\r
207         /// 折り畳みを削除する\r
208         /// </summary>\r
209         /// <param name="data">FoldingItemオブジェクト</param>\r
210         public void Remove(FoldingItem data)\r
211         {\r
212             this.collection.Remove(data);\r
213         }\r
214 \r
215         /// <summary>\r
216         /// 指定した範囲の折り畳みを取得する\r
217         /// </summary>\r
218         /// <param name="index">開始インデックス</param>\r
219         /// <param name="length">長さ</param>\r
220         /// <returns>FoldingItemイテレーター</returns>\r
221         public IEnumerable<FoldingItem> GetRange(int index, int length)\r
222         {\r
223             if (this.collection.Count == 0)\r
224                 yield break;\r
225 \r
226             this.collection.Rebuild();\r
227 \r
228             List<FoldingItem> items = this.collection.Query(new Range<int>(index, index + length - 1));\r
229             foreach (FoldingItem item in items)\r
230                 yield return item;\r
231         }\r
232 \r
233         /// <summary>\r
234         /// 指定した範囲に最も近い折り畳みを取得する\r
235         /// </summary>\r
236         /// <param name="index">開始インデックス</param>\r
237         /// <param name="length">長さ</param>\r
238         /// <returns>FoldingItemオブジェクト</returns>\r
239         public FoldingItem Get(int index, int length)\r
240         {\r
241             if (this.collection.Count == 0)\r
242                 return null;\r
243             \r
244             this.collection.Rebuild();\r
245 \r
246             List<FoldingItem> items = this.collection.Query(new Range<int>(index, index + length - 1));\r
247 \r
248             int minLength = Int32.MaxValue;\r
249             FoldingItem minItem = null;\r
250             foreach (FoldingItem item in items)\r
251                 if (index - item.Start < minLength)\r
252                     minItem = item;\r
253             return minItem;\r
254         }\r
255 \r
256         /// <summary>\r
257         /// すべて削除する\r
258         /// </summary>\r
259         public void Clear()\r
260         {\r
261             this.collection.Clear();\r
262         }\r
263 \r
264         /// <summary>\r
265         /// 展開する\r
266         /// </summary>\r
267         /// <param name="foldingData">foldingItemオブジェクト</param>\r
268         /// <remarks>親ノードも含めてすべて展開されます</remarks>\r
269         public void Expand(FoldingItem foldingData)\r
270         {\r
271             while (foldingData != null)\r
272             {\r
273                 foldingData.Expand = true;\r
274                 if (foldingData.Parent == null || foldingData.Parent.Expand)\r
275                     break;\r
276                 else\r
277                     foldingData = foldingData.Parent;\r
278             }\r
279             this.StatusChanged(this, new FoldingItemStatusChangedEventArgs(foldingData));\r
280         }\r
281 \r
282         /// <summary>\r
283         /// 折りたたむ\r
284         /// </summary>\r
285         /// <param name="foldingData">foldingItemオブジェクト</param>\r
286         /// <remarks>全ての子ノードは折りたたまれます</remarks>\r
287         public void Collapse(FoldingItem foldingData)\r
288         {\r
289             if (foldingData == null)\r
290                 return;\r
291             this.collection.Rebuild();\r
292             List<FoldingItem> items = this.collection.Query(foldingData.Range);\r
293             foldingData.Expand = false;\r
294             foreach (FoldingItem item in items)\r
295                 if (item.Start > foldingData.Start && item.End <= foldingData.End)\r
296                     item.Expand = false;\r
297             this.StatusChanged(this, new FoldingItemStatusChangedEventArgs(foldingData));\r
298         }\r
299 \r
300         /// <summary>\r
301         /// 指定した範囲に属する親ノードを取得する\r
302         /// </summary>\r
303         /// <param name="index">開始インデックス</param>\r
304         /// <param name="length">長さ</param>\r
305         /// <returns>FoldingItemオブジェクト</returns>\r
306         /// <remarks>指定した範囲には属する中で隠された親ノードだけが取得される</remarks>\r
307         public FoldingItem GetFarestHiddenFoldingData(int index, int length)\r
308         {\r
309             FoldingItem foldingData = this.Get(index, length);\r
310             if (foldingData == null)\r
311                 return null;\r
312             while (foldingData.Parent != null)\r
313             {\r
314                 if (foldingData.Parent.Expand)\r
315                     break;\r
316                 else\r
317                     foldingData = foldingData.Parent;\r
318             }\r
319             return foldingData;\r
320         }\r
321     }\r
322 }\r