/*
* Copyright (C) 2013 FooProject
* * 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
* the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see .
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FooEditEngine
{
///
/// 新しく作成されるフォールティングアイテムを表す
///
public class FoldingItem : IRangeProvider
{
///
/// 開始インデックス
///
public int Start
{
get
{
return this.Range.From;
}
}
///
/// 終了インデックス
///
public int End
{
get
{
return this.Range.To;
}
}
///
/// 展開されているなら真。そうでないなら偽
///
public bool Expand
{
get;
internal set;
}
///
/// 内部で使用しているメンバーです。外部から参照しないでください
///
public Range Range
{
get;
set;
}
///
/// コンストラクター
///
/// 開始インデックス
/// 終了インデックス
/// 展開フラグ
public FoldingItem(int start, int end,bool expand = true)
{
if (start >= end)
throw new ArgumentException("start < endである必要があります");
this.Range = new Range(start, end);
this.Expand = expand;
}
internal bool IsFirstLine(LineToIndexTable layoutLines, int row)
{
int firstRow = layoutLines.GetLineNumberFromIndex(this.Start);
return row == firstRow;
}
}
sealed class RangeItemComparer : IComparer
{
public int Compare(FoldingItem x, FoldingItem y)
{
return x.Range.CompareTo(y.Range);
}
}
///
/// イベントデーター
///
public sealed class FoldingItemStatusChangedEventArgs : EventArgs
{
///
/// 状態に変化があったアイテム
///
public FoldingItem Item;
///
/// コンストラクター
///
/// FoldingItemオブジェクト
public FoldingItemStatusChangedEventArgs(FoldingItem item)
{
this.Item = item;
}
}
///
/// 折り畳み関係のコレクションを表す
///
public sealed class FoldingCollection : IEnumerable
{
RangeTree collection = new RangeTree(new RangeItemComparer());
internal FoldingCollection()
{
this.collection.AutoRebuild = false;
this.StatusChanged += (s, e) => { };
}
internal void UpdateData(Document doc,int startIndex,int insertLength,int removeLength)
{
if (this.collection.Count == 0)
return;
int delta = insertLength - removeLength;
foreach (FoldingItem item in this.collection.Items)
{
int endIndex = startIndex + removeLength - 1;
if (startIndex <= item.Start)
{
if ((endIndex >= item.Start && endIndex <= item.End) || endIndex > item.End)
item.Range = new Range(item.Start, item.Start); //ここで削除すると例外が発生する
else
item.Range = new Range(item.Start + delta, item.End + delta);
}
else if (startIndex >= item.Start && startIndex <= item.End)
{
if (endIndex > item.End)
item.Range = new Range(item.Start, item.Start); //ここで削除すると例外が発生する
else
item.Range = new Range(item.Start, item.End + delta);
}
}
this.collection.Rebuild();
}
internal void CollectEmptyFolding(int startIndex,int endIndex)
{
foreach (FoldingItem foldingData in this.GetRange(startIndex, endIndex - startIndex + 1))
if (foldingData.Start == foldingData.End)
this.Remove(foldingData);
}
///
/// 状態が変わったことを表す
///
public event EventHandler StatusChanged;
///
/// 折り畳みを追加する
///
/// FoldingItemオブジェクト
public void Add(FoldingItem data)
{
foreach (FoldingItem item in this.collection.Items)
{
if (item.Start == data.Start && item.End == data.End)
return;
}
this.collection.Add(data);
}
///
/// 折り畳みを追加する
///
/// FoldingItemのコレクション
public void AddRange(IEnumerable collection)
{
foreach (FoldingItem data in collection)
{
this.Add(data);
}
}
///
/// 折り畳みを削除する
///
/// FoldingItemオブジェクト
public void Remove(FoldingItem data)
{
this.collection.Remove(data);
}
///
/// 指定した範囲の折り畳みを取得する
///
/// 開始インデックス
/// 長さ
/// FoldingItemイテレーター
public IEnumerable GetRange(int index, int length)
{
if (this.collection.Count == 0)
yield break;
this.collection.Rebuild();
List items = this.collection.Query(new Range(index, index + length - 1));
foreach (FoldingItem item in items)
yield return item;
}
///
/// 指定した範囲に最も近い折り畳みを取得する
///
/// 開始インデックス
/// 長さ
/// FoldingItemオブジェクト
public FoldingItem Get(int index, int length)
{
if (this.collection.Count == 0)
return null;
this.collection.Rebuild();
List items = this.collection.Query(new Range(index, index + length - 1));
int minLength = Int32.MaxValue;
FoldingItem minItem = null;
foreach (FoldingItem item in items)
if (index - item.Start < minLength)
minItem = item;
return minItem;
}
///
/// すべて削除する
///
public void Clear()
{
this.collection.Clear();
}
///
/// 展開状態を一括で変更する
///
///
public void ApplyExpandStatus(IEnumerable items)
{
foreach(var item in items)
{
var target_items = from i in this where i.Start == item.Start && i.End == item.End select i;
foreach (var target_item in target_items)
target_item.Expand = item.Expand;
}
}
///
/// 展開する
///
/// foldingItemオブジェクト
/// 親ノードも含めてすべて展開されます
public void Expand(FoldingItem foldingData)
{
if (this.collection.Count == 0)
return;
this.collection.Rebuild();
List items = this.collection.Query(foldingData.Range);
foreach (FoldingItem item in items)
item.Expand = true;
this.StatusChanged(this, new FoldingItemStatusChangedEventArgs(foldingData));
}
///
/// 折りたたむ
///
/// foldingItemオブジェクト
/// 全ての子ノードは折りたたまれます
public void Collapse(FoldingItem foldingData)
{
if (foldingData == null)
return;
this.collection.Rebuild();
List items = this.collection.Query(foldingData.Range);
foldingData.Expand = false;
foreach (FoldingItem item in items)
if (item.Start > foldingData.Start && item.End <= foldingData.End)
item.Expand = false;
this.StatusChanged(this, new FoldingItemStatusChangedEventArgs(foldingData));
}
///
/// インデックスを含むノードが折りたたまれているかを判定する
///
/// インデックス
/// 折りたたまれていれば真を返す。そうでない場合・ノードが存在しない場合は偽を返す
public bool IsHidden(int index)
{
this.collection.Rebuild();
List items = this.collection.Query(index);
if (items.Count == 0)
return false;
int hiddenCount = items.Count((item) =>{
return !item.Expand && index > item.Start && index <= item.End;
});
return hiddenCount > 0;
}
///
/// 親ノードが隠されているかどうかを判定する
///
/// 判定したいノード
/// 隠されていれば真を返す
public bool IsParentHidden(FoldingItem foldingItem)
{
if (foldingItem == null)
return false;
this.collection.Rebuild();
List items = this.collection.Query(foldingItem.Range);
if (items.Count == 0)
return false;
int hiddenCount = items.Count((item) =>
{
//自分自身ノードか
if (foldingItem.Range.Equals(item.Range))
return false;
//ノードが親かつ隠されているかどうか
return !item.Expand && item.Start < foldingItem.Start && item.End > foldingItem.End;
});
return hiddenCount > 0;
}
///
/// 親を持っているか判定する
///
/// 判定したいノード
/// 親を持っていれば真を返す
public bool IsHasParent(FoldingItem foldingItem)
{
if (foldingItem == null)
return false;
this.collection.Rebuild();
List items = this.collection.Query(foldingItem.Range);
if (items.Count == 0 || items.Count == 1)
return false;
int parentItemCount = items.Count((item) => item.Start < foldingItem.Start && item.End > foldingItem.End);
return parentItemCount > 0;
}
///
/// 指定した範囲に属する親ノードを取得する
///
/// 開始インデックス
/// 長さ
/// FoldingItemオブジェクト
/// 指定した範囲には属する中で隠された親ノードだけが取得される
public FoldingItem GetFarestHiddenFoldingData(int index, int length)
{
if (this.collection.Count == 0)
return null;
this.collection.Rebuild();
List items = this.collection.Query(new Range(index, index + length - 1));
//もっとも範囲の広いアイテムが親を表す
FoldingItem parentItem = null;
int max = 0;
foreach(FoldingItem item in items)
{
int item_length = item.End -item.Start + 1;
if(item_length > max)
{
max = item_length;
parentItem = item;
}
}
return parentItem;
}
///
/// FlodingItemの列挙子を返す
///
///
public IEnumerator GetEnumerator()
{
foreach (var item in this.collection.Items)
yield return item;
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
}