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.SelectionChanged += new EventHandler((s, e) => { });
\r
60 public event EventHandler SelectionChanged;
\r
63 /// 矩形選択モードなら真を返し、そうでない場合は偽を返す
\r
65 public bool RectSelection
\r
74 /// <remarks>SelectionLengthが0の場合、キャレット位置を表します</remarks>
\r
75 public int SelectionStart
\r
79 if (this.View.Selections.Count == 0)
\r
80 return this.AnchorIndex;
\r
82 return this.View.Selections.First().start;
\r
89 /// <remarks>矩形選択モードの場合、選択範囲の文字数ではなく、開始位置から終了位置までの長さとなります</remarks>
\r
90 public int SelectionLength
\r
94 if (this.View.Selections.Count == 0)
\r
96 Selection last = this.View.Selections.Last();
\r
97 return last.start + last.length - this.SelectionStart;
\r
105 /// 未選択状態で代入したときは追加され、そうでない場合は選択範囲の文字列と置き換えられます。
\r
107 public string SelectedText
\r
111 if (this.View.LayoutLines.Count == 0 || this.View.Selections.Count == 0)
\r
114 if (this.RectSelection)
\r
115 return GetTextFromRectangleSelectArea(this.View.Selections);
\r
117 return GetTextFromLineSelectArea(this.View.Selections).Replace(Document.NewLine.ToString(), Environment.NewLine);
\r
121 if (this.Document.FireUpdateEvent == false)
\r
122 throw new InvalidOperationException("");
\r
125 this.RepleaceSelectionArea(this.View.Selections, value.Replace(Environment.NewLine,Document.NewLine.ToString()));
\r
130 /// 選択範囲が逆転しているかどうかを判定する
\r
132 /// <returns>逆転しているなら真を返す</returns>
\r
133 public bool IsReverseSelect()
\r
135 int index = this.View.LayoutLines.GetIndexFromTextPoint(this.View.CaretPostion);
\r
136 return index < this.AnchorIndex;
\r
142 /// <param name="start"></param>
\r
143 /// <param name="length"></param>
\r
144 /// <remarks>RectSelectionの値によって動作が変わります。真の場合は矩形選択モードに、そうでない場合は行ごとに選択されます</remarks>
\r
145 public void Select(int start, int length)
\r
147 if (this.Document.FireUpdateEvent == false)
\r
148 throw new InvalidOperationException("");
\r
149 if (start < 0 || start + length < 0 || start + length > this.Document.Length)
\r
150 throw new ArgumentOutOfRangeException("startかendが指定できる範囲を超えてます");
\r
151 this.View.Selections.Clear();
\r
154 int oldStart = start;
\r
156 length = oldStart - start;
\r
158 if (this.RectSelection && length != 0)
\r
160 TextPoint startTextPoint = this.View.GetLayoutLineFromIndex(start);
\r
161 TextPoint endTextPoint = this.View.GetLayoutLineFromIndex(start + length);
\r
162 this.SelectByRectangle(new TextRectangle(startTextPoint, endTextPoint));
\r
163 if (startTextPoint.col == endTextPoint.col)
\r
164 this.View.InsertPoint = new SelectCollection(this.View.Selections);
\r
166 this.View.InsertPoint = null;
\r
168 else if(length != 0)
\r
170 this.View.Selections.Add(Selection.Create(start, length));
\r
171 this.View.InsertPoint = null;
\r
173 this.SelectionChanged(this, null);
\r
176 public void Select(TextPoint tp, int width, int height)
\r
178 if (this.Document.FireUpdateEvent == false || !this.RectSelection)
\r
179 throw new InvalidOperationException("");
\r
180 TextPoint end = tp;
\r
182 end.row = tp.row + height;
\r
183 end.col = tp.col + width;
\r
185 if (end.row > this.View.LayoutLines.Count - 1)
\r
186 throw new ArgumentOutOfRangeException("");
\r
188 this.View.Selections.Clear();
\r
190 this.SelectByRectangle(new TextRectangle(tp,end));
\r
193 this.View.InsertPoint = new SelectCollection(this.View.Selections);
\r
195 this.View.InsertPoint = null;
\r
197 this.SelectionChanged(this, null);
\r
200 private void SelectByRectangle(TextRectangle rect)
\r
202 if (this.Document.FireUpdateEvent == false)
\r
203 throw new InvalidOperationException("");
\r
204 if (rect.TopLeft <= rect.BottomRight)
\r
206 for (int i = rect.TopLeft.row; i <= rect.BottomLeft.row; i++)
\r
208 int length = this.View.LayoutLines.GetLengthFromLineNumber(i);
\r
209 int leftCol = rect.TopLeft.col, rightCol = rect.TopRight.col, lastCol = length;
\r
210 if(length > 0 && this.View.LayoutLines[i][length - 1] == Document.NewLine)
\r
211 lastCol = length - 1;
\r
214 if (rect.TopLeft.col > lastCol)
\r
216 if (rect.TopRight.col > lastCol)
\r
217 rightCol = lastCol;
\r
219 int StartIndex = this.View.LayoutLines.GetIndexFromTextPoint(new TextPoint(i, leftCol));
\r
220 int EndIndex = this.View.LayoutLines.GetIndexFromTextPoint(new TextPoint(i, rightCol));
\r
223 sel = Selection.Create(StartIndex, EndIndex - StartIndex);
\r
225 this.View.Selections.Add(sel);
\r
233 /// <param name="index">探索を開始するインデックス</param>
\r
234 public void SelectWord(int index)
\r
236 if (this.Document.FireUpdateEvent == false)
\r
237 throw new InvalidOperationException("");
\r
239 if (this.Document.Length <= 0 || index >= this.Document.Length)
\r
242 Document str = this.Document;
\r
245 while (start > 0 && !Util.IsWordSeparator(str[start]))
\r
248 if (Util.IsWordSeparator(str[start]))
\r
252 while (end < this.Document.Length && !Util.IsWordSeparator(str[end]))
\r
255 this.Select(start, end - start);
\r
259 /// 選択範囲内のUTF32コードポイントを文字列に変換します
\r
261 /// <returns>成功した場合は真。そうでない場合は偽を返す</returns>
\r
262 public bool ConvertToChar()
\r
264 if (this.SelectionLength == 0 || this.RectSelection)
\r
266 string str = this.Document.ToString(this.SelectionStart, this.SelectionLength);
\r
267 string[] codes = str.Split(new char[] { ' ' },StringSplitOptions.RemoveEmptyEntries);
\r
268 StringBuilder result = new StringBuilder();
\r
269 foreach (string code in codes)
\r
272 if (code[0] != 'U')
\r
274 if (Int32.TryParse(code.TrimStart('U'),NumberStyles.HexNumber,null, out utf32_code))
\r
275 result.Append(Char.ConvertFromUtf32(utf32_code));
\r
279 this.Document.Replace(this.SelectionStart, this.SelectionLength, result.ToString());
\r
284 /// 選択文字列をUTF32のコードポイントに変換します
\r
286 public void ConvertToCodePoint()
\r
288 if (this.SelectionLength == 0 || this.RectSelection)
\r
290 string str = this.Document.ToString(this.SelectionStart, this.SelectionLength);
\r
291 StringInfo info = new StringInfo(str);
\r
292 StringBuilder result = new StringBuilder();
\r
293 for (int i = 0; i < str.Length;)
\r
295 int utf32_code = Char.ConvertToUtf32(str, i);
\r
296 result.Append("U" + Convert.ToString(utf32_code,16));
\r
297 result.Append(' ');
\r
298 if(Char.IsHighSurrogate(str[i]))
\r
303 this.Document.Replace(this.SelectionStart, this.SelectionLength, result.ToString());
\r
309 public void DeSelectAll()
\r
311 if (this.Document.FireUpdateEvent == false)
\r
312 throw new InvalidOperationException("");
\r
314 this.View.Selections.Clear();
\r
320 /// <param name="tp"></param>
\r
321 /// <param name="type"></param>
\r
322 /// <returns>真ならマーカーがある</returns>
\r
323 public bool IsMarker(TextPoint tp,HilightType type)
\r
325 if (this.Document.FireUpdateEvent == false)
\r
326 throw new InvalidOperationException("");
\r
327 int index = this.View.LayoutLines.GetIndexFromTextPoint(tp);
\r
328 return this.IsMarker(index, type);
\r
332 /// 任意のマーカーかどうか判定する
\r
334 /// <param name="index"></param>
\r
335 /// <param name="type"></param>
\r
336 /// <returns>真ならマーカーがある</returns>
\r
337 public bool IsMarker(int index, HilightType type)
\r
339 foreach (Marker m in this.Document.GetMarkers(index))
\r
341 if (m.hilight == type)
\r
350 public void AdjustCaret()
\r
352 int row = this.View.CaretPostion.row;
\r
353 if (row > this.View.LayoutLines.Count - 1)
\r
354 row = this.View.LayoutLines.Count - 1;
\r
355 int col = this.View.CaretPostion.col;
\r
356 if (col > 0 && col > this.View.LayoutLines[row].Length)
\r
357 col = this.View.LayoutLines[row].Length;
\r
358 this.JumpCaret(row, col);
\r
362 /// キャレットを指定した位置に移動させる
\r
364 /// <param name="index"></param>
\r
365 /// <param name="autoExpand">折り畳みを展開するなら真</param>
\r
366 public void JumpCaret(int index,bool autoExpand = true)
\r
368 if (index < 0 || index > this.Document.Length)
\r
369 throw new ArgumentOutOfRangeException("indexが設定できる範囲を超えています");
\r
370 TextPoint tp = this.View.GetLayoutLineFromIndex(index);
\r
372 this.JumpCaret(tp.row, tp.col,autoExpand);
\r
376 /// キャレットを指定した位置に移動させる
\r
378 /// <param name="row"></param>
\r
379 /// <param name="col"></param>
\r
380 /// <param name="autoExpand">折り畳みを展開するなら真</param>
\r
381 public void JumpCaret(int row, int col, bool autoExpand = true)
\r
383 if (this.Document.FireUpdateEvent == false)
\r
384 throw new InvalidOperationException("");
\r
386 this.View.JumpCaret(row, col,autoExpand);
\r
388 this.View.AdjustCaretAndSrc();
\r
390 this.SelectWithMoveCaret(false);
\r
396 /// <param name="isSelected"></param>
\r
397 public void JumpToHead(bool isSelected)
\r
399 if (this.View.TryScroll(0, 0))
\r
401 this.View.JumpCaret(0, 0);
\r
402 this.View.AdjustCaretAndSrc();
\r
403 this.SelectWithMoveCaret(isSelected);
\r
407 /// ドキュメントの終わりにに移動する
\r
409 /// <param name="isSelected"></param>
\r
410 public void JumpToEnd(bool isSelected)
\r
412 int srcRow = this.View.LayoutLines.Count - this.View.LineCountOnScreen - 1;
\r
415 if (this.View.TryScroll(0, srcRow))
\r
417 this.View.JumpCaret(this.View.LayoutLines.Count - 1, 0);
\r
418 this.View.AdjustCaretAndSrc();
\r
419 this.SelectWithMoveCaret(isSelected);
\r
425 /// <param name="dir">方向を指定する</param>
\r
426 /// <param name="delta">スクロールする量。ScrollDirectionの値がUpやDownなら行数。LeftやRightならピクセル単位の値となる</param>
\r
427 /// <param name="isSelected">選択状態にするなら真</param>
\r
428 /// <param name="withCaret">同時にキャレットを移動させるなら真</param>
\r
429 public void Scroll(ScrollDirection dir, int delta, bool isSelected,bool withCaret)
\r
431 if (this.Document.FireUpdateEvent == false)
\r
432 throw new InvalidOperationException("");
\r
433 int toRow = this.View.Src.Row;
\r
434 double toX = this.View.Src.X;
\r
437 case ScrollDirection.Up:
\r
438 toRow = Math.Max(0, this.View.Src.Row - delta);
\r
439 toRow = this.View.AdjustRow(toRow, false);
\r
441 case ScrollDirection.Down:
\r
442 toRow = Math.Min(this.View.Src.Row + delta, this.View.LayoutLines.Count - 1);
\r
443 toRow = this.View.AdjustRow(toRow, true);
\r
445 case ScrollDirection.Left:
\r
448 case ScrollDirection.Right:
\r
452 throw new ArgumentOutOfRangeException();
\r
454 this.Scroll(toX, toRow, isSelected, withCaret);
\r
460 /// <param name="toX">スクロール先の座標</param>
\r
461 /// <param name="toRow">スクロール先の行</param>
\r
462 /// <param name="isSelected">選択状態にするなら真</param>
\r
463 /// <param name="withCaret">同時にキャレットを移動させるなら真</param>
\r
464 public void Scroll(double toX, int toRow, bool isSelected, bool withCaret)
\r
468 this.View.Scroll(toX, toRow);
\r
469 this.View.JumpCaret(toRow, 0);
\r
470 this.View.AdjustCaretAndSrc();
\r
471 this.SelectWithMoveCaret(isSelected);
\r
475 this.View.Scroll(toX, toRow);
\r
476 this.View.IsFocused = false;
\r
481 /// キャレットを桁方向に移動させる
\r
483 /// <returns>移動できない場合は真を返す</returns>
\r
484 /// <param name="realLength">負の値なら左側へ、そうでないなら右側へ移動する</param>
\r
485 /// <param name="isSelected">選択範囲とするなら真。そうでないなら偽</param>
\r
486 /// <param name="alignWord">単語単位で移動するなら真。そうでないなら偽</param>
\r
487 public void MoveCaretHorizontical(int realLength, bool isSelected,bool alignWord = false)
\r
489 for (int i = Math.Abs(realLength); i > 0; i--)
\r
491 bool MoveFlow = realLength > 0;
\r
492 if (this.View.render.RightToLeft)
\r
493 MoveFlow = !MoveFlow;
\r
494 this.MoveCaretHorizontical(MoveFlow);
\r
497 this.AlignNearestWord(MoveFlow);
\r
499 this.View.AdjustCaretAndSrc(AdjustFlow.Col);
\r
500 this.SelectWithMoveCaret(isSelected);
\r
503 void AlignNearestWord(bool MoveFlow)
\r
505 string str = this.View.LayoutLines[this.View.CaretPostion.row];
\r
506 while (this.View.CaretPostion.col > 0 &&
\r
507 this.View.CaretPostion.col < str.Length &&
\r
508 str[this.View.CaretPostion.col] != Document.NewLine)
\r
510 if (!Util.IsWordSeparator(str[this.View.CaretPostion.col]))
\r
512 this.MoveCaretHorizontical(MoveFlow);
\r
517 this.MoveCaretHorizontical(MoveFlow);
\r
524 /// キャレットを行方向に移動させる
\r
526 /// <returns>再描写する必要があるなら真を返す</returns>
\r
527 /// <param name="deltarow">移動量</param>
\r
528 /// <param name="isSelected"></param>
\r
529 public void MoveCaretVertical(int deltarow,bool isSelected)
\r
531 for (int i = Math.Abs(deltarow); i > 0; i--)
\r
532 this.MoveCaretVertical(deltarow > 0);
\r
533 this.View.AdjustCaretAndSrc(AdjustFlow.Both);
\r
534 this.SelectWithMoveCaret(isSelected);
\r
538 /// キャレット位置の文字を一文字削除する
\r
540 public void DoDeleteAction()
\r
542 if (this.SelectionLength != 0)
\r
544 this.SelectedText = "";
\r
548 if (this.Document.FireUpdateEvent == false)
\r
549 throw new InvalidOperationException("");
\r
551 TextPoint CaretPostion = this.View.CaretPostion;
\r
552 int index = this.View.GetIndexFromLayoutLine(CaretPostion);
\r
554 if (index == this.Document.Length)
\r
557 int lineHeadIndex = this.View.LayoutLines.GetIndexFromLineNumber(CaretPostion.row);
\r
558 int next = this.View.LayoutLines.GetLayout(CaretPostion.row).AlignIndexToNearestCluster(CaretPostion.col, AlignDirection.Forward) + lineHeadIndex;
\r
560 if (this.Document[index] == Document.NewLine)
\r
563 this.Document.Replace(index, next - index, "");
\r
567 /// キャレット位置の文字を一文字削除し、キャレット位置を後ろにずらす
\r
569 public void DoBackSpaceAction()
\r
571 if (this.View.InsertPoint != null)
\r
573 this.ReplaceBeforeSelectionArea(this.View.Selections, 1, "");
\r
576 else if (this.SelectionLength > 0)
\r
578 this.SelectedText = "";
\r
582 if (this.Document.FireUpdateEvent == false)
\r
583 throw new InvalidOperationException("");
\r
585 TextPoint CurrentPostion = this.View.CaretPostion;
\r
587 if (CurrentPostion.row == 0 && CurrentPostion.col == 0)
\r
590 int oldIndex = this.View.GetIndexFromLayoutLine(CurrentPostion);
\r
592 int newCol, newIndex;
\r
593 if (CurrentPostion.col > 0)
\r
595 newCol = this.View.LayoutLines.GetLayout(CurrentPostion.row).AlignIndexToNearestCluster(CurrentPostion.col - 1, AlignDirection.Back);
\r
596 newIndex = this.View.GetIndexFromLayoutLine(new TextPoint(CurrentPostion.row, newCol));
\r
600 newIndex = this.View.GetIndexFromLayoutLine(CurrentPostion);
\r
604 this.Document.Replace(newIndex, oldIndex - newIndex, "");
\r
610 public void DoEnterAction()
\r
612 this.DoInputChar('\n');
\r
616 /// キャレット位置に文字を入力し、その分だけキャレットを進める。isInsertModeの値により動作が変わります
\r
618 /// <param name="ch"></param>
\r
619 public void DoInputChar(char ch)
\r
621 this.DoInputString(ch.ToString());
\r
625 /// キャレット位置に文字列を挿入し、その分だけキャレットを進める。isInsertModeの値により動作が変わります
\r
627 /// <param name="str"></param>
\r
628 /// <param name="fromTip"></param>
\r
629 public void DoInputString(string str,bool fromTip = false)
\r
631 if (this.View.InsertPoint != null)
\r
633 this.ReplaceBeforeSelectionArea(this.View.Selections, 0, str);
\r
636 else if (this.SelectionLength != 0)
\r
638 this.RepleaceSelectionArea(this.View.Selections, str, fromTip);
\r
642 if (this.Document.FireUpdateEvent == false)
\r
643 throw new InvalidOperationException("");
\r
645 int index = this.View.GetIndexFromLayoutLine(this.View.CaretPostion);
\r
647 TextPoint CaretPos = this.View.CaretPostion;
\r
648 if (this.View.InsertMode == false && index < this.Document.Length && this.Document[index] != Document.NewLine)
\r
650 string lineString = this.View.LayoutLines[CaretPos.row];
\r
651 int end = this.View.LayoutLines.GetLayout(CaretPos.row).AlignIndexToNearestCluster(CaretPos.col + str.Length - 1, AlignDirection.Forward);
\r
652 if (end > lineString.Length - 1)
\r
653 end = lineString.Length - 1;
\r
654 end += this.View.LayoutLines.GetIndexFromLineNumber(CaretPos.row);
\r
655 length = end - index;
\r
657 if (str == Document.NewLine.ToString())
\r
659 int lineHeadIndex = this.View.LayoutLines.GetIndexFromLineNumber(CaretPos.row);
\r
660 int lineLength = this.View.LayoutLines.GetLengthFromLineNumber(CaretPos.row);
\r
661 FoldingItem foldingData = this.View.LayoutLines.FoldingCollection.GetFarestHiddenFoldingData(lineHeadIndex, lineLength);
\r
662 if (foldingData != null && !foldingData.Expand && index > foldingData.Start && index <= foldingData.End)
\r
663 index = foldingData.End + 1;
\r
665 this.Document.Replace(index, length, str);
\r
669 /// キャレットの移動に合わせて選択する
\r
671 /// <param name="isSelected">選択状態にするかどうか</param>
\r
673 /// キャレットを移動後、このメソッドを呼び出さない場合、Select()メソッドは正常に機能しません
\r
675 void SelectWithMoveCaret(bool isSelected)
\r
677 if (this.View.CaretPostion.col < 0 || this.View.CaretPostion.row < 0)
\r
680 if (this.Document.FireUpdateEvent == false)
\r
681 throw new InvalidOperationException("");
\r
683 int CaretPostion = this.View.GetIndexFromLayoutLine(this.View.CaretPostion);
\r
685 SelectCollection Selections = this.View.Selections;
\r
688 this.Select(this.AnchorIndex, CaretPostion - this.AnchorIndex);
\r
690 this.Select(CaretPostion, 0);
\r
691 this.AnchorIndex = CaretPostion;
\r
692 this.View.InsertPoint = null;
\r
697 /// JumpCaretで移動した位置からキャレットを移動し、選択状態にする
\r
699 /// <param name="tp"></param>
\r
700 public void MoveCaretAndSelect(TextPoint tp)
\r
702 int CaretPostion = this.View.GetIndexFromLayoutLine(tp);
\r
703 this.Select(this.AnchorIndex, CaretPostion - this.AnchorIndex);
\r
704 this.View.JumpCaret(tp.row, tp.col);
\r
705 this.View.AdjustCaretAndSrc();
\r
708 public void MoveSelectBefore(TextPoint tp)
\r
710 int NewAnchorIndex;
\r
711 int SelectionLength;
\r
712 if (this.IsReverseSelect())
\r
714 NewAnchorIndex = this.View.GetIndexFromLayoutLine(tp);
\r
715 SelectionLength = this.SelectionLength + NewAnchorIndex - this.AnchorIndex;
\r
716 this.Select(this.SelectionStart, SelectionLength);
\r
720 NewAnchorIndex = this.View.GetIndexFromLayoutLine(tp);
\r
721 SelectionLength = this.SelectionLength + this.AnchorIndex - NewAnchorIndex;
\r
722 this.Select(NewAnchorIndex, SelectionLength);
\r
724 this.AnchorIndex = NewAnchorIndex;
\r
728 /// キャレット位置を既定の位置に戻す
\r
730 public void ResetCaretPostion()
\r
736 /// 行単位で移動後のキャレット位置を取得する
\r
738 /// <param name="count">移動量</param>
\r
739 /// <param name="current">現在のキャレット位置</param>
\r
740 /// <returns>移動後のキャレット位置</returns>
\r
741 public TextPoint GetTextPointAfterMoveLine(int count, TextPoint current)
\r
743 int row = current.row + count;
\r
747 else if (row >= this.View.LayoutLines.Count)
\r
748 row = this.View.LayoutLines.Count - 1;
\r
750 row = this.View.AdjustRow(row, count > 0);
\r
752 double colpos = this.View.GetColPostionFromIndex(current.row, current.col);
\r
753 int col = this.View.GetIndexFromColPostion(row, colpos);
\r
755 return new TextPoint(row, col);
\r
759 /// 選択文字列のインデントを一つ増やす
\r
761 public void UpIndent()
\r
763 if (this.RectSelection || this.SelectionLength == 0)
\r
765 int selectionStart = this.SelectionStart;
\r
766 string text = this.InsertLineHead(GetTextFromLineSelectArea(this.View.Selections), "\t");
\r
767 this.RepleaceSelectionArea(this.View.Selections,text);
\r
768 this.Select(selectionStart, text.Length);
\r
772 /// 選択文字列のインデントを一つ減らす
\r
774 public void DownIndent()
\r
776 if (this.RectSelection || this.SelectionLength == 0)
\r
778 int selectionStart = this.SelectionStart;
\r
779 string text = this.RemoveLineHead(GetTextFromLineSelectArea(this.View.Selections), "\t");
\r
780 this.RepleaceSelectionArea(this.View.Selections, text);
\r
781 this.Select(selectionStart, text.Length);
\r
784 string InsertLineHead(string s, string str)
\r
786 string[] lines = s.Split(new string[] { Document.NewLine.ToString() }, StringSplitOptions.RemoveEmptyEntries);
\r
787 StringBuilder output = new StringBuilder();
\r
788 for (int i = 0; i < lines.Length; i++)
\r
789 output.Append(str + lines[i] + Document.NewLine);
\r
790 return output.ToString();
\r
793 public string RemoveLineHead(string s, string str)
\r
795 string[] lines = s.Split(new string[] { Document.NewLine.ToString() }, StringSplitOptions.RemoveEmptyEntries);
\r
796 StringBuilder output = new StringBuilder();
\r
797 for (int i = 0; i < lines.Length; i++)
\r
798 if (lines[i].StartsWith(str))
\r
799 output.Append(lines[i].Substring(1) + Document.NewLine);
\r
801 output.Append(lines[i] + Document.NewLine);
\r
802 return output.ToString();
\r
808 /// <param name="isMoveNext">真なら1文字すすめ、そうでなければ戻す</param>
\r
809 /// <remarks>このメソッドを呼び出した後でScrollToCaretメソッドとSelectWithMoveCaretメソッドを呼び出す必要があります</remarks>
\r
810 void MoveCaretHorizontical(bool isMoveNext)
\r
812 if (this.Document.FireUpdateEvent == false)
\r
813 throw new InvalidOperationException("");
\r
814 int delta = isMoveNext ? 0 : -1;
\r
815 int prevcol = this.View.CaretPostion.col;
\r
816 int col = this.View.CaretPostion.col + delta;
\r
817 string lineString = this.View.LayoutLines[this.View.CaretPostion.row];
\r
818 if (col < 0 || this.View.CaretPostion.row >= this.View.LayoutLines.Count)
\r
820 if (this.View.CaretPostion.row == 0)
\r
825 this.MoveCaretVertical(false);
\r
826 this.View.AdjustCaretAndSrc(AdjustFlow.Row); //この段階で調整しないとスクロールされない
\r
827 col = this.View.LayoutLines.GetLengthFromLineNumber(this.View.CaretPostion.row) - 1; //最終行以外はすべて改行コードが付くはず
\r
829 else if (col >= lineString.Length || lineString[col] == Document.NewLine)
\r
831 if (this.View.CaretPostion.row < this.View.LayoutLines.Count - 1)
\r
833 this.MoveCaretVertical(true);
\r
834 this.View.AdjustCaretAndSrc(AdjustFlow.Row); //この段階で調整しないとスクロールされない
\r
840 AlignDirection direction = isMoveNext ? AlignDirection.Forward : AlignDirection.Back;
\r
841 col = this.View.LayoutLines.GetLayout(this.View.CaretPostion.row).AlignIndexToNearestCluster(col, direction);
\r
844 this.View.JumpCaret(this.View.CaretPostion.row, col,false);
\r
848 /// キャレットを行方向に移動させる
\r
850 /// <param name="isMoveNext">プラス方向に移動するなら真</param>
\r
851 /// <remarks>このメソッドを呼び出した後でScrollToCaretメソッドとSelectWithMoveCaretメソッドを呼び出す必要があります</remarks>
\r
852 void MoveCaretVertical(bool isMoveNext)
\r
854 if (this.Document.FireUpdateEvent == false)
\r
855 throw new InvalidOperationException("");
\r
857 TextPoint nextPoint = this.GetTextPointAfterMoveLine(isMoveNext ? 1 : -1, this.View.CaretPostion);
\r
859 this.View.JumpCaret(nextPoint.row, nextPoint.col,false);
\r
862 private void ReplaceBeforeSelectionArea(SelectCollection Selections, int removeLength, string insertStr)
\r
864 if (removeLength == 0 && insertStr.Length == 0)
\r
867 if (this.RectSelection == false || this.Document.FireUpdateEvent == false)
\r
868 throw new InvalidOperationException();
\r
870 SelectCollection temp = this.View.InsertPoint;
\r
871 int selectStart = temp.First().start;
\r
872 int selectEnd = temp.Last().start + temp.Last().length;
\r
874 //ドキュメント操作後に行うとうまくいかないので、あらかじめ取得しておく
\r
875 TextPoint start = this.View.LayoutLines.GetTextPointFromIndex(selectStart);
\r
876 TextPoint end = this.View.LayoutLines.GetTextPointFromIndex(selectEnd);
\r
878 bool reverse = temp.First().start > temp.Last().start;
\r
880 int lineHeadIndex = this.View.LayoutLines.GetIndexFromLineNumber(this.View.LayoutLines.GetLineNumberFromIndex(selectStart));
\r
881 if (selectStart - removeLength < lineHeadIndex)
\r
884 this.Document.UndoManager.BeginUndoGroup();
\r
885 this.Document.FireUpdateEvent = false;
\r
889 for (int i = 0; i < temp.Count; i++)
\r
891 this.ReplaceBeforeSelection(temp[i], removeLength, insertStr);
\r
896 for (int i = temp.Count - 1; i >= 0; i--)
\r
898 this.ReplaceBeforeSelection(temp[i], removeLength, insertStr);
\r
902 this.Document.FireUpdateEvent = true;
\r
903 this.Document.UndoManager.EndUndoGroup();
\r
905 int delta = insertStr.Length - removeLength;
\r
906 start.col += delta;
\r
910 this.JumpCaret(start.row, start.col);
\r
912 this.JumpCaret(end.row, end.col);
\r
914 this.Select(start, 0, end.row - start.row);
\r
917 private void ReplaceBeforeSelection(Selection sel, int removeLength, string insertStr)
\r
919 sel = Util.NormalizeIMaker<Selection>(sel);
\r
920 this.Document.Replace(sel.start - removeLength, removeLength, insertStr);
\r
923 private void RepleaceSelectionArea(SelectCollection Selections, string value,bool updateInsertPoint = false)
\r
928 if (this.RectSelection == false)
\r
930 Selection sel = Selection.Create(this.AnchorIndex, 0);
\r
931 if (Selections.Count > 0)
\r
932 sel = Util.NormalizeIMaker<Selection>(this.View.Selections.First());
\r
934 this.Document.Replace(sel.start, sel.length, value);
\r
938 if (this.Document.FireUpdateEvent == false)
\r
939 throw new InvalidOperationException("");
\r
941 int StartIndex = this.SelectionStart;
\r
943 SelectCollection newInsertPoint = new SelectCollection();
\r
945 if (this.SelectionLength == 0)
\r
949 this.Document.UndoManager.BeginUndoGroup();
\r
951 this.Document.FireUpdateEvent = false;
\r
953 string[] line = value.Split(new string[] { Document.NewLine.ToString() }, StringSplitOptions.RemoveEmptyEntries);
\r
955 TextPoint Current = this.View.GetLayoutLineFromIndex(this.SelectionStart);
\r
957 for (i = 0; i < line.Length && Current.row < this.View.LayoutLines.Count; i++, Current.row++)
\r
959 if (Current.col > this.View.LayoutLines[Current.row].Length)
\r
960 Current.col = this.View.LayoutLines[Current.row].Length;
\r
961 StartIndex = this.View.GetIndexFromLayoutLine(Current);
\r
962 this.Document.Replace(StartIndex, 0, line[i]);
\r
963 StartIndex += line[i].Length;
\r
966 for (; i < line.Length; i++)
\r
968 StartIndex = this.Document.Length;
\r
969 string str = Document.NewLine + line[i];
\r
970 this.Document.Replace(StartIndex, 0, str);
\r
971 StartIndex += str.Length;
\r
974 this.Document.FireUpdateEvent = true;
\r
976 this.Document.UndoManager.EndUndoGroup();
\r
980 SelectCollection temp = new SelectCollection(this.View.Selections); //コピーしないとReplaceCommandを呼び出した段階で書き換えられてしまう
\r
982 this.Document.UndoManager.BeginUndoGroup();
\r
984 this.Document.FireUpdateEvent = false;
\r
986 if (temp.First().start < temp.Last().start)
\r
988 for (int i = temp.Count - 1; i >= 0; i--)
\r
990 Selection sel = Util.NormalizeIMaker<Selection>(temp[i]);
\r
992 StartIndex = sel.start;
\r
994 this.Document.Replace(sel.start, sel.length, value);
\r
996 newInsertPoint.Add(Selection.Create(sel.start + (value.Length - sel.length) * i,0));
\r
1001 for (int i = 0; i < temp.Count; i++)
\r
1003 Selection sel = Util.NormalizeIMaker<Selection>(temp[i]);
\r
1005 StartIndex = sel.start;
\r
1007 this.Document.Replace(sel.start, sel.length, value);
\r
1009 newInsertPoint.Add(Selection.Create(sel.start + (value.Length - sel.length) * i, 0));
\r
1013 this.Document.FireUpdateEvent = true;
\r
1015 this.Document.UndoManager.EndUndoGroup();
\r
1017 this.JumpCaret(StartIndex);
\r
1018 if (updateInsertPoint && newInsertPoint.Count > 0)
\r
1019 this.View.InsertPoint = newInsertPoint;
\r
1022 private string GetTextFromLineSelectArea(SelectCollection Selections)
\r
1024 Selection sel = Util.NormalizeIMaker<Selection>(Selections.First());
\r
1026 string str = this.Document.ToString(sel.start, sel.length);
\r
1031 string GetTextFromRectangleSelectArea(SelectCollection Selections)
\r
1033 StringBuilder temp = new StringBuilder();
\r
1034 if (Selections.First().start < Selections.Last().start)
\r
1036 for (int i = 0; i < this.View.Selections.Count; i++)
\r
1038 Selection sel = Util.NormalizeIMaker<Selection>(Selections[i]);
\r
1040 string str = this.Document.ToString(sel.start, sel.length);
\r
1041 if (str.IndexOf(Environment.NewLine) == -1)
\r
1042 temp.AppendLine(str);
\r
1049 for (int i = this.View.Selections.Count - 1; i >= 0; i--)
\r
1051 Selection sel = Util.NormalizeIMaker<Selection>(Selections[i]);
\r
1053 string str = this.Document.ToString(sel.start, sel.length).Replace(Document.NewLine.ToString(), Environment.NewLine);
\r
1054 if (str.IndexOf(Environment.NewLine) == -1)
\r
1055 temp.AppendLine(str);
\r
1060 return temp.ToString();
\r
1063 void View_LineBreakChanged(object sender, EventArgs e)
\r
1065 this.DeSelectAll();
\r
1066 this.AdjustCaret();
\r
1069 void View_PageBoundChanged(object sender, EventArgs e)
\r
1071 if (this.View.LineBreak == LineBreakMethod.PageBound && this.View.PageBound.Width - this.View.LineBreakingMarginWidth > 0)
\r
1072 this.View.PerfomLayouts();
\r
1073 this.AdjustCaret();
\r
1076 void render_ChangedRenderResource(object sender, ChangedRenderRsourceEventArgs e)
\r
1078 if (e.type == ResourceType.Font)
\r
1080 if (this.View.LineBreak == LineBreakMethod.PageBound)
\r
1081 this.View.PerfomLayouts();
\r
1082 this.AdjustCaret();
\r
1084 if (e.type == ResourceType.InlineChar)
\r
1086 int oldLineCountOnScreen = this.View.LineCountOnScreen;
\r
1087 this.View.CalculateLineCountOnScreen();
\r
1088 if(this.View.LineCountOnScreen != oldLineCountOnScreen)
\r
1089 this.AdjustCaret();
\r
1093 void render_ChangedRightToLeft(object sender, EventArgs e)
\r
1095 this.AdjustCaret();
\r
1098 void Document_Update(object sender, DocumentUpdateEventArgs e)
\r
1102 case UpdateType.Replace:
\r
1103 if(e.startIndex < this.Document.Length && this.Document[e.startIndex] == Document.NewLine)
\r
1104 this.View.CalculateLineCountOnScreen();
\r
1105 this.JumpCaret(e.startIndex + e.insertLength,true);
\r
1107 case UpdateType.Clear:
\r
1108 this.View.TryScroll(0, 0);
\r