OSDN Git Service

コア部分を共通プロジェクト化した
[fooeditengine/FooEditEngine.git] / Core / EditView.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.Linq;
13 using System.Collections.Generic;
14
15 namespace FooEditEngine
16 {
17     enum AdjustFlow
18     {
19         Row,
20         Col,
21         Both,
22     }
23
24     /// <summary>
25     /// キャレットとドキュメントの表示を担当します。レイアウト関連もこちらで行います
26     /// </summary>
27     sealed class EditView : ViewBase
28
29     {
30         internal const float LineMarkerThickness = 2;
31         Point _CaretLocation = new Point();
32         TextPoint _CaretPostion = new TextPoint();
33         long tickCount;
34         bool _CaretBlink,_HideRuler = true;
35         internal const int LineNumberLength = 6;
36         const int UpdateAreaPaddingWidth = 2;
37         const int UpdateAreaWidth = 4;
38         const int UpdateAreaTotalWidth = UpdateAreaWidth + UpdateAreaPaddingWidth;
39
40         /// <summary>
41         /// コンストラクター
42         /// </summary>
43         public EditView(Document doc, IEditorRender r,int MarginLeftAndRight = 5)
44             : this(doc,r,new Padding(MarginLeftAndRight, 0, MarginLeftAndRight, 0))
45         {
46         }
47
48         /// <summary>
49         /// コンストラクター
50         /// </summary>
51         /// <param name="doc">ドキュメント</param>
52         /// <param name="r">レンダー</param>
53         /// <param name="margin">マージン(1番目:左、2番目:上、3番目:右、4番目:下)</param>
54         public EditView(Document doc, IEditorRender r, Padding margin)
55             : base(doc,r,margin)
56         {
57             this.CaretBlinkTime = 500;
58             this.CaretWidthOnInsertMode = 1;
59             this.CalculateClipRect();
60             this._CaretLocation.X = this.render.TextArea.X;
61             this._CaretLocation.Y = this.render.TextArea.Y;
62             this.LayoutLines.FoldingCollection.StatusChanged += FoldingCollection_StatusChanged;
63             this.InsertPoint = null;
64             this.HideLineMarker = true;
65             this.IsFocused = false;
66             this.Selections = new SelectCollection();
67         }
68
69         /// <summary>
70         /// 選択範囲コレクション
71         /// </summary>
72         internal SelectCollection Selections
73         {
74             get;
75             private set;
76         }
77
78         /// <summary>
79         /// ラインマーカーを描くなら偽。そうでなければ真
80         /// </summary>
81         public bool HideLineMarker
82         {
83             get;
84             set;
85         }
86
87         /// <summary>
88         /// キャレットを描くなら偽。そうでなければ真
89         /// </summary>
90         public bool HideCaret
91         {
92             get;
93             set;
94         }
95
96         /// <summary>
97         /// 挿入モードなら真を返し、上書きモードなら偽を返す
98         /// </summary>
99         public bool InsertMode
100         {
101             get;
102             set;
103         }
104
105         /// <summary>
106         /// キャレットの点滅間隔
107         /// </summary>
108         public int CaretBlinkTime
109         {
110             get;
111             set;
112         }
113
114         /// <summary>
115         /// 挿入モード時のキャレットの幅
116         /// </summary>
117         public double CaretWidthOnInsertMode
118         {
119             get;
120             set;
121         }
122
123         /// <summary>
124         /// フォーカスがあるなら真をセットする
125         /// </summary>
126         public bool IsFocused
127         {
128             get;
129             set;
130         }
131
132         /// <summary>
133         /// キャレットを点滅させるなら真。そうでないなら偽
134         /// </summary>
135         /// <remarks>キャレット点滅タイマーもリセットされます</remarks>
136         public bool CaretBlink
137         {
138             get { return this._CaretBlink; }
139             set
140             {
141                 this._CaretBlink = value;
142                 if (value)
143                     this.tickCount = DateTime.Now.Ticks + this.To100nsTime(this.CaretBlinkTime);
144             }
145         }
146
147         /// <summary>
148         /// 一ページの高さに収まる行数を返す(こちらは表示されていない行も含みます)
149         /// </summary>
150         public int LineCountOnScreenWithInVisible
151         {
152             get;
153             private set;
154         }
155
156         /// <summary>
157         /// スクロール時に確保するマージン幅
158         /// </summary>
159         public double ScrollMarginWidth
160         {
161             get { return this.PageBound.Width * 20 / 100; }
162         }
163
164         /// <summary>
165         /// ルーラーを表示しないなら真、そうでないなら偽
166         /// </summary>
167         public bool HideRuler
168         {
169             get { return this._HideRuler; }
170             set
171             {
172                 this._HideRuler = value;
173                 this.LayoutLines.ClearLayoutCache();
174                 CalculateClipRect();
175                 CalculateLineCountOnScreen();
176             }
177         }
178
179         /// <summary>
180         /// 矩形選択モード中に文字列が挿入される位置を表す
181         /// </summary>
182         public SelectCollection InsertPoint
183         {
184             get;
185             set;
186         }
187
188         /// <summary>
189         /// キャレットがある領域を示す
190         /// </summary>
191         public Point CaretLocation
192         {
193             get { return this._CaretLocation; }
194         }
195
196         /// <summary>
197         /// レイアウト行のどこにキャレットがあるかを表す
198         /// </summary>
199         public TextPoint CaretPostion
200         {
201             get { return this._CaretPostion; }
202         }
203
204         /// <summary>
205         /// ヒットテストを行う
206         /// </summary>
207         /// <param name="x">x座標</param>
208         /// <param name="y">y座標</param>
209         /// <returns>テキストエリア内にあれば真。そうでなければ偽</returns>
210         public bool HitTextArea(double x, double y)
211         {
212             if (x >= this.render.TextArea.X && x <= this.render.TextArea.Right &&
213                 y >= this.render.TextArea.Y && y <= this.render.TextArea.Bottom)
214                 return true;
215             else
216                 return false;
217         }
218
219         /// <summary>
220         /// ヒットテストを行う
221         /// </summary>
222         /// <param name="x">x座標</param>
223         /// <param name="row">行</param>
224         /// <returns>ヒットした場合はFoldingDataオブジェクトが返され、そうでない場合はnullが返る</returns>
225         public FoldingItem HitFoldingData(double x, int row)
226         {
227             IEditorRender render = (IEditorRender)base.render;
228
229             if (x >= this.GetRealtiveX(AreaType.FoldingArea) && x <= this.GetRealtiveX(AreaType.FoldingArea) + render.FoldingWidth)
230             {
231                 int lineHeadIndex = this.LayoutLines.GetIndexFromLineNumber(row);
232                 int lineLength = this.LayoutLines.GetLengthFromLineNumber(row);
233                 FoldingItem foldingData = this.LayoutLines.FoldingCollection.Get(lineHeadIndex, lineLength);
234                 if (foldingData != null && foldingData.IsFirstLine(this.LayoutLines,row))
235                     return foldingData;
236             }
237             return null;
238         }
239
240         /// <summary>
241         /// Rectで指定された範囲にドキュメントを描く
242         /// </summary>
243         /// <param name="updateRect">描写する範囲</param>
244         /// <remarks>キャレットを点滅させる場合、定期的のこのメソッドを呼び出してください</remarks>
245         public override void Draw(Rectangle updateRect)
246         {
247             if (this.LayoutLines.Count == 0)
248                 return;
249
250             IEditorRender render = (IEditorRender)base.render;
251
252             if ((updateRect.Height < this.PageBound.Height ||
253                 updateRect.Width < this.PageBound.Width) && 
254                 render.IsVaildCache())
255             {
256                 render.DrawCachedBitmap(updateRect);
257             }
258             else
259             {
260                 Rectangle background = this.PageBound;
261                 render.FillBackground(background);
262
263                 if (this.HideRuler == false)
264                     this.DrawRuler();
265
266                 this.DrawLineMarker(this.CaretPostion.row);
267
268                 Point pos = this.render.TextArea.TopLeft;
269                 pos.X -= this.Src.X;
270                 double endposy = this.render.TextArea.Bottom;
271                 Size lineNumberSize = new Size(this.render.LineNemberWidth,this.render.TextArea.Height);
272                 for (int i = this.Src.Row; i < this.LayoutLines.Count; i++)
273                 {
274                     int lineIndex = this.LayoutLines.GetIndexFromLineNumber(i);
275                     int lineLength = this.LayoutLines.GetLengthFromLineNumber(i);
276                     ITextLayout layout = this.LayoutLines.GetLayout(i);
277
278                     if (pos.Y + layout.Height > endposy)
279                         break;
280
281                     FoldingItem foldingData = this.LayoutLines.FoldingCollection.Get(lineIndex, lineLength);
282
283                     if (foldingData != null)
284                     {
285                         if ((!this.LayoutLines.FoldingCollection.IsHasParent(foldingData) || 
286                              !this.LayoutLines.FoldingCollection.IsParentHidden(foldingData))
287                             && foldingData.IsFirstLine(this.LayoutLines, i))
288                             render.DrawFoldingMark(foldingData.Expand, this.PageBound.X + this.GetRealtiveX(AreaType.FoldingArea), pos.Y);
289                         if (this.LayoutLines.FoldingCollection.IsHidden(lineIndex))
290                             continue;
291                     }
292
293                     var selectRange = from s in this.Selections.Get(lineIndex, lineLength)
294                                       let n = Util.ConvertAbsIndexToRelIndex(s, lineIndex, lineLength)
295                                       select n;
296
297                     this.render.DrawOneLine(this.LayoutLines, i, pos.X, pos.Y, selectRange);
298
299                     if (this.DrawLineNumber)
300                     {
301                         this.render.DrawString((i + 1).ToString(), this.PageBound.X + this.GetRealtiveX(AreaType.LineNumberArea), pos.Y, StringAlignment.Right, lineNumberSize,StringColorType.LineNumber);
302                     }
303
304                     DrawUpdateArea(i, pos.Y);
305
306                     pos.Y += this.LayoutLines.GetLayout(i).Height;
307                 }
308
309                 if (this.InsertPoint != null)
310                     this.DrawInsertPoint();
311
312                 render.CacheContent();
313             }
314
315             this.DrawCaret();
316         }
317
318         void DrawUpdateArea(int row,double ypos)
319         {
320             IEditorRender render = (IEditorRender)base.render;
321             if(this.LayoutLines.GetDirtyFlag(row))
322             {
323                 Point pos = new Point(this.PageBound.X + this.GetRealtiveX(AreaType.UpdateArea), ypos);
324                 Rectangle rect = new Rectangle(pos.X, pos.Y, UpdateAreaWidth, this.LayoutLines.GetLayout(row).Height);
325                 render.FillRectangle(rect, FillRectType.UpdateArea);
326             }
327         }
328
329         void DrawRuler()
330         {
331             IEditorRender render = (IEditorRender)base.render;
332
333             Point pos, from, to;
334             Size emSize = render.emSize;
335             Rectangle clipRect = this.render.TextArea;
336             int count = 0;
337             double markerHeight = emSize.Height / 2;
338             if (this.render.RightToLeft)
339             {
340                 pos = new Point(clipRect.TopRight.X, clipRect.TopRight.Y - emSize.Height - LineMarkerThickness);
341                 for (; pos.X >= clipRect.TopLeft.X; pos.X -= emSize.Width, count++)
342                 {
343                     from = pos;
344                     to = new Point(pos.X, pos.Y + emSize.Height);
345                     int mod = count % 10;
346                     if (mod == 0)
347                     {
348                         string countStr = (count / 10).ToString();
349                         double counterWidth = emSize.Width * countStr.Length;
350                         this.render.DrawString(countStr, pos.X - counterWidth, pos.Y, StringAlignment.Right, new Size(counterWidth, double.MaxValue));
351                     }
352                     else if (mod == 5)
353                         from.Y = from.Y + emSize.Height / 2;
354                     else
355                         from.Y = from.Y + emSize.Height * 3 / 4;
356                     render.DrawLine(from, to);
357                     if (this.CaretLocation.X >= pos.X && this.CaretLocation.X < pos.X + emSize.Width)
358                         render.FillRectangle(new Rectangle(pos.X, pos.Y + markerHeight, emSize.Width, markerHeight), FillRectType.OverwriteCaret);
359                 }
360             }
361             else
362             {
363                 pos = new Point(clipRect.TopLeft.X, clipRect.TopLeft.Y - emSize.Height - LineMarkerThickness);
364                 for (; pos.X < clipRect.TopRight.X; pos.X += emSize.Width, count++)
365                 {
366                     from = pos;
367                     to = new Point(pos.X, pos.Y + emSize.Height);
368                     int mod = count % 10;
369                     if (mod == 0)
370                         this.render.DrawString((count / 10).ToString(), pos.X, pos.Y, StringAlignment.Left, new Size(double.MaxValue, double.MaxValue));
371                     else if (mod == 5)
372                         from.Y = from.Y + emSize.Height / 2;
373                     else
374                         from.Y = from.Y + emSize.Height * 3 / 4;
375                     render.DrawLine(from, to);
376                     if (this.CaretLocation.X >= pos.X && this.CaretLocation.X < pos.X + emSize.Width)
377                         render.FillRectangle(new Rectangle(pos.X, pos.Y + markerHeight, emSize.Width, markerHeight), FillRectType.OverwriteCaret);
378                 }
379             }
380             from = clipRect.TopLeft;
381             from.Y -= LineMarkerThickness;
382             to = clipRect.TopRight;
383             to.Y -= LineMarkerThickness;
384             render.DrawLine(from, to);
385         }
386
387         void DrawInsertPoint()
388         {
389             IEditorRender render = (IEditorRender)base.render;
390             foreach (Selection sel in this.InsertPoint)
391             {
392                 if (sel.length == 0)
393                 {
394                     TextPoint tp = this.GetLayoutLineFromIndex(sel.start);
395                     Point left = this.GetPostionFromTextPoint(tp);
396                     double lineHeight = this.LayoutLines.GetLayout(tp.row).Height;
397                     Rectangle InsertRect = new Rectangle(left.X,
398                         left.Y,
399                         CaretWidthOnInsertMode,
400                         lineHeight);
401                     render.FillRectangle(InsertRect, FillRectType.InsertPoint);
402                 }
403             }
404         }
405
406         void DrawCaret()
407         {
408             if (this.HideCaret || !this.IsFocused)
409                 return;
410
411             long diff = DateTime.Now.Ticks - this.tickCount;
412             long blinkTime = this.To100nsTime(this.CaretBlinkTime);
413
414             if (this._CaretBlink && diff % blinkTime >= blinkTime / 2)
415                 return;
416
417             Rectangle CaretRect = new Rectangle();
418
419             IEditorRender render = (IEditorRender)base.render;
420
421             int row = this.CaretPostion.row;
422             double lineHeight = this.LayoutLines.GetLayout(row).Height;
423             double charWidth = this.LayoutLines.GetLayout(row).GetWidthFromIndex(this.CaretPostion.col);
424
425             if (this.InsertMode || charWidth == 0)
426             {
427                 CaretRect.Size = new Size(CaretWidthOnInsertMode, lineHeight);
428                 CaretRect.Location = new Point(this.CaretLocation.X, this.CaretLocation.Y);
429                 render.FillRectangle(CaretRect, FillRectType.InsertCaret);
430             }
431             else
432             {
433                 double height = lineHeight / 3;
434                 CaretRect.Size = new Size(charWidth, height);
435                 CaretRect.Location = new Point(this.CaretLocation.X, this.CaretLocation.Y + lineHeight - height);
436                 render.FillRectangle(CaretRect, FillRectType.OverwriteCaret);
437             }
438         }
439
440         long To100nsTime(int ms)
441         {
442             return ms * 10000;
443         }
444
445         public void DrawLineMarker(int row)
446         {
447             if (this.HideLineMarker || !this.IsFocused)
448                 return;
449             IEditorRender render = (IEditorRender)base.render;
450             Point p = this.CaretLocation;
451             double height = this.LayoutLines.GetLayout(this.CaretPostion.row).Height;
452             double width = this.render.TextArea.Width;
453             render.FillRectangle(new Rectangle(this.PageBound.X + this.render.TextArea.X, this.CaretLocation.Y, width, height), FillRectType.LineMarker);
454         }
455
456         /// <summary>
457         /// 指定した座標の一番近くにあるTextPointを取得する
458         /// </summary>
459         /// <param name="p">テキストエリアを左上とする相対位置</param>
460         /// <returns>レイアウトラインを指し示すTextPoint</returns>
461         public TextPoint GetTextPointFromPostion(Point p)
462         {
463             if (p.Y < this.render.TextArea.TopLeft.Y || 
464                 p.Y > this.render.TextArea.BottomRight.Y)
465                 return TextPoint.Null;
466             TextPoint tp = new TextPoint();
467
468             if (this.LayoutLines.Count == 0)
469                 return tp;
470
471             p.Y -= this.render.TextArea.Y;
472
473             int lineHeadIndex, lineLength;
474             double y = 0;
475             tp.row = this.LayoutLines.Count - 1;
476             for (int i = this.Src.Row; i < this.LayoutLines.Count; i++)
477             {
478                 double height = this.LayoutLines.GetLayout(i).Height;
479
480                 lineHeadIndex = this.LayoutLines.GetIndexFromLineNumber(i);
481                 lineLength = this.LayoutLines.GetLengthFromLineNumber(i);
482
483                 if (this.LayoutLines.FoldingCollection.IsHidden(lineHeadIndex))
484                     continue;
485
486                 if (y + height > p.Y)
487                 {
488                     tp.row = i;
489                     break;
490                 }
491                 y += height;
492             }
493
494             if (p.X < this.render.TextArea.X)
495                 return tp;
496
497             tp.col = GetIndexFromColPostion(tp.row, p.X);
498
499             lineLength = this.LayoutLines.GetLengthFromLineNumber(tp.row);
500             if (tp.col > lineLength)
501                 tp.col = lineLength;
502
503             return tp;
504         }
505
506         /// <summary>
507         /// 桁方向の座標に対応するインデックスを取得する
508         /// </summary>
509         /// <param name="row">対象となる行</param>
510         /// <param name="x">テキストエリアからの相対位置</param>
511         /// <returns></returns>
512         public int GetIndexFromColPostion(int row, double x)
513         {
514             x -= this.render.TextArea.X;
515             int lineLength = this.LayoutLines.GetLengthFromLineNumber(row);
516             if (lineLength == 0)
517                 return 0;
518             int index = this.LayoutLines.GetLayout(row).GetIndexFromColPostion(this.Src.X + x);
519             return index;
520         }
521
522         /// <summary>
523         /// インデックスに対応する桁方向の座標を得る
524         /// </summary>
525         /// <param name="row">対象となる行</param>
526         /// <param name="index">インデックス</param>
527         /// <returns>テキストエリアからの相対位置を返す</returns>
528         public double GetColPostionFromIndex(int row, int index)
529         {
530             double x = this.LayoutLines.GetLayout(row).GetColPostionFromIndex(index);
531             return x - Src.X + this.render.TextArea.X;
532         }
533
534         /// <summary>
535         /// TextPointに対応する座標を得る
536         /// </summary>
537         /// <param name="tp">レイアウトライン上の位置</param>
538         /// <returns>テキストエリアを左上とする相対位置</returns>
539         public Point GetPostionFromTextPoint(TextPoint tp)
540         {
541             Point p = new Point();
542             for (int i = this.Src.Row; i < tp.row; i++)
543             {
544                 int lineHeadIndex = this.LayoutLines.GetIndexFromLineNumber(i);
545                 int lineLength = this.LayoutLines.GetLengthFromLineNumber(i);
546                 if (this.LayoutLines.FoldingCollection.IsHidden(lineHeadIndex))
547                     continue;
548                 p.Y += this.LayoutLines.GetLayout(i).Height;
549             }
550             p.X = this.GetColPostionFromIndex(tp.row, tp.col);
551             p.Y += this.render.TextArea.Y;
552             return p;
553         }
554
555         /// <summary>
556         /// キャレットを指定した位置に移動させる
557         /// </summary>
558         /// <param name="row"></param>
559         /// <param name="col"></param>
560         /// <param name="autoExpand">折り畳みを展開するなら真</param>
561         public void JumpCaret(int row, int col, bool autoExpand = true)
562         {
563             if (autoExpand)
564             {
565                 int lineHeadIndex = this.LayoutLines.GetIndexFromLineNumber(row);
566                 int lineLength = this.LayoutLines.GetLengthFromLineNumber(row);
567                 FoldingItem foldingData = this.LayoutLines.FoldingCollection.Get(lineHeadIndex, lineLength);
568                 if(foldingData != null)
569                 {
570                     if (this.LayoutLines.FoldingCollection.IsParentHidden(foldingData) || !foldingData.IsFirstLine(this.LayoutLines, row))
571                     {
572                         this.LayoutLines.FoldingCollection.Expand(foldingData);
573                     }
574                 }
575             }
576
577             this._CaretPostion.row = row;
578             this._CaretPostion.col = col;
579         }
580
581         /// <summary>
582         /// index上の文字が表示されるようにSrcを調整する
583         /// </summary>
584         /// <param name="index">インデックス</param>
585         /// <returns>調整されたら真。そうでなければ偽</returns>
586         public bool AdjustSrc(int index)
587         {
588             TextPoint startTextPoint = this.GetLayoutLineFromIndex(index);
589             double x = this.LayoutLines.GetLayout(startTextPoint.row).GetColPostionFromIndex(startTextPoint.col);
590             if (x < this.Src.X ||
591                 x > this.Src.X + this.PageBound.Width)
592             {
593                 this.TryScroll(x, this.Src.Row);
594                 return true;
595             }
596             if (startTextPoint.row < this.Src.Row ||
597                 startTextPoint.row > this.Src.Row + this.LineCountOnScreenWithInVisible)
598             {
599                 this.TryScroll(this.Src.X, startTextPoint.row);
600                 return true;
601             }
602             return false;
603         }
604
605         /// <summary>
606         /// キャレットがあるところまでスクロールする
607         /// </summary>
608         /// <return>再描写する必要があるなら真を返す</return>
609         /// <remarks>Document.Update(type == UpdateType.Clear)イベント時に呼び出した場合、例外が発生します</remarks>
610         public bool AdjustCaretAndSrc(AdjustFlow flow = AdjustFlow.Both)
611         {
612             IEditorRender render = (IEditorRender)base.render;
613
614             if (this.PageBound.Width == 0 || this.PageBound.Height == 0)
615             {
616                 this.SetCaretPostion(this.Padding.Left + render.FoldingWidth, 0);
617                 return false;
618             }
619
620             bool result = false;
621             TextPoint tp = this.CaretPostion;
622             double x = this.CaretLocation.X;
623             double y = this.CaretLocation.Y;
624
625             if (flow == AdjustFlow.Col || flow == AdjustFlow.Both)
626             {
627                 x = this.LayoutLines.GetLayout(tp.row).GetColPostionFromIndex(tp.col);
628
629                 double left = this.Src.X;
630                 double right = this.Src.X + this.render.TextArea.Width;
631
632                 if (x >= left && x <= right)    //xは表示領域にないにある
633                 {
634                     x -= left;
635                 }
636                 else if (x > right) //xは表示領域の右側にある
637                 {
638                     this._Src.X = x - this.render.TextArea.Width + this.ScrollMarginWidth;
639                     if (this.render.RightToLeft && this._Src.X > 0)
640                     {
641                         System.Diagnostics.Debug.Assert(x > 0);
642                         this._Src.X = 0;
643                     }
644                     else
645                     {
646                         x = this.render.TextArea.Width - this.ScrollMarginWidth;
647                     }
648                     result = true;
649                 }
650                 else if (x < left)    //xは表示領域の左側にある
651                 {
652                     this._Src.X = x - this.ScrollMarginWidth;
653                     if (!this.render.RightToLeft && this._Src.X < this.render.TextArea.X)
654                     {
655                         this._Src.X = 0;
656                     }
657                     else
658                     {
659                         x = this.ScrollMarginWidth;
660                     }
661                     result = true;
662                 }
663                 x += this.render.TextArea.X;
664             }
665
666             if (flow == AdjustFlow.Row || flow == AdjustFlow.Both)
667             {
668                 int caretRow = 0;
669                 int lineCount = this.LineCountOnScreenWithInVisible;
670                 if (tp.row >= this.Src.Row && tp.row < this.Src.Row + lineCount)
671                     caretRow = tp.row - this.Src.Row;
672                 else if (tp.row >= this.Src.Row + lineCount)
673                 {
674                     this._Src.Row = this.GetSrcRow(tp.row, this.LineCountOnScreen);
675                     caretRow = tp.row - this._Src.Row;
676                     result = true;
677                     CalculateLineCountOnScreen();
678                 }
679                 else if (tp.row < this.Src.Row)
680                 {
681                     this._Src.Row = tp.row;
682                     result = true;
683                     CalculateLineCountOnScreen();
684                 }
685
686                 y = 0;
687
688                 if (caretRow > 0)
689                 {
690                     for (int i = 0; i < caretRow; i++)
691                     {
692                         int currentRow = this.Src.Row + i;
693                         int lineHeadIndex = this.LayoutLines.GetIndexFromLineNumber(currentRow);
694                         int lineLength = this.LayoutLines.GetLengthFromLineNumber(currentRow);
695
696                         if (this.LayoutLines.FoldingCollection.IsHidden(lineHeadIndex))
697                             continue;
698
699                         y += this.LayoutLines.GetLayout(currentRow).Height;
700                     }
701                 }
702                 y += this.render.TextArea.Y;
703             }
704
705             this.SetCaretPostion(x, y);
706
707             if (result)
708                 this.OnSrcChanged(null);
709
710             return result;
711         }
712
713         int GetSrcRow(int row,int count)
714         {
715             if (this.LayoutLines.FoldingStrategy == null)
716                 return row - count;
717             for (int i = row; i >= 0; i--)
718             {
719                 int lineHeadIndex = this.LayoutLines.GetIndexFromLineNumber(i);
720                 int lineLength = this.LayoutLines.GetLengthFromLineNumber(i);
721                 if (this.LayoutLines.FoldingCollection.IsHidden(lineHeadIndex))
722                     continue;
723                 if (count <= 0)
724                     return i;
725                 count--;
726             }
727             return 0;
728         }
729
730         /// <summary>
731         /// レイアウト行をテキストポイントからインデックスに変換する
732         /// </summary>
733         /// <param name="tp">テキストポイント表す</param>
734         /// <returns>インデックスを返す</returns>
735         public int GetIndexFromLayoutLine(TextPoint tp)
736         {
737             return this.LayoutLines.GetIndexFromTextPoint(tp);
738         }
739
740         /// <summary>
741         /// インデックスからレイアウト行を指し示すテキストポイントに変換する
742         /// </summary>
743         /// <param name="index">インデックスを表す</param>
744         /// <returns>テキストポイント返す</returns>
745         public TextPoint GetLayoutLineFromIndex(int index)
746         {
747             return this.LayoutLines.GetTextPointFromIndex(index);
748         }
749
750         /// <summary>
751         /// 指定した座標までスクロールする
752         /// </summary>
753         /// <param name="x"></param>
754         /// <param name="row"></param>
755         /// <remarks>
756         /// 範囲外の座標を指定した場合、範囲内に収まるように調整されます
757         /// </remarks>
758         public void Scroll(double x, int row)
759         {
760             if (x < 0)
761                 x = 0;
762             if(row < 0)
763                 row = 0;
764             int endRow = this.LayoutLines.Count - 1 - this.LineCountOnScreen;
765             if (endRow < 0)
766                 endRow = 0;
767             if (row > endRow)
768                 row = endRow;
769             base.TryScroll(x, row);
770         }
771
772         /// <summary>
773         /// 指定行までスクロールする
774         /// </summary>
775         /// <param name="row">行</param>
776         /// <param name="alignTop">指定行を画面上に置くなら真。そうでないなら偽</param>
777         public void ScrollIntoView(int row, bool alignTop)
778         {
779             this.Scroll(0, row);
780             if (alignTop)
781                 return;
782             double y = this.render.TextArea.Height;
783             for (int i = row; i >= 0; i--)
784             {
785                 int lineHeadIndex = this.LayoutLines.GetIndexFromLineNumber(i);
786                 int lineLength = this.LayoutLines.GetLengthFromLineNumber(i);
787                 double height = this.LayoutLines.GetLayout(i).Height;
788                 if (y - height <= 0)
789                 {
790                     this.Scroll(0, i);
791                 }
792                 if (this.LayoutLines.FoldingCollection.IsHidden(lineHeadIndex))
793                     continue;
794                 y -= height;
795             }
796         }
797
798         public int AdjustRow(int row, bool isMoveNext)
799         {
800             if (this.LayoutLines.FoldingStrategy == null)
801                 return row;
802             int lineHeadIndex = this.LayoutLines.GetIndexFromLineNumber(row);
803             int lineLength = this.LayoutLines.GetLengthFromLineNumber(row);
804             FoldingItem foldingData = this.LayoutLines.FoldingCollection.GetFarestHiddenFoldingData(lineHeadIndex, lineLength);
805             if (foldingData != null && !foldingData.Expand)
806             {
807                 if (foldingData.End == this.Document.Length)
808                     return row;
809                 if (isMoveNext && lineHeadIndex > foldingData.Start)
810                     row = this.LayoutLines.GetLineNumberFromIndex(foldingData.End) + 1;
811                 else
812                     row = this.LayoutLines.GetLineNumberFromIndex(foldingData.Start);
813                 if(row > this.LayoutLines.Count - 1)
814                     row = this.LayoutLines.GetLineNumberFromIndex(foldingData.Start);
815             }
816             return row;
817         }
818
819         protected override void CalculateClipRect()
820         {
821             IEditorRender render = (IEditorRender)base.render;
822             double x, y, width, height;
823
824             if (this.DrawLineNumber)
825             {
826                 if (this.render.RightToLeft)
827                     x = this.Padding.Left;
828                 else
829                     x = this.Padding.Left + UpdateAreaTotalWidth + this.render.LineNemberWidth + this.LineNumberMargin + render.FoldingWidth;
830                 width = this.PageBound.Width - this.render.LineNemberWidth - this.LineNumberMargin - this.Padding.Left - this.Padding.Right - render.FoldingWidth - UpdateAreaTotalWidth;
831             }
832             else
833             {
834                 if (this.render.RightToLeft)
835                     x = this.Padding.Left;
836                 else
837                     x = this.Padding.Left + UpdateAreaTotalWidth + render.FoldingWidth;
838                 width = this.PageBound.Width - this.Padding.Left - this.Padding.Right - render.FoldingWidth - UpdateAreaTotalWidth;
839             }
840
841             y = this.Padding.Top;
842             height = this.PageBound.Height - this.Padding.Top - this.Padding.Bottom;
843
844             if (this.HideRuler == false)
845             {
846                 double rulerHeight = this.render.emSize.Height + LineMarkerThickness;
847                 y += rulerHeight;
848                 height -= rulerHeight;
849             }
850
851             if (width < 0)
852                 width = 0;
853
854             if (height < 0)
855                 height = 0;
856
857             this.render.TextArea = new Rectangle(x, y, width, height);
858
859             this.LineBreakingMarginWidth = width * 5 / 100;
860         }
861
862         public override void CalculateLineCountOnScreen()
863         {
864             if (this.LayoutLines.Count == 0 || this.PageBound.Height == 0)
865                 return;
866
867             double y = 0;
868             int i = this.Src.Row;
869             int visualCount = this.Src.Row;
870             for (; true; i++)
871             {
872                 int row = i < this.LayoutLines.Count ? i : this.LayoutLines.Count - 1;
873
874                 int lineHeadIndex = this.LayoutLines.GetIndexFromLineNumber(row);
875                 int lineLength = this.LayoutLines.GetLengthFromLineNumber(row);
876
877                 if (this.LayoutLines.FoldingCollection.IsHidden(lineHeadIndex) && row < this.LayoutLines.Count - 1)
878                     continue;
879
880                 ITextLayout layout = this.LayoutLines.GetLayout(row);
881
882                 double width = layout.Width;
883
884                 if (width > this._LongestWidth)
885                     this._LongestWidth = width;
886
887                 double lineHeight = layout.Height;
888
889                 y += lineHeight;
890
891                 if (y >= this.render.TextArea.Height)
892                     break;
893                 visualCount++;
894             }
895             this.LineCountOnScreen = Math.Max(visualCount - this.Src.Row - 1, 0);
896             this.LineCountOnScreenWithInVisible = Math.Max(i - this.Src.Row - 1, 0);
897         }
898
899         void SetCaretPostion(double x, double y)
900         {
901             this._CaretLocation = new Point(x + this.PageBound.X, y + this.PageBound.Y);
902         }
903
904         void FoldingCollection_StatusChanged(object sender, FoldingItemStatusChangedEventArgs e)
905         {
906             this.CalculateLineCountOnScreen();
907         }
908
909         enum AreaType
910         {
911             UpdateArea,
912             FoldingArea,
913             LineNumberArea,
914             TextArea
915         }
916
917         double GetRealtiveX(AreaType type)
918         {
919             IEditorRender render = (IEditorRender)base.render;
920             switch (type)
921             {
922                 case AreaType.UpdateArea:
923                     if (this.render.RightToLeft)
924                         return this.PageBound.TopRight.X - UpdateAreaTotalWidth;
925                     if (this.DrawLineNumber)
926                         return this.render.TextArea.X - this.render.LineNemberWidth - this.LineNumberMargin - render.FoldingWidth - UpdateAreaTotalWidth;
927                     else
928                         return this.render.TextArea.X - render.FoldingWidth - UpdateAreaTotalWidth;
929                 case AreaType.FoldingArea:
930                     if (this.render.RightToLeft)
931                         return this.PageBound.TopRight.X - render.FoldingWidth;
932                     if (this.DrawLineNumber)
933                         return this.render.TextArea.X - this.render.LineNemberWidth - this.LineNumberMargin - render.FoldingWidth;
934                     else
935                         return this.render.TextArea.X - render.FoldingWidth;
936                 case AreaType.LineNumberArea:
937                     if (this.DrawLineNumber == false)
938                         throw new InvalidOperationException();
939                     if (this.render.RightToLeft)
940                         return this.PageBound.TopRight.X - UpdateAreaTotalWidth - render.FoldingWidth - this.render.LineNemberWidth;
941                     else
942                         return this.render.TextArea.X - this.render.LineNemberWidth - this.LineNumberMargin;
943                 case AreaType.TextArea:
944                     return this.render.TextArea.X;
945             }
946             throw new ArgumentOutOfRangeException();
947         }
948     }
949 }