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.
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.
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/>.
12 using System.Globalization;
15 using System.Diagnostics;
16 using System.Text.RegularExpressions;
21 namespace FooEditEngine
23 internal enum MoveFlow
28 internal enum ScrollDirection
39 public enum IndentMode
46 /// ユーザー側からの処理を担当するクラス。一部を除き、こちらで行われた操作はアンドゥの対象になります
48 internal sealed class Controller
53 public Controller(Document doc, EditView view)
57 this.View.render.ChangedRenderResource += render_ChangedRenderResource;
58 this.View.PerformLayouted += View_LineBreakChanged;
59 this.View.PageBoundChanged += View_PageBoundChanged;
60 this.Document.Clear();
63 public Document Document
67 return this._Document;
71 //メモリリークを防ぐためにパンドラーを除く
72 if (this._Document != null)
74 this._Document.Update -= Document_Update;
75 this._Document.StatusUpdate -= Document_StatusChanged;
78 this._Document = value;
80 this._Document.Update += new DocumentUpdateEventHandler(Document_Update);
81 this._Document.StatusUpdate += Document_StatusChanged;
85 void Document_StatusChanged(object sender,EventArgs e)
91 /// 矩形選択モードなら真を返し、そうでない場合は偽を返す
93 public bool RectSelection
95 get { return this.Document.RectSelection; }
96 set { this.Document.RectSelection = value; }
102 public IndentMode IndentMode
104 get { return this.Document.IndentMode; }
105 set { this.Document.IndentMode = value; }
111 /// <remarks>SelectionLengthが0の場合、キャレット位置を表します</remarks>
112 public int SelectionStart
116 if (this.View.Selections.Count == 0)
117 return this.Document.AnchorIndex;
119 return this.View.Selections.First().start;
126 /// <remarks>矩形選択モードの場合、選択範囲の文字数ではなく、開始位置から終了位置までの長さとなります</remarks>
127 public int SelectionLength
131 if (this.View.Selections.Count == 0)
133 Selection last = this.View.Selections.Last();
134 return last.start + last.length - this.SelectionStart;
142 /// 未選択状態で代入したときは追加され、そうでない場合は選択範囲の文字列と置き換えられます。
144 public string SelectedText
148 if (this.View.LayoutLines.Count == 0 || this.View.Selections.Count == 0)
150 if (this.RectSelection)
151 return GetTextFromRectangleSelectArea(this.View.Selections);
153 return GetTextFromLineSelectArea(this.View.Selections).Replace(Document.NewLine.ToString(), Environment.NewLine);
157 if (this.Document.FireUpdateEvent == false)
158 throw new InvalidOperationException("");
161 this.RepleaceSelectionArea(this.View.Selections, value.Replace(Environment.NewLine,Document.NewLine.ToString()));
166 /// 選択範囲が逆転しているかどうかを判定する
168 /// <returns>逆転しているなら真を返す</returns>
169 public bool IsReverseSelect()
171 int index = this.View.LayoutLines.GetIndexFromTextPoint(this.Document.CaretPostion);
172 return index < this.Document.AnchorIndex;
176 /// 選択範囲内のUTF32コードポイントを文字列に変換します
178 /// <returns>成功した場合は真。そうでない場合は偽を返す</returns>
179 public bool ConvertToChar()
181 if (this.SelectionLength == 0 || this.RectSelection)
183 string str = this.Document.ToString(this.SelectionStart, this.SelectionLength);
184 string[] codes = str.Split(new char[] { ' ' },StringSplitOptions.RemoveEmptyEntries);
185 StringBuilder result = new StringBuilder();
186 foreach (string code in codes)
191 if (Int32.TryParse(code.TrimStart('U'),NumberStyles.HexNumber,null, out utf32_code))
192 result.Append(Char.ConvertFromUtf32(utf32_code));
196 this.Document.Lock();
197 this.Document.Replace(this.SelectionStart, this.SelectionLength, result.ToString());
198 this.Document.UnLock();
203 /// 選択文字列をUTF32のコードポイントに変換します
205 public void ConvertToCodePoint()
207 if (this.SelectionLength == 0 || this.RectSelection)
209 string str = this.Document.ToString(this.SelectionStart, this.SelectionLength);
210 StringInfo info = new StringInfo(str);
211 StringBuilder result = new StringBuilder();
212 for (int i = 0; i < str.Length;)
214 int utf32_code = Char.ConvertToUtf32(str, i);
215 result.Append("U" + Convert.ToString(utf32_code,16));
217 if(Char.IsHighSurrogate(str[i]))
222 this.Document.Lock();
223 this.Document.Replace(this.SelectionStart, this.SelectionLength, result.ToString());
224 this.Document.UnLock();
230 public void DeSelectAll()
232 if (this.Document.FireUpdateEvent == false)
233 throw new InvalidOperationException("");
235 this.View.Selections.Clear();
241 /// <param name="tp"></param>
242 /// <param name="type"></param>
243 /// <returns>真ならマーカーがある</returns>
244 public bool IsMarker(TextPoint tp,HilightType type)
246 if (this.Document.FireUpdateEvent == false)
247 throw new InvalidOperationException("");
248 int index = this.View.LayoutLines.GetIndexFromTextPoint(tp);
249 return this.IsMarker(index, type);
255 /// <param name="index"></param>
256 /// <param name="type"></param>
257 /// <returns>真ならマーカーがある</returns>
258 public bool IsMarker(int index, HilightType type)
260 foreach(int id in this.Document.Markers.IDs)
262 foreach (Marker m in this.Document.GetMarkers(index, id))
264 if (m.hilight == type)
274 public void AdjustCaret()
276 int row = this.Document.CaretPostion.row;
277 if (row > this.View.LayoutLines.Count - 1)
278 row = this.View.LayoutLines.Count - 1;
279 int col = this.Document.CaretPostion.col;
280 if (col > 0 && col > this.View.LayoutLines[row].Length)
281 col = this.View.LayoutLines[row].Length;
284 int sel_start = this.SelectionStart;
285 int sel_length = this.SelectionLength;
287 this.JumpCaret(row, col);
289 this.Document.Select(sel_start, sel_length);
293 /// キャレットを指定した位置に移動させる
295 /// <param name="index"></param>
296 /// <param name="autoExpand">折り畳みを展開するなら真</param>
297 public void JumpCaret(int index,bool autoExpand = true)
299 if (index < 0 || index > this.Document.Length)
300 throw new ArgumentOutOfRangeException("indexが設定できる範囲を超えています");
301 TextPoint tp = this.View.GetLayoutLineFromIndex(index);
303 this.JumpCaret(tp.row, tp.col,autoExpand);
307 /// キャレットを指定した位置に移動させる
309 /// <param name="row"></param>
310 /// <param name="col"></param>
311 /// <param name="autoExpand">折り畳みを展開するなら真</param>
312 public void JumpCaret(int row, int col, bool autoExpand = true)
314 if (this.Document.FireUpdateEvent == false)
315 throw new InvalidOperationException("");
317 this.View.JumpCaret(row, col,autoExpand);
319 this.View.AdjustCaretAndSrc();
321 this.SelectWithMoveCaret(false);
327 /// <param name="row">行</param>
328 /// <param name="isSelected">選択状態にするかどうか</param>
329 public void JumpToLineHead(int row,bool isSelected)
331 this.View.JumpCaret(row, 0);
332 this.View.AdjustCaretAndSrc();
333 this.SelectWithMoveCaret(isSelected);
339 /// <param name="row">行</param>
340 /// <param name="isSelected">選択状態にするかどうか</param>
341 public void JumpToLineEnd(int row, bool isSelected)
343 this.View.JumpCaret(row, this.View.LayoutLines[row].Length - 1);
344 this.View.AdjustCaretAndSrc();
345 this.SelectWithMoveCaret(isSelected);
351 /// <param name="isSelected"></param>
352 public void JumpToHead(bool isSelected)
354 if (this.View.TryScroll(0, 0))
356 this.View.JumpCaret(0, 0);
357 this.View.AdjustCaretAndSrc();
358 this.SelectWithMoveCaret(isSelected);
364 /// <param name="isSelected"></param>
365 public void JumpToEnd(bool isSelected)
367 int srcRow = this.View.LayoutLines.Count - this.View.LineCountOnScreen - 1;
370 if (this.View.TryScroll(0, srcRow))
372 this.View.JumpCaret(this.View.LayoutLines.Count - 1, 0);
373 this.View.AdjustCaretAndSrc();
374 this.SelectWithMoveCaret(isSelected);
380 /// <param name="dir">方向を指定する</param>
381 /// <param name="delta">スクロールする量。ScrollDirectionの値がUpやDownなら行数。LeftやRightならピクセル単位の値となる</param>
382 /// <param name="isSelected">選択状態にするなら真</param>
383 /// <param name="withCaret">同時にキャレットを移動させるなら真</param>
384 public void Scroll(ScrollDirection dir, int delta, bool isSelected,bool withCaret)
386 if (this.Document.FireUpdateEvent == false)
387 throw new InvalidOperationException("");
388 int toRow = this.View.Src.Row;
389 double toX = this.View.Src.X;
392 case ScrollDirection.Up:
393 toRow = Math.Max(0, this.View.Src.Row - delta);
394 toRow = this.View.AdjustRow(toRow, false);
396 case ScrollDirection.Down:
397 toRow = Math.Min(this.View.Src.Row + delta, this.View.LayoutLines.Count - 1);
398 toRow = this.View.AdjustRow(toRow, true);
400 case ScrollDirection.Left:
403 case ScrollDirection.Right:
407 throw new ArgumentOutOfRangeException();
409 this.Scroll(toX, toRow, isSelected, withCaret);
415 /// <param name="toX">スクロール先の座標</param>
416 /// <param name="toRow">スクロール先の行</param>
417 /// <param name="isSelected">選択状態にするなら真</param>
418 /// <param name="withCaret">同時にキャレットを移動させるなら真</param>
419 public void Scroll(double toX, int toRow, bool isSelected, bool withCaret)
423 this.View.Scroll(toX, toRow);
424 this.View.JumpCaret(toRow, 0);
425 this.View.AdjustCaretAndSrc();
426 this.SelectWithMoveCaret(isSelected);
430 this.View.Scroll(toX, toRow);
431 this.View.IsFocused = false;
438 /// <returns>移動できない場合は真を返す</returns>
439 /// <param name="realLength">負の値なら左側へ、そうでないなら右側へ移動する</param>
440 /// <param name="isSelected">選択範囲とするなら真。そうでないなら偽</param>
441 /// <param name="alignWord">単語単位で移動するなら真。そうでないなら偽</param>
442 public void MoveCaretHorizontical(int realLength, bool isSelected,bool alignWord = false)
444 for (int i = Math.Abs(realLength); i > 0; i--)
446 bool MoveFlow = realLength > 0;
447 if (this.Document.RightToLeft)
448 MoveFlow = !MoveFlow;
449 this.MoveCaretHorizontical(MoveFlow);
452 this.AlignNearestWord(MoveFlow);
454 this.View.AdjustCaretAndSrc(AdjustFlow.Col);
455 this.SelectWithMoveCaret(isSelected);
458 void AlignNearestWord(bool MoveFlow)
460 string str = this.View.LayoutLines[this.Document.CaretPostion.row];
461 while (this.Document.CaretPostion.col > 0 &&
462 this.Document.CaretPostion.col < str.Length &&
463 str[this.Document.CaretPostion.col] != Document.NewLine)
465 if (!Util.IsWordSeparator(str[this.Document.CaretPostion.col]))
467 this.MoveCaretHorizontical(MoveFlow);
472 this.MoveCaretHorizontical(MoveFlow);
481 /// <returns>再描写する必要があるなら真を返す</returns>
482 /// <param name="deltarow">移動量</param>
483 /// <param name="isSelected"></param>
484 public void MoveCaretVertical(int deltarow,bool isSelected)
486 for (int i = Math.Abs(deltarow); i > 0; i--)
487 this.MoveCaretVertical(deltarow > 0);
488 this.View.AdjustCaretAndSrc(AdjustFlow.Both);
489 this.SelectWithMoveCaret(isSelected);
493 /// キャレット位置の文字を一文字削除する
495 public void DoDeleteAction()
497 if (this.SelectionLength != 0)
499 this.SelectedText = "";
503 if (this.Document.FireUpdateEvent == false)
504 throw new InvalidOperationException("");
506 TextPoint CaretPostion = this.Document.CaretPostion;
507 int index = this.View.GetIndexFromLayoutLine(CaretPostion);
509 if (index == this.Document.Length)
512 int lineHeadIndex = this.View.LayoutLines.GetIndexFromLineNumber(CaretPostion.row);
513 int next = this.View.LayoutLines.GetLayout(CaretPostion.row).AlignIndexToNearestCluster(CaretPostion.col, AlignDirection.Forward) + lineHeadIndex;
515 if (this.Document[index] == Document.NewLine)
518 this.Document.Lock();
519 this.Document.Replace(index, next - index, "");
520 this.Document.UnLock();
523 public bool IsRectInsertMode()
525 if (!this.RectSelection || this.View.Selections.Count == 0)
527 foreach(Selection sel in this.View.Selections)
536 /// キャレット位置の文字を一文字削除し、キャレット位置を後ろにずらす
538 public void DoBackSpaceAction()
540 if (this.IsRectInsertMode())
542 this.ReplaceBeforeSelectionArea(this.View.Selections, 1, "");
545 else if (this.SelectionLength > 0)
547 this.SelectedText = "";
551 if (this.Document.FireUpdateEvent == false)
552 throw new InvalidOperationException("");
554 TextPoint CurrentPostion = this.Document.CaretPostion;
556 if (CurrentPostion.row == 0 && CurrentPostion.col == 0)
559 int oldIndex = this.View.GetIndexFromLayoutLine(CurrentPostion);
561 int newCol, newIndex;
562 if (CurrentPostion.col > 0)
564 newCol = this.View.LayoutLines.GetLayout(CurrentPostion.row).AlignIndexToNearestCluster(CurrentPostion.col - 1, AlignDirection.Back);
565 newIndex = this.View.GetIndexFromLayoutLine(new TextPoint(CurrentPostion.row, newCol));
569 newIndex = this.View.GetIndexFromLayoutLine(CurrentPostion);
573 this.Document.Lock();
574 this.Document.Replace(newIndex, oldIndex - newIndex, "");
575 this.Document.UnLock();
581 public void DoEnterAction()
583 this.DoInputChar('\n');
587 /// キャレット位置に文字を入力し、その分だけキャレットを進める。isInsertModeの値により動作が変わります
589 /// <param name="ch"></param>
590 public void DoInputChar(char ch)
592 this.DoInputString(ch.ToString());
595 string GetIndentSpace(int col_index)
597 int space_count = this.Document.TabStops - (col_index % this.Document.TabStops);
598 return new string(Enumerable.Repeat(' ',space_count).ToArray());
602 /// キャレット位置に文字列を挿入し、その分だけキャレットを進める。isInsertModeの値により動作が変わります
604 /// <param name="str">挿入したい文字列</param>
605 /// <param name="fromTip">真の場合、矩形選択の幅にかかわらず矩形編集モードとして動作します。そうでない場合は選択領域を文字列で置き換えます</param>
606 public void DoInputString(string str,bool fromTip = false)
608 TextPoint CaretPos = this.Document.CaretPostion;
610 if (str == "\t" && this.IndentMode == IndentMode.Space)
611 str = this.GetIndentSpace(CaretPos.col);
613 if (this.IsRectInsertMode())
615 this.ReplaceBeforeSelectionArea(this.View.Selections, 0, str);
618 else if (this.SelectionLength != 0)
620 this.RepleaceSelectionArea(this.View.Selections, str, fromTip);
624 if (this.Document.FireUpdateEvent == false)
625 throw new InvalidOperationException("");
627 int index = this.View.GetIndexFromLayoutLine(this.Document.CaretPostion);
629 if (this.View.InsertMode == false && index < this.Document.Length && this.Document[index] != Document.NewLine)
631 string lineString = this.View.LayoutLines[CaretPos.row];
632 int end = this.View.LayoutLines.GetLayout(CaretPos.row).AlignIndexToNearestCluster(CaretPos.col + str.Length - 1, AlignDirection.Forward);
633 if (end > lineString.Length - 1)
634 end = lineString.Length - 1;
635 end += this.View.LayoutLines.GetIndexFromLineNumber(CaretPos.row);
636 length = end - index;
638 if (str == Document.NewLine.ToString())
640 int lineHeadIndex = this.View.LayoutLines.GetIndexFromLineNumber(CaretPos.row);
641 int lineLength = this.View.LayoutLines.GetLengthFromLineNumber(CaretPos.row);
642 FoldingItem foldingData = this.View.LayoutLines.FoldingCollection.GetFarestHiddenFoldingData(lineHeadIndex, lineLength);
643 if (foldingData != null && !foldingData.Expand && index > foldingData.Start && index <= foldingData.End)
644 index = foldingData.End + 1;
646 this.Document.Lock();
647 this.Document.Replace(index, length, str);
648 this.Document.UnLock();
652 /// キャレットの移動に合わせて選択する
654 /// <param name="isSelected">選択状態にするかどうか</param>
656 /// キャレットを移動後、このメソッドを呼び出さない場合、Select()メソッドは正常に機能しません
658 void SelectWithMoveCaret(bool isSelected)
660 if (this.Document.CaretPostion.col < 0 || this.Document.CaretPostion.row < 0)
663 if (this.Document.FireUpdateEvent == false)
664 throw new InvalidOperationException("");
666 int CaretPostion = this.View.GetIndexFromLayoutLine(this.Document.CaretPostion);
668 SelectCollection Selections = this.View.Selections;
671 this.Document.Select(this.Document.AnchorIndex, CaretPostion - this.Document.AnchorIndex);
673 this.Document.AnchorIndex = CaretPostion;
674 this.Document.Select(CaretPostion, 0);
679 /// JumpCaretで移動した位置からキャレットを移動し、選択状態にする
681 /// <param name="tp"></param>
682 public void MoveCaretAndSelect(TextPoint tp)
684 int CaretPostion = this.View.GetIndexFromLayoutLine(tp);
685 this.Document.Select(this.Document.AnchorIndex, CaretPostion - this.Document.AnchorIndex);
686 this.View.JumpCaret(tp.row, tp.col);
687 this.View.AdjustCaretAndSrc();
691 /// グリッパーとキャレットを同時に移動する
693 /// <param name="p">ポインターの座標</param>
694 /// <param name="hittedGripper">動かす対象となるグリッパー</param>
695 /// <returns>移動できた場合は真を返す。そうでなければ偽を返す</returns>
696 /// <remarks>グリッパー内にポインターが存在しない場合、グリッパーはポインターの座標近くの行に移動する</remarks>
697 public bool MoveCaretAndGripper(Point p, Gripper hittedGripper)
699 bool HittedCaret = false;
700 TextPoint tp = this.View.GetTextPointFromPostion(p);
701 if (tp == this.Document.CaretPostion)
706 if (HittedCaret || hittedGripper != null)
708 if (hittedGripper != null)
710 tp = this.View.GetTextPointFromPostion(hittedGripper.AdjustPoint(p));
711 if (tp == TextPoint.Null)
713 if (this.IsReverseSelect())
715 if (Object.ReferenceEquals(hittedGripper, this.Document.SelectGrippers.BottomRight))
716 this.MoveSelectBefore(tp);
718 this.MoveCaretAndSelect(tp);
722 if (Object.ReferenceEquals(hittedGripper, this.Document.SelectGrippers.BottomLeft))
723 this.MoveSelectBefore(tp);
725 this.MoveCaretAndSelect(tp);
727 hittedGripper.Move(this.View, tp);
731 tp = this.View.GetTextPointFromPostion(p);
732 if (tp != TextPoint.Null)
733 this.MoveCaretAndSelect(tp);
735 this.Document.SelectGrippers.BottomLeft.Enabled = this.SelectionLength != 0;
741 public void MoveSelectBefore(TextPoint tp)
745 if (this.IsReverseSelect())
747 NewAnchorIndex = this.View.GetIndexFromLayoutLine(tp);
748 SelectionLength = this.SelectionLength + NewAnchorIndex - this.Document.AnchorIndex;
749 this.Document.Select(this.SelectionStart, SelectionLength);
753 NewAnchorIndex = this.View.GetIndexFromLayoutLine(tp);
754 SelectionLength = this.SelectionLength + this.Document.AnchorIndex - NewAnchorIndex;
755 this.Document.Select(NewAnchorIndex, SelectionLength);
757 this.Document.AnchorIndex = NewAnchorIndex;
763 public void ResetCaretPostion()
769 /// 行単位で移動後のキャレット位置を取得する
771 /// <param name="count">移動量</param>
772 /// <param name="current">現在のキャレット位置</param>
773 /// <returns>移動後のキャレット位置</returns>
774 public TextPoint GetTextPointAfterMoveLine(int count, TextPoint current)
776 int row = current.row + count;
780 else if (row >= this.View.LayoutLines.Count)
781 row = this.View.LayoutLines.Count - 1;
783 row = this.View.AdjustRow(row, count > 0);
785 double colpos = this.View.GetColPostionFromIndex(current.row, current.col);
786 int col = this.View.GetIndexFromColPostion(row, colpos);
788 return new TextPoint(row, col);
792 /// 選択文字列のインデントを一つ増やす
794 public void UpIndent()
796 if (this.RectSelection || this.SelectionLength == 0)
798 int selectionStart = this.SelectionStart;
799 string insertStr = this.IndentMode == IndentMode.Space ? this.GetIndentSpace(0) : "\t";
800 string text = this.InsertLineHead(GetTextFromLineSelectArea(this.View.Selections), insertStr);
801 this.RepleaceSelectionArea(this.View.Selections,text);
802 this.Document.Select(selectionStart, text.Length);
806 /// 選択文字列のインデントを一つ減らす
808 public void DownIndent()
810 if (this.RectSelection || this.SelectionLength == 0)
812 int selectionStart = this.SelectionStart;
813 string insertStr = this.IndentMode == IndentMode.Space ? this.GetIndentSpace(0) : "\t";
814 string text = this.RemoveLineHead(GetTextFromLineSelectArea(this.View.Selections), insertStr);
815 this.RepleaceSelectionArea(this.View.Selections, text);
816 this.Document.Select(selectionStart, text.Length);
819 string InsertLineHead(string s, string str)
821 string[] lines = s.Split(new string[] { Document.NewLine.ToString() }, StringSplitOptions.None);
822 StringBuilder output = new StringBuilder();
823 for (int i = 0; i < lines.Length; i++)
825 if(lines[i].Length > 0)
826 output.Append(str + lines[i] + Document.NewLine);
827 else if(i < lines.Length - 1)
828 output.Append(lines[i] + Document.NewLine);
830 return output.ToString();
833 public string RemoveLineHead(string s, string str)
835 string[] lines = s.Split(new string[] { Document.NewLine.ToString() }, StringSplitOptions.None);
836 StringBuilder output = new StringBuilder();
837 for (int i = 0; i < lines.Length; i++)
839 if (lines[i].StartsWith(str))
840 output.Append(lines[i].Substring(1) + Document.NewLine);
841 else if (i < lines.Length - 1)
842 output.Append(lines[i] + Document.NewLine);
844 return output.ToString();
850 /// <param name="isMoveNext">真なら1文字すすめ、そうでなければ戻す</param>
851 /// <remarks>このメソッドを呼び出した後でScrollToCaretメソッドとSelectWithMoveCaretメソッドを呼び出す必要があります</remarks>
852 void MoveCaretHorizontical(bool isMoveNext)
854 if (this.Document.FireUpdateEvent == false)
855 throw new InvalidOperationException("");
856 int delta = isMoveNext ? 0 : -1;
857 int prevcol = this.Document.CaretPostion.col;
858 int col = this.Document.CaretPostion.col + delta;
859 string lineString = this.View.LayoutLines[this.Document.CaretPostion.row];
860 if (col < 0 || this.Document.CaretPostion.row >= this.View.LayoutLines.Count)
862 if (this.Document.CaretPostion.row == 0)
867 this.MoveCaretVertical(false);
868 this.View.AdjustCaretAndSrc(AdjustFlow.Row); //この段階で調整しないとスクロールされない
869 col = this.View.LayoutLines.GetLengthFromLineNumber(this.Document.CaretPostion.row) - 1; //最終行以外はすべて改行コードが付くはず
871 else if (col >= lineString.Length || lineString[col] == Document.NewLine)
873 if (this.Document.CaretPostion.row < this.View.LayoutLines.Count - 1)
875 this.MoveCaretVertical(true);
876 this.View.AdjustCaretAndSrc(AdjustFlow.Row); //この段階で調整しないとスクロールされない
882 AlignDirection direction = isMoveNext ? AlignDirection.Forward : AlignDirection.Back;
883 col = this.View.LayoutLines.GetLayout(this.Document.CaretPostion.row).AlignIndexToNearestCluster(col, direction);
886 this.View.JumpCaret(this.Document.CaretPostion.row, col,false);
892 /// <param name="isMoveNext">プラス方向に移動するなら真</param>
893 /// <remarks>このメソッドを呼び出した後でScrollToCaretメソッドとSelectWithMoveCaretメソッドを呼び出す必要があります</remarks>
894 void MoveCaretVertical(bool isMoveNext)
896 if (this.Document.FireUpdateEvent == false)
897 throw new InvalidOperationException("");
899 TextPoint nextPoint = this.GetTextPointAfterMoveLine(isMoveNext ? 1 : -1, this.Document.CaretPostion);
901 this.View.JumpCaret(nextPoint.row, nextPoint.col,false);
904 private void ReplaceBeforeSelectionArea(SelectCollection Selections, int removeLength, string insertStr)
906 if (removeLength == 0 && insertStr.Length == 0)
909 if (this.RectSelection == false || this.Document.FireUpdateEvent == false)
910 throw new InvalidOperationException();
912 SelectCollection temp = this.View.Selections;
913 int selectStart = temp.First().start;
914 int selectEnd = temp.Last().start + temp.Last().length;
916 //ドキュメント操作後に行うとうまくいかないので、あらかじめ取得しておく
917 TextPoint start = this.View.LayoutLines.GetTextPointFromIndex(selectStart);
918 TextPoint end = this.View.LayoutLines.GetTextPointFromIndex(selectEnd);
920 bool reverse = temp.First().start > temp.Last().start;
922 int lineHeadIndex = this.View.LayoutLines.GetIndexFromLineNumber(this.View.LayoutLines.GetLineNumberFromIndex(selectStart));
923 if (selectStart - removeLength < lineHeadIndex)
926 this.Document.UndoManager.BeginUndoGroup();
927 this.Document.FireUpdateEvent = false;
931 for (int i = 0; i < temp.Count; i++)
933 this.ReplaceBeforeSelection(temp[i], removeLength, insertStr);
938 for (int i = temp.Count - 1; i >= 0; i--)
940 this.ReplaceBeforeSelection(temp[i], removeLength, insertStr);
944 this.Document.FireUpdateEvent = true;
945 this.Document.UndoManager.EndUndoGroup();
947 int delta = insertStr.Length - removeLength;
952 this.JumpCaret(start.row, start.col);
954 this.JumpCaret(end.row, end.col);
956 this.Document.Select(start, 0, end.row - start.row);
959 private void ReplaceBeforeSelection(Selection sel, int removeLength, string insertStr)
961 sel = Util.NormalizeIMaker<Selection>(sel);
962 this.Document.Lock();
963 this.Document.Replace(sel.start - removeLength, removeLength, insertStr);
964 this.Document.UnLock();
967 private void RepleaceSelectionArea(SelectCollection Selections, string value,bool updateInsertPoint = false)
972 if (this.RectSelection == false)
974 Selection sel = Selection.Create(this.Document.AnchorIndex, 0);
975 if (Selections.Count > 0)
976 sel = Util.NormalizeIMaker<Selection>(this.View.Selections.First());
978 this.Document.Lock();
979 this.Document.Replace(sel.start, sel.length, value);
980 this.Document.UnLock();
984 if (this.Document.FireUpdateEvent == false)
985 throw new InvalidOperationException("");
987 int StartIndex = this.SelectionStart;
989 SelectCollection newInsertPoint = new SelectCollection();
991 if (this.SelectionLength == 0)
995 this.Document.Lock();
997 this.Document.UndoManager.BeginUndoGroup();
999 this.Document.FireUpdateEvent = false;
1001 string[] line = value.Split(new string[] { Document.NewLine.ToString() }, StringSplitOptions.RemoveEmptyEntries);
1003 TextPoint Current = this.View.GetLayoutLineFromIndex(this.SelectionStart);
1005 for (i = 0; i < line.Length && Current.row < this.View.LayoutLines.Count; i++, Current.row++)
1007 if (Current.col > this.View.LayoutLines[Current.row].Length)
1008 Current.col = this.View.LayoutLines[Current.row].Length;
1009 StartIndex = this.View.GetIndexFromLayoutLine(Current);
1010 this.Document.Replace(StartIndex, 0, line[i]);
1011 StartIndex += line[i].Length;
1014 for (; i < line.Length; i++)
1016 StartIndex = this.Document.Length;
1017 string str = Document.NewLine + line[i];
1018 this.Document.Replace(StartIndex, 0, str);
1019 StartIndex += str.Length;
1022 this.Document.FireUpdateEvent = true;
1024 this.Document.UndoManager.EndUndoGroup();
1026 this.Document.UnLock();
1030 SelectCollection temp = new SelectCollection(this.View.Selections); //コピーしないとReplaceCommandを呼び出した段階で書き換えられてしまう
1032 this.Document.Lock();
1034 this.Document.UndoManager.BeginUndoGroup();
1036 this.Document.FireUpdateEvent = false;
1038 if (temp.First().start < temp.Last().start)
1040 for (int i = temp.Count - 1; i >= 0; i--)
1042 Selection sel = Util.NormalizeIMaker<Selection>(temp[i]);
1044 StartIndex = sel.start;
1046 this.Document.Replace(sel.start, sel.length, value);
1048 newInsertPoint.Add(Selection.Create(sel.start + (value.Length - sel.length) * i,0));
1053 for (int i = 0; i < temp.Count; i++)
1055 Selection sel = Util.NormalizeIMaker<Selection>(temp[i]);
1057 StartIndex = sel.start;
1059 this.Document.Replace(sel.start, sel.length, value);
1061 newInsertPoint.Add(Selection.Create(sel.start + (value.Length - sel.length) * i, 0));
1065 this.Document.FireUpdateEvent = true;
1067 this.Document.UndoManager.EndUndoGroup();
1069 this.Document.UnLock();
1071 this.JumpCaret(StartIndex);
1072 if (updateInsertPoint && newInsertPoint.Count > 0)
1073 this.View.Selections = newInsertPoint;
1076 private string GetTextFromLineSelectArea(SelectCollection Selections)
1078 Selection sel = Util.NormalizeIMaker<Selection>(Selections.First());
1080 string str = this.Document.ToString(sel.start, sel.length);
1085 string GetTextFromRectangleSelectArea(SelectCollection Selections)
1087 StringBuilder temp = new StringBuilder();
1088 if (Selections.First().start < Selections.Last().start)
1090 for (int i = 0; i < this.View.Selections.Count; i++)
1092 Selection sel = Util.NormalizeIMaker<Selection>(Selections[i]);
1094 string str = this.Document.ToString(sel.start, sel.length);
1095 if (str.IndexOf(Environment.NewLine) == -1)
1096 temp.AppendLine(str);
1103 for (int i = this.View.Selections.Count - 1; i >= 0; i--)
1105 Selection sel = Util.NormalizeIMaker<Selection>(Selections[i]);
1107 string str = this.Document.ToString(sel.start, sel.length).Replace(Document.NewLine.ToString(), Environment.NewLine);
1108 if (str.IndexOf(Environment.NewLine) == -1)
1109 temp.AppendLine(str);
1114 return temp.ToString();
1117 void View_LineBreakChanged(object sender, EventArgs e)
1123 void View_PageBoundChanged(object sender, EventArgs e)
1125 if (this.Document.LineBreak == LineBreakMethod.PageBound && this.View.PageBound.Width - this.View.LineBreakingMarginWidth > 0)
1126 this.View.PerfomLayouts();
1130 void render_ChangedRenderResource(object sender, ChangedRenderRsourceEventArgs e)
1132 if (e.type == ResourceType.Font)
1134 if (this.Document.LineBreak == LineBreakMethod.PageBound)
1135 this.View.PerfomLayouts();
1138 if (e.type == ResourceType.InlineChar)
1140 int oldLineCountOnScreen = this.View.LineCountOnScreen;
1141 this.View.CalculateLineCountOnScreen();
1142 if(this.View.LineCountOnScreen != oldLineCountOnScreen)
1147 void Document_Update(object sender, DocumentUpdateEventArgs e)
1151 case UpdateType.Replace:
1152 this.JumpCaret(e.startIndex + e.insertLength,true);
1154 case UpdateType.Clear:
1155 this.JumpCaret(0,0, false);