OSDN Git Service

MVVM対応に備えてシンボル系列のプロパティもDocumentに移行した
[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         public IHilighter Hilighter
208         {
209             get { return this._LayoutLines.Hilighter; }
210             set { this._LayoutLines.Hilighter = value; }
211         }
212
213         /// <summary>
214         /// すべてのレイアウト行を破棄し、再度レイアウトをやり直す
215         /// </summary>
216         public virtual void PerfomLayouts()
217         {
218             this.Document.FireUpdate(new DocumentUpdateEventArgs(UpdateType.Clear, -1, -1, -1));
219             this.Document.FireUpdate(new DocumentUpdateEventArgs(UpdateType.Replace, 0, 0, this.Document.Length));
220             CalculateLineCountOnScreen();
221             this.PerformLayouted(this, null);
222         }
223
224         /// <summary>
225         /// 余白を表す
226         /// </summary>
227         public Padding Padding
228         {
229             get {
230                 return this._Padding;
231             }
232             set {
233                 this._Padding = value;
234                 CalculateClipRect();
235                 CalculateLineCountOnScreen();
236                 if (this.Document.RightToLeft)
237                     this._LayoutLines.ClearLayoutCache();
238                 this.PageBoundChanged(this, null);
239             }
240         }
241
242         /// <summary>
243         /// ページ全体を表す領域
244         /// </summary>
245         public Rectangle PageBound
246         {
247             get { return this._Rect; }
248             set
249             {
250                 if (value.Width < 0 || value.Height < 0)
251                     throw new ArgumentOutOfRangeException("");
252                 this._Rect = value;
253                 CalculateClipRect();
254                 CalculateLineCountOnScreen();
255                 if (this.Document.RightToLeft)
256                     this._LayoutLines.ClearLayoutCache();
257                 this.PageBoundChanged(this, null);
258             }
259         }
260
261         /// <summary>
262         /// Draw()の対象となる領域の左上を表す
263         /// </summary>
264         public Point2 Src
265         {
266             get { return this._Src; }
267             set { this._Src = value; }
268         }
269
270         public virtual void Draw(Rectangle updateRect)
271         {
272         }
273
274         public virtual bool TryScroll(double x, int row)
275         {
276             if (row < 0)
277                 return true;
278             if (row > this.LayoutLines.Count - 1)
279                 return true;
280             this._Src.X = x;
281             this._Src.Row = row;
282             CalculateLineCountOnScreen();
283             this.SrcChanged(this,null);
284             return false;
285         }
286
287         public void Dispose()
288         {
289             this.Dispose(true);
290             GC.SuppressFinalize(this);
291         }
292
293         public virtual void CalculateLineCountOnScreen()
294         {
295         }
296
297         protected virtual void Dispose(bool disposing)
298         {
299             if (disposing)
300             {
301                 this.Document.UpdateCalledAlways -= new DocumentUpdateEventHandler(this.doc_Update);    //これをしないと複数のビューを作成した時に妙なエラーが発生する
302             }
303             this._LayoutLines.Clear();
304         }
305
306         protected virtual void CalculateClipRect()
307         {
308         }
309
310
311         protected virtual void OnSrcChanged(EventArgs e)
312         {
313             EventHandler handler = this.SrcChanged;
314             if (handler != null)
315                 this.SrcChanged(this, e);
316         }
317
318         protected virtual void OnPerformLayoutedChanged(EventArgs e)
319         {
320             EventHandler handler = this.PerformLayouted;
321             if (handler != null)
322                 this.PerformLayouted(this, e);
323         }
324
325         protected virtual void OnPageBoundChanged(EventArgs e)
326         {
327             EventHandler handler = this.PageBoundChanged;
328             if (handler != null)
329                 this.PageBoundChanged(this, e);
330         }
331
332         void render_ChangedRightToLeft(object sender, EventArgs e)
333         {
334             this._Src.X = 0;
335             this._LayoutLines.ClearLayoutCache();
336             this.CalculateClipRect();
337         }
338
339         void render_ChangedRenderResource(object sender, ChangedRenderRsourceEventArgs e)
340         {
341             this._LayoutLines.ClearLayoutCache();
342             if (e.type == ResourceType.Font)
343             {
344                 this.CalculateClipRect();
345                 this.CalculateLineCountOnScreen();
346             }
347         }
348
349         void doc_Update(object sender, DocumentUpdateEventArgs e)
350         {
351             switch (e.type)
352             {
353                 case UpdateType.Clear:
354                     this._LongestWidth = 0;
355                     break;
356             }
357         }
358
359         IList<LineToIndexTableData> LayoutLines_SpilitStringByPixelbase(object sender, SpilitStringEventArgs e)
360         {
361             double WrapWidth;
362             if (this.Document.LineBreak == LineBreakMethod.PageBound)
363                 WrapWidth = this.render.TextArea.Width - LineBreakingMarginWidth;  //余白を残さないと欠ける
364             else
365                 WrapWidth = this.render.emSize.Width * this.Document.LineBreakCharCount;
366
367             if (WrapWidth < 0 && this.Document.LineBreak != LineBreakMethod.None)
368                 throw new InvalidOperationException();
369
370             int startIndex = e.index;
371             int endIndex = e.index + e.length - 1;
372
373             LineToIndexTable layoutLineCollection = (LineToIndexTable)sender;
374
375             return this.render.BreakLine(e.buffer,layoutLineCollection, startIndex, endIndex, WrapWidth);
376         }
377
378         IList<LineToIndexTableData> LayoutLines_SpilitStringByChar(object sender, SpilitStringEventArgs e)
379         {
380             return this.Document.CreateLineList(e.index, e.length, Document.MaximumLineLength);
381         }
382     }
383 }