OSDN Git Service

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