OSDN Git Service

fe037cd66804a2551bbe55709f1a4bce4417ab6a
[fooeditengine/FooEditEngine.git] / Core / ViewBase.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.Collections.Generic;
13 using System.Linq;
14 using System.Text;
15 using System.Text.RegularExpressions;
16 using System.Threading.Tasks;
17
18 namespace FooEditEngine
19 {
20     /// <summary>
21     /// LineBreakMethod列挙体
22     /// </summary>
23     public enum LineBreakMethod
24     {
25         /// <summary>
26         /// 折り返さない
27         /// </summary>
28         None = 0,
29         /// <summary>
30         /// 右端で折り返す
31         /// </summary>
32         PageBound = 1,
33         /// <summary>
34         /// 文字数で折り返す
35         /// </summary>
36         CharUnit = 2
37     }
38
39     /// <summary>
40     /// 余白を表す
41     /// </summary>
42     public struct Padding
43     {
44         /// <summary>
45         /// 左余白
46         /// </summary>
47         public int Left;
48         /// <summary>
49         /// 上余白
50         /// </summary>
51         public int Top;
52         /// <summary>
53         /// 右余白
54         /// </summary>
55         public int Right;
56         /// <summary>
57         /// 下余白
58         /// </summary>
59         public int Bottom;
60         /// <summary>
61         /// コンストラクター
62         /// </summary>
63         /// <param name="left">左</param>
64         /// <param name="top">上</param>
65         /// <param name="right">右</param>
66         /// <param name="bottom">下</param>
67         public Padding(int left, int top, int right, int bottom)
68         {
69             this.Left = left;
70             this.Top = top;
71             this.Right = right;
72             this.Bottom = bottom;
73         }
74     }
75
76     abstract class ViewBase : IDisposable
77     {
78         const int SpiltCharCount = 1024;
79
80         Document _Document;
81         protected Rectangle _Rect;
82         protected double _LongestWidth,_LongestHeight;
83         Padding _Padding;
84
85         public ViewBase(Document doc, ITextRender r,Padding padding)
86         {
87             this._Padding = padding;
88             this.Document = doc;
89             this._LayoutLines.SpilitString = new SpilitStringEventHandler(LayoutLines_SpilitStringByChar);
90             this.render = r;
91             this.render.ChangedRenderResource += new ChangedRenderResourceEventHandler(render_ChangedRenderResource);
92             this.render.ChangedRightToLeft += render_ChangedRightToLeft;
93             this.SrcChanged += new EventHandler((s, e) => { });
94             this.PageBoundChanged += new EventHandler((s, e) => { });
95         }
96
97         public Document Document
98         {
99             get
100             {
101                 return this._Document;
102             }
103             set
104             {
105                 if(this._Document != null)
106                 {
107                     this._Document.Update -= new DocumentUpdateEventHandler(doc_Update);
108                     this._Document.LineBreakChanged -= Document_LineBreakChanged;
109                     this._Document.StatusUpdate -= Document_StatusUpdate;
110                     this._Document.PerformLayouted -= _Document_PerformLayouted;
111                 }
112
113                 this._Document = value;
114
115                 this._Document.Update += new DocumentUpdateEventHandler(doc_Update);
116                 this._Document.LineBreakChanged += Document_LineBreakChanged;
117                 this._Document.StatusUpdate += Document_StatusUpdate;
118                 this._Document.PerformLayouted += _Document_PerformLayouted;
119
120                 this.Document_LineBreakChanged(this, null);
121
122                 this.Document_StatusUpdate(this, null);
123             }
124         }
125
126         private void _Document_PerformLayouted(object sender, EventArgs e)
127         {
128             CalculateLineCountOnScreen();
129             if(this.PerformLayouted != null)
130                 this.PerformLayouted(this, e);
131         }
132
133         private void Document_StatusUpdate(object sender, EventArgs e)
134         {
135             if (this.render == null)
136                 return;
137             if (this.render.TabWidthChar != this.Document.TabStops)
138                 this.render.TabWidthChar = this.Document.TabStops;
139             if (this.render.RightToLeft != this.Document.RightToLeft)
140                 this.render.RightToLeft = this.Document.RightToLeft;
141             if (this.render.ShowFullSpace != this.Document.ShowFullSpace)
142                 this.render.ShowFullSpace = this.Document.ShowFullSpace;
143             if (this.render.ShowHalfSpace != this.Document.ShowHalfSpace)
144                 this.render.ShowHalfSpace = this.Document.ShowHalfSpace;
145             if (this.render.ShowTab != this.Document.ShowTab)
146                 this.render.ShowTab = this.Document.ShowTab;
147             if (this.render.ShowLineBreak != this.Document.ShowLineBreak)
148                 this.render.ShowLineBreak = this.Document.ShowLineBreak;
149             CalculateClipRect();
150             CalculateLineCountOnScreen();
151             this._LayoutLines.ClearLayoutCache();
152         }
153
154         private void Document_LineBreakChanged(object sender, EventArgs e)
155         {
156             if (this.Document.LineBreak == LineBreakMethod.PageBound)
157                 this._LayoutLines.WrapWidth = this.render.TextArea.Width - LineBreakingMarginWidth;  //余白を残さないと欠ける
158             else if (this.Document.LineBreak == LineBreakMethod.CharUnit)
159                 this._LayoutLines.WrapWidth = this.render.emSize.Width * this.Document.LineBreakCharCount;
160             else
161                 this._LayoutLines.WrapWidth = LineToIndexTable.NONE_BREAK_LINE;
162
163             this._LayoutLines.ClearLayoutCache();
164         }
165
166         protected LineToIndexTable _LayoutLines
167         {
168             get
169             {
170                 return this.Document.LayoutLines;
171             }
172         }
173
174         public event EventHandler SrcChanged;
175
176         [Obsolete]
177         public event EventHandler PerformLayouted;
178
179         public event EventHandler PageBoundChanged;
180
181         /// <summary>
182         /// テキストレンダラ―
183         /// </summary>
184         public ITextRender render
185         {
186             get;
187             set;
188         }
189
190         /// <summary>
191         /// 一ページの高さに収まる行数を返す
192         /// </summary>
193         public int LineCountOnScreen
194         {
195             get;
196             protected set;
197         }
198         
199         /// <summary>
200         /// 折り返し時の右マージン
201         /// </summary>
202         public double LineBreakingMarginWidth
203         {
204             get;
205             protected set;
206         }
207
208         /// <summary>
209         /// 保持しているレイアウト行
210         /// </summary>
211         public LineToIndexTable LayoutLines
212         {
213             get { return this._LayoutLines; }
214         }
215
216         /// <summary>
217         /// 最も長い行の幅
218         /// </summary>
219         public double LongestWidth
220         {
221             get { return this._LongestWidth; }
222         }
223
224         public double LineNumberMargin
225         {
226             get
227             {
228                 return this.render.emSize.Width;
229             }
230         }
231
232         /// <summary>
233         /// シンタックスハイライター
234         /// </summary>
235         /// <remarks>差し替えた場合、再構築する必要があります</remarks>
236         public IHilighter Hilighter
237         {
238             get { return this._LayoutLines.Hilighter; }
239             set { this._LayoutLines.Hilighter = value; this._LayoutLines.ClearLayoutCache(); }
240         }
241
242         /// <summary>
243         /// すべてのレイアウト行を破棄し、再度レイアウトをやり直す
244         /// </summary>
245         [Obsolete]
246         public virtual void PerfomLayouts()
247         {
248             //互換性を保つために残しておく
249             this.Document.PerformLayout();
250         }
251
252         /// <summary>
253         /// 余白を表す
254         /// </summary>
255         public Padding Padding
256         {
257             get {
258                 return this._Padding;
259             }
260             set {
261                 this._Padding = value;
262                 CalculateClipRect();
263                 CalculateLineCountOnScreen();
264                 if (this.Document.RightToLeft)
265                     this._LayoutLines.ClearLayoutCache();
266                 this.PageBoundChanged(this, null);
267             }
268         }
269
270         /// <summary>
271         /// ページ全体を表す領域
272         /// </summary>
273         public Rectangle PageBound
274         {
275             get { return this._Rect; }
276             set
277             {
278                 if (value.Width < 0 || value.Height < 0)
279                     throw new ArgumentOutOfRangeException("");
280                 this._Rect = value;
281                 CalculateClipRect();
282                 CalculateLineCountOnScreen();
283                 if (this.Document.RightToLeft)
284                     this._LayoutLines.ClearLayoutCache();
285                 this.PageBoundChanged(this, null);
286             }
287         }
288
289         /// <summary>
290         /// Draw()の対象となる領域の左上を表す
291         /// </summary>
292         public SrcPoint Src
293         {
294             get { return this.Document.Src; }
295             set { this.Document.Src = value; }
296         }
297
298         public virtual void Draw(Rectangle updateRect, bool force = false)
299         {
300             return;
301         }
302
303         /// <summary>
304         /// スクロールを試行する
305         /// </summary>
306         /// <param name="x">X座標</param>
307         /// <param name="row">行</param>
308         /// <param name="rel_y">各行の左上を0とするY座標</param>
309         /// <returns>成功すれば偽、そうでなければ真</returns>
310         public virtual bool TryScroll(double x, int row,double rel_y = 0)
311         {
312             if (row < 0)
313                 return true;
314             if (row > this.LayoutLines.Count - 1)
315                 return true;
316             this.Document.Src = new SrcPoint(x, row, rel_y);
317             this.SrcChanged(this,null);
318             return false;
319         }
320
321         /// <summary>
322         /// スクロールを試行する
323         /// </summary>
324         /// <param name="offset_x">X方向の移動量</param>
325         /// <param name="offset_y">Y方向の移動量</param>
326         /// <returns>成功すれば偽、そうでなければ真</returns>
327         public virtual bool TryScroll(double offset_x,double offset_y)
328         {
329             double x = this.Document.Src.X - offset_x;
330             if (x < 0)
331                 return true;
332             var t = GetNearstRowAndOffsetY(this.Document.Src.Row, -this.Document.Src.OffsetY + offset_y);
333             if (t == null)
334                 return true;
335             this.Document.Src = new SrcPoint(x, t.Item1, -t.Item2);
336             return false;
337         }
338
339         /// <summary>
340         /// srcRowを起点としてrect_heightが収まる行とオフセットYを求めます
341         /// </summary>
342         /// <param name="srcRow">起点となる行</param>
343         /// <param name="rect_hight">Y方向のバウンディングボックス</param>
344         /// <returns>失敗した場合、NULL。成功した場合、行とオフセットY</returns>
345         public Tuple<int,double> GetNearstRowAndOffsetY(int srcRow, double rect_hight)
346         {
347             if (rect_hight > 0)
348             {
349                 for (int i = srcRow; i < this.Document.LayoutLines.Count; i++)
350                 {
351                     ITextLayout layout = this.Document.LayoutLines.GetLayout(i);
352
353                     int lineHeadIndex = this.LayoutLines.GetIndexFromLineNumber(i);
354                     int lineLength = this.LayoutLines.GetLengthFromLineNumber(i);
355                     double layoutHeight = layout.Height;
356
357                     if (this.LayoutLines.FoldingCollection.IsHidden(lineHeadIndex))
358                         continue;
359
360                     if (rect_hight == 0)
361                         return new Tuple<int, double>(i, 0);
362
363                     if (rect_hight - layoutHeight < 0)
364                         return new Tuple<int, double>(i, rect_hight);
365
366                     rect_hight -= layoutHeight;
367                 }
368             }
369             else if(rect_hight < 0)
370             {
371                 for (int i = srcRow - 1; i >= 0; i--)
372                 {
373                     ITextLayout layout = this.Document.LayoutLines.GetLayout(i);
374
375                     int lineHeadIndex = this.LayoutLines.GetIndexFromLineNumber(i);
376                     int lineLength = this.LayoutLines.GetLengthFromLineNumber(i);
377                     double layoutHeight = layout.Height;
378
379                     if (this.LayoutLines.FoldingCollection.IsHidden(lineHeadIndex))
380                         continue;
381
382                     if(rect_hight == 0)
383                         return new Tuple<int, double>(i, 0);
384
385                     if (rect_hight + layoutHeight >= 0)
386                         return new Tuple<int, double>(i, layoutHeight + rect_hight);
387
388                     rect_hight += layoutHeight;
389                 }
390                 return new Tuple<int, double>(0, 0);
391             }
392             return null;
393         }
394
395         public void Dispose()
396         {
397             this.Dispose(true);
398             GC.SuppressFinalize(this);
399         }
400
401         public virtual void CalculateLineCountOnScreen()
402         {
403         }
404
405         public virtual void CalculateWhloeViewPort()
406         {
407         }
408
409         protected virtual void Dispose(bool disposing)
410         {
411             if (disposing)
412             {
413                 this._Document.Update -= new DocumentUpdateEventHandler(this.doc_Update);    //これをしないと複数のビューを作成した時に妙なエラーが発生する
414                 this._Document.LineBreakChanged -= Document_LineBreakChanged;
415                 this._Document.StatusUpdate -= Document_StatusUpdate;
416                 this._Document.PerformLayouted -= _Document_PerformLayouted;
417             }
418             this._LayoutLines.Clear();
419         }
420
421         protected virtual void CalculateClipRect()
422         {
423         }
424
425
426         protected virtual void OnSrcChanged(EventArgs e)
427         {
428             EventHandler handler = this.SrcChanged;
429             if (handler != null)
430                 this.SrcChanged(this, e);
431         }
432
433         protected virtual void OnPerformLayoutedChanged(EventArgs e)
434         {
435             EventHandler handler = this.PerformLayouted;
436             if (handler != null)
437                 this.PerformLayouted(this, e);
438         }
439
440         protected virtual void OnPageBoundChanged(EventArgs e)
441         {
442             EventHandler handler = this.PageBoundChanged;
443             if (handler != null)
444                 this.PageBoundChanged(this, e);
445         }
446
447         void render_ChangedRightToLeft(object sender, EventArgs e)
448         {
449             this.Document.Src = new SrcPoint(0, this.Document.Src.Row, this.Src.OffsetY);
450         }
451
452         void render_ChangedRenderResource(object sender, ChangedRenderRsourceEventArgs e)
453         {
454             this._LayoutLines.ClearLayoutCache();
455             if (e.type == ResourceType.Font)
456             {
457                 this.CalculateClipRect();
458                 this.CalculateLineCountOnScreen();
459                 this.CalculateWhloeViewPort();
460             }
461         }
462
463         void doc_Update(object sender, DocumentUpdateEventArgs e)
464         {
465             switch (e.type)
466             {
467                 case UpdateType.RebuildLayout:
468                 case UpdateType.Clear:
469                     this._LongestWidth = 0;
470                     break;
471             }
472         }
473
474         IList<LineToIndexTableData> LayoutLines_SpilitStringByChar(object sender, SpilitStringEventArgs e)
475         {
476             return this.Document.CreateLineList(e.index, e.length, Document.MaximumLineLength);
477         }
478     }
479 }