OSDN Git Service

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