/*
* 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.Globalization;
using System.Text;
using System.Linq;
using System.Diagnostics;
using System.Text.RegularExpressions;
#if WINFORM
using System.Drawing;
#endif
namespace FooEditEngine
{
internal enum MoveFlow
{
Horizontical,
Vertical,
}
internal enum ScrollDirection
{
Up,
Down,
Left,
Right,
}
///
/// ユーザー側からの処理を担当するクラス。一部を除き、こちらで行われた操作はアンドゥの対象になります
///
internal sealed class Controller
{
EditView View;
Document Document;
int AnchorIndex;
public Controller(Document doc, EditView view)
{
this.Document = doc;
this.Document.Update += new DocumentUpdateEventHandler(Document_Update);
this.View = view;
this.View.render.ChangedRightToLeft += render_ChangedRightToLeft;
this.View.render.ChangedRenderResource += render_ChangedRenderResource;
this.View.PerformLayouted += View_LineBreakChanged;
this.View.PageBoundChanged += View_PageBoundChanged;
this.Document.Clear();
this.SelectionChanged += new EventHandler((s, e) => { });
}
///
/// 選択領域変更時に通知される
///
public event EventHandler SelectionChanged;
///
/// 矩形選択モードなら真を返し、そうでない場合は偽を返す
///
public bool RectSelection
{
get;
set;
}
///
/// 選択範囲の開始位置
///
/// SelectionLengthが0の場合、キャレット位置を表します
public int SelectionStart
{
get
{
if (this.View.Selections.Count == 0)
return this.AnchorIndex;
else
return this.View.Selections.First().start;
}
}
///
/// 選択範囲の長さ
///
/// 矩形選択モードの場合、選択範囲の文字数ではなく、開始位置から終了位置までの長さとなります
public int SelectionLength
{
get
{
if (this.View.Selections.Count == 0)
return 0;
Selection last = this.View.Selections.Last();
return last.start + last.length - this.SelectionStart;
}
}
///
/// 選択範囲内の文字列を返す
///
///
/// 未選択状態で代入したときは追加され、そうでない場合は選択範囲の文字列と置き換えられます。
///
public string SelectedText
{
get
{
if (this.View.LayoutLines.Count == 0 || this.View.Selections.Count == 0)
return null;
if (this.RectSelection)
return GetTextFromRectangleSelectArea(this.View.Selections);
else
return GetTextFromLineSelectArea(this.View.Selections).Replace(Document.NewLine.ToString(), Environment.NewLine);
}
set
{
if (this.Document.FireUpdateEvent == false)
throw new InvalidOperationException("");
if (value == null)
return;
this.RepleaceSelectionArea(this.View.Selections, value.Replace(Environment.NewLine,Document.NewLine.ToString()));
}
}
///
/// 選択範囲が逆転しているかどうかを判定する
///
/// 逆転しているなら真を返す
public bool IsReverseSelect()
{
int index = this.View.LayoutLines.GetIndexFromTextPoint(this.View.CaretPostion);
return index < this.AnchorIndex;
}
///
/// 指定された範囲を選択する
///
///
///
/// RectSelectionの値によって動作が変わります。真の場合は矩形選択モードに、そうでない場合は行ごとに選択されます
public void Select(int start, int length)
{
if (this.Document.FireUpdateEvent == false)
throw new InvalidOperationException("");
if (start < 0 || start + length < 0 || start + length > this.Document.Length)
throw new ArgumentOutOfRangeException("startかendが指定できる範囲を超えてます");
this.View.Selections.Clear();
if (length < 0)
{
int oldStart = start;
start += length;
length = oldStart - start;
}
if (this.RectSelection && length != 0)
{
TextPoint startTextPoint = this.View.GetLayoutLineFromIndex(start);
TextPoint endTextPoint = this.View.GetLayoutLineFromIndex(start + length);
this.SelectByRectangle(new TextRectangle(startTextPoint, endTextPoint));
if (startTextPoint.col == endTextPoint.col)
this.View.InsertPoint = new SelectCollection(this.View.Selections);
else
this.View.InsertPoint = null;
}
else if(length != 0)
{
this.View.Selections.Add(Selection.Create(start, length));
this.View.InsertPoint = null;
}
this.SelectionChanged(this, null);
}
public void Select(TextPoint tp, int width, int height)
{
if (this.Document.FireUpdateEvent == false || !this.RectSelection)
throw new InvalidOperationException("");
TextPoint end = tp;
end.row = tp.row + height;
end.col = tp.col + width;
if (end.row > this.View.LayoutLines.Count - 1)
throw new ArgumentOutOfRangeException("");
this.View.Selections.Clear();
this.SelectByRectangle(new TextRectangle(tp,end));
if (width == 0)
this.View.InsertPoint = new SelectCollection(this.View.Selections);
else
this.View.InsertPoint = null;
this.SelectionChanged(this, null);
}
private void SelectByRectangle(TextRectangle rect)
{
if (this.Document.FireUpdateEvent == false)
throw new InvalidOperationException("");
if (rect.TopLeft <= rect.BottomRight)
{
for (int i = rect.TopLeft.row; i <= rect.BottomLeft.row; i++)
{
int length = this.View.LayoutLines.GetLengthFromLineNumber(i);
int leftCol = rect.TopLeft.col, rightCol = rect.TopRight.col, lastCol = length;
if(length > 0 && this.View.LayoutLines[i][length - 1] == Document.NewLine)
lastCol = length - 1;
if (lastCol < 0)
lastCol = 0;
if (rect.TopLeft.col > lastCol)
leftCol = lastCol;
if (rect.TopRight.col > lastCol)
rightCol = lastCol;
int StartIndex = this.View.LayoutLines.GetIndexFromTextPoint(new TextPoint(i, leftCol));
int EndIndex = this.View.LayoutLines.GetIndexFromTextPoint(new TextPoint(i, rightCol));
Selection sel;
sel = Selection.Create(StartIndex, EndIndex - StartIndex);
this.View.Selections.Add(sel);
}
}
}
///
/// 単語単位で選択する
///
/// 探索を開始するインデックス
public void SelectWord(int index)
{
if (this.Document.FireUpdateEvent == false)
throw new InvalidOperationException("");
if (this.Document.Length <= 0 || index >= this.Document.Length)
return;
Document str = this.Document;
int start = index;
while (start > 0 && !Util.IsWordSeparator(str[start]))
start--;
if (Util.IsWordSeparator(str[start]))
start++;
int end = index;
while (end < this.Document.Length && !Util.IsWordSeparator(str[end]))
end++;
this.Select(start, end - start);
}
///
/// 選択範囲内のUTF32コードポイントを文字列に変換します
///
/// 成功した場合は真。そうでない場合は偽を返す
public bool ConvertToChar()
{
if (this.SelectionLength == 0 || this.RectSelection)
return false;
string str = this.Document.ToString(this.SelectionStart, this.SelectionLength);
string[] codes = str.Split(new char[] { ' ' },StringSplitOptions.RemoveEmptyEntries);
StringBuilder result = new StringBuilder();
foreach (string code in codes)
{
int utf32_code;
if (code[0] != 'U')
return false;
if (Int32.TryParse(code.TrimStart('U'),NumberStyles.HexNumber,null, out utf32_code))
result.Append(Char.ConvertFromUtf32(utf32_code));
else
return false;
}
this.Document.Replace(this.SelectionStart, this.SelectionLength, result.ToString());
return true;
}
///
/// 選択文字列をUTF32のコードポイントに変換します
///
public void ConvertToCodePoint()
{
if (this.SelectionLength == 0 || this.RectSelection)
return;
string str = this.Document.ToString(this.SelectionStart, this.SelectionLength);
StringInfo info = new StringInfo(str);
StringBuilder result = new StringBuilder();
for (int i = 0; i < str.Length;)
{
int utf32_code = Char.ConvertToUtf32(str, i);
result.Append("U" + Convert.ToString(utf32_code,16));
result.Append(' ');
if(Char.IsHighSurrogate(str[i]))
i += 2;
else
i++;
}
this.Document.Replace(this.SelectionStart, this.SelectionLength, result.ToString());
}
///
/// 選択を解除する
///
public void DeSelectAll()
{
if (this.Document.FireUpdateEvent == false)
throw new InvalidOperationException("");
this.View.Selections.Clear();
}
///
/// 任意のマーカーかどうか
///
///
///
/// 真ならマーカーがある
public bool IsMarker(TextPoint tp,HilightType type)
{
if (this.Document.FireUpdateEvent == false)
throw new InvalidOperationException("");
int index = this.View.LayoutLines.GetIndexFromTextPoint(tp);
return this.IsMarker(index, type);
}
///
/// 任意のマーカーかどうか判定する
///
///
///
/// 真ならマーカーがある
public bool IsMarker(int index, HilightType type)
{
foreach(int id in this.Document.Markers.IDs)
{
foreach (Marker m in this.Document.GetMarkers(index, id))
{
if (m.hilight == type)
return true;
}
}
return false;
}
///
/// キャレット位置を再調整する
///
public void AdjustCaret()
{
int row = this.View.CaretPostion.row;
if (row > this.View.LayoutLines.Count - 1)
row = this.View.LayoutLines.Count - 1;
int col = this.View.CaretPostion.col;
if (col > 0 && col > this.View.LayoutLines[row].Length)
col = this.View.LayoutLines[row].Length;
this.JumpCaret(row, col);
}
///
/// キャレットを指定した位置に移動させる
///
///
/// 折り畳みを展開するなら真
public void JumpCaret(int index,bool autoExpand = true)
{
if (index < 0 || index > this.Document.Length)
throw new ArgumentOutOfRangeException("indexが設定できる範囲を超えています");
TextPoint tp = this.View.GetLayoutLineFromIndex(index);
this.JumpCaret(tp.row, tp.col,autoExpand);
}
///
/// キャレットを指定した位置に移動させる
///
///
///
/// 折り畳みを展開するなら真
public void JumpCaret(int row, int col, bool autoExpand = true)
{
if (this.Document.FireUpdateEvent == false)
throw new InvalidOperationException("");
this.View.JumpCaret(row, col,autoExpand);
this.View.AdjustCaretAndSrc();
this.SelectWithMoveCaret(false);
}
///
/// ドキュメントの先頭に移動する
///
///
public void JumpToHead(bool isSelected)
{
if (this.View.TryScroll(0, 0))
return;
this.View.JumpCaret(0, 0);
this.View.AdjustCaretAndSrc();
this.SelectWithMoveCaret(isSelected);
}
///
/// ドキュメントの終わりにに移動する
///
///
public void JumpToEnd(bool isSelected)
{
int srcRow = this.View.LayoutLines.Count - this.View.LineCountOnScreen - 1;
if(srcRow < 0)
srcRow = 0;
if (this.View.TryScroll(0, srcRow))
return;
this.View.JumpCaret(this.View.LayoutLines.Count - 1, 0);
this.View.AdjustCaretAndSrc();
this.SelectWithMoveCaret(isSelected);
}
///
/// スクロールする
///
/// 方向を指定する
/// スクロールする量。ScrollDirectionの値がUpやDownなら行数。LeftやRightならピクセル単位の値となる
/// 選択状態にするなら真
/// 同時にキャレットを移動させるなら真
public void Scroll(ScrollDirection dir, int delta, bool isSelected,bool withCaret)
{
if (this.Document.FireUpdateEvent == false)
throw new InvalidOperationException("");
int toRow = this.View.Src.Row;
double toX = this.View.Src.X;
switch (dir)
{
case ScrollDirection.Up:
toRow = Math.Max(0, this.View.Src.Row - delta);
toRow = this.View.AdjustRow(toRow, false);
break;
case ScrollDirection.Down:
toRow = Math.Min(this.View.Src.Row + delta, this.View.LayoutLines.Count - 1);
toRow = this.View.AdjustRow(toRow, true);
break;
case ScrollDirection.Left:
toX -= delta;
break;
case ScrollDirection.Right:
toX += delta;
break;
default:
throw new ArgumentOutOfRangeException();
}
this.Scroll(toX, toRow, isSelected, withCaret);
}
///
/// スクロールする
///
/// スクロール先の座標
/// スクロール先の行
/// 選択状態にするなら真
/// 同時にキャレットを移動させるなら真
public void Scroll(double toX, int toRow, bool isSelected, bool withCaret)
{
if (withCaret)
{
this.View.Scroll(toX, toRow);
this.View.JumpCaret(toRow, 0);
this.View.AdjustCaretAndSrc();
this.SelectWithMoveCaret(isSelected);
}
else
{
this.View.Scroll(toX, toRow);
this.View.IsFocused = false;
}
}
///
/// キャレットを桁方向に移動させる
///
/// 移動できない場合は真を返す
/// 負の値なら左側へ、そうでないなら右側へ移動する
/// 選択範囲とするなら真。そうでないなら偽
/// 単語単位で移動するなら真。そうでないなら偽
public void MoveCaretHorizontical(int realLength, bool isSelected,bool alignWord = false)
{
for (int i = Math.Abs(realLength); i > 0; i--)
{
bool MoveFlow = realLength > 0;
if (this.View.render.RightToLeft)
MoveFlow = !MoveFlow;
this.MoveCaretHorizontical(MoveFlow);
if (alignWord)
this.AlignNearestWord(MoveFlow);
}
this.View.AdjustCaretAndSrc(AdjustFlow.Col);
this.SelectWithMoveCaret(isSelected);
}
void AlignNearestWord(bool MoveFlow)
{
string str = this.View.LayoutLines[this.View.CaretPostion.row];
while (this.View.CaretPostion.col > 0 &&
this.View.CaretPostion.col < str.Length &&
str[this.View.CaretPostion.col] != Document.NewLine)
{
if (!Util.IsWordSeparator(str[this.View.CaretPostion.col]))
{
this.MoveCaretHorizontical(MoveFlow);
}
else
{
if(MoveFlow)
this.MoveCaretHorizontical(MoveFlow);
break;
}
}
}
///
/// キャレットを行方向に移動させる
///
/// 再描写する必要があるなら真を返す
/// 移動量
///
public void MoveCaretVertical(int deltarow,bool isSelected)
{
for (int i = Math.Abs(deltarow); i > 0; i--)
this.MoveCaretVertical(deltarow > 0);
this.View.AdjustCaretAndSrc(AdjustFlow.Both);
this.SelectWithMoveCaret(isSelected);
}
///
/// キャレット位置の文字を一文字削除する
///
public void DoDeleteAction()
{
if (this.SelectionLength != 0)
{
this.SelectedText = "";
return;
}
if (this.Document.FireUpdateEvent == false)
throw new InvalidOperationException("");
TextPoint CaretPostion = this.View.CaretPostion;
int index = this.View.GetIndexFromLayoutLine(CaretPostion);
if (index == this.Document.Length)
return;
int lineHeadIndex = this.View.LayoutLines.GetIndexFromLineNumber(CaretPostion.row);
int next = this.View.LayoutLines.GetLayout(CaretPostion.row).AlignIndexToNearestCluster(CaretPostion.col, AlignDirection.Forward) + lineHeadIndex;
if (this.Document[index] == Document.NewLine)
next = index + 1;
this.Document.Replace(index, next - index, "");
}
///
/// キャレット位置の文字を一文字削除し、キャレット位置を後ろにずらす
///
public void DoBackSpaceAction()
{
if (this.View.InsertPoint != null)
{
this.ReplaceBeforeSelectionArea(this.View.Selections, 1, "");
return;
}
else if (this.SelectionLength > 0)
{
this.SelectedText = "";
return;
}
if (this.Document.FireUpdateEvent == false)
throw new InvalidOperationException("");
TextPoint CurrentPostion = this.View.CaretPostion;
if (CurrentPostion.row == 0 && CurrentPostion.col == 0)
return;
int oldIndex = this.View.GetIndexFromLayoutLine(CurrentPostion);
int newCol, newIndex;
if (CurrentPostion.col > 0)
{
newCol = this.View.LayoutLines.GetLayout(CurrentPostion.row).AlignIndexToNearestCluster(CurrentPostion.col - 1, AlignDirection.Back);
newIndex = this.View.GetIndexFromLayoutLine(new TextPoint(CurrentPostion.row, newCol));
}
else
{
newIndex = this.View.GetIndexFromLayoutLine(CurrentPostion);
newIndex--;
}
this.Document.Replace(newIndex, oldIndex - newIndex, "");
}
///
/// キャレット位置で行を分割する
///
public void DoEnterAction()
{
this.DoInputChar('\n');
}
///
/// キャレット位置に文字を入力し、その分だけキャレットを進める。isInsertModeの値により動作が変わります
///
///
public void DoInputChar(char ch)
{
this.DoInputString(ch.ToString());
}
///
/// キャレット位置に文字列を挿入し、その分だけキャレットを進める。isInsertModeの値により動作が変わります
///
///
///
public void DoInputString(string str,bool fromTip = false)
{
if (this.View.InsertPoint != null)
{
this.ReplaceBeforeSelectionArea(this.View.Selections, 0, str);
return;
}
else if (this.SelectionLength != 0)
{
this.RepleaceSelectionArea(this.View.Selections, str, fromTip);
return;
}
if (this.Document.FireUpdateEvent == false)
throw new InvalidOperationException("");
int index = this.View.GetIndexFromLayoutLine(this.View.CaretPostion);
int length = 0;
TextPoint CaretPos = this.View.CaretPostion;
if (this.View.InsertMode == false && index < this.Document.Length && this.Document[index] != Document.NewLine)
{
string lineString = this.View.LayoutLines[CaretPos.row];
int end = this.View.LayoutLines.GetLayout(CaretPos.row).AlignIndexToNearestCluster(CaretPos.col + str.Length - 1, AlignDirection.Forward);
if (end > lineString.Length - 1)
end = lineString.Length - 1;
end += this.View.LayoutLines.GetIndexFromLineNumber(CaretPos.row);
length = end - index;
}
if (str == Document.NewLine.ToString())
{
int lineHeadIndex = this.View.LayoutLines.GetIndexFromLineNumber(CaretPos.row);
int lineLength = this.View.LayoutLines.GetLengthFromLineNumber(CaretPos.row);
FoldingItem foldingData = this.View.LayoutLines.FoldingCollection.GetFarestHiddenFoldingData(lineHeadIndex, lineLength);
if (foldingData != null && !foldingData.Expand && index > foldingData.Start && index <= foldingData.End)
index = foldingData.End + 1;
}
this.Document.Replace(index, length, str);
}
///
/// キャレットの移動に合わせて選択する
///
/// 選択状態にするかどうか
///
/// キャレットを移動後、このメソッドを呼び出さない場合、Select()メソッドは正常に機能しません
///
void SelectWithMoveCaret(bool isSelected)
{
if (this.View.CaretPostion.col < 0 || this.View.CaretPostion.row < 0)
return;
if (this.Document.FireUpdateEvent == false)
throw new InvalidOperationException("");
int CaretPostion = this.View.GetIndexFromLayoutLine(this.View.CaretPostion);
SelectCollection Selections = this.View.Selections;
if (isSelected)
{
this.Select(this.AnchorIndex, CaretPostion - this.AnchorIndex);
}else{
this.Select(CaretPostion, 0);
this.AnchorIndex = CaretPostion;
this.View.InsertPoint = null;
}
}
///
/// JumpCaretで移動した位置からキャレットを移動し、選択状態にする
///
///
public void MoveCaretAndSelect(TextPoint tp)
{
int CaretPostion = this.View.GetIndexFromLayoutLine(tp);
this.Select(this.AnchorIndex, CaretPostion - this.AnchorIndex);
this.View.JumpCaret(tp.row, tp.col);
this.View.AdjustCaretAndSrc();
}
public void MoveSelectBefore(TextPoint tp)
{
int NewAnchorIndex;
int SelectionLength;
if (this.IsReverseSelect())
{
NewAnchorIndex = this.View.GetIndexFromLayoutLine(tp);
SelectionLength = this.SelectionLength + NewAnchorIndex - this.AnchorIndex;
this.Select(this.SelectionStart, SelectionLength);
}
else
{
NewAnchorIndex = this.View.GetIndexFromLayoutLine(tp);
SelectionLength = this.SelectionLength + this.AnchorIndex - NewAnchorIndex;
this.Select(NewAnchorIndex, SelectionLength);
}
this.AnchorIndex = NewAnchorIndex;
}
///
/// キャレット位置を既定の位置に戻す
///
public void ResetCaretPostion()
{
this.JumpCaret(0);
}
///
/// 行単位で移動後のキャレット位置を取得する
///
/// 移動量
/// 現在のキャレット位置
/// 移動後のキャレット位置
public TextPoint GetTextPointAfterMoveLine(int count, TextPoint current)
{
int row = current.row + count;
if (row < 0)
row = 0;
else if (row >= this.View.LayoutLines.Count)
row = this.View.LayoutLines.Count - 1;
row = this.View.AdjustRow(row, count > 0);
double colpos = this.View.GetColPostionFromIndex(current.row, current.col);
int col = this.View.GetIndexFromColPostion(row, colpos);
return new TextPoint(row, col);
}
///
/// 選択文字列のインデントを一つ増やす
///
public void UpIndent()
{
if (this.RectSelection || this.SelectionLength == 0)
return;
int selectionStart = this.SelectionStart;
string text = this.InsertLineHead(GetTextFromLineSelectArea(this.View.Selections), "\t");
this.RepleaceSelectionArea(this.View.Selections,text);
this.Select(selectionStart, text.Length);
}
///
/// 選択文字列のインデントを一つ減らす
///
public void DownIndent()
{
if (this.RectSelection || this.SelectionLength == 0)
return;
int selectionStart = this.SelectionStart;
string text = this.RemoveLineHead(GetTextFromLineSelectArea(this.View.Selections), "\t");
this.RepleaceSelectionArea(this.View.Selections, text);
this.Select(selectionStart, text.Length);
}
string InsertLineHead(string s, string str)
{
string[] lines = s.Split(new string[] { Document.NewLine.ToString() }, StringSplitOptions.RemoveEmptyEntries);
StringBuilder output = new StringBuilder();
for (int i = 0; i < lines.Length; i++)
output.Append(str + lines[i] + Document.NewLine);
return output.ToString();
}
public string RemoveLineHead(string s, string str)
{
string[] lines = s.Split(new string[] { Document.NewLine.ToString() }, StringSplitOptions.RemoveEmptyEntries);
StringBuilder output = new StringBuilder();
for (int i = 0; i < lines.Length; i++)
if (lines[i].StartsWith(str))
output.Append(lines[i].Substring(1) + Document.NewLine);
else
output.Append(lines[i] + Document.NewLine);
return output.ToString();
}
///
/// キャレットを一文字移動させる
///
/// 真なら1文字すすめ、そうでなければ戻す
/// このメソッドを呼び出した後でScrollToCaretメソッドとSelectWithMoveCaretメソッドを呼び出す必要があります
void MoveCaretHorizontical(bool isMoveNext)
{
if (this.Document.FireUpdateEvent == false)
throw new InvalidOperationException("");
int delta = isMoveNext ? 0 : -1;
int prevcol = this.View.CaretPostion.col;
int col = this.View.CaretPostion.col + delta;
string lineString = this.View.LayoutLines[this.View.CaretPostion.row];
if (col < 0 || this.View.CaretPostion.row >= this.View.LayoutLines.Count)
{
if (this.View.CaretPostion.row == 0)
{
col = 0;
return;
}
this.MoveCaretVertical(false);
this.View.AdjustCaretAndSrc(AdjustFlow.Row); //この段階で調整しないとスクロールされない
col = this.View.LayoutLines.GetLengthFromLineNumber(this.View.CaretPostion.row) - 1; //最終行以外はすべて改行コードが付くはず
}
else if (col >= lineString.Length || lineString[col] == Document.NewLine)
{
if (this.View.CaretPostion.row < this.View.LayoutLines.Count - 1)
{
this.MoveCaretVertical(true);
this.View.AdjustCaretAndSrc(AdjustFlow.Row); //この段階で調整しないとスクロールされない
col = 0;
}
}
else
{
AlignDirection direction = isMoveNext ? AlignDirection.Forward : AlignDirection.Back;
col = this.View.LayoutLines.GetLayout(this.View.CaretPostion.row).AlignIndexToNearestCluster(col, direction);
}
this.View.JumpCaret(this.View.CaretPostion.row, col,false);
}
///
/// キャレットを行方向に移動させる
///
/// プラス方向に移動するなら真
/// このメソッドを呼び出した後でScrollToCaretメソッドとSelectWithMoveCaretメソッドを呼び出す必要があります
void MoveCaretVertical(bool isMoveNext)
{
if (this.Document.FireUpdateEvent == false)
throw new InvalidOperationException("");
TextPoint nextPoint = this.GetTextPointAfterMoveLine(isMoveNext ? 1 : -1, this.View.CaretPostion);
this.View.JumpCaret(nextPoint.row, nextPoint.col,false);
}
private void ReplaceBeforeSelectionArea(SelectCollection Selections, int removeLength, string insertStr)
{
if (removeLength == 0 && insertStr.Length == 0)
return;
if (this.RectSelection == false || this.Document.FireUpdateEvent == false)
throw new InvalidOperationException();
SelectCollection temp = this.View.InsertPoint;
int selectStart = temp.First().start;
int selectEnd = temp.Last().start + temp.Last().length;
//ドキュメント操作後に行うとうまくいかないので、あらかじめ取得しておく
TextPoint start = this.View.LayoutLines.GetTextPointFromIndex(selectStart);
TextPoint end = this.View.LayoutLines.GetTextPointFromIndex(selectEnd);
bool reverse = temp.First().start > temp.Last().start;
int lineHeadIndex = this.View.LayoutLines.GetIndexFromLineNumber(this.View.LayoutLines.GetLineNumberFromIndex(selectStart));
if (selectStart - removeLength < lineHeadIndex)
return;
this.Document.UndoManager.BeginUndoGroup();
this.Document.FireUpdateEvent = false;
if (reverse)
{
for (int i = 0; i < temp.Count; i++)
{
this.ReplaceBeforeSelection(temp[i], removeLength, insertStr);
}
}
else
{
for (int i = temp.Count - 1; i >= 0; i--)
{
this.ReplaceBeforeSelection(temp[i], removeLength, insertStr);
}
}
this.Document.FireUpdateEvent = true;
this.Document.UndoManager.EndUndoGroup();
int delta = insertStr.Length - removeLength;
start.col += delta;
end.col += delta;
if (reverse)
this.JumpCaret(start.row, start.col);
else
this.JumpCaret(end.row, end.col);
this.Select(start, 0, end.row - start.row);
}
private void ReplaceBeforeSelection(Selection sel, int removeLength, string insertStr)
{
sel = Util.NormalizeIMaker(sel);
this.Document.Replace(sel.start - removeLength, removeLength, insertStr);
}
private void RepleaceSelectionArea(SelectCollection Selections, string value,bool updateInsertPoint = false)
{
if (value == null)
return;
if (this.RectSelection == false)
{
Selection sel = Selection.Create(this.AnchorIndex, 0);
if (Selections.Count > 0)
sel = Util.NormalizeIMaker(this.View.Selections.First());
this.Document.Replace(sel.start, sel.length, value);
return;
}
if (this.Document.FireUpdateEvent == false)
throw new InvalidOperationException("");
int StartIndex = this.SelectionStart;
SelectCollection newInsertPoint = new SelectCollection();
if (this.SelectionLength == 0)
{
int i;
this.Document.UndoManager.BeginUndoGroup();
this.Document.FireUpdateEvent = false;
string[] line = value.Split(new string[] { Document.NewLine.ToString() }, StringSplitOptions.RemoveEmptyEntries);
TextPoint Current = this.View.GetLayoutLineFromIndex(this.SelectionStart);
for (i = 0; i < line.Length && Current.row < this.View.LayoutLines.Count; i++, Current.row++)
{
if (Current.col > this.View.LayoutLines[Current.row].Length)
Current.col = this.View.LayoutLines[Current.row].Length;
StartIndex = this.View.GetIndexFromLayoutLine(Current);
this.Document.Replace(StartIndex, 0, line[i]);
StartIndex += line[i].Length;
}
for (; i < line.Length; i++)
{
StartIndex = this.Document.Length;
string str = Document.NewLine + line[i];
this.Document.Replace(StartIndex, 0, str);
StartIndex += str.Length;
}
this.Document.FireUpdateEvent = true;
this.Document.UndoManager.EndUndoGroup();
}
else
{
SelectCollection temp = new SelectCollection(this.View.Selections); //コピーしないとReplaceCommandを呼び出した段階で書き換えられてしまう
this.Document.UndoManager.BeginUndoGroup();
this.Document.FireUpdateEvent = false;
if (temp.First().start < temp.Last().start)
{
for (int i = temp.Count - 1; i >= 0; i--)
{
Selection sel = Util.NormalizeIMaker(temp[i]);
StartIndex = sel.start;
this.Document.Replace(sel.start, sel.length, value);
newInsertPoint.Add(Selection.Create(sel.start + (value.Length - sel.length) * i,0));
}
}
else
{
for (int i = 0; i < temp.Count; i++)
{
Selection sel = Util.NormalizeIMaker(temp[i]);
StartIndex = sel.start;
this.Document.Replace(sel.start, sel.length, value);
newInsertPoint.Add(Selection.Create(sel.start + (value.Length - sel.length) * i, 0));
}
}
this.Document.FireUpdateEvent = true;
this.Document.UndoManager.EndUndoGroup();
}
this.JumpCaret(StartIndex);
if (updateInsertPoint && newInsertPoint.Count > 0)
this.View.InsertPoint = newInsertPoint;
}
private string GetTextFromLineSelectArea(SelectCollection Selections)
{
Selection sel = Util.NormalizeIMaker(Selections.First());
string str = this.Document.ToString(sel.start, sel.length);
return str;
}
string GetTextFromRectangleSelectArea(SelectCollection Selections)
{
StringBuilder temp = new StringBuilder();
if (Selections.First().start < Selections.Last().start)
{
for (int i = 0; i < this.View.Selections.Count; i++)
{
Selection sel = Util.NormalizeIMaker(Selections[i]);
string str = this.Document.ToString(sel.start, sel.length);
if (str.IndexOf(Environment.NewLine) == -1)
temp.AppendLine(str);
else
temp.Append(str);
}
}
else
{
for (int i = this.View.Selections.Count - 1; i >= 0; i--)
{
Selection sel = Util.NormalizeIMaker(Selections[i]);
string str = this.Document.ToString(sel.start, sel.length).Replace(Document.NewLine.ToString(), Environment.NewLine);
if (str.IndexOf(Environment.NewLine) == -1)
temp.AppendLine(str);
else
temp.Append(str);
}
}
return temp.ToString();
}
void View_LineBreakChanged(object sender, EventArgs e)
{
this.DeSelectAll();
this.AdjustCaret();
}
void View_PageBoundChanged(object sender, EventArgs e)
{
if (this.View.LineBreak == LineBreakMethod.PageBound && this.View.PageBound.Width - this.View.LineBreakingMarginWidth > 0)
this.View.PerfomLayouts();
this.AdjustCaret();
}
void render_ChangedRenderResource(object sender, ChangedRenderRsourceEventArgs e)
{
if (e.type == ResourceType.Font)
{
if (this.View.LineBreak == LineBreakMethod.PageBound)
this.View.PerfomLayouts();
this.AdjustCaret();
}
if (e.type == ResourceType.InlineChar)
{
int oldLineCountOnScreen = this.View.LineCountOnScreen;
this.View.CalculateLineCountOnScreen();
if(this.View.LineCountOnScreen != oldLineCountOnScreen)
this.AdjustCaret();
}
}
void render_ChangedRightToLeft(object sender, EventArgs e)
{
this.AdjustCaret();
}
void Document_Update(object sender, DocumentUpdateEventArgs e)
{
switch (e.type)
{
case UpdateType.Replace:
if(e.startIndex < this.Document.Length && this.Document[e.startIndex] == Document.NewLine)
this.View.CalculateLineCountOnScreen();
this.JumpCaret(e.startIndex + e.insertLength,true);
break;
case UpdateType.Clear:
this.View.TryScroll(0, 0);
break;
}
}
}
}