2 * Copyright (C) 2013 FooProject
\r
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
\r
4 * the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
\r
6 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
7 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
\r
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/>.
\r
12 using System.Globalization;
\r
15 using System.Diagnostics;
\r
16 using System.Text.RegularExpressions;
\r
18 using System.Drawing;
\r
21 namespace FooEditEngine
\r
23 internal enum MoveFlow
\r
28 internal enum ScrollDirection
\r
39 public enum IndentMode
\r
46 /// ユーザー側からの処理を担当するクラス。一部を除き、こちらで行われた操作はアンドゥの対象になります
\r
48 internal sealed class Controller
\r
54 public Controller(Document doc, EditView view)
\r
56 this.Document = doc;
\r
57 this.Document.Update += new DocumentUpdateEventHandler(Document_Update);
\r
59 this.View.render.ChangedRightToLeft += render_ChangedRightToLeft;
\r
60 this.View.render.ChangedRenderResource += render_ChangedRenderResource;
\r
61 this.View.PerformLayouted += View_LineBreakChanged;
\r
62 this.View.PageBoundChanged += View_PageBoundChanged;
\r
63 this.SelectionChanged += new EventHandler((s, e) => { });
\r
64 this.Document.Clear();
\r
70 public event EventHandler SelectionChanged;
\r
73 /// 矩形選択モードなら真を返し、そうでない場合は偽を返す
\r
75 public bool RectSelection
\r
84 public IndentMode IndentMode
\r
93 /// <remarks>SelectionLengthが0の場合、キャレット位置を表します</remarks>
\r
94 public int SelectionStart
\r
98 if (this.View.Selections.Count == 0)
\r
99 return this.AnchorIndex;
\r
101 return this.View.Selections.First().start;
\r
108 /// <remarks>矩形選択モードの場合、選択範囲の文字数ではなく、開始位置から終了位置までの長さとなります</remarks>
\r
109 public int SelectionLength
\r
113 if (this.View.Selections.Count == 0)
\r
115 Selection last = this.View.Selections.Last();
\r
116 return last.start + last.length - this.SelectionStart;
\r
124 /// 未選択状態で代入したときは追加され、そうでない場合は選択範囲の文字列と置き換えられます。
\r
126 public string SelectedText
\r
130 if (this.View.LayoutLines.Count == 0 || this.View.Selections.Count == 0)
\r
132 if (this.RectSelection)
\r
133 return GetTextFromRectangleSelectArea(this.View.Selections);
\r
135 return GetTextFromLineSelectArea(this.View.Selections).Replace(Document.NewLine.ToString(), Environment.NewLine);
\r
139 if (this.Document.FireUpdateEvent == false)
\r
140 throw new InvalidOperationException("");
\r
143 this.RepleaceSelectionArea(this.View.Selections, value.Replace(Environment.NewLine,Document.NewLine.ToString()));
\r
148 /// 選択範囲が逆転しているかどうかを判定する
\r
150 /// <returns>逆転しているなら真を返す</returns>
\r
151 public bool IsReverseSelect()
\r
153 int index = this.View.LayoutLines.GetIndexFromTextPoint(this.View.CaretPostion);
\r
154 return index < this.AnchorIndex;
\r
160 /// <param name="start"></param>
\r
161 /// <param name="length"></param>
\r
162 /// <remarks>RectSelectionの値によって動作が変わります。真の場合は矩形選択モードに、そうでない場合は行ごとに選択されます</remarks>
\r
163 public void Select(int start, int length)
\r
165 if (this.Document.FireUpdateEvent == false)
\r
166 throw new InvalidOperationException("");
\r
167 if (start < 0 || start + length < 0 || start + length > this.Document.Length)
\r
168 throw new ArgumentOutOfRangeException("startかendが指定できる範囲を超えてます");
\r
169 this.View.Selections.Clear();
\r
172 int oldStart = start;
\r
174 length = oldStart - start;
\r
176 if (this.RectSelection && length != 0)
\r
178 TextPoint startTextPoint = this.View.GetLayoutLineFromIndex(start);
\r
179 TextPoint endTextPoint = this.View.GetLayoutLineFromIndex(start + length);
\r
180 this.SelectByRectangle(new TextRectangle(startTextPoint, endTextPoint));
\r
181 if (startTextPoint.col == endTextPoint.col)
\r
182 this.View.InsertPoint = new SelectCollection(this.View.Selections);
\r
184 this.View.InsertPoint = null;
\r
186 else if(length != 0)
\r
188 this.View.Selections.Add(Selection.Create(start, length));
\r
189 this.View.InsertPoint = null;
\r
191 this.SelectionChanged(this, null);
\r
194 public void Select(TextPoint tp, int width, int height)
\r
196 if (this.Document.FireUpdateEvent == false || !this.RectSelection)
\r
197 throw new InvalidOperationException("");
\r
198 TextPoint end = tp;
\r
200 end.row = tp.row + height;
\r
201 end.col = tp.col + width;
\r
203 if (end.row > this.View.LayoutLines.Count - 1)
\r
204 throw new ArgumentOutOfRangeException("");
\r
206 this.View.Selections.Clear();
\r
208 this.SelectByRectangle(new TextRectangle(tp,end));
\r
211 this.View.InsertPoint = new SelectCollection(this.View.Selections);
\r
213 this.View.InsertPoint = null;
\r
215 this.SelectionChanged(this, null);
\r
218 private void SelectByRectangle(TextRectangle rect)
\r
220 if (this.Document.FireUpdateEvent == false)
\r
221 throw new InvalidOperationException("");
\r
222 if (rect.TopLeft <= rect.BottomRight)
\r
224 for (int i = rect.TopLeft.row; i <= rect.BottomLeft.row; i++)
\r
226 int length = this.View.LayoutLines.GetLengthFromLineNumber(i);
\r
227 int leftCol = rect.TopLeft.col, rightCol = rect.TopRight.col, lastCol = length;
\r
228 if(length > 0 && this.View.LayoutLines[i][length - 1] == Document.NewLine)
\r
229 lastCol = length - 1;
\r
232 if (rect.TopLeft.col > lastCol)
\r
234 if (rect.TopRight.col > lastCol)
\r
235 rightCol = lastCol;
\r
237 int StartIndex = this.View.LayoutLines.GetIndexFromTextPoint(new TextPoint(i, leftCol));
\r
238 int EndIndex = this.View.LayoutLines.GetIndexFromTextPoint(new TextPoint(i, rightCol));
\r
241 sel = Selection.Create(StartIndex, EndIndex - StartIndex);
\r
243 this.View.Selections.Add(sel);
\r
251 /// <param name="index">探索を開始するインデックス</param>
\r
252 public void SelectWord(int index)
\r
254 if (this.Document.FireUpdateEvent == false)
\r
255 throw new InvalidOperationException("");
\r
257 if (this.Document.Length <= 0 || index >= this.Document.Length)
\r
260 Document str = this.Document;
\r
263 while (start > 0 && !Util.IsWordSeparator(str[start]))
\r
266 if (Util.IsWordSeparator(str[start]))
\r
270 while (end < this.Document.Length && !Util.IsWordSeparator(str[end]))
\r
273 this.Select(start, end - start);
\r
277 /// 選択範囲内のUTF32コードポイントを文字列に変換します
\r
279 /// <returns>成功した場合は真。そうでない場合は偽を返す</returns>
\r
280 public bool ConvertToChar()
\r
282 if (this.SelectionLength == 0 || this.RectSelection)
\r
284 string str = this.Document.ToString(this.SelectionStart, this.SelectionLength);
\r
285 string[] codes = str.Split(new char[] { ' ' },StringSplitOptions.RemoveEmptyEntries);
\r
286 StringBuilder result = new StringBuilder();
\r
287 foreach (string code in codes)
\r
290 if (code[0] != 'U')
\r
292 if (Int32.TryParse(code.TrimStart('U'),NumberStyles.HexNumber,null, out utf32_code))
\r
293 result.Append(Char.ConvertFromUtf32(utf32_code));
\r
297 this.Document.Lock();
\r
298 this.Document.Replace(this.SelectionStart, this.SelectionLength, result.ToString());
\r
299 this.Document.UnLock();
\r
304 /// 選択文字列をUTF32のコードポイントに変換します
\r
306 public void ConvertToCodePoint()
\r
308 if (this.SelectionLength == 0 || this.RectSelection)
\r
310 string str = this.Document.ToString(this.SelectionStart, this.SelectionLength);
\r
311 StringInfo info = new StringInfo(str);
\r
312 StringBuilder result = new StringBuilder();
\r
313 for (int i = 0; i < str.Length;)
\r
315 int utf32_code = Char.ConvertToUtf32(str, i);
\r
316 result.Append("U" + Convert.ToString(utf32_code,16));
\r
317 result.Append(' ');
\r
318 if(Char.IsHighSurrogate(str[i]))
\r
323 this.Document.Lock();
\r
324 this.Document.Replace(this.SelectionStart, this.SelectionLength, result.ToString());
\r
325 this.Document.UnLock();
\r
331 public void DeSelectAll()
\r
333 if (this.Document.FireUpdateEvent == false)
\r
334 throw new InvalidOperationException("");
\r
336 this.View.Selections.Clear();
\r
342 /// <param name="tp"></param>
\r
343 /// <param name="type"></param>
\r
344 /// <returns>真ならマーカーがある</returns>
\r
345 public bool IsMarker(TextPoint tp,HilightType type)
\r
347 if (this.Document.FireUpdateEvent == false)
\r
348 throw new InvalidOperationException("");
\r
349 int index = this.View.LayoutLines.GetIndexFromTextPoint(tp);
\r
350 return this.IsMarker(index, type);
\r
354 /// 任意のマーカーかどうか判定する
\r
356 /// <param name="index"></param>
\r
357 /// <param name="type"></param>
\r
358 /// <returns>真ならマーカーがある</returns>
\r
359 public bool IsMarker(int index, HilightType type)
\r
361 foreach(int id in this.Document.Markers.IDs)
\r
363 foreach (Marker m in this.Document.GetMarkers(index, id))
\r
365 if (m.hilight == type)
\r
375 public void AdjustCaret()
\r
377 int row = this.View.CaretPostion.row;
\r
378 if (row > this.View.LayoutLines.Count - 1)
\r
379 row = this.View.LayoutLines.Count - 1;
\r
380 int col = this.View.CaretPostion.col;
\r
381 if (col > 0 && col > this.View.LayoutLines[row].Length)
\r
382 col = this.View.LayoutLines[row].Length;
\r
383 this.JumpCaret(row, col);
\r
387 /// キャレットを指定した位置に移動させる
\r
389 /// <param name="index"></param>
\r
390 /// <param name="autoExpand">折り畳みを展開するなら真</param>
\r
391 public void JumpCaret(int index,bool autoExpand = true)
\r
393 if (index < 0 || index > this.Document.Length)
\r
394 throw new ArgumentOutOfRangeException("indexが設定できる範囲を超えています");
\r
395 TextPoint tp = this.View.GetLayoutLineFromIndex(index);
\r
397 this.JumpCaret(tp.row, tp.col,autoExpand);
\r
401 /// キャレットを指定した位置に移動させる
\r
403 /// <param name="row"></param>
\r
404 /// <param name="col"></param>
\r
405 /// <param name="autoExpand">折り畳みを展開するなら真</param>
\r
406 public void JumpCaret(int row, int col, bool autoExpand = true)
\r
408 if (this.Document.FireUpdateEvent == false)
\r
409 throw new InvalidOperationException("");
\r
411 this.View.JumpCaret(row, col,autoExpand);
\r
413 this.View.AdjustCaretAndSrc();
\r
415 this.SelectWithMoveCaret(false);
\r
421 /// <param name="row">行</param>
\r
422 /// <param name="isSelected">選択状態にするかどうか</param>
\r
423 public void JumpToLineHead(int row,bool isSelected)
\r
425 this.View.JumpCaret(row, 0);
\r
426 this.View.AdjustCaretAndSrc();
\r
427 this.SelectWithMoveCaret(isSelected);
\r
433 /// <param name="row">行</param>
\r
434 /// <param name="isSelected">選択状態にするかどうか</param>
\r
435 public void JumpToLineEnd(int row, bool isSelected)
\r
437 this.View.JumpCaret(row, this.View.LayoutLines[row].Length - 1);
\r
438 this.View.AdjustCaretAndSrc();
\r
439 this.SelectWithMoveCaret(isSelected);
\r
445 /// <param name="isSelected"></param>
\r
446 public void JumpToHead(bool isSelected)
\r
448 if (this.View.TryScroll(0, 0))
\r
450 this.View.JumpCaret(0, 0);
\r
451 this.View.AdjustCaretAndSrc();
\r
452 this.SelectWithMoveCaret(isSelected);
\r
456 /// ドキュメントの終わりにに移動する
\r
458 /// <param name="isSelected"></param>
\r
459 public void JumpToEnd(bool isSelected)
\r
461 int srcRow = this.View.LayoutLines.Count - this.View.LineCountOnScreen - 1;
\r
464 if (this.View.TryScroll(0, srcRow))
\r
466 this.View.JumpCaret(this.View.LayoutLines.Count - 1, 0);
\r
467 this.View.AdjustCaretAndSrc();
\r
468 this.SelectWithMoveCaret(isSelected);
\r
474 /// <param name="dir">方向を指定する</param>
\r
475 /// <param name="delta">スクロールする量。ScrollDirectionの値がUpやDownなら行数。LeftやRightならピクセル単位の値となる</param>
\r
476 /// <param name="isSelected">選択状態にするなら真</param>
\r
477 /// <param name="withCaret">同時にキャレットを移動させるなら真</param>
\r
478 public void Scroll(ScrollDirection dir, int delta, bool isSelected,bool withCaret)
\r
480 if (this.Document.FireUpdateEvent == false)
\r
481 throw new InvalidOperationException("");
\r
482 int toRow = this.View.Src.Row;
\r
483 double toX = this.View.Src.X;
\r
486 case ScrollDirection.Up:
\r
487 toRow = Math.Max(0, this.View.Src.Row - delta);
\r
488 toRow = this.View.AdjustRow(toRow, false);
\r
490 case ScrollDirection.Down:
\r
491 toRow = Math.Min(this.View.Src.Row + delta, this.View.LayoutLines.Count - 1);
\r
492 toRow = this.View.AdjustRow(toRow, true);
\r
494 case ScrollDirection.Left:
\r
497 case ScrollDirection.Right:
\r
501 throw new ArgumentOutOfRangeException();
\r
503 this.Scroll(toX, toRow, isSelected, withCaret);
\r
509 /// <param name="toX">スクロール先の座標</param>
\r
510 /// <param name="toRow">スクロール先の行</param>
\r
511 /// <param name="isSelected">選択状態にするなら真</param>
\r
512 /// <param name="withCaret">同時にキャレットを移動させるなら真</param>
\r
513 public void Scroll(double toX, int toRow, bool isSelected, bool withCaret)
\r
517 this.View.Scroll(toX, toRow);
\r
518 this.View.JumpCaret(toRow, 0);
\r
519 this.View.AdjustCaretAndSrc();
\r
520 this.SelectWithMoveCaret(isSelected);
\r
524 this.View.Scroll(toX, toRow);
\r
525 this.View.IsFocused = false;
\r
530 /// キャレットを桁方向に移動させる
\r
532 /// <returns>移動できない場合は真を返す</returns>
\r
533 /// <param name="realLength">負の値なら左側へ、そうでないなら右側へ移動する</param>
\r
534 /// <param name="isSelected">選択範囲とするなら真。そうでないなら偽</param>
\r
535 /// <param name="alignWord">単語単位で移動するなら真。そうでないなら偽</param>
\r
536 public void MoveCaretHorizontical(int realLength, bool isSelected,bool alignWord = false)
\r
538 for (int i = Math.Abs(realLength); i > 0; i--)
\r
540 bool MoveFlow = realLength > 0;
\r
541 if (this.View.render.RightToLeft)
\r
542 MoveFlow = !MoveFlow;
\r
543 this.MoveCaretHorizontical(MoveFlow);
\r
546 this.AlignNearestWord(MoveFlow);
\r
548 this.View.AdjustCaretAndSrc(AdjustFlow.Col);
\r
549 this.SelectWithMoveCaret(isSelected);
\r
552 void AlignNearestWord(bool MoveFlow)
\r
554 string str = this.View.LayoutLines[this.View.CaretPostion.row];
\r
555 while (this.View.CaretPostion.col > 0 &&
\r
556 this.View.CaretPostion.col < str.Length &&
\r
557 str[this.View.CaretPostion.col] != Document.NewLine)
\r
559 if (!Util.IsWordSeparator(str[this.View.CaretPostion.col]))
\r
561 this.MoveCaretHorizontical(MoveFlow);
\r
566 this.MoveCaretHorizontical(MoveFlow);
\r
573 /// キャレットを行方向に移動させる
\r
575 /// <returns>再描写する必要があるなら真を返す</returns>
\r
576 /// <param name="deltarow">移動量</param>
\r
577 /// <param name="isSelected"></param>
\r
578 public void MoveCaretVertical(int deltarow,bool isSelected)
\r
580 for (int i = Math.Abs(deltarow); i > 0; i--)
\r
581 this.MoveCaretVertical(deltarow > 0);
\r
582 this.View.AdjustCaretAndSrc(AdjustFlow.Both);
\r
583 this.SelectWithMoveCaret(isSelected);
\r
587 /// キャレット位置の文字を一文字削除する
\r
589 public void DoDeleteAction()
\r
591 if (this.SelectionLength != 0)
\r
593 this.SelectedText = "";
\r
597 if (this.Document.FireUpdateEvent == false)
\r
598 throw new InvalidOperationException("");
\r
600 TextPoint CaretPostion = this.View.CaretPostion;
\r
601 int index = this.View.GetIndexFromLayoutLine(CaretPostion);
\r
603 if (index == this.Document.Length)
\r
606 int lineHeadIndex = this.View.LayoutLines.GetIndexFromLineNumber(CaretPostion.row);
\r
607 int next = this.View.LayoutLines.GetLayout(CaretPostion.row).AlignIndexToNearestCluster(CaretPostion.col, AlignDirection.Forward) + lineHeadIndex;
\r
609 if (this.Document[index] == Document.NewLine)
\r
612 this.Document.Lock();
\r
613 this.Document.Replace(index, next - index, "");
\r
614 this.Document.UnLock();
\r
618 /// キャレット位置の文字を一文字削除し、キャレット位置を後ろにずらす
\r
620 public void DoBackSpaceAction()
\r
622 if (this.View.InsertPoint != null)
\r
624 this.ReplaceBeforeSelectionArea(this.View.Selections, 1, "");
\r
627 else if (this.SelectionLength > 0)
\r
629 this.SelectedText = "";
\r
633 if (this.Document.FireUpdateEvent == false)
\r
634 throw new InvalidOperationException("");
\r
636 TextPoint CurrentPostion = this.View.CaretPostion;
\r
638 if (CurrentPostion.row == 0 && CurrentPostion.col == 0)
\r
641 int oldIndex = this.View.GetIndexFromLayoutLine(CurrentPostion);
\r
643 int newCol, newIndex;
\r
644 if (CurrentPostion.col > 0)
\r
646 newCol = this.View.LayoutLines.GetLayout(CurrentPostion.row).AlignIndexToNearestCluster(CurrentPostion.col - 1, AlignDirection.Back);
\r
647 newIndex = this.View.GetIndexFromLayoutLine(new TextPoint(CurrentPostion.row, newCol));
\r
651 newIndex = this.View.GetIndexFromLayoutLine(CurrentPostion);
\r
655 this.Document.Lock();
\r
656 this.Document.Replace(newIndex, oldIndex - newIndex, "");
\r
657 this.Document.UnLock();
\r
663 public void DoEnterAction()
\r
665 this.DoInputChar('\n');
\r
669 /// キャレット位置に文字を入力し、その分だけキャレットを進める。isInsertModeの値により動作が変わります
\r
671 /// <param name="ch"></param>
\r
672 public void DoInputChar(char ch)
\r
674 this.DoInputString(ch.ToString());
\r
677 string GetIndentSpace(int col_index)
\r
679 int space_count = this.View.TabStops - (col_index % this.View.TabStops);
\r
680 return new string(Enumerable.Repeat(' ',space_count).ToArray());
\r
684 /// キャレット位置に文字列を挿入し、その分だけキャレットを進める。isInsertModeの値により動作が変わります
\r
686 /// <param name="str"></param>
\r
687 /// <param name="fromTip"></param>
\r
688 public void DoInputString(string str,bool fromTip = false)
\r
690 TextPoint CaretPos = this.View.CaretPostion;
\r
692 if (str == "\t" && this.IndentMode == IndentMode.Space)
\r
693 str = this.GetIndentSpace(CaretPos.col);
\r
695 if (this.View.InsertPoint != null)
\r
697 this.ReplaceBeforeSelectionArea(this.View.Selections, 0, str);
\r
700 else if (this.SelectionLength != 0)
\r
702 this.RepleaceSelectionArea(this.View.Selections, str, fromTip);
\r
706 if (this.Document.FireUpdateEvent == false)
\r
707 throw new InvalidOperationException("");
\r
709 int index = this.View.GetIndexFromLayoutLine(this.View.CaretPostion);
\r
711 if (this.View.InsertMode == false && index < this.Document.Length && this.Document[index] != Document.NewLine)
\r
713 string lineString = this.View.LayoutLines[CaretPos.row];
\r
714 int end = this.View.LayoutLines.GetLayout(CaretPos.row).AlignIndexToNearestCluster(CaretPos.col + str.Length - 1, AlignDirection.Forward);
\r
715 if (end > lineString.Length - 1)
\r
716 end = lineString.Length - 1;
\r
717 end += this.View.LayoutLines.GetIndexFromLineNumber(CaretPos.row);
\r
718 length = end - index;
\r
720 if (str == Document.NewLine.ToString())
\r
722 int lineHeadIndex = this.View.LayoutLines.GetIndexFromLineNumber(CaretPos.row);
\r
723 int lineLength = this.View.LayoutLines.GetLengthFromLineNumber(CaretPos.row);
\r
724 FoldingItem foldingData = this.View.LayoutLines.FoldingCollection.GetFarestHiddenFoldingData(lineHeadIndex, lineLength);
\r
725 if (foldingData != null && !foldingData.Expand && index > foldingData.Start && index <= foldingData.End)
\r
726 index = foldingData.End + 1;
\r
728 this.Document.Lock();
\r
729 this.Document.Replace(index, length, str);
\r
730 this.Document.UnLock();
\r
734 /// キャレットの移動に合わせて選択する
\r
736 /// <param name="isSelected">選択状態にするかどうか</param>
\r
738 /// キャレットを移動後、このメソッドを呼び出さない場合、Select()メソッドは正常に機能しません
\r
740 void SelectWithMoveCaret(bool isSelected)
\r
742 if (this.View.CaretPostion.col < 0 || this.View.CaretPostion.row < 0)
\r
745 if (this.Document.FireUpdateEvent == false)
\r
746 throw new InvalidOperationException("");
\r
748 int CaretPostion = this.View.GetIndexFromLayoutLine(this.View.CaretPostion);
\r
750 SelectCollection Selections = this.View.Selections;
\r
753 this.Select(this.AnchorIndex, CaretPostion - this.AnchorIndex);
\r
755 this.AnchorIndex = CaretPostion;
\r
756 this.View.InsertPoint = null;
\r
757 this.Select(CaretPostion, 0);
\r
762 /// JumpCaretで移動した位置からキャレットを移動し、選択状態にする
\r
764 /// <param name="tp"></param>
\r
765 public void MoveCaretAndSelect(TextPoint tp)
\r
767 int CaretPostion = this.View.GetIndexFromLayoutLine(tp);
\r
768 this.Select(this.AnchorIndex, CaretPostion - this.AnchorIndex);
\r
769 this.View.JumpCaret(tp.row, tp.col);
\r
770 this.View.AdjustCaretAndSrc();
\r
773 public void MoveSelectBefore(TextPoint tp)
\r
775 int NewAnchorIndex;
\r
776 int SelectionLength;
\r
777 if (this.IsReverseSelect())
\r
779 NewAnchorIndex = this.View.GetIndexFromLayoutLine(tp);
\r
780 SelectionLength = this.SelectionLength + NewAnchorIndex - this.AnchorIndex;
\r
781 this.Select(this.SelectionStart, SelectionLength);
\r
785 NewAnchorIndex = this.View.GetIndexFromLayoutLine(tp);
\r
786 SelectionLength = this.SelectionLength + this.AnchorIndex - NewAnchorIndex;
\r
787 this.Select(NewAnchorIndex, SelectionLength);
\r
789 this.AnchorIndex = NewAnchorIndex;
\r
793 /// キャレット位置を既定の位置に戻す
\r
795 public void ResetCaretPostion()
\r
801 /// 行単位で移動後のキャレット位置を取得する
\r
803 /// <param name="count">移動量</param>
\r
804 /// <param name="current">現在のキャレット位置</param>
\r
805 /// <returns>移動後のキャレット位置</returns>
\r
806 public TextPoint GetTextPointAfterMoveLine(int count, TextPoint current)
\r
808 int row = current.row + count;
\r
812 else if (row >= this.View.LayoutLines.Count)
\r
813 row = this.View.LayoutLines.Count - 1;
\r
815 row = this.View.AdjustRow(row, count > 0);
\r
817 double colpos = this.View.GetColPostionFromIndex(current.row, current.col);
\r
818 int col = this.View.GetIndexFromColPostion(row, colpos);
\r
820 return new TextPoint(row, col);
\r
824 /// 選択文字列のインデントを一つ増やす
\r
826 public void UpIndent()
\r
828 if (this.RectSelection || this.SelectionLength == 0)
\r
830 int selectionStart = this.SelectionStart;
\r
831 string insertStr = this.IndentMode == IndentMode.Space ? this.GetIndentSpace(0) : "\t";
\r
832 string text = this.InsertLineHead(GetTextFromLineSelectArea(this.View.Selections), insertStr);
\r
833 this.RepleaceSelectionArea(this.View.Selections,text);
\r
834 this.Select(selectionStart, text.Length);
\r
838 /// 選択文字列のインデントを一つ減らす
\r
840 public void DownIndent()
\r
842 if (this.RectSelection || this.SelectionLength == 0)
\r
844 int selectionStart = this.SelectionStart;
\r
845 string insertStr = this.IndentMode == IndentMode.Space ? this.GetIndentSpace(0) : "\t";
\r
846 string text = this.RemoveLineHead(GetTextFromLineSelectArea(this.View.Selections), insertStr);
\r
847 this.RepleaceSelectionArea(this.View.Selections, text);
\r
848 this.Select(selectionStart, text.Length);
\r
851 string InsertLineHead(string s, string str)
\r
853 string[] lines = s.Split(new string[] { Document.NewLine.ToString() }, StringSplitOptions.None);
\r
854 StringBuilder output = new StringBuilder();
\r
855 for (int i = 0; i < lines.Length; i++)
\r
857 if(lines[i].Length > 0)
\r
858 output.Append(str + lines[i] + Document.NewLine);
\r
859 else if(i < lines.Length - 1)
\r
860 output.Append(lines[i] + Document.NewLine);
\r
862 return output.ToString();
\r
865 public string RemoveLineHead(string s, string str)
\r
867 string[] lines = s.Split(new string[] { Document.NewLine.ToString() }, StringSplitOptions.None);
\r
868 StringBuilder output = new StringBuilder();
\r
869 for (int i = 0; i < lines.Length; i++)
\r
871 if (lines[i].StartsWith(str))
\r
872 output.Append(lines[i].Substring(1) + Document.NewLine);
\r
873 else if (i < lines.Length - 1)
\r
874 output.Append(lines[i] + Document.NewLine);
\r
876 return output.ToString();
\r
882 /// <param name="isMoveNext">真なら1文字すすめ、そうでなければ戻す</param>
\r
883 /// <remarks>このメソッドを呼び出した後でScrollToCaretメソッドとSelectWithMoveCaretメソッドを呼び出す必要があります</remarks>
\r
884 void MoveCaretHorizontical(bool isMoveNext)
\r
886 if (this.Document.FireUpdateEvent == false)
\r
887 throw new InvalidOperationException("");
\r
888 int delta = isMoveNext ? 0 : -1;
\r
889 int prevcol = this.View.CaretPostion.col;
\r
890 int col = this.View.CaretPostion.col + delta;
\r
891 string lineString = this.View.LayoutLines[this.View.CaretPostion.row];
\r
892 if (col < 0 || this.View.CaretPostion.row >= this.View.LayoutLines.Count)
\r
894 if (this.View.CaretPostion.row == 0)
\r
899 this.MoveCaretVertical(false);
\r
900 this.View.AdjustCaretAndSrc(AdjustFlow.Row); //この段階で調整しないとスクロールされない
\r
901 col = this.View.LayoutLines.GetLengthFromLineNumber(this.View.CaretPostion.row) - 1; //最終行以外はすべて改行コードが付くはず
\r
903 else if (col >= lineString.Length || lineString[col] == Document.NewLine)
\r
905 if (this.View.CaretPostion.row < this.View.LayoutLines.Count - 1)
\r
907 this.MoveCaretVertical(true);
\r
908 this.View.AdjustCaretAndSrc(AdjustFlow.Row); //この段階で調整しないとスクロールされない
\r
914 AlignDirection direction = isMoveNext ? AlignDirection.Forward : AlignDirection.Back;
\r
915 col = this.View.LayoutLines.GetLayout(this.View.CaretPostion.row).AlignIndexToNearestCluster(col, direction);
\r
918 this.View.JumpCaret(this.View.CaretPostion.row, col,false);
\r
922 /// キャレットを行方向に移動させる
\r
924 /// <param name="isMoveNext">プラス方向に移動するなら真</param>
\r
925 /// <remarks>このメソッドを呼び出した後でScrollToCaretメソッドとSelectWithMoveCaretメソッドを呼び出す必要があります</remarks>
\r
926 void MoveCaretVertical(bool isMoveNext)
\r
928 if (this.Document.FireUpdateEvent == false)
\r
929 throw new InvalidOperationException("");
\r
931 TextPoint nextPoint = this.GetTextPointAfterMoveLine(isMoveNext ? 1 : -1, this.View.CaretPostion);
\r
933 this.View.JumpCaret(nextPoint.row, nextPoint.col,false);
\r
936 private void ReplaceBeforeSelectionArea(SelectCollection Selections, int removeLength, string insertStr)
\r
938 if (removeLength == 0 && insertStr.Length == 0)
\r
941 if (this.RectSelection == false || this.Document.FireUpdateEvent == false)
\r
942 throw new InvalidOperationException();
\r
944 SelectCollection temp = this.View.InsertPoint;
\r
945 int selectStart = temp.First().start;
\r
946 int selectEnd = temp.Last().start + temp.Last().length;
\r
948 //ドキュメント操作後に行うとうまくいかないので、あらかじめ取得しておく
\r
949 TextPoint start = this.View.LayoutLines.GetTextPointFromIndex(selectStart);
\r
950 TextPoint end = this.View.LayoutLines.GetTextPointFromIndex(selectEnd);
\r
952 bool reverse = temp.First().start > temp.Last().start;
\r
954 int lineHeadIndex = this.View.LayoutLines.GetIndexFromLineNumber(this.View.LayoutLines.GetLineNumberFromIndex(selectStart));
\r
955 if (selectStart - removeLength < lineHeadIndex)
\r
958 this.Document.UndoManager.BeginUndoGroup();
\r
959 this.Document.FireUpdateEvent = false;
\r
963 for (int i = 0; i < temp.Count; i++)
\r
965 this.ReplaceBeforeSelection(temp[i], removeLength, insertStr);
\r
970 for (int i = temp.Count - 1; i >= 0; i--)
\r
972 this.ReplaceBeforeSelection(temp[i], removeLength, insertStr);
\r
976 this.Document.FireUpdateEvent = true;
\r
977 this.Document.UndoManager.EndUndoGroup();
\r
979 int delta = insertStr.Length - removeLength;
\r
980 start.col += delta;
\r
984 this.JumpCaret(start.row, start.col);
\r
986 this.JumpCaret(end.row, end.col);
\r
988 this.Select(start, 0, end.row - start.row);
\r
991 private void ReplaceBeforeSelection(Selection sel, int removeLength, string insertStr)
\r
993 sel = Util.NormalizeIMaker<Selection>(sel);
\r
994 this.Document.Lock();
\r
995 this.Document.Replace(sel.start - removeLength, removeLength, insertStr);
\r
996 this.Document.UnLock();
\r
999 private void RepleaceSelectionArea(SelectCollection Selections, string value,bool updateInsertPoint = false)
\r
1001 if (value == null)
\r
1004 if (this.RectSelection == false)
\r
1006 Selection sel = Selection.Create(this.AnchorIndex, 0);
\r
1007 if (Selections.Count > 0)
\r
1008 sel = Util.NormalizeIMaker<Selection>(this.View.Selections.First());
\r
1010 this.Document.Lock();
\r
1011 this.Document.Replace(sel.start, sel.length, value);
\r
1012 this.Document.UnLock();
\r
1016 if (this.Document.FireUpdateEvent == false)
\r
1017 throw new InvalidOperationException("");
\r
1019 int StartIndex = this.SelectionStart;
\r
1021 SelectCollection newInsertPoint = new SelectCollection();
\r
1023 if (this.SelectionLength == 0)
\r
1027 this.Document.Lock();
\r
1029 this.Document.UndoManager.BeginUndoGroup();
\r
1031 this.Document.FireUpdateEvent = false;
\r
1033 string[] line = value.Split(new string[] { Document.NewLine.ToString() }, StringSplitOptions.RemoveEmptyEntries);
\r
1035 TextPoint Current = this.View.GetLayoutLineFromIndex(this.SelectionStart);
\r
1037 for (i = 0; i < line.Length && Current.row < this.View.LayoutLines.Count; i++, Current.row++)
\r
1039 if (Current.col > this.View.LayoutLines[Current.row].Length)
\r
1040 Current.col = this.View.LayoutLines[Current.row].Length;
\r
1041 StartIndex = this.View.GetIndexFromLayoutLine(Current);
\r
1042 this.Document.Replace(StartIndex, 0, line[i]);
\r
1043 StartIndex += line[i].Length;
\r
1046 for (; i < line.Length; i++)
\r
1048 StartIndex = this.Document.Length;
\r
1049 string str = Document.NewLine + line[i];
\r
1050 this.Document.Replace(StartIndex, 0, str);
\r
1051 StartIndex += str.Length;
\r
1054 this.Document.FireUpdateEvent = true;
\r
1056 this.Document.UndoManager.EndUndoGroup();
\r
1058 this.Document.UnLock();
\r
1062 SelectCollection temp = new SelectCollection(this.View.Selections); //コピーしないとReplaceCommandを呼び出した段階で書き換えられてしまう
\r
1064 this.Document.Lock();
\r
1066 this.Document.UndoManager.BeginUndoGroup();
\r
1068 this.Document.FireUpdateEvent = false;
\r
1070 if (temp.First().start < temp.Last().start)
\r
1072 for (int i = temp.Count - 1; i >= 0; i--)
\r
1074 Selection sel = Util.NormalizeIMaker<Selection>(temp[i]);
\r
1076 StartIndex = sel.start;
\r
1078 this.Document.Replace(sel.start, sel.length, value);
\r
1080 newInsertPoint.Add(Selection.Create(sel.start + (value.Length - sel.length) * i,0));
\r
1085 for (int i = 0; i < temp.Count; i++)
\r
1087 Selection sel = Util.NormalizeIMaker<Selection>(temp[i]);
\r
1089 StartIndex = sel.start;
\r
1091 this.Document.Replace(sel.start, sel.length, value);
\r
1093 newInsertPoint.Add(Selection.Create(sel.start + (value.Length - sel.length) * i, 0));
\r
1097 this.Document.FireUpdateEvent = true;
\r
1099 this.Document.UndoManager.EndUndoGroup();
\r
1101 this.Document.UnLock();
\r
1103 this.JumpCaret(StartIndex);
\r
1104 if (updateInsertPoint && newInsertPoint.Count > 0)
\r
1105 this.View.InsertPoint = newInsertPoint;
\r
1108 private string GetTextFromLineSelectArea(SelectCollection Selections)
\r
1110 Selection sel = Util.NormalizeIMaker<Selection>(Selections.First());
\r
1112 string str = this.Document.ToString(sel.start, sel.length);
\r
1117 string GetTextFromRectangleSelectArea(SelectCollection Selections)
\r
1119 StringBuilder temp = new StringBuilder();
\r
1120 if (Selections.First().start < Selections.Last().start)
\r
1122 for (int i = 0; i < this.View.Selections.Count; i++)
\r
1124 Selection sel = Util.NormalizeIMaker<Selection>(Selections[i]);
\r
1126 string str = this.Document.ToString(sel.start, sel.length);
\r
1127 if (str.IndexOf(Environment.NewLine) == -1)
\r
1128 temp.AppendLine(str);
\r
1135 for (int i = this.View.Selections.Count - 1; i >= 0; i--)
\r
1137 Selection sel = Util.NormalizeIMaker<Selection>(Selections[i]);
\r
1139 string str = this.Document.ToString(sel.start, sel.length).Replace(Document.NewLine.ToString(), Environment.NewLine);
\r
1140 if (str.IndexOf(Environment.NewLine) == -1)
\r
1141 temp.AppendLine(str);
\r
1146 return temp.ToString();
\r
1149 void View_LineBreakChanged(object sender, EventArgs e)
\r
1151 this.DeSelectAll();
\r
1152 this.AdjustCaret();
\r
1155 void View_PageBoundChanged(object sender, EventArgs e)
\r
1157 if (this.View.LineBreak == LineBreakMethod.PageBound && this.View.PageBound.Width - this.View.LineBreakingMarginWidth > 0)
\r
1158 this.View.PerfomLayouts();
\r
1159 this.AdjustCaret();
\r
1162 void render_ChangedRenderResource(object sender, ChangedRenderRsourceEventArgs e)
\r
1164 if (e.type == ResourceType.Font)
\r
1166 if (this.View.LineBreak == LineBreakMethod.PageBound)
\r
1167 this.View.PerfomLayouts();
\r
1168 this.AdjustCaret();
\r
1170 if (e.type == ResourceType.InlineChar)
\r
1172 int oldLineCountOnScreen = this.View.LineCountOnScreen;
\r
1173 this.View.CalculateLineCountOnScreen();
\r
1174 if(this.View.LineCountOnScreen != oldLineCountOnScreen)
\r
1175 this.AdjustCaret();
\r
1179 void render_ChangedRightToLeft(object sender, EventArgs e)
\r
1181 this.AdjustCaret();
\r
1184 void Document_Update(object sender, DocumentUpdateEventArgs e)
\r
1188 case UpdateType.Replace:
\r
1189 if(e.startIndex < this.Document.Length && this.Document[e.startIndex] == Document.NewLine)
\r
1190 this.View.CalculateLineCountOnScreen();
\r
1191 this.JumpCaret(e.startIndex + e.insertLength,true);
\r
1193 case UpdateType.Clear:
\r
1194 this.JumpCaret(0,0, false);
\r