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;
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.UpdateCalledAlways -= 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.UpdateCalledAlways += 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_StatusUpdate(this, null);
121             }
122         }
123
124         private void _Document_PerformLayouted(object sender, EventArgs e)
125         {
126             CalculateLineCountOnScreen();
127             if(this.PerformLayouted != null)
128                 this.PerformLayouted(this, e);
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             if (this.Document.LineBreak != LineBreakMethod.None)
155                 this._LayoutLines.SpilitString = new SpilitStringEventHandler(LayoutLines_SpilitStringByPixelbase);
156             else
157                 this._LayoutLines.SpilitString = new SpilitStringEventHandler(LayoutLines_SpilitStringByChar);
158         }
159
160         protected LineToIndexTable _LayoutLines
161         {
162             get
163             {
164                 return this.Document.LayoutLines;
165             }
166         }
167
168         public event EventHandler SrcChanged;
169
170         [Obsolete]
171         public event EventHandler PerformLayouted;
172
173         public event EventHandler PageBoundChanged;
174
175         /// <summary>
176         /// テキストレンダラ―
177         /// </summary>
178         public ITextRender render
179         {
180             get;
181             set;
182         }
183
184         /// <summary>
185         /// 一ページの高さに収まる行数を返す
186         /// </summary>
187         public int LineCountOnScreen
188         {
189             get;
190             protected set;
191         }
192
193         /// <summary>
194         /// 折り返し時の右マージン
195         /// </summary>
196         public double LineBreakingMarginWidth
197         {
198             get;
199             protected set;
200         }
201
202         /// <summary>
203         /// 保持しているレイアウト行
204         /// </summary>
205         public LineToIndexTable LayoutLines
206         {
207             get { return this._LayoutLines; }
208         }
209
210         /// <summary>
211         /// 最も長い行の幅
212         /// </summary>
213         public double LongestWidth
214         {
215             get { return this._LongestWidth; }
216         }
217
218         public double LineNumberMargin
219         {
220             get
221             {
222                 return this.render.emSize.Width;
223             }
224         }
225
226         /// <summary>
227         /// シンタックスハイライター
228         /// </summary>
229         /// <remarks>差し替えた場合、再構築する必要があります</remarks>
230         public IHilighter Hilighter
231         {
232             get { return this._LayoutLines.Hilighter; }
233             set { this._LayoutLines.Hilighter = value; this._LayoutLines.ClearLayoutCache(); }
234         }
235
236         /// <summary>
237         /// すべてのレイアウト行を破棄し、再度レイアウトをやり直す
238         /// </summary>
239         [Obsolete]
240         public virtual void PerfomLayouts()
241         {
242             //互換性を保つために残しておく
243             this.Document.PerformLayout();
244         }
245
246         /// <summary>
247         /// 余白を表す
248         /// </summary>
249         public Padding Padding
250         {
251             get {
252                 return this._Padding;
253             }
254             set {
255                 this._Padding = value;
256                 CalculateClipRect();
257                 CalculateLineCountOnScreen();
258                 if (this.Document.RightToLeft)
259                     this._LayoutLines.ClearLayoutCache();
260                 this.PageBoundChanged(this, null);
261             }
262         }
263
264         /// <summary>
265         /// ページ全体を表す領域
266         /// </summary>
267         public Rectangle PageBound
268         {
269             get { return this._Rect; }
270             set
271             {
272                 if (value.Width < 0 || value.Height < 0)
273                     throw new ArgumentOutOfRangeException("");
274                 this._Rect = value;
275                 CalculateClipRect();
276                 CalculateLineCountOnScreen();
277                 if (this.Document.RightToLeft)
278                     this._LayoutLines.ClearLayoutCache();
279                 this.PageBoundChanged(this, null);
280             }
281         }
282
283         /// <summary>
284         /// Draw()の対象となる領域の左上を表す
285         /// </summary>
286         public SrcPoint Src
287         {
288             get { return this.Document.Src; }
289             set { this.Document.Src = value; }
290         }
291
292         public virtual void Draw(Rectangle updateRect, bool force = false)
293         {
294             return;
295         }
296
297         public virtual bool TryScroll(double x, int row)
298         {
299             if (row < 0)
300                 return true;
301             if (row > this.LayoutLines.Count - 1)
302                 return true;
303             this.Document.Src = new SrcPoint(x, row, row * this.render.emSize.Height);
304             this.SrcChanged(this,null);
305             return false;
306         }
307
308         public void Dispose()
309         {
310             this.Dispose(true);
311             GC.SuppressFinalize(this);
312         }
313
314         public virtual void CalculateLineCountOnScreen()
315         {
316         }
317
318         protected virtual void Dispose(bool disposing)
319         {
320             if (disposing)
321             {
322                 this._Document.UpdateCalledAlways -= new DocumentUpdateEventHandler(this.doc_Update);    //これをしないと複数のビューを作成した時に妙なエラーが発生する
323                 this._Document.LineBreakChanged -= Document_LineBreakChanged;
324                 this._Document.StatusUpdate -= Document_StatusUpdate;
325                 this._Document.PerformLayouted -= _Document_PerformLayouted;
326             }
327             this._LayoutLines.Clear();
328         }
329
330         protected virtual void CalculateClipRect()
331         {
332         }
333
334
335         protected virtual void OnSrcChanged(EventArgs e)
336         {
337             EventHandler handler = this.SrcChanged;
338             if (handler != null)
339                 this.SrcChanged(this, e);
340         }
341
342         protected virtual void OnPerformLayoutedChanged(EventArgs e)
343         {
344             EventHandler handler = this.PerformLayouted;
345             if (handler != null)
346                 this.PerformLayouted(this, e);
347         }
348
349         protected virtual void OnPageBoundChanged(EventArgs e)
350         {
351             EventHandler handler = this.PageBoundChanged;
352             if (handler != null)
353                 this.PageBoundChanged(this, e);
354         }
355
356         void render_ChangedRightToLeft(object sender, EventArgs e)
357         {
358             this.Document.Src = new SrcPoint(0, this.Document.Src.Row, this.Src.Y);
359         }
360
361         void render_ChangedRenderResource(object sender, ChangedRenderRsourceEventArgs e)
362         {
363             this._LayoutLines.ClearLayoutCache();
364             if (e.type == ResourceType.Font)
365             {
366                 this.CalculateClipRect();
367                 this.CalculateLineCountOnScreen();
368             }
369         }
370
371         void doc_Update(object sender, DocumentUpdateEventArgs e)
372         {
373             switch (e.type)
374             {
375                 case UpdateType.Clear:
376                     this._LongestWidth = 0;
377                     break;
378             }
379         }
380
381         IList<LineToIndexTableData> LayoutLines_SpilitStringByPixelbase(object sender, SpilitStringEventArgs e)
382         {
383             double WrapWidth;
384             if (this.Document.LineBreak == LineBreakMethod.PageBound)
385                 WrapWidth = this.render.TextArea.Width - LineBreakingMarginWidth;  //余白を残さないと欠ける
386             else
387                 WrapWidth = this.render.emSize.Width * this.Document.LineBreakCharCount;
388
389             if (WrapWidth < 0 && this.Document.LineBreak != LineBreakMethod.None)
390                 throw new InvalidOperationException();
391
392             int startIndex = e.index;
393             int endIndex = e.index + e.length - 1;
394
395             LineToIndexTable layoutLineCollection = (LineToIndexTable)sender;
396
397             return this.render.BreakLine(e.buffer,layoutLineCollection, startIndex, endIndex, WrapWidth);
398         }
399
400         IList<LineToIndexTableData> LayoutLines_SpilitStringByChar(object sender, SpilitStringEventArgs e)
401         {
402             return this.Document.CreateLineList(e.index, e.length, Document.MaximumLineLength);
403         }
404     }
405 }