OSDN Git Service

インストーラーのパスを変更した
[fooeditengine/FooEditEngine.git] / Core / Controller.cs
1 /*
2  * Copyright (C) 2013 FooProject
3  * * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
5
6  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 
7  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
8
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/>.
10  */
11 using System;
12 using System.Globalization;
13 using System.Text;
14 using System.Linq;
15 using System.Diagnostics;
16 using System.Text.RegularExpressions;
17 #if WINFORM
18 using System.Drawing;
19 #endif
20
21 namespace FooEditEngine
22 {
23     internal enum MoveFlow
24     {
25         Horizontical,
26         Vertical,
27     }
28     internal enum ScrollDirection
29     {
30         Up,
31         Down,
32         Left,
33         Right,
34     }
35
36     /// <summary>
37     /// インデントの方法を表す
38     /// </summary>
39     public enum IndentMode
40     {
41         Tab,
42         Space,
43     }
44
45     /// <summary>
46     /// ユーザー側からの処理を担当するクラス。一部を除き、こちらで行われた操作はアンドゥの対象になります
47     /// </summary>
48     internal sealed class Controller
49     {
50         EditView View;
51         Document Document;
52         int AnchorIndex;
53         
54         public Controller(Document doc, EditView view)
55         {
56             this.Document = doc;
57             this.Document.Update += new DocumentUpdateEventHandler(Document_Update);
58             this.View = view;
59             this.View.render.ChangedRightToLeft += render_ChangedRightToLeft;
60             this.View.render.ChangedRenderResource += render_ChangedRenderResource;
61             this.View.PerformLayouted += View_LineBreakChanged;
62             this.View.PageBoundChanged += View_PageBoundChanged;
63             this.SelectionChanged += new EventHandler((s, e) => { });
64             this.Document.Clear();
65         }
66
67         /// <summary>
68         /// 選択領域変更時に通知される
69         /// </summary>
70         public event EventHandler SelectionChanged;
71
72         /// <summary>
73         /// 矩形選択モードなら真を返し、そうでない場合は偽を返す
74         /// </summary>
75         public bool RectSelection
76         {
77             get;
78             set;
79         }
80
81         /// <summary>
82         /// インデントの方法を表す
83         /// </summary>
84         public IndentMode IndentMode
85         {
86             get;
87             set;
88         }
89
90         /// <summary>
91         /// 選択範囲の開始位置
92         /// </summary>
93         /// <remarks>SelectionLengthが0の場合、キャレット位置を表します</remarks>
94         public int SelectionStart
95         {
96             get
97             {
98                 if (this.View.Selections.Count == 0)
99                     return this.AnchorIndex;
100                 else
101                     return this.View.Selections.First().start;
102             }
103         }
104
105         /// <summary>
106         /// 選択範囲の長さ
107         /// </summary>
108         /// <remarks>矩形選択モードの場合、選択範囲の文字数ではなく、開始位置から終了位置までの長さとなります</remarks>
109         public int SelectionLength
110         {
111             get
112             {
113                 if (this.View.Selections.Count == 0)
114                     return 0;
115                 Selection last = this.View.Selections.Last();
116                 return last.start + last.length - this.SelectionStart;
117             }
118         }
119
120         /// <summary>
121         /// 選択範囲内の文字列を返す
122         /// </summary>
123         /// <remarks>
124         /// 未選択状態で代入したときは追加され、そうでない場合は選択範囲の文字列と置き換えられます。
125         /// </remarks>
126         public string SelectedText
127         {
128             get
129             {
130                 if (this.View.LayoutLines.Count == 0 || this.View.Selections.Count == 0)
131                     return null;
132                 if (this.RectSelection)
133                     return GetTextFromRectangleSelectArea(this.View.Selections);
134                 else
135                     return GetTextFromLineSelectArea(this.View.Selections).Replace(Document.NewLine.ToString(), Environment.NewLine);
136             }
137             set
138             {
139                 if (this.Document.FireUpdateEvent == false)
140                     throw new InvalidOperationException("");
141                 if (value == null)
142                     return;
143                 this.RepleaceSelectionArea(this.View.Selections, value.Replace(Environment.NewLine,Document.NewLine.ToString()));
144             }
145         }
146
147         /// <summary>
148         /// 選択範囲が逆転しているかどうかを判定する
149         /// </summary>
150         /// <returns>逆転しているなら真を返す</returns>
151         public bool IsReverseSelect()
152         {
153             int index = this.View.LayoutLines.GetIndexFromTextPoint(this.View.CaretPostion);
154             return index < this.AnchorIndex;
155         }
156
157         /// <summary>
158         /// 指定された範囲を選択する
159         /// </summary>
160         /// <param name="start"></param>
161         /// <param name="length"></param>
162         /// <remarks>RectSelectionの値によって動作が変わります。真の場合は矩形選択モードに、そうでない場合は行ごとに選択されます</remarks>
163         public void Select(int start, int length)
164         {
165             if (this.Document.FireUpdateEvent == false)
166                 throw new InvalidOperationException("");
167             if (start < 0 || start + length < 0 || start + length > this.Document.Length)
168                 throw new ArgumentOutOfRangeException("startかendが指定できる範囲を超えてます");
169             this.View.Selections.Clear();
170             if (length < 0)
171             {
172                 int oldStart = start;
173                 start += length;
174                 length = oldStart - start;
175             }
176             if (this.RectSelection && length != 0)
177             {
178                 TextPoint startTextPoint = this.View.GetLayoutLineFromIndex(start);
179                 TextPoint endTextPoint = this.View.GetLayoutLineFromIndex(start + length);
180                 this.SelectByRectangle(new TextRectangle(startTextPoint, endTextPoint));
181                 if (startTextPoint.col == endTextPoint.col)
182                     this.View.InsertPoint = new SelectCollection(this.View.Selections);
183                 else
184                     this.View.InsertPoint = null;
185             }
186             else if(length != 0)
187             {
188                 this.View.Selections.Add(Selection.Create(start, length));
189                 this.View.InsertPoint = null;
190             }
191             this.SelectionChanged(this, null);
192         }
193
194         public void Select(TextPoint tp, int width, int height)
195         {
196             if (this.Document.FireUpdateEvent == false || !this.RectSelection)
197                 throw new InvalidOperationException("");
198             TextPoint end = tp;
199
200             end.row = tp.row + height;
201             end.col = tp.col + width;
202             
203             if (end.row > this.View.LayoutLines.Count - 1)
204                 throw new ArgumentOutOfRangeException("");
205             
206             this.View.Selections.Clear();
207             
208             this.SelectByRectangle(new TextRectangle(tp,end));
209             
210             if (width == 0)
211                 this.View.InsertPoint = new SelectCollection(this.View.Selections);
212             else
213                 this.View.InsertPoint = null;
214
215             this.SelectionChanged(this, null);
216         }
217
218         private void SelectByRectangle(TextRectangle rect)
219         {
220             if (this.Document.FireUpdateEvent == false)
221                 throw new InvalidOperationException("");
222             if (rect.TopLeft <= rect.BottomRight)
223             {
224                 for (int i = rect.TopLeft.row; i <= rect.BottomLeft.row; i++)
225                 {
226                     int length = this.View.LayoutLines.GetLengthFromLineNumber(i);
227                     int leftCol = rect.TopLeft.col, rightCol = rect.TopRight.col, lastCol = length;
228                     if(length > 0 && this.View.LayoutLines[i][length - 1] == Document.NewLine)
229                         lastCol =  length - 1;
230                     if (lastCol < 0)
231                         lastCol = 0;
232                     if (rect.TopLeft.col > lastCol)
233                         leftCol = lastCol;
234                     if (rect.TopRight.col > lastCol)
235                         rightCol = lastCol;
236
237                     int StartIndex = this.View.LayoutLines.GetIndexFromTextPoint(new TextPoint(i, leftCol));
238                     int EndIndex = this.View.LayoutLines.GetIndexFromTextPoint(new TextPoint(i, rightCol));
239
240                     Selection sel;
241                     sel = Selection.Create(StartIndex, EndIndex - StartIndex);
242
243                     this.View.Selections.Add(sel);
244                 }
245             }
246         }
247
248         /// <summary>
249         /// 単語単位で選択する
250         /// </summary>
251         /// <param name="index">探索を開始するインデックス</param>
252         public void SelectWord(int index)
253         {
254             if (this.Document.FireUpdateEvent == false)
255                 throw new InvalidOperationException("");
256
257             if (this.Document.Length <= 0 || index >= this.Document.Length)
258                 return;
259
260             Document str = this.Document;
261
262             int start = index;
263             while (start > 0 && !Util.IsWordSeparator(str[start]))
264                 start--;
265
266             if (Util.IsWordSeparator(str[start]))
267                 start++;
268
269             int end = index;
270             while (end < this.Document.Length && !Util.IsWordSeparator(str[end]))
271                 end++;
272
273             this.Select(start, end - start);
274         }
275
276         /// <summary>
277         /// 選択範囲内のUTF32コードポイントを文字列に変換します
278         /// </summary>
279         /// <returns>成功した場合は真。そうでない場合は偽を返す</returns>
280         public bool ConvertToChar()
281         {
282             if (this.SelectionLength == 0 || this.RectSelection)
283                 return false;
284             string str = this.Document.ToString(this.SelectionStart, this.SelectionLength);
285             string[] codes = str.Split(new char[] { ' ' },StringSplitOptions.RemoveEmptyEntries);
286             StringBuilder result = new StringBuilder();
287             foreach (string code in codes)
288             {
289                 int utf32_code;
290                 if (code[0] != 'U')
291                     return false;
292                 if (Int32.TryParse(code.TrimStart('U'),NumberStyles.HexNumber,null, out utf32_code))
293                     result.Append(Char.ConvertFromUtf32(utf32_code));
294                 else
295                     return false;
296             }
297             this.Document.Lock();
298             this.Document.Replace(this.SelectionStart, this.SelectionLength, result.ToString());
299             this.Document.UnLock();
300             return true;
301         }
302
303         /// <summary>
304         /// 選択文字列をUTF32のコードポイントに変換します
305         /// </summary>
306         public void ConvertToCodePoint()
307         {
308             if (this.SelectionLength == 0 || this.RectSelection)
309                 return;
310             string str = this.Document.ToString(this.SelectionStart, this.SelectionLength);
311             StringInfo info = new StringInfo(str);
312             StringBuilder result = new StringBuilder();
313             for (int i = 0; i < str.Length;)
314             {
315                 int utf32_code = Char.ConvertToUtf32(str, i); 
316                 result.Append("U" + Convert.ToString(utf32_code,16));
317                 result.Append(' ');
318                 if(Char.IsHighSurrogate(str[i]))
319                     i += 2;
320                 else
321                     i++;
322             }
323             this.Document.Lock();
324             this.Document.Replace(this.SelectionStart, this.SelectionLength, result.ToString());
325             this.Document.UnLock();
326         }
327
328         /// <summary>
329         /// 選択を解除する
330         /// </summary>
331         public void DeSelectAll()
332         {
333             if (this.Document.FireUpdateEvent == false)
334                 throw new InvalidOperationException("");
335
336             this.View.Selections.Clear();
337         }
338
339         /// <summary>
340         /// 任意のマーカーかどうか
341         /// </summary>
342         /// <param name="tp"></param>
343         /// <param name="type"></param>
344         /// <returns>真ならマーカーがある</returns>
345         public bool IsMarker(TextPoint tp,HilightType type)
346         {
347             if (this.Document.FireUpdateEvent == false)
348                 throw new InvalidOperationException("");
349             int index = this.View.LayoutLines.GetIndexFromTextPoint(tp);
350             return this.IsMarker(index, type);
351         }
352
353         /// <summary>
354         /// 任意のマーカーかどうか判定する
355         /// </summary>
356         /// <param name="index"></param>
357         /// <param name="type"></param>
358         /// <returns>真ならマーカーがある</returns>
359         public bool IsMarker(int index, HilightType type)
360         {
361             foreach(int id in this.Document.Markers.IDs)
362             {
363                 foreach (Marker m in this.Document.GetMarkers(index, id))
364                 {
365                     if (m.hilight == type)
366                         return true;
367                 }
368             }
369             return false;
370         }
371
372         /// <summary>
373         /// キャレット位置を再調整する
374         /// </summary>
375         public void AdjustCaret()
376         {
377             int row = this.View.CaretPostion.row;
378             if (row > this.View.LayoutLines.Count - 1)
379                 row = this.View.LayoutLines.Count - 1;
380             int col = this.View.CaretPostion.col;
381             if (col > 0 && col > this.View.LayoutLines[row].Length)
382                 col = this.View.LayoutLines[row].Length;
383             this.JumpCaret(row, col);
384         }
385
386         /// <summary>
387         /// キャレットを指定した位置に移動させる
388         /// </summary>
389         /// <param name="index"></param>
390         /// <param name="autoExpand">折り畳みを展開するなら真</param>
391         public void JumpCaret(int index,bool autoExpand = true)
392         {
393             if (index < 0 || index > this.Document.Length)
394                 throw new ArgumentOutOfRangeException("indexが設定できる範囲を超えています");
395             TextPoint tp = this.View.GetLayoutLineFromIndex(index);
396
397             this.JumpCaret(tp.row, tp.col,autoExpand);
398          }
399
400         /// <summary>
401         /// キャレットを指定した位置に移動させる
402         /// </summary>
403         /// <param name="row"></param>
404         /// <param name="col"></param>
405         /// <param name="autoExpand">折り畳みを展開するなら真</param>
406         public void JumpCaret(int row, int col, bool autoExpand = true)
407         {
408             if (this.Document.FireUpdateEvent == false)
409                 throw new InvalidOperationException("");
410
411             this.View.JumpCaret(row, col,autoExpand);
412
413             this.View.AdjustCaretAndSrc();
414
415             this.SelectWithMoveCaret(false);
416         }
417
418         /// <summary>
419         /// 行の先頭に移動する
420         /// </summary>
421         /// <param name="row">行</param>
422         /// <param name="isSelected">選択状態にするかどうか</param>
423         public void JumpToLineHead(int row,bool isSelected)
424         {
425             this.View.JumpCaret(row, 0);
426             this.View.AdjustCaretAndSrc();
427             this.SelectWithMoveCaret(isSelected);
428         }
429
430         /// <summary>
431         /// 行の終わりに移動する
432         /// </summary>
433         /// <param name="row">行</param>
434         /// <param name="isSelected">選択状態にするかどうか</param>
435         public void JumpToLineEnd(int row, bool isSelected)
436         {
437             this.View.JumpCaret(row, this.View.LayoutLines[row].Length - 1);
438             this.View.AdjustCaretAndSrc();
439             this.SelectWithMoveCaret(isSelected);
440         }
441
442         /// <summary>
443         /// ドキュメントの先頭に移動する
444         /// </summary>
445         /// <param name="isSelected"></param>
446         public void JumpToHead(bool isSelected)
447         {
448             if (this.View.TryScroll(0, 0))
449                 return;
450             this.View.JumpCaret(0, 0);
451             this.View.AdjustCaretAndSrc();
452             this.SelectWithMoveCaret(isSelected);
453         }
454
455         /// <summary>
456         /// ドキュメントの終わりにに移動する
457         /// </summary>
458         /// <param name="isSelected"></param>
459         public void JumpToEnd(bool isSelected)
460         {
461             int srcRow = this.View.LayoutLines.Count - this.View.LineCountOnScreen - 1;
462             if(srcRow < 0)
463                 srcRow = 0;
464             if (this.View.TryScroll(0, srcRow))
465                 return;
466             this.View.JumpCaret(this.View.LayoutLines.Count - 1, 0);
467             this.View.AdjustCaretAndSrc();
468             this.SelectWithMoveCaret(isSelected);
469         }
470
471         /// <summary>
472         /// スクロールする
473         /// </summary>
474         /// <param name="dir">方向を指定する</param>
475         /// <param name="delta">スクロールする量。ScrollDirectionの値がUpやDownなら行数。LeftやRightならピクセル単位の値となる</param>
476         /// <param name="isSelected">選択状態にするなら真</param>
477         /// <param name="withCaret">同時にキャレットを移動させるなら真</param>
478         public void Scroll(ScrollDirection dir, int delta, bool isSelected,bool withCaret)
479         {
480             if (this.Document.FireUpdateEvent == false)
481                 throw new InvalidOperationException("");
482             int toRow = this.View.Src.Row;
483             double toX = this.View.Src.X;
484             switch (dir)
485             {
486                 case ScrollDirection.Up:
487                     toRow = Math.Max(0, this.View.Src.Row - delta);
488                     toRow = this.View.AdjustRow(toRow, false);
489                     break;
490                 case ScrollDirection.Down:
491                     toRow = Math.Min(this.View.Src.Row + delta, this.View.LayoutLines.Count - 1);
492                     toRow = this.View.AdjustRow(toRow, true);
493                     break;
494                 case ScrollDirection.Left:
495                     toX -= delta;
496                     break;
497                 case ScrollDirection.Right:
498                     toX += delta;
499                     break;
500                 default:
501                     throw new ArgumentOutOfRangeException();
502             }
503             this.Scroll(toX, toRow, isSelected, withCaret);
504         }
505
506         /// <summary>
507         /// スクロールする
508         /// </summary>
509         /// <param name="toX">スクロール先の座標</param>
510         /// <param name="toRow">スクロール先の行</param>
511         /// <param name="isSelected">選択状態にするなら真</param>
512         /// <param name="withCaret">同時にキャレットを移動させるなら真</param>
513         public void Scroll(double toX, int toRow, bool isSelected, bool withCaret)
514         {
515             if (withCaret)
516             {
517                 this.View.Scroll(toX, toRow);
518                 this.View.JumpCaret(toRow, 0);
519                 this.View.AdjustCaretAndSrc();
520                 this.SelectWithMoveCaret(isSelected);
521             }
522             else
523             {
524                 this.View.Scroll(toX, toRow);
525                 this.View.IsFocused = false;
526             }
527         }
528
529         /// <summary>
530         /// キャレットを桁方向に移動させる
531         /// </summary>
532         /// <returns>移動できない場合は真を返す</returns>
533         /// <param name="realLength">負の値なら左側へ、そうでないなら右側へ移動する</param>
534         /// <param name="isSelected">選択範囲とするなら真。そうでないなら偽</param>
535         /// <param name="alignWord">単語単位で移動するなら真。そうでないなら偽</param>
536         public void MoveCaretHorizontical(int realLength, bool isSelected,bool alignWord = false)
537         {
538             for (int i = Math.Abs(realLength); i > 0; i--)
539             {
540                 bool MoveFlow = realLength > 0;
541                 if (this.View.render.RightToLeft)
542                     MoveFlow = !MoveFlow;
543                 this.MoveCaretHorizontical(MoveFlow);
544
545                 if (alignWord)
546                     this.AlignNearestWord(MoveFlow);
547             }
548             this.View.AdjustCaretAndSrc(AdjustFlow.Col);
549             this.SelectWithMoveCaret(isSelected);
550         }
551
552         void AlignNearestWord(bool MoveFlow)
553         {
554             string str = this.View.LayoutLines[this.View.CaretPostion.row];
555             while (this.View.CaretPostion.col > 0 &&
556                 this.View.CaretPostion.col < str.Length &&
557                 str[this.View.CaretPostion.col] != Document.NewLine)
558             {
559                 if (!Util.IsWordSeparator(str[this.View.CaretPostion.col]))
560                 {
561                     this.MoveCaretHorizontical(MoveFlow);
562                 }
563                 else
564                 {
565                     if(MoveFlow)
566                         this.MoveCaretHorizontical(MoveFlow);
567                     break;
568                 }
569             }
570         }
571
572         /// <summary>
573         /// キャレットを行方向に移動させる
574         /// </summary>
575         /// <returns>再描写する必要があるなら真を返す</returns>
576         /// <param name="deltarow">移動量</param>
577         /// <param name="isSelected"></param>
578         public void MoveCaretVertical(int deltarow,bool isSelected)
579         {
580             for (int i = Math.Abs(deltarow); i > 0; i--)
581                 this.MoveCaretVertical(deltarow > 0);
582             this.View.AdjustCaretAndSrc(AdjustFlow.Both);
583             this.SelectWithMoveCaret(isSelected);
584         }
585
586         /// <summary>
587         /// キャレット位置の文字を一文字削除する
588         /// </summary>
589         public void DoDeleteAction()
590         {
591             if (this.SelectionLength != 0)
592             {
593                 this.SelectedText = "";
594                 return;
595             }
596             
597             if (this.Document.FireUpdateEvent == false)
598                 throw new InvalidOperationException("");
599
600             TextPoint CaretPostion = this.View.CaretPostion;
601             int index = this.View.GetIndexFromLayoutLine(CaretPostion);
602
603             if (index == this.Document.Length)
604                 return;
605
606             int lineHeadIndex = this.View.LayoutLines.GetIndexFromLineNumber(CaretPostion.row);
607             int next = this.View.LayoutLines.GetLayout(CaretPostion.row).AlignIndexToNearestCluster(CaretPostion.col, AlignDirection.Forward) + lineHeadIndex;
608
609             if (this.Document[index] == Document.NewLine)
610                 next = index + 1;
611
612             this.Document.Lock();
613             this.Document.Replace(index, next - index, "");
614             this.Document.UnLock();
615         }
616
617         /// <summary>
618         /// キャレット位置の文字を一文字削除し、キャレット位置を後ろにずらす
619         /// </summary>
620         public void DoBackSpaceAction()
621         {
622             if (this.View.InsertPoint != null)
623             {
624                 this.ReplaceBeforeSelectionArea(this.View.Selections, 1, "");
625                 return;
626             }
627             else if (this.SelectionLength > 0)
628             {
629                 this.SelectedText = "";
630                 return;
631             }
632
633             if (this.Document.FireUpdateEvent == false)
634                 throw new InvalidOperationException("");
635
636             TextPoint CurrentPostion = this.View.CaretPostion;
637
638             if (CurrentPostion.row == 0 && CurrentPostion.col == 0)
639                 return;
640
641             int oldIndex = this.View.GetIndexFromLayoutLine(CurrentPostion);
642
643             int newCol, newIndex;
644             if (CurrentPostion.col > 0)
645             {
646                 newCol = this.View.LayoutLines.GetLayout(CurrentPostion.row).AlignIndexToNearestCluster(CurrentPostion.col - 1, AlignDirection.Back);
647                 newIndex = this.View.GetIndexFromLayoutLine(new TextPoint(CurrentPostion.row, newCol));
648             }
649             else
650             {
651                 newIndex = this.View.GetIndexFromLayoutLine(CurrentPostion);
652                 newIndex--;
653             }
654
655             this.Document.Lock();
656             this.Document.Replace(newIndex, oldIndex - newIndex, "");
657             this.Document.UnLock();
658         }
659
660         /// <summary>
661         /// キャレット位置で行を分割する
662         /// </summary>
663         public void DoEnterAction()
664         {            
665             this.DoInputChar('\n');
666         }
667
668         /// <summary>
669         /// キャレット位置に文字を入力し、その分だけキャレットを進める。isInsertModeの値により動作が変わります
670         /// </summary>
671         /// <param name="ch"></param>
672         public void DoInputChar(char ch)
673         {
674             this.DoInputString(ch.ToString());
675         }
676
677         string GetIndentSpace(int col_index)
678         {
679             int space_count = this.View.TabStops - (col_index % this.View.TabStops);
680             return new string(Enumerable.Repeat(' ',space_count).ToArray());
681         }
682
683         /// <summary>
684         /// キャレット位置に文字列を挿入し、その分だけキャレットを進める。isInsertModeの値により動作が変わります
685         /// </summary>
686         /// <param name="str"></param>
687         /// <param name="fromTip"></param>
688         public void DoInputString(string str,bool fromTip = false)
689         {
690             TextPoint CaretPos = this.View.CaretPostion;
691
692             if (str == "\t" && this.IndentMode == IndentMode.Space)
693                 str = this.GetIndentSpace(CaretPos.col);
694
695             if (this.View.InsertPoint != null)
696             {
697                 this.ReplaceBeforeSelectionArea(this.View.Selections, 0, str);
698                 return;
699             }
700             else if (this.SelectionLength != 0)
701             {
702                 this.RepleaceSelectionArea(this.View.Selections, str, fromTip);
703                 return;
704             }
705
706             if (this.Document.FireUpdateEvent == false)
707                 throw new InvalidOperationException("");
708
709             int index = this.View.GetIndexFromLayoutLine(this.View.CaretPostion);
710             int length = 0;
711             if (this.View.InsertMode == false && index < this.Document.Length && this.Document[index] != Document.NewLine)
712             {
713                 string lineString = this.View.LayoutLines[CaretPos.row];
714                 int end = this.View.LayoutLines.GetLayout(CaretPos.row).AlignIndexToNearestCluster(CaretPos.col + str.Length - 1, AlignDirection.Forward);
715                 if (end > lineString.Length - 1)
716                     end = lineString.Length - 1;
717                 end += this.View.LayoutLines.GetIndexFromLineNumber(CaretPos.row);
718                 length = end - index;
719             }
720             if (str == Document.NewLine.ToString())
721             {
722                 int lineHeadIndex = this.View.LayoutLines.GetIndexFromLineNumber(CaretPos.row);
723                 int lineLength = this.View.LayoutLines.GetLengthFromLineNumber(CaretPos.row);
724                 FoldingItem foldingData = this.View.LayoutLines.FoldingCollection.GetFarestHiddenFoldingData(lineHeadIndex, lineLength);
725                 if (foldingData != null && !foldingData.Expand && index > foldingData.Start && index <= foldingData.End)
726                     index = foldingData.End + 1;
727             }
728             this.Document.Lock();
729             this.Document.Replace(index, length, str);
730             this.Document.UnLock();
731         }
732
733         /// <summary>
734         /// キャレットの移動に合わせて選択する
735         /// </summary>
736         /// <param name="isSelected">選択状態にするかどうか</param>
737         /// <remarks>
738         /// キャレットを移動後、このメソッドを呼び出さない場合、Select()メソッドは正常に機能しません
739         /// </remarks>
740         void SelectWithMoveCaret(bool isSelected)
741         {
742             if (this.View.CaretPostion.col < 0 || this.View.CaretPostion.row < 0)
743                 return;
744
745             if (this.Document.FireUpdateEvent == false)
746                 throw new InvalidOperationException("");
747
748             int CaretPostion = this.View.GetIndexFromLayoutLine(this.View.CaretPostion);
749             
750             SelectCollection Selections = this.View.Selections;
751             if (isSelected)
752             {
753                 this.Select(this.AnchorIndex, CaretPostion - this.AnchorIndex);
754             }else{
755                 this.AnchorIndex = CaretPostion;
756                 this.View.InsertPoint = null;
757                 this.Select(CaretPostion, 0);
758             }
759         }
760
761         /// <summary>
762         /// JumpCaretで移動した位置からキャレットを移動し、選択状態にする
763         /// </summary>
764         /// <param name="tp"></param>
765         public void MoveCaretAndSelect(TextPoint tp)
766         {
767             int CaretPostion = this.View.GetIndexFromLayoutLine(tp);
768             this.Select(this.AnchorIndex, CaretPostion - this.AnchorIndex);
769             this.View.JumpCaret(tp.row, tp.col);
770             this.View.AdjustCaretAndSrc();
771         }
772
773         public void MoveSelectBefore(TextPoint tp)
774         {
775             int NewAnchorIndex;
776             int SelectionLength;
777             if (this.IsReverseSelect())
778             {
779                 NewAnchorIndex = this.View.GetIndexFromLayoutLine(tp);
780                 SelectionLength = this.SelectionLength + NewAnchorIndex - this.AnchorIndex;
781                 this.Select(this.SelectionStart, SelectionLength);
782             }
783             else
784             {
785                 NewAnchorIndex = this.View.GetIndexFromLayoutLine(tp);
786                 SelectionLength = this.SelectionLength + this.AnchorIndex - NewAnchorIndex;
787                 this.Select(NewAnchorIndex, SelectionLength);
788             }
789             this.AnchorIndex = NewAnchorIndex;
790         }
791
792         /// <summary>
793         /// キャレット位置を既定の位置に戻す
794         /// </summary>
795         public void ResetCaretPostion()
796         {
797             this.JumpCaret(0);
798         }
799
800         /// <summary>
801         /// 行単位で移動後のキャレット位置を取得する
802         /// </summary>
803         /// <param name="count">移動量</param>
804         /// <param name="current">現在のキャレット位置</param>
805         /// <returns>移動後のキャレット位置</returns>
806         public TextPoint GetTextPointAfterMoveLine(int count, TextPoint current)
807         {
808             int row = current.row + count;
809
810             if (row < 0)
811                 row = 0;
812             else if (row >= this.View.LayoutLines.Count)
813                 row = this.View.LayoutLines.Count - 1;
814
815             row = this.View.AdjustRow(row, count > 0);
816
817             double colpos = this.View.GetColPostionFromIndex(current.row, current.col);
818             int col = this.View.GetIndexFromColPostion(row, colpos);
819
820             return new TextPoint(row, col);
821         }
822
823         /// <summary>
824         /// 選択文字列のインデントを一つ増やす
825         /// </summary>
826         public void UpIndent()
827         {
828             if (this.RectSelection || this.SelectionLength == 0)
829                 return;
830             int selectionStart = this.SelectionStart;
831             string insertStr = this.IndentMode == IndentMode.Space ? this.GetIndentSpace(0) : "\t";
832             string text = this.InsertLineHead(GetTextFromLineSelectArea(this.View.Selections), insertStr);
833             this.RepleaceSelectionArea(this.View.Selections,text);
834             this.Select(selectionStart, text.Length);
835         }
836
837         /// <summary>
838         /// 選択文字列のインデントを一つ減らす
839         /// </summary>
840         public void DownIndent()
841         {
842             if (this.RectSelection || this.SelectionLength == 0)
843                 return;
844             int selectionStart = this.SelectionStart;
845             string insertStr = this.IndentMode == IndentMode.Space ? this.GetIndentSpace(0) : "\t";
846             string text = this.RemoveLineHead(GetTextFromLineSelectArea(this.View.Selections), insertStr);
847             this.RepleaceSelectionArea(this.View.Selections, text);
848             this.Select(selectionStart, text.Length);
849         }
850
851         string InsertLineHead(string s, string str)
852         {
853             string[] lines = s.Split(new string[] { Document.NewLine.ToString() }, StringSplitOptions.None);
854             StringBuilder output = new StringBuilder();
855             for (int i = 0; i < lines.Length; i++)
856             {
857                 if(lines[i].Length > 0)
858                     output.Append(str + lines[i] + Document.NewLine);
859                 else if(i < lines.Length - 1)
860                     output.Append(lines[i] + Document.NewLine);
861             }
862             return output.ToString();
863         }
864
865         public string RemoveLineHead(string s, string str)
866         {
867             string[] lines = s.Split(new string[] { Document.NewLine.ToString() }, StringSplitOptions.None);
868             StringBuilder output = new StringBuilder();
869             for (int i = 0; i < lines.Length; i++)
870             {
871                 if (lines[i].StartsWith(str))
872                     output.Append(lines[i].Substring(1) + Document.NewLine);
873                 else if (i < lines.Length - 1)
874                     output.Append(lines[i] + Document.NewLine);
875             }
876             return output.ToString();
877         }
878
879         /// <summary>
880         /// キャレットを一文字移動させる
881         /// </summary>
882         /// <param name="isMoveNext">真なら1文字すすめ、そうでなければ戻す</param>
883         /// <remarks>このメソッドを呼び出した後でScrollToCaretメソッドとSelectWithMoveCaretメソッドを呼び出す必要があります</remarks>
884         void MoveCaretHorizontical(bool isMoveNext)
885         {
886             if (this.Document.FireUpdateEvent == false)
887                 throw new InvalidOperationException("");
888             int delta = isMoveNext ? 0 : -1;
889             int prevcol = this.View.CaretPostion.col;
890             int col = this.View.CaretPostion.col + delta;
891             string lineString = this.View.LayoutLines[this.View.CaretPostion.row];
892             if (col < 0 || this.View.CaretPostion.row >= this.View.LayoutLines.Count)
893             {
894                 if (this.View.CaretPostion.row == 0)
895                 {
896                     col = 0;
897                     return;
898                 }
899                 this.MoveCaretVertical(false);
900                 this.View.AdjustCaretAndSrc(AdjustFlow.Row);  //この段階で調整しないとスクロールされない
901                 col = this.View.LayoutLines.GetLengthFromLineNumber(this.View.CaretPostion.row) - 1;  //最終行以外はすべて改行コードが付くはず
902             }
903             else if (col >= lineString.Length || lineString[col] == Document.NewLine)
904             {
905                 if (this.View.CaretPostion.row < this.View.LayoutLines.Count - 1)
906                 {
907                     this.MoveCaretVertical(true);
908                     this.View.AdjustCaretAndSrc(AdjustFlow.Row);  //この段階で調整しないとスクロールされない
909                     col = 0;
910                 }
911             }
912             else
913             {
914                 AlignDirection direction = isMoveNext ? AlignDirection.Forward : AlignDirection.Back;
915                 col = this.View.LayoutLines.GetLayout(this.View.CaretPostion.row).AlignIndexToNearestCluster(col, direction);
916             }
917
918             this.View.JumpCaret(this.View.CaretPostion.row, col,false);
919         }
920
921         /// <summary>
922         /// キャレットを行方向に移動させる
923         /// </summary>
924         /// <param name="isMoveNext">プラス方向に移動するなら真</param>
925         /// <remarks>このメソッドを呼び出した後でScrollToCaretメソッドとSelectWithMoveCaretメソッドを呼び出す必要があります</remarks>
926         void MoveCaretVertical(bool isMoveNext)
927         {
928             if (this.Document.FireUpdateEvent == false)
929                 throw new InvalidOperationException("");
930
931             TextPoint nextPoint = this.GetTextPointAfterMoveLine(isMoveNext ? 1 : -1, this.View.CaretPostion);
932             
933             this.View.JumpCaret(nextPoint.row, nextPoint.col,false);
934         }
935
936         private void ReplaceBeforeSelectionArea(SelectCollection Selections, int removeLength, string insertStr)
937         {
938             if (removeLength == 0 && insertStr.Length == 0)
939                 return;
940
941             if (this.RectSelection == false || this.Document.FireUpdateEvent == false)
942                 throw new InvalidOperationException();
943
944             SelectCollection temp = this.View.InsertPoint;
945             int selectStart = temp.First().start;
946             int selectEnd = temp.Last().start + temp.Last().length;
947
948             //ドキュメント操作後に行うとうまくいかないので、あらかじめ取得しておく
949             TextPoint start = this.View.LayoutLines.GetTextPointFromIndex(selectStart);
950             TextPoint end = this.View.LayoutLines.GetTextPointFromIndex(selectEnd);
951
952             bool reverse = temp.First().start > temp.Last().start;
953
954             int lineHeadIndex = this.View.LayoutLines.GetIndexFromLineNumber(this.View.LayoutLines.GetLineNumberFromIndex(selectStart));
955             if (selectStart - removeLength < lineHeadIndex)
956                 return;
957
958             this.Document.UndoManager.BeginUndoGroup();
959             this.Document.FireUpdateEvent = false;
960
961             if (reverse)
962             {
963                 for (int i = 0; i < temp.Count; i++)
964                 {
965                     this.ReplaceBeforeSelection(temp[i], removeLength, insertStr);
966                 }
967             }
968             else
969             {
970                 for (int i = temp.Count - 1; i >= 0; i--)
971                 {
972                     this.ReplaceBeforeSelection(temp[i], removeLength, insertStr);
973                 }
974             }
975
976             this.Document.FireUpdateEvent = true;
977             this.Document.UndoManager.EndUndoGroup();
978
979             int delta = insertStr.Length - removeLength;
980             start.col += delta;
981             end.col += delta;
982
983             if (reverse)
984                 this.JumpCaret(start.row, start.col);
985             else
986                 this.JumpCaret(end.row, end.col);
987             
988             this.Select(start, 0, end.row - start.row);
989         }
990
991         private void ReplaceBeforeSelection(Selection sel, int removeLength, string insertStr)
992         {
993             sel = Util.NormalizeIMaker<Selection>(sel);
994             this.Document.Lock();
995             this.Document.Replace(sel.start - removeLength, removeLength, insertStr);
996             this.Document.UnLock();
997         }
998
999         private void RepleaceSelectionArea(SelectCollection Selections, string value,bool updateInsertPoint = false)
1000         {
1001             if (value == null)
1002                 return;
1003
1004             if (this.RectSelection == false)
1005             {
1006                 Selection sel = Selection.Create(this.AnchorIndex, 0);
1007                 if (Selections.Count > 0)
1008                     sel = Util.NormalizeIMaker<Selection>(this.View.Selections.First());
1009
1010                 this.Document.Lock();
1011                 this.Document.Replace(sel.start, sel.length, value);
1012                 this.Document.UnLock();
1013                 return;
1014             }
1015
1016             if (this.Document.FireUpdateEvent == false)
1017                 throw new InvalidOperationException("");
1018
1019             int StartIndex = this.SelectionStart;
1020
1021             SelectCollection newInsertPoint = new SelectCollection();
1022
1023             if (this.SelectionLength == 0)
1024             {
1025                 int i;
1026
1027                 this.Document.Lock();
1028
1029                 this.Document.UndoManager.BeginUndoGroup();
1030
1031                 this.Document.FireUpdateEvent = false;
1032
1033                 string[] line = value.Split(new string[] { Document.NewLine.ToString() }, StringSplitOptions.RemoveEmptyEntries);
1034
1035                 TextPoint Current = this.View.GetLayoutLineFromIndex(this.SelectionStart);
1036
1037                 for (i = 0; i < line.Length && Current.row < this.View.LayoutLines.Count; i++, Current.row++)
1038                 {
1039                     if (Current.col > this.View.LayoutLines[Current.row].Length)
1040                         Current.col = this.View.LayoutLines[Current.row].Length;
1041                     StartIndex = this.View.GetIndexFromLayoutLine(Current);
1042                     this.Document.Replace(StartIndex, 0, line[i]);
1043                     StartIndex += line[i].Length;
1044                 }
1045
1046                 for (; i < line.Length; i++)
1047                 {
1048                     StartIndex = this.Document.Length;
1049                     string str = Document.NewLine + line[i];
1050                     this.Document.Replace(StartIndex, 0, str);
1051                     StartIndex += str.Length;
1052                 }
1053
1054                 this.Document.FireUpdateEvent = true;
1055
1056                 this.Document.UndoManager.EndUndoGroup();
1057
1058                 this.Document.UnLock();
1059             }
1060             else
1061             {
1062                 SelectCollection temp = new SelectCollection(this.View.Selections); //コピーしないとReplaceCommandを呼び出した段階で書き換えられてしまう
1063
1064                 this.Document.Lock();
1065
1066                 this.Document.UndoManager.BeginUndoGroup();
1067
1068                 this.Document.FireUpdateEvent = false;
1069
1070                 if (temp.First().start < temp.Last().start)
1071                 {
1072                     for (int i = temp.Count - 1; i >= 0; i--)
1073                     {
1074                         Selection sel = Util.NormalizeIMaker<Selection>(temp[i]);
1075
1076                         StartIndex = sel.start;
1077
1078                         this.Document.Replace(sel.start, sel.length, value);
1079
1080                         newInsertPoint.Add(Selection.Create(sel.start + (value.Length - sel.length) * i,0));
1081                     }
1082                 }
1083                 else
1084                 {
1085                     for (int i = 0; i < temp.Count; i++)
1086                     {
1087                         Selection sel = Util.NormalizeIMaker<Selection>(temp[i]);
1088
1089                         StartIndex = sel.start;
1090
1091                         this.Document.Replace(sel.start, sel.length, value);
1092
1093                         newInsertPoint.Add(Selection.Create(sel.start + (value.Length - sel.length) * i, 0));
1094                     }
1095                 }
1096
1097                 this.Document.FireUpdateEvent = true;
1098
1099                 this.Document.UndoManager.EndUndoGroup();
1100
1101                 this.Document.UnLock();
1102             }
1103             this.JumpCaret(StartIndex);
1104             if (updateInsertPoint && newInsertPoint.Count > 0)
1105                 this.View.InsertPoint = newInsertPoint;
1106         }
1107
1108         private string GetTextFromLineSelectArea(SelectCollection Selections)
1109         {
1110             Selection sel = Util.NormalizeIMaker<Selection>(Selections.First());
1111
1112             string str = this.Document.ToString(sel.start, sel.length);
1113
1114             return str;
1115         }
1116
1117         string GetTextFromRectangleSelectArea(SelectCollection Selections)
1118         {
1119             StringBuilder temp = new StringBuilder();
1120             if (Selections.First().start < Selections.Last().start)
1121             {
1122                 for (int i = 0; i < this.View.Selections.Count; i++)
1123                 {
1124                     Selection sel = Util.NormalizeIMaker<Selection>(Selections[i]);
1125
1126                     string str = this.Document.ToString(sel.start, sel.length);
1127                     if (str.IndexOf(Environment.NewLine) == -1)
1128                         temp.AppendLine(str);
1129                     else
1130                         temp.Append(str);
1131                 }
1132             }
1133             else
1134             {
1135                 for (int i = this.View.Selections.Count - 1; i >= 0; i--)
1136                 {
1137                     Selection sel = Util.NormalizeIMaker<Selection>(Selections[i]);
1138
1139                     string str = this.Document.ToString(sel.start, sel.length).Replace(Document.NewLine.ToString(), Environment.NewLine);
1140                     if (str.IndexOf(Environment.NewLine) == -1)
1141                         temp.AppendLine(str);
1142                     else
1143                         temp.Append(str);
1144                 }
1145             }
1146             return temp.ToString();
1147         }
1148
1149         void View_LineBreakChanged(object sender, EventArgs e)
1150         {
1151             this.DeSelectAll();
1152             this.AdjustCaret();
1153         }
1154
1155         void View_PageBoundChanged(object sender, EventArgs e)
1156         {
1157             if (this.View.LineBreak == LineBreakMethod.PageBound && this.View.PageBound.Width - this.View.LineBreakingMarginWidth > 0)
1158                 this.View.PerfomLayouts();
1159             this.AdjustCaret();
1160         }
1161
1162         void render_ChangedRenderResource(object sender, ChangedRenderRsourceEventArgs e)
1163         {
1164             if (e.type == ResourceType.Font)
1165             {
1166                 if (this.View.LineBreak == LineBreakMethod.PageBound)
1167                     this.View.PerfomLayouts();
1168                 this.AdjustCaret();
1169             }
1170             if (e.type == ResourceType.InlineChar)
1171             {
1172                 int oldLineCountOnScreen = this.View.LineCountOnScreen;
1173                 this.View.CalculateLineCountOnScreen();
1174                 if(this.View.LineCountOnScreen != oldLineCountOnScreen)
1175                     this.AdjustCaret();
1176             }
1177         }
1178
1179         void render_ChangedRightToLeft(object sender, EventArgs e)
1180         {
1181             this.AdjustCaret();
1182         }
1183
1184         void Document_Update(object sender, DocumentUpdateEventArgs e)
1185         {
1186             switch (e.type)
1187             {
1188                 case UpdateType.Replace:
1189                     if(e.startIndex < this.Document.Length && this.Document[e.startIndex] == Document.NewLine)
1190                         this.View.CalculateLineCountOnScreen();
1191                     this.JumpCaret(e.startIndex + e.insertLength,true);
1192                     break;
1193                 case UpdateType.Clear:
1194                     this.JumpCaret(0,0, false);
1195                     break;
1196             }
1197         }
1198     }
1199 }