OSDN Git Service

互換性のないメソッドをいつか廃止した
[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.render = r;
89             this.Document = doc;
90             this._LayoutLines.SpilitString = new SpilitStringEventHandler(LayoutLines_SpilitStringByChar);
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         }
130
131         private void Document_StatusUpdate(object sender, EventArgs e)
132         {
133             if (this.render == null)
134                 return;
135             if (this.render.TabWidthChar != this.Document.TabStops)
136                 this.render.TabWidthChar = this.Document.TabStops;
137             if (this.render.RightToLeft != this.Document.RightToLeft)
138                 this.render.RightToLeft = this.Document.RightToLeft;
139             if (this.render.ShowFullSpace != this.Document.ShowFullSpace)
140                 this.render.ShowFullSpace = this.Document.ShowFullSpace;
141             if (this.render.ShowHalfSpace != this.Document.ShowHalfSpace)
142                 this.render.ShowHalfSpace = this.Document.ShowHalfSpace;
143             if (this.render.ShowTab != this.Document.ShowTab)
144                 this.render.ShowTab = this.Document.ShowTab;
145             if (this.render.ShowLineBreak != this.Document.ShowLineBreak)
146                 this.render.ShowLineBreak = this.Document.ShowLineBreak;
147             CalculateClipRect();
148             CalculateLineCountOnScreen();
149             this._LayoutLines.ClearLayoutCache();
150         }
151
152         private void Document_LineBreakChanged(object sender, EventArgs e)
153         {
154             this.CalculateLineBreak();
155         }
156
157         protected LineToIndexTable _LayoutLines
158         {
159             get
160             {
161                 return this.Document.LayoutLines;
162             }
163         }
164
165         public event EventHandler SrcChanged;
166
167         public event EventHandler PageBoundChanged;
168
169         /// <summary>
170         /// テキストレンダラ―
171         /// </summary>
172         public ITextRender render
173         {
174             get;
175             set;
176         }
177
178         /// <summary>
179         /// 一ページの高さに収まる行数を返す
180         /// </summary>
181         public int LineCountOnScreen
182         {
183             get;
184             protected set;
185         }
186         
187         /// <summary>
188         /// 折り返し時の右マージン
189         /// </summary>
190         public double LineBreakingMarginWidth
191         {
192             get;
193             protected set;
194         }
195
196         /// <summary>
197         /// 保持しているレイアウト行
198         /// </summary>
199         public LineToIndexTable LayoutLines
200         {
201             get { return this._LayoutLines; }
202         }
203
204         /// <summary>
205         /// 最も長い行の幅
206         /// </summary>
207         public double LongestWidth
208         {
209             get { return this._LongestWidth; }
210         }
211
212         public double LineNumberMargin
213         {
214             get
215             {
216                 return this.render.emSize.Width;
217             }
218         }
219
220         /// <summary>
221         /// シンタックスハイライター
222         /// </summary>
223         /// <remarks>差し替えた場合、再構築する必要があります</remarks>
224         public IHilighter Hilighter
225         {
226             get { return this._LayoutLines.Hilighter; }
227             set { this._LayoutLines.Hilighter = value; this._LayoutLines.ClearLayoutCache(); }
228         }
229
230         /// <summary>
231         /// 余白を表す
232         /// </summary>
233         public Padding Padding
234         {
235             get {
236                 return this._Padding;
237             }
238             set {
239                 this._Padding = value;
240                 CalculateClipRect();
241                 CalculateLineBreak();
242                 CalculateLineCountOnScreen();
243                 if (this.Document.RightToLeft)
244                     this._LayoutLines.ClearLayoutCache();
245                 this.PageBoundChanged(this, null);
246             }
247         }
248
249         /// <summary>
250         /// ページ全体を表す領域
251         /// </summary>
252         public Rectangle PageBound
253         {
254             get { return this._Rect; }
255             set
256             {
257                 if (value.Width < 0 || value.Height < 0)
258                     throw new ArgumentOutOfRangeException("");
259                 this._Rect = value;
260                 CalculateClipRect();
261                 CalculateLineBreak();
262                 CalculateLineCountOnScreen();
263                 if (this.Document.RightToLeft)
264                     this._LayoutLines.ClearLayoutCache();
265                 this.PageBoundChanged(this, null);
266             }
267         }
268
269         /// <summary>
270         /// Draw()の対象となる領域の左上を表す
271         /// </summary>
272         public SrcPoint Src
273         {
274             get { return this.Document.Src; }
275             set { this.Document.Src = value; }
276         }
277
278         public virtual void Draw(Rectangle updateRect, bool force = false)
279         {
280             return;
281         }
282
283         /// <summary>
284         /// スクロールを試行する
285         /// </summary>
286         /// <param name="x">X座標</param>
287         /// <param name="row">行</param>
288         /// <param name="rel_y">各行の左上を0とするY座標</param>
289         /// <returns>成功すれば偽、そうでなければ真</returns>
290         public virtual bool TryScroll(double x, int row,double rel_y = 0)
291         {
292             if (row < 0)
293                 return true;
294             if (row > this.LayoutLines.Count - 1)
295                 return true;
296             this.Document.Src = new SrcPoint(x, row, rel_y);
297             this.SrcChanged(this,null);
298             return false;
299         }
300
301         /// <summary>
302         /// スクロールを試行する
303         /// </summary>
304         /// <param name="offset_x">X方向の移動量</param>
305         /// <param name="offset_y">Y方向の移動量</param>
306         /// <returns>成功すれば偽、そうでなければ真</returns>
307         public virtual bool TryScroll(double offset_x,double offset_y)
308         {
309             double x = this.Document.Src.X - offset_x;
310             if (x < 0)
311                 return true;
312             var t = GetNearstRowAndOffsetY(this.Document.Src.Row, -this.Document.Src.OffsetY + offset_y);
313             if (t == null)
314                 return true;
315             this.Document.Src = new SrcPoint(x, t.Item1, -t.Item2);
316             return false;
317         }
318
319         /// <summary>
320         /// srcRowを起点としてrect_heightが収まる行とオフセットYを求めます
321         /// </summary>
322         /// <param name="srcRow">起点となる行</param>
323         /// <param name="rect_hight">Y方向のバウンディングボックス</param>
324         /// <returns>失敗した場合、NULL。成功した場合、行とオフセットY</returns>
325         public Tuple<int,double> GetNearstRowAndOffsetY(int srcRow, double rect_hight)
326         {
327             if (rect_hight > 0)
328             {
329                 for (int i = srcRow; i < this.Document.LayoutLines.Count; i++)
330                 {
331                     ITextLayout layout = this.Document.LayoutLines.GetLayout(i);
332
333                     int lineHeadIndex = this.LayoutLines.GetIndexFromLineNumber(i);
334                     int lineLength = this.LayoutLines.GetLengthFromLineNumber(i);
335                     double layoutHeight = layout.Height;
336
337                     if (this.LayoutLines.FoldingCollection.IsHidden(lineHeadIndex))
338                         continue;
339
340                     if (rect_hight == 0)
341                         return new Tuple<int, double>(i, 0);
342
343                     if (rect_hight - layoutHeight < 0)
344                         return new Tuple<int, double>(i, rect_hight);
345
346                     rect_hight -= layoutHeight;
347                 }
348             }
349             else if(rect_hight < 0)
350             {
351                 for (int i = srcRow - 1; i >= 0; i--)
352                 {
353                     ITextLayout layout = this.Document.LayoutLines.GetLayout(i);
354
355                     int lineHeadIndex = this.LayoutLines.GetIndexFromLineNumber(i);
356                     int lineLength = this.LayoutLines.GetLengthFromLineNumber(i);
357                     double layoutHeight = layout.Height;
358
359                     if (this.LayoutLines.FoldingCollection.IsHidden(lineHeadIndex))
360                         continue;
361
362                     if(rect_hight == 0)
363                         return new Tuple<int, double>(i, 0);
364
365                     if (rect_hight + layoutHeight >= 0)
366                         return new Tuple<int, double>(i, layoutHeight + rect_hight);
367
368                     rect_hight += layoutHeight;
369                 }
370                 return new Tuple<int, double>(0, 0);
371             }
372             return null;
373         }
374
375         public void Dispose()
376         {
377             this.Dispose(true);
378             GC.SuppressFinalize(this);
379         }
380
381         public virtual void CalculateLineBreak()
382         {
383             if (this.Document.LineBreak == LineBreakMethod.PageBound)
384                 this._LayoutLines.WrapWidth = this.render.TextArea.Width - LineBreakingMarginWidth;  //余白を残さないと欠ける
385             else if (this.Document.LineBreak == LineBreakMethod.CharUnit)
386                 this._LayoutLines.WrapWidth = this.render.emSize.Width * this.Document.LineBreakCharCount;
387             else
388                 this._LayoutLines.WrapWidth = LineToIndexTable.NONE_BREAK_LINE;
389         }
390
391         public virtual void CalculateLineCountOnScreen()
392         {
393         }
394
395         public virtual void CalculateWhloeViewPort()
396         {
397         }
398
399         protected virtual void Dispose(bool disposing)
400         {
401             if (disposing)
402             {
403                 this._Document.Update -= new DocumentUpdateEventHandler(this.doc_Update);    //これをしないと複数のビューを作成した時に妙なエラーが発生する
404                 this._Document.LineBreakChanged -= Document_LineBreakChanged;
405                 this._Document.StatusUpdate -= Document_StatusUpdate;
406                 this._Document.PerformLayouted -= _Document_PerformLayouted;
407             }
408             this._LayoutLines.Clear();
409         }
410
411         protected virtual void CalculateClipRect()
412         {
413         }
414
415
416         protected virtual void OnSrcChanged(EventArgs e)
417         {
418             EventHandler handler = this.SrcChanged;
419             if (handler != null)
420                 this.SrcChanged(this, e);
421         }
422
423         protected virtual void OnPageBoundChanged(EventArgs e)
424         {
425             EventHandler handler = this.PageBoundChanged;
426             if (handler != null)
427                 this.PageBoundChanged(this, e);
428         }
429
430         void render_ChangedRightToLeft(object sender, EventArgs e)
431         {
432             this.Document.Src = new SrcPoint(0, this.Document.Src.Row, this.Src.OffsetY);
433         }
434
435         void render_ChangedRenderResource(object sender, ChangedRenderRsourceEventArgs e)
436         {
437             this._LayoutLines.ClearLayoutCache();
438             if (e.type == ResourceType.Font)
439             {
440                 this.CalculateClipRect();
441                 this.CalculateLineCountOnScreen();
442                 this.CalculateWhloeViewPort();
443             }
444         }
445
446         void doc_Update(object sender, DocumentUpdateEventArgs e)
447         {
448             switch (e.type)
449             {
450                 case UpdateType.RebuildLayout:
451                 case UpdateType.Clear:
452                     this._LongestWidth = 0;
453                     break;
454             }
455         }
456
457         IList<LineToIndexTableData> LayoutLines_SpilitStringByChar(object sender, SpilitStringEventArgs e)
458         {
459             return this.Document.CreateLineList(e.index, e.length, Document.MaximumLineLength);
460         }
461     }
462 }