/*
* 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.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;
}
internal FoldingItem Parent;
///
/// コンストラクター
///
/// 開始インデックス
/// 終了インデックス
public FoldingItem(int start, int end)
{
if (start >= end)
throw new ArgumentException("start < endである必要があります");
this.Range = new Range(start, end);
this.Expand = true;
}
internal bool IsParentHidden()
{
if (this.Parent != null && !this.Parent.Expand)
return true;
else
return false;
}
internal bool IsHidden(int index)
{
if (this.Parent != null && !this.Parent.Expand)
return true;
if (!this.Expand && index > this.Start && index <= this.End)
return true;
return false;
}
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
{
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;
if (item.Parent != null && data.Start < item.Parent.Start && data.End >= item.Parent.End)
continue;
else if (item.Start < data.Start && item.End > data.End)
data.Parent = item;
else if (item.Start > data.Start && item.End <= data.End)
item.Parent = data;
}
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();
}
///
/// 展開する
///
/// foldingItemオブジェクト
/// 親ノードも含めてすべて展開されます
public void Expand(FoldingItem foldingData)
{
while (foldingData != null)
{
foldingData.Expand = true;
if (foldingData.Parent == null || foldingData.Parent.Expand)
break;
else
foldingData = foldingData.Parent;
}
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));
}
///
/// 指定した範囲に属する親ノードを取得する
///
/// 開始インデックス
/// 長さ
/// FoldingItemオブジェクト
/// 指定した範囲には属する中で隠された親ノードだけが取得される
public FoldingItem GetFarestHiddenFoldingData(int index, int length)
{
FoldingItem foldingData = this.Get(index, length);
if (foldingData == null)
return null;
while (foldingData.Parent != null)
{
if (foldingData.Parent.Expand)
break;
else
foldingData = foldingData.Parent;
}
return foldingData;
}
}
}