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
54 public Controller(Document doc, EditView view)
57 this.Document.Update += new DocumentUpdateEventHandler(Document_Update);
59 this.View.render.ChangedRightToLeft += render_ChangedRightToLeft;
60 this.View.render.ChangedRenderResource += render_ChangedRenderResource;
61 this.View.PerformLayouted += View_LineBreakChanged;
62 this.View.PageBoundChanged += View_PageBoundChanged;
63 this.SelectionChanged += new EventHandler((s, e) => { });
64 this.Document.Clear();
70 public event EventHandler SelectionChanged;
73 /// 矩形選択モードなら真を返し、そうでない場合は偽を返す
75 public bool RectSelection
84 public IndentMode IndentMode
93 /// <remarks>SelectionLengthが0の場合、キャレット位置を表します</remarks>
94 public int SelectionStart
98 if (this.View.Selections.Count == 0)
99 return this.AnchorIndex;
101 return this.View.Selections.First().start;
108 /// <remarks>矩形選択モードの場合、選択範囲の文字数ではなく、開始位置から終了位置までの長さとなります</remarks>
109 public int SelectionLength
113 if (this.View.Selections.Count == 0)
115 Selection last = this.View.Selections.Last();
116 return last.start + last.length - this.SelectionStart;
124 /// 未選択状態で代入したときは追加され、そうでない場合は選択範囲の文字列と置き換えられます。
126 public string SelectedText
130 if (this.View.LayoutLines.Count == 0 || this.View.Selections.Count == 0)
132 if (this.RectSelection)
133 return GetTextFromRectangleSelectArea(this.View.Selections);
135 return GetTextFromLineSelectArea(this.View.Selections).Replace(Document.NewLine.ToString(), Environment.NewLine);
139 if (this.Document.FireUpdateEvent == false)
140 throw new InvalidOperationException("");
143 this.RepleaceSelectionArea(this.View.Selections, value.Replace(Environment.NewLine,Document.NewLine.ToString()));
148 /// 選択範囲が逆転しているかどうかを判定する
150 /// <returns>逆転しているなら真を返す</returns>
151 public bool IsReverseSelect()
153 int index = this.View.LayoutLines.GetIndexFromTextPoint(this.View.CaretPostion);
154 return index < this.AnchorIndex;
160 /// <param name="start"></param>
161 /// <param name="length"></param>
162 /// <remarks>RectSelectionの値によって動作が変わります。真の場合は矩形選択モードに、そうでない場合は行ごとに選択されます</remarks>
163 public void Select(int start, int length)
165 if (this.Document.FireUpdateEvent == false)
166 throw new InvalidOperationException("");
167 if (start < 0 || start + length < 0 || start + length > this.Document.Length)
168 throw new ArgumentOutOfRangeException("startかendが指定できる範囲を超えてます");
169 this.View.Selections.Clear();
172 int oldStart = start;
174 length = oldStart - start;
176 if (this.RectSelection && length != 0)
178 TextPoint startTextPoint = this.View.GetLayoutLineFromIndex(start);
179 TextPoint endTextPoint = this.View.GetLayoutLineFromIndex(start + length);
180 this.SelectByRectangle(new TextRectangle(startTextPoint, endTextPoint));
184 this.View.Selections.Add(Selection.Create(start, length));
186 this.SelectionChanged(this, null);
189 public void Select(TextPoint tp, int width, int height)
191 if (this.Document.FireUpdateEvent == false || !this.RectSelection)
192 throw new InvalidOperationException("");
195 end.row = tp.row + height;
196 end.col = tp.col + width;
198 if (end.row > this.View.LayoutLines.Count - 1)
199 throw new ArgumentOutOfRangeException("");
201 this.View.Selections.Clear();
203 this.SelectByRectangle(new TextRectangle(tp,end));
205 this.SelectionChanged(this, null);
208 private void SelectByRectangle(TextRectangle rect)
210 if (this.Document.FireUpdateEvent == false)
211 throw new InvalidOperationException("");
212 if (rect.TopLeft <= rect.BottomRight)
214 for (int i = rect.TopLeft.row; i <= rect.BottomLeft.row; i++)
216 int length = this.View.LayoutLines.GetLengthFromLineNumber(i);
217 int leftCol = rect.TopLeft.col, rightCol = rect.TopRight.col, lastCol = length;
218 if(length > 0 && this.View.LayoutLines[i][length - 1] == Document.NewLine)
219 lastCol = length - 1;
222 if (rect.TopLeft.col > lastCol)
224 if (rect.TopRight.col > lastCol)
227 int StartIndex = this.View.LayoutLines.GetIndexFromTextPoint(new TextPoint(i, leftCol));
228 int EndIndex = this.View.LayoutLines.GetIndexFromTextPoint(new TextPoint(i, rightCol));
231 sel = Selection.Create(StartIndex, EndIndex - StartIndex);
233 this.View.Selections.Add(sel);
241 /// <param name="index">探索を開始するインデックス</param>
242 /// <param name="changeAnchor">選択の起点となるとインデックスを変更するなら真。そうでなければ偽</param>
243 public void SelectWord(int index, bool changeAnchor = false)
245 if (this.Document.FireUpdateEvent == false)
246 throw new InvalidOperationException("");
248 if (this.Document.Length <= 0 || index >= this.Document.Length)
251 Document str = this.Document;
254 while (start > 0 && !Util.IsWordSeparator(str[start]))
257 if (Util.IsWordSeparator(str[start]))
261 while (end < this.Document.Length && !Util.IsWordSeparator(str[end]))
264 this.Select(start, end - start);
267 this.AnchorIndex = start;
271 /// 選択範囲内のUTF32コードポイントを文字列に変換します
273 /// <returns>成功した場合は真。そうでない場合は偽を返す</returns>
274 public bool ConvertToChar()
276 if (this.SelectionLength == 0 || this.RectSelection)
278 string str = this.Document.ToString(this.SelectionStart, this.SelectionLength);
279 string[] codes = str.Split(new char[] { ' ' },StringSplitOptions.RemoveEmptyEntries);
280 StringBuilder result = new StringBuilder();
281 foreach (string code in codes)
286 if (Int32.TryParse(code.TrimStart('U'),NumberStyles.HexNumber,null, out utf32_code))
287 result.Append(Char.ConvertFromUtf32(utf32_code));
291 this.Document.Lock();
292 this.Document.Replace(this.SelectionStart, this.SelectionLength, result.ToString());
293 this.Document.UnLock();
298 /// 選択文字列をUTF32のコードポイントに変換します
300 public void ConvertToCodePoint()
302 if (this.SelectionLength == 0 || this.RectSelection)
304 string str = this.Document.ToString(this.SelectionStart, this.SelectionLength);
305 StringInfo info = new StringInfo(str);
306 StringBuilder result = new StringBuilder();
307 for (int i = 0; i < str.Length;)
309 int utf32_code = Char.ConvertToUtf32(str, i);
310 result.Append("U" + Convert.ToString(utf32_code,16));
312 if(Char.IsHighSurrogate(str[i]))
317 this.Document.Lock();
318 this.Document.Replace(this.SelectionStart, this.SelectionLength, result.ToString());
319 this.Document.UnLock();
325 public void DeSelectAll()
327 if (this.Document.FireUpdateEvent == false)
328 throw new InvalidOperationException("");
330 this.View.Selections.Clear();
336 /// <param name="tp"></param>
337 /// <param name="type"></param>
338 /// <returns>真ならマーカーがある</returns>
339 public bool IsMarker(TextPoint tp,HilightType type)
341 if (this.Document.FireUpdateEvent == false)
342 throw new InvalidOperationException("");
343 int index = this.View.LayoutLines.GetIndexFromTextPoint(tp);
344 return this.IsMarker(index, type);
350 /// <param name="index"></param>
351 /// <param name="type"></param>
352 /// <returns>真ならマーカーがある</returns>
353 public bool IsMarker(int index, HilightType type)
355 foreach(int id in this.Document.Markers.IDs)
357 foreach (Marker m in this.Document.GetMarkers(index, id))
359 if (m.hilight == type)
369 public void AdjustCaret()
371 int row = this.View.CaretPostion.row;
372 if (row > this.View.LayoutLines.Count - 1)
373 row = this.View.LayoutLines.Count - 1;
374 int col = this.View.CaretPostion.col;
375 if (col > 0 && col > this.View.LayoutLines[row].Length)
376 col = this.View.LayoutLines[row].Length;
377 this.JumpCaret(row, col);
381 /// キャレットを指定した位置に移動させる
383 /// <param name="index"></param>
384 /// <param name="autoExpand">折り畳みを展開するなら真</param>
385 public void JumpCaret(int index,bool autoExpand = true)
387 if (index < 0 || index > this.Document.Length)
388 throw new ArgumentOutOfRangeException("indexが設定できる範囲を超えています");
389 TextPoint tp = this.View.GetLayoutLineFromIndex(index);
391 this.JumpCaret(tp.row, tp.col,autoExpand);
395 /// キャレットを指定した位置に移動させる
397 /// <param name="row"></param>
398 /// <param name="col"></param>
399 /// <param name="autoExpand">折り畳みを展開するなら真</param>
400 public void JumpCaret(int row, int col, bool autoExpand = true)
402 if (this.Document.FireUpdateEvent == false)
403 throw new InvalidOperationException("");
405 this.View.JumpCaret(row, col,autoExpand);
407 this.View.AdjustCaretAndSrc();
409 this.SelectWithMoveCaret(false);
415 /// <param name="row">行</param>
416 /// <param name="isSelected">選択状態にするかどうか</param>
417 public void JumpToLineHead(int row,bool isSelected)
419 this.View.JumpCaret(row, 0);
420 this.View.AdjustCaretAndSrc();
421 this.SelectWithMoveCaret(isSelected);
427 /// <param name="row">行</param>
428 /// <param name="isSelected">選択状態にするかどうか</param>
429 public void JumpToLineEnd(int row, bool isSelected)
431 this.View.JumpCaret(row, this.View.LayoutLines[row].Length - 1);
432 this.View.AdjustCaretAndSrc();
433 this.SelectWithMoveCaret(isSelected);
439 /// <param name="isSelected"></param>
440 public void JumpToHead(bool isSelected)
442 if (this.View.TryScroll(0, 0))
444 this.View.JumpCaret(0, 0);
445 this.View.AdjustCaretAndSrc();
446 this.SelectWithMoveCaret(isSelected);
452 /// <param name="isSelected"></param>
453 public void JumpToEnd(bool isSelected)
455 int srcRow = this.View.LayoutLines.Count - this.View.LineCountOnScreen - 1;
458 if (this.View.TryScroll(0, srcRow))
460 this.View.JumpCaret(this.View.LayoutLines.Count - 1, 0);
461 this.View.AdjustCaretAndSrc();
462 this.SelectWithMoveCaret(isSelected);
468 /// <param name="dir">方向を指定する</param>
469 /// <param name="delta">スクロールする量。ScrollDirectionの値がUpやDownなら行数。LeftやRightならピクセル単位の値となる</param>
470 /// <param name="isSelected">選択状態にするなら真</param>
471 /// <param name="withCaret">同時にキャレットを移動させるなら真</param>
472 public void Scroll(ScrollDirection dir, int delta, bool isSelected,bool withCaret)
474 if (this.Document.FireUpdateEvent == false)
475 throw new InvalidOperationException("");
476 int toRow = this.View.Src.Row;
477 double toX = this.View.Src.X;
480 case ScrollDirection.Up:
481 toRow = Math.Max(0, this.View.Src.Row - delta);
482 toRow = this.View.AdjustRow(toRow, false);
484 case ScrollDirection.Down:
485 toRow = Math.Min(this.View.Src.Row + delta, this.View.LayoutLines.Count - 1);
486 toRow = this.View.AdjustRow(toRow, true);
488 case ScrollDirection.Left:
491 case ScrollDirection.Right:
495 throw new ArgumentOutOfRangeException();
497 this.Scroll(toX, toRow, isSelected, withCaret);
503 /// <param name="toX">スクロール先の座標</param>
504 /// <param name="toRow">スクロール先の行</param>
505 /// <param name="isSelected">選択状態にするなら真</param>
506 /// <param name="withCaret">同時にキャレットを移動させるなら真</param>
507 public void Scroll(double toX, int toRow, bool isSelected, bool withCaret)
511 this.View.Scroll(toX, toRow);
512 this.View.JumpCaret(toRow, 0);
513 this.View.AdjustCaretAndSrc();
514 this.SelectWithMoveCaret(isSelected);
518 this.View.Scroll(toX, toRow);
519 this.View.IsFocused = false;
526 /// <returns>移動できない場合は真を返す</returns>
527 /// <param name="realLength">負の値なら左側へ、そうでないなら右側へ移動する</param>
528 /// <param name="isSelected">選択範囲とするなら真。そうでないなら偽</param>
529 /// <param name="alignWord">単語単位で移動するなら真。そうでないなら偽</param>
530 public void MoveCaretHorizontical(int realLength, bool isSelected,bool alignWord = false)
532 for (int i = Math.Abs(realLength); i > 0; i--)
534 bool MoveFlow = realLength > 0;
535 if (this.View.render.RightToLeft)
536 MoveFlow = !MoveFlow;
537 this.MoveCaretHorizontical(MoveFlow);
540 this.AlignNearestWord(MoveFlow);
542 this.View.AdjustCaretAndSrc(AdjustFlow.Col);
543 this.SelectWithMoveCaret(isSelected);
546 void AlignNearestWord(bool MoveFlow)
548 string str = this.View.LayoutLines[this.View.CaretPostion.row];
549 while (this.View.CaretPostion.col > 0 &&
550 this.View.CaretPostion.col < str.Length &&
551 str[this.View.CaretPostion.col] != Document.NewLine)
553 if (!Util.IsWordSeparator(str[this.View.CaretPostion.col]))
555 this.MoveCaretHorizontical(MoveFlow);
560 this.MoveCaretHorizontical(MoveFlow);
569 /// <returns>再描写する必要があるなら真を返す</returns>
570 /// <param name="deltarow">移動量</param>
571 /// <param name="isSelected"></param>
572 public void MoveCaretVertical(int deltarow,bool isSelected)
574 for (int i = Math.Abs(deltarow); i > 0; i--)
575 this.MoveCaretVertical(deltarow > 0);
576 this.View.AdjustCaretAndSrc(AdjustFlow.Both);
577 this.SelectWithMoveCaret(isSelected);
581 /// キャレット位置の文字を一文字削除する
583 public void DoDeleteAction()
585 if (this.SelectionLength != 0)
587 this.SelectedText = "";
591 if (this.Document.FireUpdateEvent == false)
592 throw new InvalidOperationException("");
594 TextPoint CaretPostion = this.View.CaretPostion;
595 int index = this.View.GetIndexFromLayoutLine(CaretPostion);
597 if (index == this.Document.Length)
600 int lineHeadIndex = this.View.LayoutLines.GetIndexFromLineNumber(CaretPostion.row);
601 int next = this.View.LayoutLines.GetLayout(CaretPostion.row).AlignIndexToNearestCluster(CaretPostion.col, AlignDirection.Forward) + lineHeadIndex;
603 if (this.Document[index] == Document.NewLine)
606 this.Document.Lock();
607 this.Document.Replace(index, next - index, "");
608 this.Document.UnLock();
611 public bool IsRectInsertMode()
613 if (!this.RectSelection)
615 foreach(Selection sel in this.View.Selections)
624 /// キャレット位置の文字を一文字削除し、キャレット位置を後ろにずらす
626 public void DoBackSpaceAction()
628 if (this.IsRectInsertMode())
630 this.ReplaceBeforeSelectionArea(this.View.Selections, 1, "");
633 else if (this.SelectionLength > 0)
635 this.SelectedText = "";
639 if (this.Document.FireUpdateEvent == false)
640 throw new InvalidOperationException("");
642 TextPoint CurrentPostion = this.View.CaretPostion;
644 if (CurrentPostion.row == 0 && CurrentPostion.col == 0)
647 int oldIndex = this.View.GetIndexFromLayoutLine(CurrentPostion);
649 int newCol, newIndex;
650 if (CurrentPostion.col > 0)
652 newCol = this.View.LayoutLines.GetLayout(CurrentPostion.row).AlignIndexToNearestCluster(CurrentPostion.col - 1, AlignDirection.Back);
653 newIndex = this.View.GetIndexFromLayoutLine(new TextPoint(CurrentPostion.row, newCol));
657 newIndex = this.View.GetIndexFromLayoutLine(CurrentPostion);
661 this.Document.Lock();
662 this.Document.Replace(newIndex, oldIndex - newIndex, "");
663 this.Document.UnLock();
669 public void DoEnterAction()
671 this.DoInputChar('\n');
675 /// キャレット位置に文字を入力し、その分だけキャレットを進める。isInsertModeの値により動作が変わります
677 /// <param name="ch"></param>
678 public void DoInputChar(char ch)
680 this.DoInputString(ch.ToString());
683 string GetIndentSpace(int col_index)
685 int space_count = this.View.TabStops - (col_index % this.View.TabStops);
686 return new string(Enumerable.Repeat(' ',space_count).ToArray());
690 /// キャレット位置に文字列を挿入し、その分だけキャレットを進める。isInsertModeの値により動作が変わります
692 /// <param name="str"></param>
693 /// <param name="fromTip"></param>
694 public void DoInputString(string str,bool fromTip = false)
696 TextPoint CaretPos = this.View.CaretPostion;
698 if (str == "\t" && this.IndentMode == IndentMode.Space)
699 str = this.GetIndentSpace(CaretPos.col);
701 if (this.IsRectInsertMode())
703 this.ReplaceBeforeSelectionArea(this.View.Selections, 0, str);
706 else if (this.SelectionLength != 0)
708 this.RepleaceSelectionArea(this.View.Selections, str, fromTip);
712 if (this.Document.FireUpdateEvent == false)
713 throw new InvalidOperationException("");
715 int index = this.View.GetIndexFromLayoutLine(this.View.CaretPostion);
717 if (this.View.InsertMode == false && index < this.Document.Length && this.Document[index] != Document.NewLine)
719 string lineString = this.View.LayoutLines[CaretPos.row];
720 int end = this.View.LayoutLines.GetLayout(CaretPos.row).AlignIndexToNearestCluster(CaretPos.col + str.Length - 1, AlignDirection.Forward);
721 if (end > lineString.Length - 1)
722 end = lineString.Length - 1;
723 end += this.View.LayoutLines.GetIndexFromLineNumber(CaretPos.row);
724 length = end - index;
726 if (str == Document.NewLine.ToString())
728 int lineHeadIndex = this.View.LayoutLines.GetIndexFromLineNumber(CaretPos.row);
729 int lineLength = this.View.LayoutLines.GetLengthFromLineNumber(CaretPos.row);
730 FoldingItem foldingData = this.View.LayoutLines.FoldingCollection.GetFarestHiddenFoldingData(lineHeadIndex, lineLength);
731 if (foldingData != null && !foldingData.Expand && index > foldingData.Start && index <= foldingData.End)
732 index = foldingData.End + 1;
734 this.Document.Lock();
735 this.Document.Replace(index, length, str);
736 this.Document.UnLock();
740 /// キャレットの移動に合わせて選択する
742 /// <param name="isSelected">選択状態にするかどうか</param>
744 /// キャレットを移動後、このメソッドを呼び出さない場合、Select()メソッドは正常に機能しません
746 void SelectWithMoveCaret(bool isSelected)
748 if (this.View.CaretPostion.col < 0 || this.View.CaretPostion.row < 0)
751 if (this.Document.FireUpdateEvent == false)
752 throw new InvalidOperationException("");
754 int CaretPostion = this.View.GetIndexFromLayoutLine(this.View.CaretPostion);
756 SelectCollection Selections = this.View.Selections;
759 this.Select(this.AnchorIndex, CaretPostion - this.AnchorIndex);
761 this.AnchorIndex = CaretPostion;
762 this.Select(CaretPostion, 0);
767 /// JumpCaretで移動した位置からキャレットを移動し、選択状態にする
769 /// <param name="tp"></param>
770 public void MoveCaretAndSelect(TextPoint tp)
772 int CaretPostion = this.View.GetIndexFromLayoutLine(tp);
773 this.Select(this.AnchorIndex, CaretPostion - this.AnchorIndex);
774 this.View.JumpCaret(tp.row, tp.col);
775 this.View.AdjustCaretAndSrc();
779 /// グリッパーとキャレットを同時に移動する
781 /// <param name="p">ポインターの座標</param>
782 /// <param name="hittedGripper">動かす対象となるグリッパー</param>
783 /// <returns>移動できた場合は真を返す。そうでなければ偽を返す</returns>
784 /// <remarks>グリッパー内にポインターが存在しない場合、グリッパーはポインターの座標近くの行に移動する</remarks>
785 public bool MoveCaretAndGripper(Point p, GripperView hittedGripper)
787 bool HittedCaret = false;
788 TextPoint tp = this.View.GetTextPointFromPostion(p);
789 if (tp == this.View.CaretPostion)
794 if (HittedCaret || hittedGripper != null)
796 if (hittedGripper != null)
798 tp = this.View.GetTextPointFromPostion(hittedGripper.AdjustPoint(p));
799 if (this.IsReverseSelect())
801 if (Object.ReferenceEquals(hittedGripper, this.View.SelectGrippers.BottomRight))
802 this.MoveSelectBefore(tp);
804 this.MoveCaretAndSelect(tp);
808 if (Object.ReferenceEquals(hittedGripper, this.View.SelectGrippers.BottomLeft))
809 this.MoveSelectBefore(tp);
811 this.MoveCaretAndSelect(tp);
813 hittedGripper.Move(this.View, tp);
817 tp = this.View.GetTextPointFromPostion(p);
818 if (tp != TextPoint.Null)
819 this.MoveCaretAndSelect(tp);
821 this.View.SelectGrippers.BottomLeft.Enabled = this.SelectionLength != 0;
827 public void MoveSelectBefore(TextPoint tp)
831 if (this.IsReverseSelect())
833 NewAnchorIndex = this.View.GetIndexFromLayoutLine(tp);
834 SelectionLength = this.SelectionLength + NewAnchorIndex - this.AnchorIndex;
835 this.Select(this.SelectionStart, SelectionLength);
839 NewAnchorIndex = this.View.GetIndexFromLayoutLine(tp);
840 SelectionLength = this.SelectionLength + this.AnchorIndex - NewAnchorIndex;
841 this.Select(NewAnchorIndex, SelectionLength);
843 this.AnchorIndex = NewAnchorIndex;
849 public void ResetCaretPostion()
855 /// 行単位で移動後のキャレット位置を取得する
857 /// <param name="count">移動量</param>
858 /// <param name="current">現在のキャレット位置</param>
859 /// <returns>移動後のキャレット位置</returns>
860 public TextPoint GetTextPointAfterMoveLine(int count, TextPoint current)
862 int row = current.row + count;
866 else if (row >= this.View.LayoutLines.Count)
867 row = this.View.LayoutLines.Count - 1;
869 row = this.View.AdjustRow(row, count > 0);
871 double colpos = this.View.GetColPostionFromIndex(current.row, current.col);
872 int col = this.View.GetIndexFromColPostion(row, colpos);
874 return new TextPoint(row, col);
878 /// 選択文字列のインデントを一つ増やす
880 public void UpIndent()
882 if (this.RectSelection || this.SelectionLength == 0)
884 int selectionStart = this.SelectionStart;
885 string insertStr = this.IndentMode == IndentMode.Space ? this.GetIndentSpace(0) : "\t";
886 string text = this.InsertLineHead(GetTextFromLineSelectArea(this.View.Selections), insertStr);
887 this.RepleaceSelectionArea(this.View.Selections,text);
888 this.Select(selectionStart, text.Length);
892 /// 選択文字列のインデントを一つ減らす
894 public void DownIndent()
896 if (this.RectSelection || this.SelectionLength == 0)
898 int selectionStart = this.SelectionStart;
899 string insertStr = this.IndentMode == IndentMode.Space ? this.GetIndentSpace(0) : "\t";
900 string text = this.RemoveLineHead(GetTextFromLineSelectArea(this.View.Selections), insertStr);
901 this.RepleaceSelectionArea(this.View.Selections, text);
902 this.Select(selectionStart, text.Length);
905 string InsertLineHead(string s, string str)
907 string[] lines = s.Split(new string[] { Document.NewLine.ToString() }, StringSplitOptions.None);
908 StringBuilder output = new StringBuilder();
909 for (int i = 0; i < lines.Length; i++)
911 if(lines[i].Length > 0)
912 output.Append(str + lines[i] + Document.NewLine);
913 else if(i < lines.Length - 1)
914 output.Append(lines[i] + Document.NewLine);
916 return output.ToString();
919 public string RemoveLineHead(string s, string str)
921 string[] lines = s.Split(new string[] { Document.NewLine.ToString() }, StringSplitOptions.None);
922 StringBuilder output = new StringBuilder();
923 for (int i = 0; i < lines.Length; i++)
925 if (lines[i].StartsWith(str))
926 output.Append(lines[i].Substring(1) + Document.NewLine);
927 else if (i < lines.Length - 1)
928 output.Append(lines[i] + Document.NewLine);
930 return output.ToString();
936 /// <param name="isMoveNext">真なら1文字すすめ、そうでなければ戻す</param>
937 /// <remarks>このメソッドを呼び出した後でScrollToCaretメソッドとSelectWithMoveCaretメソッドを呼び出す必要があります</remarks>
938 void MoveCaretHorizontical(bool isMoveNext)
940 if (this.Document.FireUpdateEvent == false)
941 throw new InvalidOperationException("");
942 int delta = isMoveNext ? 0 : -1;
943 int prevcol = this.View.CaretPostion.col;
944 int col = this.View.CaretPostion.col + delta;
945 string lineString = this.View.LayoutLines[this.View.CaretPostion.row];
946 if (col < 0 || this.View.CaretPostion.row >= this.View.LayoutLines.Count)
948 if (this.View.CaretPostion.row == 0)
953 this.MoveCaretVertical(false);
954 this.View.AdjustCaretAndSrc(AdjustFlow.Row); //この段階で調整しないとスクロールされない
955 col = this.View.LayoutLines.GetLengthFromLineNumber(this.View.CaretPostion.row) - 1; //最終行以外はすべて改行コードが付くはず
957 else if (col >= lineString.Length || lineString[col] == Document.NewLine)
959 if (this.View.CaretPostion.row < this.View.LayoutLines.Count - 1)
961 this.MoveCaretVertical(true);
962 this.View.AdjustCaretAndSrc(AdjustFlow.Row); //この段階で調整しないとスクロールされない
968 AlignDirection direction = isMoveNext ? AlignDirection.Forward : AlignDirection.Back;
969 col = this.View.LayoutLines.GetLayout(this.View.CaretPostion.row).AlignIndexToNearestCluster(col, direction);
972 this.View.JumpCaret(this.View.CaretPostion.row, col,false);
978 /// <param name="isMoveNext">プラス方向に移動するなら真</param>
979 /// <remarks>このメソッドを呼び出した後でScrollToCaretメソッドとSelectWithMoveCaretメソッドを呼び出す必要があります</remarks>
980 void MoveCaretVertical(bool isMoveNext)
982 if (this.Document.FireUpdateEvent == false)
983 throw new InvalidOperationException("");
985 TextPoint nextPoint = this.GetTextPointAfterMoveLine(isMoveNext ? 1 : -1, this.View.CaretPostion);
987 this.View.JumpCaret(nextPoint.row, nextPoint.col,false);
990 private void ReplaceBeforeSelectionArea(SelectCollection Selections, int removeLength, string insertStr)
992 if (removeLength == 0 && insertStr.Length == 0)
995 if (this.RectSelection == false || this.Document.FireUpdateEvent == false)
996 throw new InvalidOperationException();
998 SelectCollection temp = this.View.Selections;
999 int selectStart = temp.First().start;
1000 int selectEnd = temp.Last().start + temp.Last().length;
1002 //ドキュメント操作後に行うとうまくいかないので、あらかじめ取得しておく
1003 TextPoint start = this.View.LayoutLines.GetTextPointFromIndex(selectStart);
1004 TextPoint end = this.View.LayoutLines.GetTextPointFromIndex(selectEnd);
1006 bool reverse = temp.First().start > temp.Last().start;
1008 int lineHeadIndex = this.View.LayoutLines.GetIndexFromLineNumber(this.View.LayoutLines.GetLineNumberFromIndex(selectStart));
1009 if (selectStart - removeLength < lineHeadIndex)
1012 this.Document.UndoManager.BeginUndoGroup();
1013 this.Document.FireUpdateEvent = false;
1017 for (int i = 0; i < temp.Count; i++)
1019 this.ReplaceBeforeSelection(temp[i], removeLength, insertStr);
1024 for (int i = temp.Count - 1; i >= 0; i--)
1026 this.ReplaceBeforeSelection(temp[i], removeLength, insertStr);
1030 this.Document.FireUpdateEvent = true;
1031 this.Document.UndoManager.EndUndoGroup();
1033 int delta = insertStr.Length - removeLength;
1038 this.JumpCaret(start.row, start.col);
1040 this.JumpCaret(end.row, end.col);
1042 this.Select(start, 0, end.row - start.row);
1045 private void ReplaceBeforeSelection(Selection sel, int removeLength, string insertStr)
1047 sel = Util.NormalizeIMaker<Selection>(sel);
1048 this.Document.Lock();
1049 this.Document.Replace(sel.start - removeLength, removeLength, insertStr);
1050 this.Document.UnLock();
1053 private void RepleaceSelectionArea(SelectCollection Selections, string value,bool updateInsertPoint = false)
1058 if (this.RectSelection == false)
1060 Selection sel = Selection.Create(this.AnchorIndex, 0);
1061 if (Selections.Count > 0)
1062 sel = Util.NormalizeIMaker<Selection>(this.View.Selections.First());
1064 this.Document.Lock();
1065 this.Document.Replace(sel.start, sel.length, value);
1066 this.Document.UnLock();
1070 if (this.Document.FireUpdateEvent == false)
1071 throw new InvalidOperationException("");
1073 int StartIndex = this.SelectionStart;
1075 SelectCollection newInsertPoint = new SelectCollection();
1077 if (this.SelectionLength == 0)
1081 this.Document.Lock();
1083 this.Document.UndoManager.BeginUndoGroup();
1085 this.Document.FireUpdateEvent = false;
1087 string[] line = value.Split(new string[] { Document.NewLine.ToString() }, StringSplitOptions.RemoveEmptyEntries);
1089 TextPoint Current = this.View.GetLayoutLineFromIndex(this.SelectionStart);
1091 for (i = 0; i < line.Length && Current.row < this.View.LayoutLines.Count; i++, Current.row++)
1093 if (Current.col > this.View.LayoutLines[Current.row].Length)
1094 Current.col = this.View.LayoutLines[Current.row].Length;
1095 StartIndex = this.View.GetIndexFromLayoutLine(Current);
1096 this.Document.Replace(StartIndex, 0, line[i]);
1097 StartIndex += line[i].Length;
1100 for (; i < line.Length; i++)
1102 StartIndex = this.Document.Length;
1103 string str = Document.NewLine + line[i];
1104 this.Document.Replace(StartIndex, 0, str);
1105 StartIndex += str.Length;
1108 this.Document.FireUpdateEvent = true;
1110 this.Document.UndoManager.EndUndoGroup();
1112 this.Document.UnLock();
1116 SelectCollection temp = new SelectCollection(this.View.Selections); //コピーしないとReplaceCommandを呼び出した段階で書き換えられてしまう
1118 this.Document.Lock();
1120 this.Document.UndoManager.BeginUndoGroup();
1122 this.Document.FireUpdateEvent = false;
1124 if (temp.First().start < temp.Last().start)
1126 for (int i = temp.Count - 1; i >= 0; i--)
1128 Selection sel = Util.NormalizeIMaker<Selection>(temp[i]);
1130 StartIndex = sel.start;
1132 this.Document.Replace(sel.start, sel.length, value);
1134 newInsertPoint.Add(Selection.Create(sel.start + (value.Length - sel.length) * i,0));
1139 for (int i = 0; i < temp.Count; i++)
1141 Selection sel = Util.NormalizeIMaker<Selection>(temp[i]);
1143 StartIndex = sel.start;
1145 this.Document.Replace(sel.start, sel.length, value);
1147 newInsertPoint.Add(Selection.Create(sel.start + (value.Length - sel.length) * i, 0));
1151 this.Document.FireUpdateEvent = true;
1153 this.Document.UndoManager.EndUndoGroup();
1155 this.Document.UnLock();
1157 this.JumpCaret(StartIndex);
1158 if (updateInsertPoint && newInsertPoint.Count > 0)
1159 this.View.Selections = newInsertPoint;
1162 private string GetTextFromLineSelectArea(SelectCollection Selections)
1164 Selection sel = Util.NormalizeIMaker<Selection>(Selections.First());
1166 string str = this.Document.ToString(sel.start, sel.length);
1171 string GetTextFromRectangleSelectArea(SelectCollection Selections)
1173 StringBuilder temp = new StringBuilder();
1174 if (Selections.First().start < Selections.Last().start)
1176 for (int i = 0; i < this.View.Selections.Count; i++)
1178 Selection sel = Util.NormalizeIMaker<Selection>(Selections[i]);
1180 string str = this.Document.ToString(sel.start, sel.length);
1181 if (str.IndexOf(Environment.NewLine) == -1)
1182 temp.AppendLine(str);
1189 for (int i = this.View.Selections.Count - 1; i >= 0; i--)
1191 Selection sel = Util.NormalizeIMaker<Selection>(Selections[i]);
1193 string str = this.Document.ToString(sel.start, sel.length).Replace(Document.NewLine.ToString(), Environment.NewLine);
1194 if (str.IndexOf(Environment.NewLine) == -1)
1195 temp.AppendLine(str);
1200 return temp.ToString();
1203 void View_LineBreakChanged(object sender, EventArgs e)
1209 void View_PageBoundChanged(object sender, EventArgs e)
1211 if (this.View.LineBreak == LineBreakMethod.PageBound && this.View.PageBound.Width - this.View.LineBreakingMarginWidth > 0)
1212 this.View.PerfomLayouts();
1216 void render_ChangedRenderResource(object sender, ChangedRenderRsourceEventArgs e)
1218 if (e.type == ResourceType.Font)
1220 if (this.View.LineBreak == LineBreakMethod.PageBound)
1221 this.View.PerfomLayouts();
1224 if (e.type == ResourceType.InlineChar)
1226 int oldLineCountOnScreen = this.View.LineCountOnScreen;
1227 this.View.CalculateLineCountOnScreen();
1228 if(this.View.LineCountOnScreen != oldLineCountOnScreen)
1233 void render_ChangedRightToLeft(object sender, EventArgs e)
1238 void Document_Update(object sender, DocumentUpdateEventArgs e)
1242 case UpdateType.Replace:
1243 if(e.startIndex < this.Document.Length && this.Document[e.startIndex] == Document.NewLine)
1244 this.View.CalculateLineCountOnScreen();
1245 this.JumpCaret(e.startIndex + e.insertLength,true);
1247 case UpdateType.Clear:
1248 this.JumpCaret(0,0, false);