OSDN Git Service

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