OSDN Git Service

c91d38abb96dd390c23e7030e42d6b7846083795
[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         protected Document Document;
81         protected Point2 _Src = new Point2();
82         protected Rectangle _Rect;
83         protected double _LongestWidth;
84         Padding _Padding;
85
86         public ViewBase(Document doc, ITextRender r,Padding padding)
87         {
88             this._Padding = padding;
89             this.Document = doc;
90             this.Document.UpdateCalledAlways += new DocumentUpdateEventHandler(doc_Update);
91             this._LayoutLines.SpilitString = new SpilitStringEventHandler(LayoutLines_SpilitStringByChar);
92             this.render = r;
93             this.render.ChangedRenderResource += new ChangedRenderResourceEventHandler(render_ChangedRenderResource);
94             this.render.ChangedRightToLeft += render_ChangedRightToLeft;
95             this.SrcChanged += new EventHandler((s, e) => { });
96             this.PerformLayouted += new EventHandler((s, e) => { });
97             this.PageBoundChanged += new EventHandler((s, e) => { });
98             this.Document.LineBreakChanged += (s, e) =>
99             {
100                 if (this.Document.LineBreak != LineBreakMethod.None)
101                     this._LayoutLines.SpilitString = new SpilitStringEventHandler(LayoutLines_SpilitStringByPixelbase);
102                 else
103                     this._LayoutLines.SpilitString = new SpilitStringEventHandler(LayoutLines_SpilitStringByChar);
104             };
105             this.Document.TabStopsChanged += (s, e) =>
106             {
107                 this.render.TabWidthChar = this.Document.TabStops;
108             };
109             this.Document.DrawLineNumberChanged += (s, e) =>
110             {
111                 CalculateClipRect();
112             };
113             this.Document.RightToLeftChanged += (s, e) =>
114             {
115                 this.render.RightToLeft = this.Document.RightToLeft;
116             };
117             this.Document.ShowFullSpaceChanged += (s, e) =>
118             {
119                 this.render.ShowFullSpace = this.Document.ShowFullSpace;
120                 this._LayoutLines.ClearLayoutCache();
121             };
122             this.Document.ShowHalfSpaceChanged += (s, e) =>
123             {
124                 this.render.ShowHalfSpace = this.Document.ShowHalfSpace;
125                 this._LayoutLines.ClearLayoutCache();
126             };
127             this.Document.ShowTabChanged += (s, e) =>
128             {
129                 this.render.ShowTab = this.Document.ShowTab;
130                 this._LayoutLines.ClearLayoutCache();
131             };
132             this.Document.ShowLineBreakChanged += (s, e) =>
133             {
134                 this.render.ShowLineBreak = this.Document.ShowLineBreak;
135                 this._LayoutLines.ClearLayoutCache();
136             };
137         }
138
139         protected LineToIndexTable _LayoutLines
140         {
141             get
142             {
143                 return this.Document.LayoutLines;
144             }
145         }
146
147         public event EventHandler SrcChanged;
148
149         public event EventHandler PerformLayouted;
150
151         public event EventHandler PageBoundChanged;
152
153         /// <summary>
154         /// テキストレンダラ―
155         /// </summary>
156         public ITextRender render
157         {
158             get;
159             set;
160         }
161
162         /// <summary>
163         /// 一ページの高さに収まる行数を返す
164         /// </summary>
165         public int LineCountOnScreen
166         {
167             get;
168             protected set;
169         }
170
171         /// <summary>
172         /// 折り返し時の右マージン
173         /// </summary>
174         public double LineBreakingMarginWidth
175         {
176             get;
177             protected set;
178         }
179
180         /// <summary>
181         /// 保持しているレイアウト行
182         /// </summary>
183         public LineToIndexTable LayoutLines
184         {
185             get { return this._LayoutLines; }
186         }
187
188         /// <summary>
189         /// 最も長い行の幅
190         /// </summary>
191         public double LongestWidth
192         {
193             get { return this._LongestWidth; }
194         }
195
196         public double LineNumberMargin
197         {
198             get
199             {
200                 return this.render.emSize.Width;
201             }
202         }
203
204         /// <summary>
205         /// シンタックスハイライター
206         /// </summary>
207         /// <remarks>差し替えた場合、再構築する必要があります</remarks>
208         public IHilighter Hilighter
209         {
210             get { return this._LayoutLines.Hilighter; }
211             set { this._LayoutLines.Hilighter = value; }
212         }
213
214         /// <summary>
215         /// すべてのレイアウト行を破棄し、再度レイアウトをやり直す
216         /// </summary>
217         public virtual void PerfomLayouts()
218         {
219             this.Document.FireUpdate(new DocumentUpdateEventArgs(UpdateType.Clear, -1, -1, -1));
220             this.Document.FireUpdate(new DocumentUpdateEventArgs(UpdateType.Replace, 0, 0, this.Document.Length));
221             CalculateLineCountOnScreen();
222             this.PerformLayouted(this, null);
223         }
224
225         /// <summary>
226         /// 余白を表す
227         /// </summary>
228         public Padding Padding
229         {
230             get {
231                 return this._Padding;
232             }
233             set {
234                 this._Padding = value;
235                 CalculateClipRect();
236                 CalculateLineCountOnScreen();
237                 if (this.Document.RightToLeft)
238                     this._LayoutLines.ClearLayoutCache();
239                 this.PageBoundChanged(this, null);
240             }
241         }
242
243         /// <summary>
244         /// ページ全体を表す領域
245         /// </summary>
246         public Rectangle PageBound
247         {
248             get { return this._Rect; }
249             set
250             {
251                 if (value.Width < 0 || value.Height < 0)
252                     throw new ArgumentOutOfRangeException("");
253                 this._Rect = value;
254                 CalculateClipRect();
255                 CalculateLineCountOnScreen();
256                 if (this.Document.RightToLeft)
257                     this._LayoutLines.ClearLayoutCache();
258                 this.PageBoundChanged(this, null);
259             }
260         }
261
262         /// <summary>
263         /// Draw()の対象となる領域の左上を表す
264         /// </summary>
265         public Point2 Src
266         {
267             get { return this._Src; }
268             set { this._Src = value; }
269         }
270
271         public virtual void Draw(Rectangle updateRect)
272         {
273         }
274
275         public virtual bool TryScroll(double x, int row)
276         {
277             if (row < 0)
278                 return true;
279             if (row > this.LayoutLines.Count - 1)
280                 return true;
281             this._Src.X = x;
282             this._Src.Row = row;
283             CalculateLineCountOnScreen();
284             this.SrcChanged(this,null);
285             return false;
286         }
287
288         public void Dispose()
289         {
290             this.Dispose(true);
291             GC.SuppressFinalize(this);
292         }
293
294         public virtual void CalculateLineCountOnScreen()
295         {
296         }
297
298         protected virtual void Dispose(bool disposing)
299         {
300             if (disposing)
301             {
302                 this.Document.UpdateCalledAlways -= new DocumentUpdateEventHandler(this.doc_Update);    //これをしないと複数のビューを作成した時に妙なエラーが発生する
303             }
304             this._LayoutLines.Clear();
305         }
306
307         protected virtual void CalculateClipRect()
308         {
309         }
310
311
312         protected virtual void OnSrcChanged(EventArgs e)
313         {
314             EventHandler handler = this.SrcChanged;
315             if (handler != null)
316                 this.SrcChanged(this, e);
317         }
318
319         protected virtual void OnPerformLayoutedChanged(EventArgs e)
320         {
321             EventHandler handler = this.PerformLayouted;
322             if (handler != null)
323                 this.PerformLayouted(this, e);
324         }
325
326         protected virtual void OnPageBoundChanged(EventArgs e)
327         {
328             EventHandler handler = this.PageBoundChanged;
329             if (handler != null)
330                 this.PageBoundChanged(this, e);
331         }
332
333         void render_ChangedRightToLeft(object sender, EventArgs e)
334         {
335             this._Src.X = 0;
336             this._LayoutLines.ClearLayoutCache();
337             this.CalculateClipRect();
338         }
339
340         void render_ChangedRenderResource(object sender, ChangedRenderRsourceEventArgs e)
341         {
342             this._LayoutLines.ClearLayoutCache();
343             if (e.type == ResourceType.Font)
344             {
345                 this.CalculateClipRect();
346                 this.CalculateLineCountOnScreen();
347             }
348         }
349
350         void doc_Update(object sender, DocumentUpdateEventArgs e)
351         {
352             switch (e.type)
353             {
354                 case UpdateType.Clear:
355                     this._LongestWidth = 0;
356                     break;
357             }
358         }
359
360         IList<LineToIndexTableData> LayoutLines_SpilitStringByPixelbase(object sender, SpilitStringEventArgs e)
361         {
362             double WrapWidth;
363             if (this.Document.LineBreak == LineBreakMethod.PageBound)
364                 WrapWidth = this.render.TextArea.Width - LineBreakingMarginWidth;  //余白を残さないと欠ける
365             else
366                 WrapWidth = this.render.emSize.Width * this.Document.LineBreakCharCount;
367
368             if (WrapWidth < 0 && this.Document.LineBreak != LineBreakMethod.None)
369                 throw new InvalidOperationException();
370
371             int startIndex = e.index;
372             int endIndex = e.index + e.length - 1;
373
374             LineToIndexTable layoutLineCollection = (LineToIndexTable)sender;
375
376             return this.render.BreakLine(e.buffer,layoutLineCollection, startIndex, endIndex, WrapWidth);
377         }
378
379         IList<LineToIndexTableData> LayoutLines_SpilitStringByChar(object sender, SpilitStringEventArgs e)
380         {
381             return this.Document.CreateLineList(e.index, e.length, Document.MaximumLineLength);
382         }
383     }
384 }