OSDN Git Service

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