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
36 /// ユーザー側からの処理を担当するクラス。一部を除き、こちらで行われた操作はアンドゥの対象になります
\r
38 internal sealed class Controller
\r
44 public Controller(Document doc, EditView view)
\r
46 this.Document = doc;
\r
47 this.Document.Update += new DocumentUpdateEventHandler(Document_Update);
\r
49 this.View.render.ChangedRightToLeft += render_ChangedRightToLeft;
\r
50 this.View.render.ChangedRenderResource += render_ChangedRenderResource;
\r
51 this.View.PerformLayouted += View_LineBreakChanged;
\r
52 this.View.PageBoundChanged += View_PageBoundChanged;
\r
53 this.Document.Clear();
\r
54 this.CaretMoved += new EventHandler((s, e) => { });
\r
60 public event EventHandler CaretMoved;
\r
64 /// 矩形選択モードなら真を返し、そうでない場合は偽を返す
\r
66 public bool RectSelection
\r
75 /// <remarks>SelectionLengthが0の場合、キャレット位置を表します</remarks>
\r
76 public int SelectionStart
\r
80 if (this.View.Selections.Count == 0)
\r
81 return this.AnchorIndex;
\r
83 return this.View.Selections.First().start;
\r
90 /// <remarks>矩形選択モードの場合、選択範囲の文字数ではなく、開始位置から終了位置までの長さとなります</remarks>
\r
91 public int SelectionLength
\r
95 if (this.View.Selections.Count == 0)
\r
97 Selection last = this.View.Selections.Last();
\r
98 return last.start + last.length - this.SelectionStart;
\r
106 /// 未選択状態で代入したときは追加され、そうでない場合は選択範囲の文字列と置き換えられます。
\r
108 public string SelectedText
\r
112 if (this.View.LayoutLines.Count == 0 || this.View.Selections.Count == 0)
\r
115 if (this.RectSelection)
\r
116 str = GetTextFromRectangleSelectArea(this.View.Selections);
\r
118 str = GetTextFromLineSelectArea(this.View.Selections);
\r
119 return str.Replace(Document.NewLine.ToString(), Environment.NewLine);
\r
123 if (this.Document.FireUpdateEvent == false)
\r
124 throw new InvalidOperationException("");
\r
127 this.RepleaceSelectionArea(this.View.Selections, value.Replace(Environment.NewLine,Document.NewLine.ToString()));
\r
132 /// 選択範囲が逆転しているかどうかを判定する
\r
134 /// <returns>逆転しているなら真を返す</returns>
\r
135 public bool IsReverseSelect()
\r
137 int index = this.View.LayoutLines.GetIndexFromTextPoint(this.View.CaretPostion);
\r
138 return index < this.AnchorIndex;
\r
144 /// <param name="start"></param>
\r
145 /// <param name="length"></param>
\r
146 /// <remarks>RectSelectionの値によって動作が変わります。真の場合は矩形選択モードに、そうでない場合は行ごとに選択されます</remarks>
\r
147 public void Select(int start, int length)
\r
149 if (this.Document.FireUpdateEvent == false)
\r
150 throw new InvalidOperationException("");
\r
151 if (start < 0 || start + length < 0 || start + length > this.Document.Length)
\r
152 throw new ArgumentOutOfRangeException("startかendが指定できる範囲を超えてます");
\r
153 this.View.Selections.Clear();
\r
156 int oldStart = start;
\r
158 length = oldStart - start;
\r
160 if (this.RectSelection && length != 0)
\r
162 TextPoint startTextPoint = this.View.GetLayoutLineFromIndex(start);
\r
163 TextPoint endTextPoint = this.View.GetLayoutLineFromIndex(start + length);
\r
164 this.SelectByRectangle(new TextRectangle(startTextPoint, endTextPoint));
\r
165 if (startTextPoint.col == endTextPoint.col)
\r
166 this.View.InsertPoint = new SelectCollection(this.View.Selections);
\r
168 this.View.InsertPoint = null;
\r
172 this.View.Selections.Add(Selection.Create(start, length));
\r
173 this.View.InsertPoint = null;
\r
177 public void Select(TextPoint tp, int width, int height)
\r
179 if (this.Document.FireUpdateEvent == false || !this.RectSelection)
\r
180 throw new InvalidOperationException("");
\r
181 TextPoint end = tp;
\r
183 end.row = tp.row + height;
\r
184 end.col = tp.col + width;
\r
186 if (end.row > this.View.LayoutLines.Count - 1)
\r
187 throw new ArgumentOutOfRangeException("");
\r
189 this.View.Selections.Clear();
\r
191 this.SelectByRectangle(new TextRectangle(tp,end));
\r
194 this.View.InsertPoint = new SelectCollection(this.View.Selections);
\r
196 this.View.InsertPoint = null;
\r
199 private void SelectByRectangle(TextRectangle rect)
\r
201 if (this.Document.FireUpdateEvent == false)
\r
202 throw new InvalidOperationException("");
\r
203 if (rect.TopLeft <= rect.BottomRight)
\r
205 for (int i = rect.TopLeft.row; i <= rect.BottomLeft.row; i++)
\r
207 int length = this.View.LayoutLines.GetLengthFromLineNumber(i);
\r
208 int leftCol = rect.TopLeft.col, rightCol = rect.TopRight.col, lastCol = length;
\r
209 if(length > 0 && this.View.LayoutLines[i][length - 1] == Document.NewLine)
\r
210 lastCol = length - 1;
\r
213 if (rect.TopLeft.col > lastCol)
\r
215 if (rect.TopRight.col > lastCol)
\r
216 rightCol = lastCol;
\r
218 int StartIndex = this.View.LayoutLines.GetIndexFromTextPoint(new TextPoint(i, leftCol));
\r
219 int EndIndex = this.View.LayoutLines.GetIndexFromTextPoint(new TextPoint(i, rightCol));
\r
222 sel = Selection.Create(StartIndex, EndIndex - StartIndex);
\r
224 this.View.Selections.Add(sel);
\r
232 /// <param name="index">探索を開始するインデックス</param>
\r
233 public void SelectWord(int index)
\r
235 if (this.Document.FireUpdateEvent == false)
\r
236 throw new InvalidOperationException("");
\r
238 if (this.Document.Length <= 0 || index >= this.Document.Length)
\r
241 Document str = this.Document;
\r
244 while (start > 0 && !Util.IsWordSeparator(str[start]))
\r
247 if (Util.IsWordSeparator(str[start]))
\r
251 while (end < this.Document.Length && !Util.IsWordSeparator(str[end]))
\r
254 this.Select(start, end - start);
\r
258 /// 選択範囲内のUTF32コードポイントを文字列に変換します
\r
260 /// <returns>成功した場合は真。そうでない場合は偽を返す</returns>
\r
261 public bool ConvertToChar()
\r
263 if (this.SelectionLength == 0 || this.RectSelection)
\r
265 string str = this.Document.ToString(this.SelectionStart, this.SelectionLength);
\r
266 string[] codes = str.Split(new char[] { ' ' },StringSplitOptions.RemoveEmptyEntries);
\r
267 StringBuilder result = new StringBuilder();
\r
268 foreach (string code in codes)
\r
271 if (code[0] != 'U')
\r
273 if (Int32.TryParse(code.TrimStart('U'),NumberStyles.HexNumber,null, out utf32_code))
\r
274 result.Append(Char.ConvertFromUtf32(utf32_code));
\r
278 this.Document.Replace(this.SelectionStart, this.SelectionLength, result.ToString());
\r
283 /// 選択文字列をUTF32のコードポイントに変換します
\r
285 public void ConvertToCodePoint()
\r
287 if (this.SelectionLength == 0 || this.RectSelection)
\r
289 string str = this.Document.ToString(this.SelectionStart, this.SelectionLength);
\r
290 StringInfo info = new StringInfo(str);
\r
291 StringBuilder result = new StringBuilder();
\r
292 for (int i = 0; i < str.Length;)
\r
294 int utf32_code = Char.ConvertToUtf32(str, i);
\r
295 result.Append("U" + Convert.ToString(utf32_code,16));
\r
296 result.Append(' ');
\r
297 if(Char.IsHighSurrogate(str[i]))
\r
302 this.Document.Replace(this.SelectionStart, this.SelectionLength, result.ToString());
\r
308 public void DeSelectAll()
\r
310 if (this.Document.FireUpdateEvent == false)
\r
311 throw new InvalidOperationException("");
\r
313 this.View.Selections.Clear();
\r
319 /// <param name="tp"></param>
\r
320 /// <param name="type"></param>
\r
321 /// <returns>真ならマーカーがある</returns>
\r
322 public bool IsMarker(TextPoint tp,HilightType type)
\r
324 if (this.Document.FireUpdateEvent == false)
\r
325 throw new InvalidOperationException("");
\r
326 int index = this.View.LayoutLines.GetIndexFromTextPoint(tp);
\r
327 return this.IsMarker(index, type);
\r
331 /// 任意のマーカーかどうか判定する
\r
333 /// <param name="index"></param>
\r
334 /// <param name="type"></param>
\r
335 /// <returns>真ならマーカーがある</returns>
\r
336 public bool IsMarker(int index, HilightType type)
\r
338 foreach (Marker m in this.Document.GetMarkers(index))
\r
340 if (m.hilight == type)
\r
349 public void AdjustCaret()
\r
351 int row = this.View.CaretPostion.row;
\r
352 if (row > this.View.LayoutLines.Count - 1)
\r
353 row = this.View.LayoutLines.Count - 1;
\r
354 int col = this.View.CaretPostion.col;
\r
355 if (col > 0 && col > this.View.LayoutLines[row].Length)
\r
356 col = this.View.LayoutLines[row].Length;
\r
357 this.JumpCaret(row, col);
\r
361 /// キャレットを指定した位置に移動させる
\r
363 /// <param name="index"></param>
\r
364 /// <param name="autoExpand">折り畳みを展開するなら真</param>
\r
365 public void JumpCaret(int index,bool autoExpand = true)
\r
367 if (index < 0 || index > this.Document.Length)
\r
368 throw new ArgumentOutOfRangeException("indexが設定できる範囲を超えています");
\r
369 TextPoint tp = this.View.GetLayoutLineFromIndex(index);
\r
371 this.JumpCaret(tp.row, tp.col,autoExpand);
\r
375 /// キャレットを指定した位置に移動させる
\r
377 /// <param name="row"></param>
\r
378 /// <param name="col"></param>
\r
379 /// <param name="autoExpand">折り畳みを展開するなら真</param>
\r
380 public void JumpCaret(int row, int col, bool autoExpand = true)
\r
382 if (this.Document.FireUpdateEvent == false)
\r
383 throw new InvalidOperationException("");
\r
385 this.View.JumpCaret(row, col,autoExpand);
\r
387 this.View.AdjustCaretAndSrc();
\r
389 this.SelectWithMoveCaret(false);
\r
391 this.CaretMoved(this, null);
\r
397 /// <param name="isSelected"></param>
\r
398 public void JumpToHead(bool isSelected)
\r
400 if (this.View.TryScroll(0, 0))
\r
402 this.View.JumpCaret(0, 0);
\r
403 this.View.AdjustCaretAndSrc();
\r
404 this.SelectWithMoveCaret(isSelected);
\r
405 this.CaretMoved(this, null);
\r
409 /// ドキュメントの終わりにに移動する
\r
411 /// <param name="isSelected"></param>
\r
412 public void JumpToEnd(bool isSelected)
\r
414 int srcRow = this.View.LayoutLines.Count - this.View.LineCountOnScreen - 1;
\r
417 if (this.View.TryScroll(0, srcRow))
\r
419 this.View.JumpCaret(this.View.LayoutLines.Count - 1, 0);
\r
420 this.View.AdjustCaretAndSrc();
\r
421 this.SelectWithMoveCaret(isSelected);
\r
422 this.CaretMoved(this, null);
\r
428 /// <param name="dir">方向を指定する</param>
\r
429 /// <param name="delta">スクロールする量。ScrollDirectionの値がUpやDownなら行数。LeftやRightならピクセル単位の値となる</param>
\r
430 /// <param name="isSelected">選択状態にするなら真</param>
\r
431 /// <param name="withCaret">同時にキャレットを移動させるなら真</param>
\r
432 public void Scroll(ScrollDirection dir, int delta, bool isSelected,bool withCaret)
\r
434 if (this.Document.FireUpdateEvent == false)
\r
435 throw new InvalidOperationException("");
\r
436 int toRow = this.View.Src.Row;
\r
437 double toX = this.View.Src.X;
\r
440 case ScrollDirection.Up:
\r
441 toRow = Math.Max(0, this.View.Src.Row - delta);
\r
442 toRow = this.View.AdjustRow(toRow, false);
\r
444 case ScrollDirection.Down:
\r
445 toRow = Math.Min(this.View.Src.Row + delta, this.View.LayoutLines.Count - 1);
\r
446 toRow = this.View.AdjustRow(toRow, true);
\r
448 case ScrollDirection.Left:
\r
451 case ScrollDirection.Right:
\r
455 throw new ArgumentOutOfRangeException();
\r
457 this.Scroll(toX, toRow, isSelected, withCaret);
\r
463 /// <param name="toX">スクロール先の座標</param>
\r
464 /// <param name="toRow">スクロール先の行</param>
\r
465 /// <param name="isSelected">選択状態にするなら真</param>
\r
466 /// <param name="withCaret">同時にキャレットを移動させるなら真</param>
\r
467 public void Scroll(double toX, int toRow, bool isSelected, bool withCaret)
\r
471 this.View.Scroll(toX, toRow);
\r
472 this.View.JumpCaret(toRow, 0);
\r
473 this.View.AdjustCaretAndSrc();
\r
474 this.SelectWithMoveCaret(isSelected);
\r
475 this.CaretMoved(this, null);
\r
479 this.View.Scroll(toX, toRow);
\r
480 this.View.HideCaret = true;
\r
481 this.View.HideLineMarker = true;
\r
486 /// キャレットを桁方向に移動させる
\r
488 /// <returns>移動できない場合は真を返す</returns>
\r
489 /// <param name="realLength">負の値なら左側へ、そうでないなら右側へ移動する</param>
\r
490 /// <param name="isSelected">選択範囲とするなら真。そうでないなら偽</param>
\r
491 /// <param name="alignWord">単語単位で移動するなら真。そうでないなら偽</param>
\r
492 public void MoveCaretHorizontical(int realLength, bool isSelected,bool alignWord = false)
\r
494 for (int i = Math.Abs(realLength); i > 0; i--)
\r
496 bool MoveFlow = realLength > 0;
\r
497 if (this.View.render.RightToLeft)
\r
498 MoveFlow = !MoveFlow;
\r
499 this.MoveCaretHorizontical(MoveFlow);
\r
502 this.AlignNearestWord(MoveFlow);
\r
504 this.View.AdjustCaretAndSrc(AdjustFlow.Col);
\r
505 this.SelectWithMoveCaret(isSelected);
\r
506 this.CaretMoved(this, null);
\r
509 void AlignNearestWord(bool MoveFlow)
\r
511 string str = this.View.LayoutLines[this.View.CaretPostion.row];
\r
512 while (this.View.CaretPostion.col > 0 &&
\r
513 this.View.CaretPostion.col < str.Length &&
\r
514 str[this.View.CaretPostion.col] != Document.NewLine)
\r
516 if (!Util.IsWordSeparator(str[this.View.CaretPostion.col]))
\r
518 this.MoveCaretHorizontical(MoveFlow);
\r
523 this.MoveCaretHorizontical(MoveFlow);
\r
530 /// キャレットを行方向に移動させる
\r
532 /// <returns>再描写する必要があるなら真を返す</returns>
\r
533 /// <param name="deltarow">移動量</param>
\r
534 /// <param name="isSelected"></param>
\r
535 public void MoveCaretVertical(int deltarow,bool isSelected)
\r
537 for (int i = Math.Abs(deltarow); i > 0; i--)
\r
538 this.MoveCaretVertical(deltarow > 0);
\r
539 this.View.AdjustCaretAndSrc(AdjustFlow.Both);
\r
540 this.SelectWithMoveCaret(isSelected);
\r
541 this.CaretMoved(this, null);
\r
545 /// キャレット位置の文字を一文字削除する
\r
547 public void DoDeleteAction()
\r
549 if (this.SelectionLength != 0)
\r
551 this.SelectedText = "";
\r
555 if (this.Document.FireUpdateEvent == false)
\r
556 throw new InvalidOperationException("");
\r
558 TextPoint CaretPostion = this.View.CaretPostion;
\r
559 int index = this.View.GetIndexFromLayoutLine(CaretPostion);
\r
561 if (index == this.Document.Length)
\r
564 int lineHeadIndex = this.View.LayoutLines.GetIndexFromLineNumber(CaretPostion.row);
\r
565 int next = this.View.LayoutLines.GetLayout(CaretPostion.row).AlignIndexToNearestCluster(CaretPostion.col, AlignDirection.Forward) + lineHeadIndex;
\r
567 if (this.Document[index] == Document.NewLine)
\r
570 this.Document.Replace(index, next - index, "");
\r
574 /// キャレット位置の文字を一文字削除し、キャレット位置を後ろにずらす
\r
576 public void DoBackSpaceAction()
\r
578 if (this.View.InsertPoint != null)
\r
580 this.ReplaceBeforeSelectionArea(this.View.Selections, 1, "");
\r
583 else if (this.SelectionLength > 0)
\r
585 this.SelectedText = "";
\r
589 if (this.Document.FireUpdateEvent == false)
\r
590 throw new InvalidOperationException("");
\r
592 TextPoint CurrentPostion = this.View.CaretPostion;
\r
594 if (CurrentPostion.row == 0 && CurrentPostion.col == 0)
\r
597 int oldIndex = this.View.GetIndexFromLayoutLine(CurrentPostion);
\r
599 int newCol, newIndex;
\r
600 if (CurrentPostion.col > 0)
\r
602 newCol = this.View.LayoutLines.GetLayout(CurrentPostion.row).AlignIndexToNearestCluster(CurrentPostion.col - 1, AlignDirection.Back);
\r
603 newIndex = this.View.GetIndexFromLayoutLine(new TextPoint(CurrentPostion.row, newCol));
\r
607 newIndex = this.View.GetIndexFromLayoutLine(CurrentPostion);
\r
611 this.Document.Replace(newIndex, oldIndex - newIndex, "");
\r
617 public void DoEnterAction()
\r
619 this.DoInputChar('\n');
\r
623 /// キャレット位置に文字を入力し、その分だけキャレットを進める。isInsertModeの値により動作が変わります
\r
625 /// <param name="ch"></param>
\r
626 public void DoInputChar(char ch)
\r
628 this.DoInputString(ch.ToString());
\r
632 /// キャレット位置に文字列を挿入し、その分だけキャレットを進める。isInsertModeの値により動作が変わります
\r
634 /// <param name="str"></param>
\r
635 /// <param name="fromTip"></param>
\r
636 public void DoInputString(string str,bool fromTip = false)
\r
638 if (this.View.InsertPoint != null)
\r
640 this.ReplaceBeforeSelectionArea(this.View.Selections, 0, str);
\r
643 else if (this.SelectionLength != 0)
\r
645 this.RepleaceSelectionArea(this.View.Selections, str, fromTip);
\r
649 if (this.Document.FireUpdateEvent == false)
\r
650 throw new InvalidOperationException("");
\r
652 int index = this.View.GetIndexFromLayoutLine(this.View.CaretPostion);
\r
654 TextPoint CaretPos = this.View.CaretPostion;
\r
655 if (this.View.InsertMode == false && index < this.Document.Length && this.Document[index] != Document.NewLine)
\r
657 string lineString = this.View.LayoutLines[CaretPos.row];
\r
658 int end = this.View.LayoutLines.GetLayout(CaretPos.row).AlignIndexToNearestCluster(CaretPos.col + str.Length - 1, AlignDirection.Forward);
\r
659 if (end > lineString.Length - 1)
\r
660 end = lineString.Length - 1;
\r
661 end += this.View.LayoutLines.GetIndexFromLineNumber(CaretPos.row);
\r
662 length = end - index;
\r
664 if (str == Document.NewLine.ToString())
\r
666 int lineHeadIndex = this.View.LayoutLines.GetIndexFromLineNumber(CaretPos.row);
\r
667 int lineLength = this.View.LayoutLines.GetLengthFromLineNumber(CaretPos.row);
\r
668 FoldingItem foldingData = this.View.LayoutLines.FoldingCollection.GetFarestHiddenFoldingData(lineHeadIndex, lineLength);
\r
669 if (foldingData != null && !foldingData.Expand && index > foldingData.Start && index <= foldingData.End)
\r
670 index = foldingData.End + 1;
\r
672 this.Document.Replace(index, length, str);
\r
676 /// キャレットの移動に合わせて選択する
\r
678 /// <param name="isSelected">選択状態にするかどうか</param>
\r
680 /// キャレットを移動後、このメソッドを呼び出さない場合、Select()メソッドは正常に機能しません
\r
682 void SelectWithMoveCaret(bool isSelected)
\r
684 if (this.View.CaretPostion.col < 0 || this.View.CaretPostion.row < 0)
\r
687 if (this.Document.FireUpdateEvent == false)
\r
688 throw new InvalidOperationException("");
\r
690 int CaretPostion = this.View.GetIndexFromLayoutLine(this.View.CaretPostion);
\r
692 SelectCollection Selections = this.View.Selections;
\r
694 this.Select(this.AnchorIndex, CaretPostion - this.AnchorIndex);
\r
697 this.View.Selections.Clear();
\r
698 this.AnchorIndex = CaretPostion;
\r
699 this.View.InsertPoint = null;
\r
704 /// JumpCaretで移動した位置からキャレットを移動し、選択状態にする
\r
706 /// <param name="tp"></param>
\r
707 public void MoveCaretAndSelect(TextPoint tp)
\r
709 int CaretPostion = this.View.GetIndexFromLayoutLine(tp);
\r
710 this.Select(this.AnchorIndex, CaretPostion - this.AnchorIndex);
\r
711 this.View.JumpCaret(tp.row, tp.col);
\r
712 this.View.AdjustCaretAndSrc();
\r
715 public void MoveSelectBefore(TextPoint tp)
\r
717 int NewAnchorIndex;
\r
718 int SelectionLength;
\r
719 if (this.IsReverseSelect())
\r
721 NewAnchorIndex = this.View.GetIndexFromLayoutLine(tp);
\r
722 SelectionLength = this.SelectionLength + NewAnchorIndex - this.AnchorIndex;
\r
723 this.Select(this.SelectionStart, SelectionLength);
\r
727 NewAnchorIndex = this.View.GetIndexFromLayoutLine(tp);
\r
728 SelectionLength = this.SelectionLength + this.AnchorIndex - NewAnchorIndex;
\r
729 this.Select(NewAnchorIndex, SelectionLength);
\r
731 this.AnchorIndex = NewAnchorIndex;
\r
735 /// キャレット位置を既定の位置に戻す
\r
737 public void ResetCaretPostion()
\r
743 /// 行単位で移動後のキャレット位置を取得する
\r
745 /// <param name="count">移動量</param>
\r
746 /// <param name="current">現在のキャレット位置</param>
\r
747 /// <returns>移動後のキャレット位置</returns>
\r
748 public TextPoint GetTextPointAfterMoveLine(int count, TextPoint current)
\r
750 int row = current.row + count;
\r
754 else if (row >= this.View.LayoutLines.Count)
\r
755 row = this.View.LayoutLines.Count - 1;
\r
757 row = this.View.AdjustRow(row, count > 0);
\r
759 double colpos = this.View.GetColPostionFromIndex(current.row, current.col);
\r
760 int col = this.View.GetIndexFromColPostion(row, colpos);
\r
762 return new TextPoint(row, col);
\r
766 /// 選択文字列のインデントを一つ増やす
\r
768 public void UpIndent()
\r
770 if (this.RectSelection || this.SelectionLength == 0)
\r
772 int selectionStart = this.SelectionStart;
\r
773 string text = this.InsertLineHead(GetTextFromLineSelectArea(this.View.Selections), "\t");
\r
774 this.RepleaceSelectionArea(this.View.Selections,text);
\r
775 this.Select(selectionStart, text.Length);
\r
779 /// 選択文字列のインデントを一つ減らす
\r
781 public void DownIndent()
\r
783 if (this.RectSelection || this.SelectionLength == 0)
\r
785 int selectionStart = this.SelectionStart;
\r
786 string text = this.RemoveLineHead(GetTextFromLineSelectArea(this.View.Selections), "\t");
\r
787 this.RepleaceSelectionArea(this.View.Selections, text);
\r
788 this.Select(selectionStart, text.Length);
\r
791 string InsertLineHead(string s, string str)
\r
793 string[] lines = s.Split(new string[] { Document.NewLine.ToString() }, StringSplitOptions.RemoveEmptyEntries);
\r
794 StringBuilder output = new StringBuilder();
\r
795 for (int i = 0; i < lines.Length; i++)
\r
796 output.Append(str + lines[i] + Document.NewLine);
\r
797 return output.ToString();
\r
800 public string RemoveLineHead(string s, string str)
\r
802 string[] lines = s.Split(new string[] { Document.NewLine.ToString() }, StringSplitOptions.RemoveEmptyEntries);
\r
803 StringBuilder output = new StringBuilder();
\r
804 for (int i = 0; i < lines.Length; i++)
\r
805 if (lines[i].StartsWith(str))
\r
806 output.Append(lines[i].Substring(1) + Document.NewLine);
\r
808 output.Append(lines[i] + Document.NewLine);
\r
809 return output.ToString();
\r
815 /// <param name="isMoveNext">真なら1文字すすめ、そうでなければ戻す</param>
\r
816 /// <remarks>このメソッドを呼び出した後でScrollToCaretメソッドとSelectWithMoveCaretメソッドを呼び出す必要があります</remarks>
\r
817 void MoveCaretHorizontical(bool isMoveNext)
\r
819 if (this.Document.FireUpdateEvent == false)
\r
820 throw new InvalidOperationException("");
\r
821 int delta = isMoveNext ? 0 : -1;
\r
822 int prevcol = this.View.CaretPostion.col;
\r
823 int col = this.View.CaretPostion.col + delta;
\r
824 string lineString = this.View.LayoutLines[this.View.CaretPostion.row];
\r
825 if (col < 0 || this.View.CaretPostion.row >= this.View.LayoutLines.Count)
\r
827 if (this.View.CaretPostion.row == 0)
\r
832 this.MoveCaretVertical(false);
\r
833 this.View.AdjustCaretAndSrc(AdjustFlow.Row); //この段階で調整しないとスクロールされない
\r
834 col = this.View.LayoutLines.GetLengthFromLineNumber(this.View.CaretPostion.row) - 1; //最終行以外はすべて改行コードが付くはず
\r
836 else if (col >= lineString.Length || lineString[col] == Document.NewLine)
\r
838 if (this.View.CaretPostion.row < this.View.LayoutLines.Count - 1)
\r
840 this.MoveCaretVertical(true);
\r
841 this.View.AdjustCaretAndSrc(AdjustFlow.Row); //この段階で調整しないとスクロールされない
\r
847 AlignDirection direction = isMoveNext ? AlignDirection.Forward : AlignDirection.Back;
\r
848 col = this.View.LayoutLines.GetLayout(this.View.CaretPostion.row).AlignIndexToNearestCluster(col, direction);
\r
851 this.View.JumpCaret(this.View.CaretPostion.row, col,false);
\r
855 /// キャレットを行方向に移動させる
\r
857 /// <param name="isMoveNext">プラス方向に移動するなら真</param>
\r
858 /// <remarks>このメソッドを呼び出した後でScrollToCaretメソッドとSelectWithMoveCaretメソッドを呼び出す必要があります</remarks>
\r
859 void MoveCaretVertical(bool isMoveNext)
\r
861 if (this.Document.FireUpdateEvent == false)
\r
862 throw new InvalidOperationException("");
\r
864 TextPoint nextPoint = this.GetTextPointAfterMoveLine(isMoveNext ? 1 : -1, this.View.CaretPostion);
\r
866 this.View.JumpCaret(nextPoint.row, nextPoint.col,false);
\r
869 private void ReplaceBeforeSelectionArea(SelectCollection Selections, int removeLength, string insertStr)
\r
871 if (removeLength == 0 && insertStr.Length == 0)
\r
874 if (this.RectSelection == false || this.Document.FireUpdateEvent == false)
\r
875 throw new InvalidOperationException();
\r
877 SelectCollection temp = this.View.InsertPoint;
\r
878 int selectStart = temp.First().start;
\r
879 int selectEnd = temp.Last().start + temp.Last().length;
\r
881 //ドキュメント操作後に行うとうまくいかないので、あらかじめ取得しておく
\r
882 TextPoint start = this.View.LayoutLines.GetTextPointFromIndex(selectStart);
\r
883 TextPoint end = this.View.LayoutLines.GetTextPointFromIndex(selectEnd);
\r
885 bool reverse = temp.First().start > temp.Last().start;
\r
887 int lineHeadIndex = this.View.LayoutLines.GetIndexFromLineNumber(this.View.LayoutLines.GetLineNumberFromIndex(selectStart));
\r
888 if (selectStart - removeLength < lineHeadIndex)
\r
891 this.Document.UndoManager.BeginUndoGroup();
\r
892 this.Document.FireUpdateEvent = false;
\r
896 for (int i = 0; i < temp.Count; i++)
\r
898 this.ReplaceBeforeSelection(temp[i], removeLength, insertStr);
\r
903 for (int i = temp.Count - 1; i >= 0; i--)
\r
905 this.ReplaceBeforeSelection(temp[i], removeLength, insertStr);
\r
909 this.Document.FireUpdateEvent = true;
\r
910 this.Document.UndoManager.EndUndoGroup();
\r
912 int delta = insertStr.Length - removeLength;
\r
913 start.col += delta;
\r
917 this.JumpCaret(start.row, start.col);
\r
919 this.JumpCaret(end.row, end.col);
\r
921 this.Select(start, 0, end.row - start.row);
\r
924 private void ReplaceBeforeSelection(Selection sel, int removeLength, string insertStr)
\r
926 sel = Util.NormalizeIMaker<Selection>(sel);
\r
927 this.Document.Replace(sel.start - removeLength, removeLength, insertStr);
\r
930 private void RepleaceSelectionArea(SelectCollection Selections, string value,bool updateInsertPoint = false)
\r
935 if (this.RectSelection == false)
\r
937 Selection sel = Selection.Create(this.AnchorIndex, 0);
\r
938 if (Selections.Count > 0)
\r
939 sel = Util.NormalizeIMaker<Selection>(this.View.Selections.First());
\r
941 this.Document.Replace(sel.start, sel.length, value);
\r
945 if (this.Document.FireUpdateEvent == false)
\r
946 throw new InvalidOperationException("");
\r
948 int StartIndex = this.SelectionStart;
\r
950 SelectCollection newInsertPoint = new SelectCollection();
\r
952 if (this.SelectionLength == 0)
\r
956 this.Document.UndoManager.BeginUndoGroup();
\r
958 this.Document.FireUpdateEvent = false;
\r
960 string[] line = value.Split(new string[] { Document.NewLine.ToString() }, StringSplitOptions.RemoveEmptyEntries);
\r
962 TextPoint Current = this.View.GetLayoutLineFromIndex(this.SelectionStart);
\r
964 for (i = 0; i < line.Length && Current.row < this.View.LayoutLines.Count; i++, Current.row++)
\r
966 if (Current.col > this.View.LayoutLines[Current.row].Length)
\r
967 Current.col = this.View.LayoutLines[Current.row].Length;
\r
968 StartIndex = this.View.GetIndexFromLayoutLine(Current);
\r
969 this.Document.Replace(StartIndex, 0, line[i]);
\r
970 StartIndex += line[i].Length;
\r
973 for (; i < line.Length; i++)
\r
975 StartIndex = this.Document.Length;
\r
976 string str = Document.NewLine + line[i];
\r
977 this.Document.Replace(StartIndex, 0, str);
\r
978 StartIndex += str.Length;
\r
981 this.Document.FireUpdateEvent = true;
\r
983 this.Document.UndoManager.EndUndoGroup();
\r
987 SelectCollection temp = new SelectCollection(this.View.Selections); //コピーしないとReplaceCommandを呼び出した段階で書き換えられてしまう
\r
989 this.Document.UndoManager.BeginUndoGroup();
\r
991 this.Document.FireUpdateEvent = false;
\r
993 if (temp.First().start < temp.Last().start)
\r
995 for (int i = temp.Count - 1; i >= 0; i--)
\r
997 Selection sel = Util.NormalizeIMaker<Selection>(temp[i]);
\r
999 StartIndex = sel.start;
\r
1001 this.Document.Replace(sel.start, sel.length, value);
\r
1003 newInsertPoint.Add(Selection.Create(sel.start + (value.Length - sel.length) * i,0));
\r
1008 for (int i = 0; i < temp.Count; i++)
\r
1010 Selection sel = Util.NormalizeIMaker<Selection>(temp[i]);
\r
1012 StartIndex = sel.start;
\r
1014 this.Document.Replace(sel.start, sel.length, value);
\r
1016 newInsertPoint.Add(Selection.Create(sel.start + (value.Length - sel.length) * i, 0));
\r
1020 this.Document.FireUpdateEvent = true;
\r
1022 this.Document.UndoManager.EndUndoGroup();
\r
1024 this.JumpCaret(StartIndex);
\r
1025 if (updateInsertPoint && newInsertPoint.Count > 0)
\r
1026 this.View.InsertPoint = newInsertPoint;
\r
1029 private string GetTextFromLineSelectArea(SelectCollection Selections)
\r
1031 Selection sel = Util.NormalizeIMaker<Selection>(Selections.First());
\r
1033 string str = this.Document.ToString(sel.start, sel.length);
\r
1038 string GetTextFromRectangleSelectArea(SelectCollection Selections)
\r
1040 StringBuilder temp = new StringBuilder();
\r
1041 if (Selections.First().start < Selections.Last().start)
\r
1043 for (int i = 0; i < this.View.Selections.Count; i++)
\r
1045 Selection sel = Util.NormalizeIMaker<Selection>(Selections[i]);
\r
1047 string str = this.Document.ToString(sel.start, sel.length);
\r
1048 if (str.IndexOf(Environment.NewLine) == -1)
\r
1049 temp.AppendLine(str);
\r
1056 for (int i = this.View.Selections.Count - 1; i >= 0; i--)
\r
1058 Selection sel = Util.NormalizeIMaker<Selection>(Selections[i]);
\r
1060 string str = this.Document.ToString(sel.start, sel.length).Replace(Document.NewLine.ToString(), Environment.NewLine);
\r
1061 if (str.IndexOf(Environment.NewLine) == -1)
\r
1062 temp.AppendLine(str);
\r
1067 return temp.ToString();
\r
1070 void View_LineBreakChanged(object sender, EventArgs e)
\r
1072 this.DeSelectAll();
\r
1073 this.AdjustCaret();
\r
1076 void View_PageBoundChanged(object sender, EventArgs e)
\r
1078 if (this.View.LineBreak == LineBreakMethod.PageBound && this.View.PageBound.Width - this.View.LineBreakingMarginWidth > 0)
\r
1079 this.View.PerfomLayouts();
\r
1080 this.AdjustCaret();
\r
1083 void render_ChangedRenderResource(object sender, ChangedRenderRsourceEventArgs e)
\r
1085 if (e.type == ResourceType.Font)
\r
1087 if (this.View.LineBreak == LineBreakMethod.PageBound)
\r
1088 this.View.PerfomLayouts();
\r
1089 this.AdjustCaret();
\r
1091 if (e.type == ResourceType.InlineChar)
\r
1093 int oldLineCountOnScreen = this.View.LineCountOnScreen;
\r
1094 this.View.CalculateLineCountOnScreen();
\r
1095 if(this.View.LineCountOnScreen != oldLineCountOnScreen)
\r
1096 this.AdjustCaret();
\r
1100 void render_ChangedRightToLeft(object sender, EventArgs e)
\r
1102 this.AdjustCaret();
\r
1105 void Document_Update(object sender, DocumentUpdateEventArgs e)
\r
1109 case UpdateType.Replace:
\r
1110 if(e.startIndex < this.Document.Length && this.Document[e.startIndex] == Document.NewLine)
\r
1111 this.View.CalculateLineCountOnScreen();
\r
1112 this.JumpCaret(e.startIndex + e.insertLength,true);
\r
1114 case UpdateType.Clear:
\r
1115 this.View.TryScroll(0, 0);
\r