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
48 this.Document.Progress += Document_Progress;
\r
50 this.View.render.ChangedRightToLeft += render_ChangedRightToLeft;
\r
51 this.View.render.ChangedRenderResource += render_ChangedRenderResource;
\r
52 this.View.PerformLayouted += View_LineBreakChanged;
\r
53 this.View.PageBoundChanged += View_PageBoundChanged;
\r
54 this.Document.Clear();
\r
55 this.CaretMoved += new EventHandler((s, e) => { });
\r
61 public event EventHandler CaretMoved;
\r
65 /// 矩形選択モードなら真を返し、そうでない場合は偽を返す
\r
67 public bool RectSelection
\r
76 /// <remarks>SelectionLengthが0の場合、キャレット位置を表します</remarks>
\r
77 public int SelectionStart
\r
81 if (this.View.Selections.Count == 0)
\r
82 return this.AnchorIndex;
\r
84 return this.View.Selections.First().start;
\r
91 /// <remarks>矩形選択モードの場合、選択範囲の文字数ではなく、開始位置から終了位置までの長さとなります</remarks>
\r
92 public int SelectionLength
\r
96 if (this.View.Selections.Count == 0)
\r
98 Selection last = this.View.Selections.Last();
\r
99 return last.start + last.length - this.SelectionStart;
\r
107 /// 未選択状態で代入したときは追加され、そうでない場合は選択範囲の文字列と置き換えられます。
\r
109 public string SelectedText
\r
113 if (this.View.LayoutLines.Count == 0 || this.View.Selections.Count == 0)
\r
116 if (this.RectSelection)
\r
117 str = GetTextFromRectangleSelectArea(this.View.Selections);
\r
119 str = GetTextFromLineSelectArea(this.View.Selections);
\r
120 return str.Replace(Document.NewLine.ToString(), Environment.NewLine);
\r
124 if (this.Document.FireUpdateEvent == false)
\r
125 throw new InvalidOperationException("");
\r
128 this.RepleaceSelectionArea(this.View.Selections, value.Replace(Environment.NewLine,Document.NewLine.ToString()));
\r
133 /// 選択範囲が逆転しているかどうかを判定する
\r
135 /// <returns>逆転しているなら真を返す</returns>
\r
136 public bool IsReverseSelect()
\r
138 int index = this.View.LayoutLines.GetIndexFromTextPoint(this.View.CaretPostion);
\r
139 return index < this.AnchorIndex;
\r
145 /// <param name="start"></param>
\r
146 /// <param name="length"></param>
\r
147 /// <remarks>RectSelectionの値によって動作が変わります。真の場合は矩形選択モードに、そうでない場合は行ごとに選択されます</remarks>
\r
148 public void Select(int start, int length)
\r
150 if (this.Document.FireUpdateEvent == false)
\r
151 throw new InvalidOperationException("");
\r
152 if (start < 0 || start + length < 0 || start + length > this.Document.Length)
\r
153 throw new ArgumentOutOfRangeException("startかendが指定できる範囲を超えてます");
\r
154 this.View.Selections.Clear();
\r
157 int oldStart = start;
\r
159 length = oldStart - start;
\r
161 if (this.RectSelection && length != 0)
\r
163 TextPoint startTextPoint = this.View.GetLayoutLineFromIndex(start);
\r
164 TextPoint endTextPoint = this.View.GetLayoutLineFromIndex(start + length);
\r
165 this.SelectByRectangle(new TextRectangle(startTextPoint, endTextPoint));
\r
166 if (startTextPoint.col == endTextPoint.col)
\r
167 this.View.InsertPoint = new SelectCollection(this.View.Selections);
\r
169 this.View.InsertPoint = null;
\r
173 this.View.Selections.Add(Selection.Create(start, length));
\r
174 this.View.InsertPoint = null;
\r
178 public void Select(TextPoint tp, int width, int height)
\r
180 if (this.Document.FireUpdateEvent == false || !this.RectSelection)
\r
181 throw new InvalidOperationException("");
\r
182 TextPoint end = tp;
\r
184 end.row = tp.row + height;
\r
185 end.col = tp.col + width;
\r
187 if (end.row > this.View.LayoutLines.Count - 1)
\r
188 throw new ArgumentOutOfRangeException("");
\r
190 this.View.Selections.Clear();
\r
192 this.SelectByRectangle(new TextRectangle(tp,end));
\r
195 this.View.InsertPoint = new SelectCollection(this.View.Selections);
\r
197 this.View.InsertPoint = 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 LineToIndexTableData lineData = this.View.LayoutLines.GetData(i);
\r
209 int leftCol = rect.TopLeft.col,
\r
210 rightCol = rect.TopRight.col,
\r
211 lastCol = this.View.LayoutLines[i][lineData.Length - 1] == Document.NewLine ? lineData.Length - 1 : lineData.Length;
\r
214 if (rect.TopLeft.col > lastCol)
\r
216 if (rect.TopRight.col > lastCol)
\r
217 rightCol = lastCol;
\r
218 int padding = rect.TopLeft.col - leftCol;
\r
220 int StartIndex = this.View.LayoutLines.GetIndexFromTextPoint(new TextPoint(i, leftCol));
\r
221 int EndIndex = this.View.LayoutLines.GetIndexFromTextPoint(new TextPoint(i, rightCol));
\r
224 sel = Selection.Create(StartIndex, EndIndex - StartIndex,padding);
\r
226 this.View.Selections.Add(sel);
\r
234 /// <param name="index">探索を開始するインデックス</param>
\r
235 public void SelectWord(int index)
\r
237 if (this.Document.FireUpdateEvent == false)
\r
238 throw new InvalidOperationException("");
\r
240 if (this.Document.Length <= 0 || index >= this.Document.Length)
\r
243 Document str = this.Document;
\r
246 while (start > 0 && !Util.IsWordSeparator(str[start]))
\r
249 if (Util.IsWordSeparator(str[start]))
\r
253 while (end < this.Document.Length && !Util.IsWordSeparator(str[end]))
\r
256 this.Select(start, end - start);
\r
260 /// 選択範囲内のUTF32コードポイントを文字列に変換します
\r
262 /// <returns>成功した場合は真。そうでない場合は偽を返す</returns>
\r
263 public bool ConvertToChar()
\r
265 if (this.SelectionLength == 0 || this.RectSelection)
\r
267 string str = this.Document.ToString(this.SelectionStart, this.SelectionLength);
\r
268 string[] codes = str.Split(new char[] { ' ' },StringSplitOptions.RemoveEmptyEntries);
\r
269 StringBuilder result = new StringBuilder();
\r
270 foreach (string code in codes)
\r
273 if (code[0] != 'U')
\r
275 if (Int32.TryParse(code.TrimStart('U'),NumberStyles.HexNumber,null, out utf32_code))
\r
276 result.Append(Char.ConvertFromUtf32(utf32_code));
\r
280 this.Document.Replace(this.SelectionStart, this.SelectionLength, result.ToString());
\r
285 /// 選択文字列をUTF32のコードポイントに変換します
\r
287 public void ConvertToCodePoint()
\r
289 if (this.SelectionLength == 0 || this.RectSelection)
\r
291 string str = this.Document.ToString(this.SelectionStart, this.SelectionLength);
\r
292 StringInfo info = new StringInfo(str);
\r
293 StringBuilder result = new StringBuilder();
\r
294 for (int i = 0; i < str.Length;)
\r
296 int utf32_code = Char.ConvertToUtf32(str, i);
\r
297 result.Append("U" + Convert.ToString(utf32_code,16));
\r
298 result.Append(' ');
\r
299 if(Char.IsHighSurrogate(str[i]))
\r
304 this.Document.Replace(this.SelectionStart, this.SelectionLength, result.ToString());
\r
310 public void DeSelectAll()
\r
312 if (this.Document.FireUpdateEvent == false)
\r
313 throw new InvalidOperationException("");
\r
315 this.View.Selections.Clear();
\r
321 /// <param name="tp"></param>
\r
322 /// <param name="type"></param>
\r
323 /// <returns>真ならマーカーがある</returns>
\r
324 public bool IsMarker(TextPoint tp,HilightType type)
\r
326 if (this.Document.FireUpdateEvent == false)
\r
327 throw new InvalidOperationException("");
\r
328 int index = this.View.LayoutLines.GetIndexFromTextPoint(tp);
\r
329 return this.IsMarker(index, type);
\r
333 /// 任意のマーカーかどうか判定する
\r
335 /// <param name="index"></param>
\r
336 /// <param name="type"></param>
\r
337 /// <returns>真ならマーカーがある</returns>
\r
338 public bool IsMarker(int index, HilightType type)
\r
340 foreach (Marker m in this.Document.GetMarkers(index))
\r
342 if (m.hilight == type)
\r
351 public void AdjustCaret()
\r
353 int row = this.View.CaretPostion.row;
\r
354 if (row > this.View.LayoutLines.Count - 1)
\r
355 row = this.View.LayoutLines.Count - 1;
\r
356 int col = this.View.CaretPostion.col;
\r
357 if (col > 0 && col > this.View.LayoutLines[row].Length)
\r
358 col = this.View.LayoutLines[row].Length;
\r
359 this.JumpCaret(row, col);
\r
363 /// キャレットを指定した位置に移動させる
\r
365 /// <param name="index"></param>
\r
366 /// <param name="autoExpand">折り畳みを展開するなら真</param>
\r
367 public void JumpCaret(int index,bool autoExpand = true)
\r
369 if (index < 0 || index > this.Document.Length)
\r
370 throw new ArgumentOutOfRangeException("indexが設定できる範囲を超えています");
\r
371 TextPoint tp = this.View.GetLayoutLineFromIndex(index);
\r
373 this.JumpCaret(tp.row, tp.col,autoExpand);
\r
377 /// キャレットを指定した位置に移動させる
\r
379 /// <param name="row"></param>
\r
380 /// <param name="col"></param>
\r
381 /// <param name="autoExpand">折り畳みを展開するなら真</param>
\r
382 public void JumpCaret(int row, int col, bool autoExpand = true)
\r
384 if (this.Document.FireUpdateEvent == false)
\r
385 throw new InvalidOperationException("");
\r
387 this.View.JumpCaret(row, col,autoExpand);
\r
389 this.View.AdjustCaretAndSrc();
\r
391 this.SelectWithMoveCaret(false);
\r
393 this.CaretMoved(this, null);
\r
399 /// <param name="isSelected"></param>
\r
400 public void JumpToHead(bool isSelected)
\r
402 if (this.View.TryScroll(0, 0))
\r
404 this.View.JumpCaret(0, 0);
\r
405 this.View.AdjustCaretAndSrc();
\r
406 this.SelectWithMoveCaret(isSelected);
\r
407 this.CaretMoved(this, null);
\r
411 /// ドキュメントの終わりにに移動する
\r
413 /// <param name="isSelected"></param>
\r
414 public void JumpToEnd(bool isSelected)
\r
416 int srcRow = this.View.LayoutLines.Count - this.View.LineCountOnScreen - 1;
\r
419 if (this.View.TryScroll(0, srcRow))
\r
421 this.View.JumpCaret(this.View.LayoutLines.Count - 1, 0);
\r
422 this.View.AdjustCaretAndSrc();
\r
423 this.SelectWithMoveCaret(isSelected);
\r
424 this.CaretMoved(this, null);
\r
430 /// <param name="dir">方向を指定する</param>
\r
431 /// <param name="delta">スクロールする量。ScrollDirectionの値がUpやDownなら行数。LeftやRightならピクセル単位の値となる</param>
\r
432 /// <param name="isSelected">選択状態にするなら真</param>
\r
433 /// <param name="withCaret">同時にキャレットを移動させるなら真</param>
\r
434 public void Scroll(ScrollDirection dir, int delta, bool isSelected,bool withCaret)
\r
436 if (this.Document.FireUpdateEvent == false)
\r
437 throw new InvalidOperationException("");
\r
438 int toRow = this.View.Src.Row;
\r
439 double toX = this.View.Src.X;
\r
442 case ScrollDirection.Up:
\r
443 toRow = Math.Max(0, this.View.Src.Row - delta);
\r
444 toRow = this.View.AdjustRow(toRow, false);
\r
446 case ScrollDirection.Down:
\r
447 toRow = Math.Min(this.View.Src.Row + delta, this.View.LayoutLines.Count - 1);
\r
448 toRow = this.View.AdjustRow(toRow, true);
\r
450 case ScrollDirection.Left:
\r
453 case ScrollDirection.Right:
\r
457 throw new ArgumentOutOfRangeException();
\r
459 this.Scroll(toX, toRow, isSelected, withCaret);
\r
465 /// <param name="toX">スクロール先の座標</param>
\r
466 /// <param name="toRow">スクロール先の行</param>
\r
467 /// <param name="isSelected">選択状態にするなら真</param>
\r
468 /// <param name="withCaret">同時にキャレットを移動させるなら真</param>
\r
469 public void Scroll(double toX, int toRow, bool isSelected, bool withCaret)
\r
473 this.View.Scroll(toX, toRow);
\r
474 this.View.JumpCaret(toRow, 0);
\r
475 this.View.AdjustCaretAndSrc();
\r
476 this.SelectWithMoveCaret(isSelected);
\r
477 this.CaretMoved(this, null);
\r
481 this.View.Scroll(toX, toRow);
\r
482 this.View.HideCaret = true;
\r
487 /// キャレットを動かすことができるかどうか
\r
489 /// <param name="realLength"></param>
\r
490 /// <param name="flow"></param>
\r
491 /// <returns>移動できるなら真。そうでないなら偽</returns>
\r
492 public bool IsCanMoveCaret(int realLength,MoveFlow flow)
\r
494 LineToIndexTableData lineData = this.View.LayoutLines.GetData(this.View.CaretPostion.row);
\r
495 int index = lineData.Index + this.View.CaretPostion.col;
\r
496 int documentEndIndex = this.Document.Length - 1;
\r
497 FoldingItem foldingData = this.View.LayoutLines.FoldingCollection.GetFarestHiddenFoldingData(index, lineData.Length);
\r
498 if (foldingData != null && !foldingData.Expand && foldingData.End == documentEndIndex)
\r
500 documentEndIndex = lineData.Index + lineData.Length - 1;
\r
501 if (this.Document[documentEndIndex] == Document.NewLine)
\r
502 documentEndIndex--;
\r
505 if (flow == MoveFlow.Vertical)
\r
507 if (realLength < 0 && index == 0)
\r
509 else if (realLength > 0 && index >= documentEndIndex)
\r
512 else if (flow == MoveFlow.Horizontical)
\r
514 bool IncrimentCaret = realLength > 0;
\r
515 if (this.View.render.RightToLeft)
\r
516 IncrimentCaret = !IncrimentCaret;
\r
517 if (!IncrimentCaret && index == 0)
\r
519 else if (IncrimentCaret && index > documentEndIndex)
\r
526 /// キャレットを桁方向に移動させる
\r
528 /// <returns>移動できない場合は真を返す</returns>
\r
529 /// <param name="realLength">負の値なら左側へ、そうでないなら右側へ移動する</param>
\r
530 /// <param name="isSelected">選択範囲とするなら真。そうでないなら偽</param>
\r
531 /// <param name="alignWord">単語単位で移動するなら真。そうでないなら偽</param>
\r
532 public void MoveCaretHorizontical(int realLength, bool isSelected,bool alignWord = false)
\r
534 for (int i = Math.Abs(realLength); i > 0; i--)
\r
536 bool MoveFlow = realLength > 0;
\r
537 if (this.View.render.RightToLeft)
\r
538 MoveFlow = !MoveFlow;
\r
539 this.MoveCaretHorizontical(MoveFlow);
\r
542 this.AlignNearestWord(MoveFlow);
\r
544 this.View.AdjustCaretAndSrc(AdjustFlow.Col);
\r
545 this.SelectWithMoveCaret(isSelected);
\r
546 this.CaretMoved(this, null);
\r
549 void AlignNearestWord(bool MoveFlow)
\r
551 string str = this.View.LayoutLines[this.View.CaretPostion.row];
\r
552 while (this.View.CaretPostion.col > 0 &&
\r
553 this.View.CaretPostion.col < str.Length &&
\r
554 str[this.View.CaretPostion.col] != Document.NewLine)
\r
556 if (!Util.IsWordSeparator(str[this.View.CaretPostion.col]))
\r
558 this.MoveCaretHorizontical(MoveFlow);
\r
563 this.MoveCaretHorizontical(MoveFlow);
\r
570 /// キャレットを行方向に移動させる
\r
572 /// <returns>再描写する必要があるなら真を返す</returns>
\r
573 /// <param name="deltarow">移動量</param>
\r
574 /// <param name="isSelected"></param>
\r
575 public void MoveCaretVertical(int deltarow,bool isSelected)
\r
577 for (int i = Math.Abs(deltarow); i > 0; i--)
\r
578 this.MoveCaretVertical(deltarow > 0);
\r
579 this.View.AdjustCaretAndSrc(AdjustFlow.Both);
\r
580 this.SelectWithMoveCaret(isSelected);
\r
581 this.CaretMoved(this, null);
\r
585 /// キャレット位置の文字を一文字削除する
\r
587 public void DoDeleteAction()
\r
589 if (this.SelectionLength != 0)
\r
591 this.SelectedText = "";
\r
595 if (this.Document.FireUpdateEvent == false)
\r
596 throw new InvalidOperationException("");
\r
598 TextPoint CaretPostion = this.View.CaretPostion;
\r
599 int index = this.View.GetIndexFromLayoutLine(CaretPostion);
\r
601 if (index == this.Document.Length)
\r
604 LineToIndexTableData currentLine = this.View.LayoutLines.GetData(CaretPostion.row);
\r
606 int next = currentLine.Layout.AlignIndexToNearestCluster(CaretPostion.col, AlignDirection.Forward) + currentLine.Index;
\r
608 if (this.Document[index] == Document.NewLine)
\r
611 this.Document.Replace(index, next - index, "");
\r
615 /// キャレット位置の文字を一文字削除し、キャレット位置を後ろにずらす
\r
617 public void DoBackSpaceAction()
\r
619 if (this.View.InsertPoint != null)
\r
621 this.ReplaceBeforeSelectionArea(this.View.Selections, 1, "");
\r
624 else if (this.SelectionLength > 0)
\r
626 this.SelectedText = "";
\r
630 if (this.Document.FireUpdateEvent == false)
\r
631 throw new InvalidOperationException("");
\r
633 TextPoint CurrentPostion = this.View.CaretPostion;
\r
635 if (CurrentPostion.row == 0 && CurrentPostion.col == 0)
\r
638 int oldIndex = this.View.GetIndexFromLayoutLine(CurrentPostion);
\r
640 int newCol, newIndex;
\r
641 if (CurrentPostion.col > 0)
\r
643 newCol = this.View.LayoutLines.GetData(CurrentPostion.row).Layout.AlignIndexToNearestCluster(CurrentPostion.col - 1, AlignDirection.Back);
\r
644 newIndex = this.View.GetIndexFromLayoutLine(new TextPoint(CurrentPostion.row, newCol));
\r
648 newIndex = this.View.GetIndexFromLayoutLine(CurrentPostion);
\r
652 this.Document.Replace(newIndex, oldIndex - newIndex, "");
\r
658 public void DoEnterAction()
\r
660 this.DoInputChar('\n');
\r
664 /// キャレット位置に文字を入力し、その分だけキャレットを進める。isInsertModeの値により動作が変わります
\r
666 /// <param name="ch"></param>
\r
667 public void DoInputChar(char ch)
\r
669 this.DoInputString(ch.ToString());
\r
673 /// キャレット位置に文字列を挿入し、その分だけキャレットを進める。isInsertModeの値により動作が変わります
\r
675 /// <param name="str"></param>
\r
676 /// <param name="fromTip"></param>
\r
677 public void DoInputString(string str,bool fromTip = false)
\r
679 if (this.View.InsertPoint != null)
\r
681 this.ReplaceBeforeSelectionArea(this.View.Selections, 0, str);
\r
684 else if (this.SelectionLength != 0)
\r
686 this.RepleaceSelectionArea(this.View.Selections, str, fromTip);
\r
690 if (this.Document.FireUpdateEvent == false)
\r
691 throw new InvalidOperationException("");
\r
693 int index = this.View.GetIndexFromLayoutLine(this.View.CaretPostion);
\r
695 if (this.View.InsertMode == false && index < this.Document.Length && this.Document[index] != Document.NewLine)
\r
697 TextPoint CaretPos = this.View.CaretPostion;
\r
698 string lineString = this.View.LayoutLines[CaretPos.row];
\r
699 int end = this.View.LayoutLines.GetData(CaretPos.row).Layout.AlignIndexToNearestCluster(CaretPos.col + str.Length - 1, AlignDirection.Forward);
\r
700 if (end > lineString.Length - 1)
\r
701 end = lineString.Length - 1;
\r
702 end += this.View.LayoutLines.GetData(CaretPos.row).Index;
\r
703 length = end - index;
\r
705 if (str == Document.NewLine.ToString())
\r
707 LineToIndexTableData lineData = this.View.LayoutLines.GetData(this.View.CaretPostion.row);
\r
708 FoldingItem foldingData = this.View.LayoutLines.FoldingCollection.GetFarestHiddenFoldingData(lineData.Index, lineData.Length);
\r
709 if (foldingData != null && !foldingData.Expand && index > foldingData.Start && index <= foldingData.End)
\r
710 index = foldingData.End + 1;
\r
712 this.Document.Replace(index, length, str);
\r
716 /// キャレットの移動に合わせて選択する
\r
718 /// <param name="isSelected">選択状態にするかどうか</param>
\r
720 /// キャレットを移動後、このメソッドを呼び出さない場合、Select()メソッドは正常に機能しません
\r
722 void SelectWithMoveCaret(bool isSelected)
\r
724 if (this.View.CaretPostion.col < 0 || this.View.CaretPostion.row < 0)
\r
727 if (this.Document.FireUpdateEvent == false)
\r
728 throw new InvalidOperationException("");
\r
730 int CaretPostion = this.View.GetIndexFromLayoutLine(this.View.CaretPostion);
\r
732 SelectCollection Selections = this.View.Selections;
\r
734 this.Select(this.AnchorIndex, CaretPostion - this.AnchorIndex);
\r
737 this.View.Selections.Clear();
\r
738 this.AnchorIndex = CaretPostion;
\r
739 this.View.InsertPoint = null;
\r
744 /// JumpCaretで移動した位置からキャレットを移動し、選択状態にする
\r
746 /// <param name="tp"></param>
\r
747 public void MoveCaretAndSelect(TextPoint tp)
\r
749 int CaretPostion = this.View.GetIndexFromLayoutLine(tp);
\r
750 this.Select(this.AnchorIndex, CaretPostion - this.AnchorIndex);
\r
751 this.View.JumpCaret(tp.row, tp.col);
\r
752 this.View.AdjustCaretAndSrc();
\r
755 public void MoveSelectBefore(TextPoint tp)
\r
757 int NewAnchorIndex;
\r
758 int SelectionLength;
\r
759 if (this.IsReverseSelect())
\r
761 NewAnchorIndex = this.View.GetIndexFromLayoutLine(tp);
\r
762 SelectionLength = this.SelectionLength + NewAnchorIndex - this.AnchorIndex;
\r
763 this.Select(this.SelectionStart, SelectionLength);
\r
767 NewAnchorIndex = this.View.GetIndexFromLayoutLine(tp);
\r
768 SelectionLength = this.SelectionLength + this.AnchorIndex - NewAnchorIndex;
\r
769 this.Select(NewAnchorIndex, SelectionLength);
\r
771 this.AnchorIndex = NewAnchorIndex;
\r
775 /// キャレット位置を既定の位置に戻す
\r
777 public void ResetCaretPostion()
\r
783 /// 行単位で移動後のキャレット位置を取得する
\r
785 /// <param name="count">移動量</param>
\r
786 /// <param name="current">現在のキャレット位置</param>
\r
787 /// <returns>移動後のキャレット位置</returns>
\r
788 public TextPoint GetTextPointAfterMoveLine(int count, TextPoint current)
\r
790 int row = current.row + count;
\r
794 else if (row >= this.View.LayoutLines.Count)
\r
795 row = this.View.LayoutLines.Count - 1;
\r
797 row = this.View.AdjustRow(row, count > 0);
\r
799 double x = this.View.GetXFromIndex(current.row, current.col);
\r
800 int col = this.View.GetIndexFromX(row, x);
\r
802 return new TextPoint(row, col);
\r
806 /// 選択文字列のインデントを一つ増やす
\r
808 public void UpIndent()
\r
810 if (this.RectSelection || this.SelectionLength == 0)
\r
812 int selectionStart = this.SelectionStart;
\r
813 string text = this.InsertLineHead(GetTextFromLineSelectArea(this.View.Selections), "\t");
\r
814 this.RepleaceSelectionArea(this.View.Selections,text);
\r
815 this.Select(selectionStart, text.Length);
\r
819 /// 選択文字列のインデントを一つ減らす
\r
821 public void DownIndent()
\r
823 if (this.RectSelection || this.SelectionLength == 0)
\r
825 int selectionStart = this.SelectionStart;
\r
826 string text = this.RemoveLineHead(GetTextFromLineSelectArea(this.View.Selections), "\t");
\r
827 this.RepleaceSelectionArea(this.View.Selections, text);
\r
828 this.Select(selectionStart, text.Length);
\r
831 string InsertLineHead(string s, string str)
\r
833 string[] lines = s.Split(new string[] { Document.NewLine.ToString() }, StringSplitOptions.RemoveEmptyEntries);
\r
834 StringBuilder output = new StringBuilder();
\r
835 for (int i = 0; i < lines.Length; i++)
\r
836 output.AppendLine(str + lines[i]);
\r
837 return output.ToString();
\r
840 public string RemoveLineHead(string s, string str)
\r
842 string[] lines = s.Split(new string[] { Document.NewLine.ToString() }, StringSplitOptions.RemoveEmptyEntries);
\r
843 StringBuilder output = new StringBuilder();
\r
844 for (int i = 0; i < lines.Length; i++)
\r
845 if (lines[i].StartsWith(str))
\r
846 output.AppendLine(lines[i].Substring(1));
\r
848 output.AppendLine(lines[i]);
\r
849 return output.ToString();
\r
855 /// <param name="isMoveNext">真なら1文字すすめ、そうでなければ戻す</param>
\r
856 /// <remarks>このメソッドを呼び出した後でScrollToCaretメソッドとSelectWithMoveCaretメソッドを呼び出す必要があります</remarks>
\r
857 void MoveCaretHorizontical(bool isMoveNext)
\r
859 if (this.Document.FireUpdateEvent == false)
\r
860 throw new InvalidOperationException("");
\r
861 int delta = isMoveNext ? 0 : -1;
\r
862 int prevcol = this.View.CaretPostion.col;
\r
863 int col = this.View.CaretPostion.col + delta;
\r
864 string lineString = this.View.LayoutLines[this.View.CaretPostion.row];
\r
865 if (col < 0 || this.View.CaretPostion.row >= this.View.LayoutLines.Count)
\r
867 if (this.View.CaretPostion.row == 0)
\r
872 this.MoveCaretVertical(false);
\r
873 this.View.AdjustCaretAndSrc(AdjustFlow.Row); //この段階で調整しないとスクロールされない
\r
874 col = this.View.LayoutLines.GetData(this.View.CaretPostion.row).Length - 1; //最終行以外はすべて改行コードが付くはず
\r
876 else if (col >= lineString.Length || lineString[col] == Document.NewLine)
\r
878 if (this.View.CaretPostion.row < this.View.LayoutLines.Count - 1)
\r
880 this.MoveCaretVertical(true);
\r
881 this.View.AdjustCaretAndSrc(AdjustFlow.Row); //この段階で調整しないとスクロールされない
\r
887 AlignDirection direction = isMoveNext ? AlignDirection.Forward : AlignDirection.Back;
\r
888 col = this.View.LayoutLines.GetData(this.View.CaretPostion.row).Layout.AlignIndexToNearestCluster(col, direction);
\r
891 this.View.JumpCaret(this.View.CaretPostion.row, col,false);
\r
895 /// キャレットを行方向に移動させる
\r
897 /// <param name="isMoveNext">プラス方向に移動するなら真</param>
\r
898 /// <remarks>このメソッドを呼び出した後でScrollToCaretメソッドとSelectWithMoveCaretメソッドを呼び出す必要があります</remarks>
\r
899 void MoveCaretVertical(bool isMoveNext)
\r
901 if (this.Document.FireUpdateEvent == false)
\r
902 throw new InvalidOperationException("");
\r
904 TextPoint nextPoint = this.GetTextPointAfterMoveLine(isMoveNext ? 1 : -1, this.View.CaretPostion);
\r
906 this.View.JumpCaret(nextPoint.row, nextPoint.col,false);
\r
909 private void ReplaceBeforeSelectionArea(SelectCollection Selections, int removeLength, string insertStr)
\r
911 if (removeLength == 0 && insertStr.Length == 0)
\r
914 if (this.RectSelection == false || this.Document.FireUpdateEvent == false)
\r
915 throw new InvalidOperationException();
\r
917 SelectCollection temp = this.View.InsertPoint;
\r
918 int selectStart = temp.First().start;
\r
919 int selectEnd = temp.Last().start + temp.Last().length;
\r
921 //ドキュメント操作後に行うとうまくいかないので、あらかじめ取得しておく
\r
922 TextPoint start = this.View.LayoutLines.GetTextPointFromIndex(selectStart);
\r
923 TextPoint end = this.View.LayoutLines.GetTextPointFromIndex(selectEnd);
\r
925 bool reverse = temp.First().start > temp.Last().start;
\r
927 int lineHeadIndex = this.View.LayoutLines.GetIndexFromLineNumber(this.View.LayoutLines.GetLineNumberFromIndex(selectStart));
\r
928 if (selectStart - removeLength < lineHeadIndex)
\r
931 this.Document.UndoManager.BeginUndoGroup();
\r
932 this.Document.FireUpdateEvent = false;
\r
936 for (int i = 0; i < temp.Count; i++)
\r
938 this.ReplaceBeforeSelection(temp[i], removeLength, insertStr);
\r
943 for (int i = temp.Count - 1; i >= 0; i--)
\r
945 this.ReplaceBeforeSelection(temp[i], removeLength, insertStr);
\r
949 this.Document.FireUpdateEvent = true;
\r
950 this.Document.UndoManager.EndUndoGroup();
\r
952 int delta = insertStr.Length - removeLength;
\r
953 start.col += delta;
\r
957 this.JumpCaret(start.row, start.col);
\r
959 this.JumpCaret(end.row, end.col);
\r
961 this.Select(start, 0, end.row - start.row);
\r
964 private void ReplaceBeforeSelection(Selection sel, int removeLength, string insertStr)
\r
966 sel = Util.NormalizeIMaker<Selection>(sel);
\r
967 insertStr = Util.Generate(' ', sel.padding) + insertStr;
\r
968 this.Document.Replace(sel.start - removeLength, removeLength, insertStr);
\r
971 private void RepleaceSelectionArea(SelectCollection Selections, string value,bool updateInsertPoint = false)
\r
976 if (this.RectSelection == false)
\r
978 Selection sel = Selection.Create(this.AnchorIndex, 0);
\r
979 if (Selections.Count > 0)
\r
980 sel = Util.NormalizeIMaker<Selection>(this.View.Selections.First());
\r
982 this.Document.Replace(sel.start, sel.length, value);
\r
986 if (this.Document.FireUpdateEvent == false)
\r
987 throw new InvalidOperationException("");
\r
989 int StartIndex = this.SelectionStart;
\r
991 SelectCollection newInsertPoint = new SelectCollection();
\r
993 if (this.SelectionLength == 0)
\r
997 this.Document.UndoManager.BeginUndoGroup();
\r
999 this.Document.FireUpdateEvent = false;
\r
1001 string[] line = value.Split(new string[] { Document.NewLine.ToString() }, StringSplitOptions.RemoveEmptyEntries);
\r
1003 TextPoint Current = this.View.GetLayoutLineFromIndex(this.SelectionStart);
\r
1005 for (i = 0; i < line.Length && Current.row < this.View.LayoutLines.Count; i++, Current.row++)
\r
1007 if (Current.col > this.View.LayoutLines[Current.row].Length)
\r
1008 Current.col = this.View.LayoutLines[Current.row].Length;
\r
1009 StartIndex = this.View.GetIndexFromLayoutLine(Current);
\r
1010 this.Document.Replace(StartIndex, 0, line[i]);
\r
1011 StartIndex += line[i].Length;
\r
1014 for (; i < line.Length; i++)
\r
1016 StartIndex = this.Document.Length;
\r
1017 string str = Document.NewLine + line[i];
\r
1018 this.Document.Replace(StartIndex, 0, str);
\r
1019 StartIndex += str.Length;
\r
1022 this.Document.FireUpdateEvent = true;
\r
1024 this.Document.UndoManager.EndUndoGroup();
\r
1028 SelectCollection temp = new SelectCollection(this.View.Selections); //コピーしないとReplaceCommandを呼び出した段階で書き換えられてしまう
\r
1030 this.Document.UndoManager.BeginUndoGroup();
\r
1032 this.Document.FireUpdateEvent = false;
\r
1034 if (temp.First().start < temp.Last().start)
\r
1036 for (int i = temp.Count - 1; i >= 0; i--)
\r
1038 Selection sel = Util.NormalizeIMaker<Selection>(temp[i]);
\r
1040 StartIndex = sel.start;
\r
1042 this.Document.Replace(sel.start, sel.length, value);
\r
1044 newInsertPoint.Add(Selection.Create(sel.start + (value.Length - sel.length) * i,0));
\r
1049 for (int i = 0; i < temp.Count; i++)
\r
1051 Selection sel = Util.NormalizeIMaker<Selection>(temp[i]);
\r
1053 StartIndex = sel.start;
\r
1055 this.Document.Replace(sel.start, sel.length, value);
\r
1057 newInsertPoint.Add(Selection.Create(sel.start + (value.Length - sel.length) * i, 0));
\r
1061 this.Document.FireUpdateEvent = true;
\r
1063 this.Document.UndoManager.EndUndoGroup();
\r
1065 this.JumpCaret(StartIndex);
\r
1066 if (updateInsertPoint && newInsertPoint.Count > 0)
\r
1067 this.View.InsertPoint = newInsertPoint;
\r
1070 private string GetTextFromLineSelectArea(SelectCollection Selections)
\r
1072 Selection sel = Util.NormalizeIMaker<Selection>(Selections.First());
\r
1074 string str = this.Document.ToString(sel.start, sel.length);
\r
1079 string GetTextFromRectangleSelectArea(SelectCollection Selections)
\r
1081 StringBuilder temp = new StringBuilder();
\r
1082 if (Selections.First().start < Selections.Last().start)
\r
1084 for (int i = 0; i < this.View.Selections.Count; i++)
\r
1086 Selection sel = Util.NormalizeIMaker<Selection>(Selections[i]);
\r
1088 string str = this.Document.ToString(sel.start, sel.length);
\r
1089 if (str.IndexOf(Environment.NewLine) == -1)
\r
1090 temp.AppendLine(str);
\r
1097 for (int i = this.View.Selections.Count - 1; i >= 0; i--)
\r
1099 Selection sel = Util.NormalizeIMaker<Selection>(Selections[i]);
\r
1101 string str = this.Document.ToString(sel.start, sel.length).Replace(Document.NewLine.ToString(), Environment.NewLine);
\r
1102 if (str.IndexOf(Environment.NewLine) == -1)
\r
1103 temp.AppendLine(str);
\r
1108 return temp.ToString();
\r
1111 void View_LineBreakChanged(object sender, EventArgs e)
\r
1113 this.DeSelectAll();
\r
1114 this.AdjustCaret();
\r
1117 void View_PageBoundChanged(object sender, EventArgs e)
\r
1119 if (this.View.LineBreak == LineBreakMethod.PageBound && this.View.PageBound.Width - this.View.LineBreakingMarginWidth > 0)
\r
1120 this.View.PerfomLayouts();
\r
1121 this.AdjustCaret();
\r
1124 void render_ChangedRenderResource(object sender, ChangedRenderRsourceEventArgs e)
\r
1126 if (e.type == ResourceType.Font)
\r
1128 if (this.View.LineBreak != LineBreakMethod.None)
\r
1129 this.View.PerfomLayouts();
\r
1130 this.AdjustCaret();
\r
1134 void render_ChangedRightToLeft(object sender, EventArgs e)
\r
1136 this.AdjustCaret();
\r
1139 void Document_Update(object sender, DocumentUpdateEventArgs e)
\r
1143 case UpdateType.Replace:
\r
1144 this.JumpCaret(e.startIndex + e.insertLength,true);
\r
1146 case UpdateType.Clear:
\r
1147 this.View.TryScroll(0, 0);
\r
1152 void Document_Progress(object sender, ProgressEventArgs e)
\r
1154 if (e.state == ProgressState.Complete)
\r
1155 this.JumpCaret(0);
\r