/* * Copyright (C) 2013 FooProject * * 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 * the Free Software Foundation; either version 3 of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace FooEditEngine { /// /// LineBreakMethod列挙体 /// public enum LineBreakMethod { /// /// 折り返さない /// None = 0, /// /// 右端で折り返す /// PageBound = 1, /// /// 文字数で折り返す /// CharUnit = 2 } /// /// 余白を表す /// public struct Padding { /// /// 左余白 /// public int Left; /// /// 上余白 /// public int Top; /// /// 右余白 /// public int Right; /// /// 下余白 /// public int Bottom; /// /// コンストラクター /// /// 左 /// 上 /// 右 /// 下 public Padding(int left, int top, int right, int bottom) { this.Left = left; this.Top = top; this.Right = right; this.Bottom = bottom; } } abstract class ViewBase : IDisposable { const int SpiltCharCount = 1024; protected Document Document; protected Point2 _Src = new Point2(); protected Rectangle _Rect; protected double _LongestWidth; Padding _Padding; bool _DrawLineNumber,_UrlMark; LineBreakMethod _LineBreak; int _LineBreakCharCount = 80; public ViewBase(Document doc, ITextRender r,Padding padding) { this._Padding = padding; this.Document = doc; this.Document.UpdateCalledAlways += new DocumentUpdateEventHandler(doc_Update); this._LayoutLines.SpilitString = new SpilitStringEventHandler(LayoutLines_SpilitStringByChar); this.render = r; this.render.ChangedRenderResource += new ChangedRenderResourceEventHandler(render_ChangedRenderResource); this.render.ChangedRightToLeft += render_ChangedRightToLeft; this.SrcChanged += new EventHandler((s, e) => { }); this.PerformLayouted += new EventHandler((s, e) => { }); this.PageBoundChanged += new EventHandler((s, e) => { }); this.MarkerPatternSet = new MarkerPatternSet(this._LayoutLines, doc.Markers); this.MarkerPatternSet.Updated += WacthDogPattern_Updated; } protected LineToIndexTable _LayoutLines { get { return this.Document.LayoutLines; } } public event EventHandler SrcChanged; public event EventHandler PerformLayouted; public event EventHandler PageBoundChanged; /// /// マーカーパターンセット /// public MarkerPatternSet MarkerPatternSet { get; private set; } /// /// URLをハイパーリンクとして表示するなら真。そうでないなら偽 /// public bool UrlMark { get { return this._UrlMark; } set { this._UrlMark = value; if (value) { Regex regex = new Regex("(http|https|ftp)(:\\/\\/[-_.!~*\\'()a-zA-Z0-9;\\/?:\\@&=+\\$,%#]+)"); this.MarkerPatternSet.Add(MarkerIDs.URL, new RegexMarkerPattern(regex, HilightType.Url,new Color())); } else { this.MarkerPatternSet.Remove(MarkerIDs.URL); } } } /// /// テキストレンダラ― /// public ITextRender render { get; set; } /// /// 一ページの高さに収まる行数を返す /// public int LineCountOnScreen { get; protected set; } /// /// 折り返し時の右マージン /// public double LineBreakingMarginWidth { get; protected set; } /// /// 保持しているレイアウト行 /// public LineToIndexTable LayoutLines { get { return this._LayoutLines; } } /// /// 最も長い行の幅 /// public double LongestWidth { get { return this._LongestWidth; } } /// /// 桁折り処理の方法を指定する /// /// /// 変更した場合、呼び出し側で再描写とレイアウトの再構築を行う必要があります /// public LineBreakMethod LineBreak { get { return this._LineBreak; } set { this._LineBreak = value; if (value != LineBreakMethod.None) this._LayoutLines.SpilitString = new SpilitStringEventHandler(LayoutLines_SpilitStringByPixelbase); else this._LayoutLines.SpilitString = new SpilitStringEventHandler(LayoutLines_SpilitStringByChar); } } /// /// 折り返し行う文字数。実際に折り返しが行われる幅はem単位×この値となります /// public int LineBreakCharCount { get { return this._LineBreakCharCount; } set { this._LineBreakCharCount = value; } } public double LineNumberMargin { get { return this.render.emSize.Width; } } /// /// シンタックスハイライター /// public IHilighter Hilighter { get { return this._LayoutLines.Hilighter; } set { this._LayoutLines.Hilighter = value; } } /// /// タブの幅 /// /// 変更した場合、呼び出し側で再描写する必要があります public int TabStops { get { return this.render.TabWidthChar; } set { this.render.TabWidthChar = value; } } /// /// すべてのレイアウト行を破棄し、再度レイアウトをやり直す /// public virtual void PerfomLayouts() { this.Document.FireUpdate(new DocumentUpdateEventArgs(UpdateType.Clear, -1, -1, -1)); this.Document.FireUpdate(new DocumentUpdateEventArgs(UpdateType.Replace, 0, 0, this.Document.Length)); CalculateLineCountOnScreen(); this.PerformLayouted(this, null); } /// /// 余白を表す /// public Padding Padding { get { return this._Padding; } set { this._Padding = value; CalculateClipRect(); CalculateLineCountOnScreen(); if (this.render.RightToLeft) this._LayoutLines.ClearLayoutCache(); this.PageBoundChanged(this, null); } } /// /// ページ全体を表す領域 /// public Rectangle PageBound { get { return this._Rect; } set { if (value.Width < 0 || value.Height < 0) throw new ArgumentOutOfRangeException(""); this._Rect = value; CalculateClipRect(); CalculateLineCountOnScreen(); if (this.render.RightToLeft) this._LayoutLines.ClearLayoutCache(); this.PageBoundChanged(this, null); } } /// /// Draw()の対象となる領域の左上を表す /// public Point2 Src { get { return this._Src; } set { this._Src = value; } } /// /// 行番号を表示するかどうか /// public bool DrawLineNumber { get { return this._DrawLineNumber; } set { this._DrawLineNumber = value; this._LayoutLines.ClearLayoutCache(); CalculateClipRect(); } } public virtual void Draw(Rectangle updateRect) { } public virtual bool TryScroll(double x, int row) { if (row < 0) return true; if (row > this.LayoutLines.Count - 1) return true; this._Src.X = x; this._Src.Row = row; CalculateLineCountOnScreen(); this.SrcChanged(this,null); return false; } public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } public virtual void CalculateLineCountOnScreen() { } protected virtual void Dispose(bool disposing) { if (disposing) { this.Document.UpdateCalledAlways -= new DocumentUpdateEventHandler(this.doc_Update); //これをしないと複数のビューを作成した時に妙なエラーが発生する } this._LayoutLines.Clear(); } protected virtual void CalculateClipRect() { } protected virtual void OnSrcChanged(EventArgs e) { EventHandler handler = this.SrcChanged; if (handler != null) this.SrcChanged(this, e); } protected virtual void OnPerformLayoutedChanged(EventArgs e) { EventHandler handler = this.PerformLayouted; if (handler != null) this.PerformLayouted(this, e); } protected virtual void OnPageBoundChanged(EventArgs e) { EventHandler handler = this.PageBoundChanged; if (handler != null) this.PageBoundChanged(this, e); } void WacthDogPattern_Updated(object sender, EventArgs e) { this._LayoutLines.ClearLayoutCache(); } void render_ChangedRightToLeft(object sender, EventArgs e) { this._Src.X = 0; this._LayoutLines.ClearLayoutCache(); this.CalculateClipRect(); } void render_ChangedRenderResource(object sender, ChangedRenderRsourceEventArgs e) { this._LayoutLines.ClearLayoutCache(); if (e.type == ResourceType.Font) { this.CalculateClipRect(); this.CalculateLineCountOnScreen(); } } void doc_Update(object sender, DocumentUpdateEventArgs e) { switch (e.type) { case UpdateType.Clear: this._LongestWidth = 0; break; } } IList LayoutLines_SpilitStringByPixelbase(object sender, SpilitStringEventArgs e) { double WrapWidth; if (_LineBreak == LineBreakMethod.PageBound) WrapWidth = this.render.TextArea.Width - LineBreakingMarginWidth; //余白を残さないと欠ける else WrapWidth = this.render.emSize.Width * this._LineBreakCharCount; if (WrapWidth < 0 && this._LineBreak != LineBreakMethod.None) throw new InvalidOperationException(); int startIndex = e.index; int endIndex = e.index + e.length - 1; LineToIndexTable layoutLineCollection = (LineToIndexTable)sender; return this.render.BreakLine(e.buffer,layoutLineCollection, startIndex, endIndex, WrapWidth); } IList LayoutLines_SpilitStringByChar(object sender, SpilitStringEventArgs e) { return this.Document.CreateLineList(e.index, e.length,1000); } } }