/// ドキュメント全体が削除されたことを表す
/// </summary>
Clear,
+ /// <summary>
+ /// レイアウトが再構築されたことを表す
+ /// </summary>
+ RebuildLayout,
}
/// <summary>
else
this.buffer = new StringBuffer(doc.buffer);
this.buffer.Update = new DocumentUpdateEventHandler(buffer_Update);
- this.UpdateCalledAlways += (s, e) => { };
this.Update += new DocumentUpdateEventHandler((s, e) => { });
this.ChangeFireUpdateEvent += new EventHandler((s, e) => { });
this.StatusUpdate += new EventHandler((s, e) => { });
this.SelectionChanged += new EventHandler((s, e) => { });
this.CaretChanged += (s, e) => { };
this.AutoIndentHook += (s, e) => { };
+ this.LineBreakChanged += (s, e) => { };
+ this.Dirty = false;
}
void WacthDogPattern_Updated(object sender, EventArgs e)
}
/// <summary>
+ /// ダーティフラグ。保存されていなければ真、そうでなければ偽。
+ /// </summary>
+ public bool Dirty
+ {
+ get;
+ set;
+ }
+
+ /// <summary>
/// キャレットでの選択の起点となる位置
/// </summary>
internal int AnchorIndex
set;
}
+ /// <summary>
+ /// 補完候補プロセッサーが切り替わったときに発生するイベント
+ /// </summary>
+ public event EventHandler AutoCompleteChanged;
+
+ AutoCompleteBoxBase _AutoComplete;
+ /// <summary>
+ /// 補完候補プロセッサー
+ /// </summary>
public AutoCompleteBoxBase AutoComplete
{
- get;
- set;
+ get
+ {
+ return this._AutoComplete;
+ }
+ set
+ {
+ this._AutoComplete = value;
+ if (this.AutoCompleteChanged != null)
+ this.AutoCompleteChanged(this, null);
+ }
}
+ /// <summary>
+ /// 読み込み中に発生するイベント
+ /// </summary>
public event ProgressEventHandler LoadProgress;
/// <summary>
public event DocumentUpdateEventHandler Update;
/// <summary>
- /// ドキュメントが更新された時に呼びされるイベント
- /// </summary>
- /// <remarks>
- /// FireUpdateEventの値に関わらず常に呼びされます
- /// </remarks>
- internal event DocumentUpdateEventHandler UpdateCalledAlways;
-
- /// <summary>
/// FireUpdateEventの値が変わったときに呼び出されるイベント
/// </summary>
public event EventHandler ChangeFireUpdateEvent;
this.IsRequestRedraw = true;
}
+ /// <summary>
+ /// レイアウト行が再構成されたときに発生するイベント
+ /// </summary>
public event EventHandler PerformLayouted;
/// <summary>
/// レイアウト行をすべて破棄し、再度レイアウトを行う
/// </summary>
public void PerformLayout()
{
- //単に再構築するだけなので行ダーティフラグは更新してはいけない
this.LayoutLines.IsFrozneDirtyFlag = true;
- this.FireUpdate(new DocumentUpdateEventArgs(UpdateType.Clear, -1, -1, -1));
- this.FireUpdate(new DocumentUpdateEventArgs(UpdateType.Replace, 0, 0, this.Length));
+ this.FireUpdate(new DocumentUpdateEventArgs(UpdateType.RebuildLayout, -1, -1, -1));
this.LayoutLines.IsFrozneDirtyFlag = false;
if (this.PerformLayouted != null)
this.PerformLayouted(this, null);
throw new InvalidOperationException("");
if (start < 0 || start + length < 0 || start + length > this.Length)
throw new ArgumentOutOfRangeException("startかendが指定できる範囲を超えてます");
+ //選択範囲が消されたとき
+ foreach (Selection sel in this.Selections)
+ this.LayoutLines.ClearLayoutCache(sel.start, sel.length);
this.Selections.Clear();
if (length < 0)
{
TextPoint startTextPoint = this.LayoutLines.GetTextPointFromIndex(start);
TextPoint endTextPoint = this.LayoutLines.GetTextPointFromIndex(start + length);
this.SelectByRectangle(new TextRectangle(startTextPoint, endTextPoint));
+ this.LayoutLines.ClearLayoutCache(start, length);
}
else if (length != 0)
{
this.Selections.Add(Selection.Create(start, length));
+ this.LayoutLines.ClearLayoutCache(start, length);
}
this.SelectionChanged(this, null);
}
/// <param name="changeAnchor">選択の起点となるとインデックスを変更するなら真。そうでなければ偽</param>
public void SelectWord(int index, bool changeAnchor = false)
{
- if (this.FireUpdateEvent == false)
- throw new InvalidOperationException("");
+ this.SelectSepartor(index, (c) => Util.IsWordSeparator(c), changeAnchor);
+ }
+
+ /// <summary>
+ /// 行単位で選択する
+ /// </summary>
+ /// <param name="index">探索を開始するインデックス</param>
+ /// <param name="changeAnchor">選択の起点となるとインデックスを変更するなら真。そうでなければ偽</param>
+ public void SelectLine(int index,bool changeAnchor = false)
+ {
+ this.SelectSepartor(index, (c) => c == Document.NewLine, changeAnchor);
+ }
+
+ /// <summary>
+ /// セパレーターで区切られた領域を取得する
+ /// </summary>
+ /// <param name="index">探索を開始するインデックス</param>
+ /// <param name="find_sep_func">セパレーターなら真を返し、そうでないなら偽を返す</param>
+ /// <returns>開始インデックス、終了インデックス</returns>
+ public Tuple<int,int> GetSepartor(int index, Func<char, bool> find_sep_func)
+ {
+ if (find_sep_func == null)
+ throw new ArgumentNullException("find_sep_func must not be null");
if (this.Length <= 0 || index >= this.Length)
- return;
+ return null;
Document str = this;
int start = index;
- while (start > 0 && !Util.IsWordSeparator(str[start]))
+ while (start > 0 && !find_sep_func(str[start]))
start--;
- if (Util.IsWordSeparator(str[start]))
+ if (find_sep_func(str[start]))
+ {
start++;
+ }
int end = index;
- while (end < this.Length && !Util.IsWordSeparator(str[end]))
+ while (end < this.Length && !find_sep_func(str[end]))
end++;
+ return new Tuple<int, int>(start, end);
+ }
+
+ /// <summary>
+ /// セパレーターで囲まれた範囲内を選択する
+ /// </summary>
+ /// <param name="index">探索を開始するインデックス</param>
+ /// <param name="find_sep_func">セパレーターなら真を返し、そうでないなら偽を返す</param>
+ /// <param name="changeAnchor">選択の起点となるとインデックスを変更するなら真。そうでなければ偽</param>
+ public void SelectSepartor(int index,Func<char,bool> find_sep_func, bool changeAnchor = false)
+ {
+ if (this.FireUpdateEvent == false)
+ throw new InvalidOperationException("");
+
+ if (find_sep_func == null)
+ throw new ArgumentNullException("find_sep_func must not be null");
+
+ var t = this.GetSepartor(index, find_sep_func);
+ if (t == null)
+ return;
+
+ int start = t.Item1, end = t.Item2;
+
this.Select(start, end - start);
if (changeAnchor)
var input_str = string.Empty;
if (s == Document.NewLine.ToString())
input_str = s;
+ else if (s == string.Empty && length > 0)
+ input_str = "\b";
//入力は終わっているので空文字を渡すが処理の都合で一部文字だけはそのまま渡す
if (this.AutoComplete != null)
this.AutoComplete.ParseInput(input_str);
public void Clear()
{
this.buffer.Clear();
+ this.Dirty = false;
}
/// <summary>
try
{
this.Clear();
- this.LayoutLines.IsFrozneDirtyFlag = true;
await this.buffer.LoadAsync(fs, tokenSource);
}
finally
{
this.PerformLayout();
- //これ以降の操作にだけダーティフラグを適用しないとおかしなことになる
- this.LayoutLines.IsFrozneDirtyFlag = false;
if (this.LoadProgress != null)
this.LoadProgress(this, new ProgressEventArgs(ProgressState.Complete));
}
/// <remarks>非同期操作中はこのメソッドを実行することはできません</remarks>
public async Task SaveAsync(TextWriter fs, CancellationTokenSource tokenSource = null)
{
- try
- {
- await this.buffer.LockAsync().ConfigureAwait(false);
- StringBuilder line = new StringBuilder();
- for (int i = 0; i < this.Length; i++)
- {
- char c = this[i];
- line.Append(c);
- if (c == Document.NewLine || i == this.Length - 1)
- {
- string str = line.ToString();
- str = str.Replace(Document.NewLine.ToString(), fs.NewLine);
- await fs.WriteAsync(str).ConfigureAwait(false);
- line.Clear();
- if (tokenSource != null)
- tokenSource.Token.ThrowIfCancellationRequested();
-#if TEST_ASYNC
- System.Threading.Thread.Sleep(10);
-#endif
- }
- }
- }
- finally
- {
- this.buffer.UnLock();
- }
+ await this.buffer.SaveAsync(fs, tokenSource);
}
/// <summary>
{
switch (e.type)
{
+ case UpdateType.RebuildLayout:
+ this._LayoutLines.Clear();
+ this._LayoutLines.UpdateAsReplace(0, 0, this.Length);
+ break;
case UpdateType.Replace:
if (e.row == null)
{
this._LayoutLines.UpdateLineAsReplace(e.row.Value, e.removeLength, e.insertLength);
this.Markers.UpdateMarkers(this.LayoutLines.GetIndexFromLineNumber(e.row.Value), e.insertLength, e.removeLength);
}
+ this.Dirty = true;
break;
case UpdateType.Clear:
this._LayoutLines.Clear();
+ this.Dirty = true;
break;
}
- this.UpdateCalledAlways(this, e);
if(this.FireUpdateEvent)
this.Update(this, e);
}
}
}
- // このコードは、破棄可能なパターンを正しく実装できるように追加されました。
+ /// <summary>
+ /// ドキュメントを破棄する
+ /// </summary>
public void Dispose()
{
Dispose(true);