OSDN Git Service

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