OSDN Git Service

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