/* * 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; Document _Document; protected Rectangle _Rect; protected double _LongestWidth; Padding _Padding; public ViewBase(Document doc, ITextRender r,Padding padding) { this._Padding = padding; this.Document = doc; 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.PageBoundChanged += new EventHandler((s, e) => { }); } public Document Document { get { return this._Document; } set { if(this._Document != null) { this._Document.Update -= new DocumentUpdateEventHandler(doc_Update); this._Document.LineBreakChanged -= Document_LineBreakChanged; this._Document.StatusUpdate -= Document_StatusUpdate; this._Document.PerformLayouted -= _Document_PerformLayouted; } this._Document = value; this._Document.Update += new DocumentUpdateEventHandler(doc_Update); this._Document.LineBreakChanged += Document_LineBreakChanged; this._Document.StatusUpdate += Document_StatusUpdate; this._Document.PerformLayouted += _Document_PerformLayouted; this.Document_LineBreakChanged(this, null); this.Document_StatusUpdate(this, null); } } private void _Document_PerformLayouted(object sender, EventArgs e) { CalculateLineCountOnScreen(); if(this.PerformLayouted != null) this.PerformLayouted(this, e); } private void Document_StatusUpdate(object sender, EventArgs e) { if (this.render == null) return; if (this.render.TabWidthChar != this.Document.TabStops) this.render.TabWidthChar = this.Document.TabStops; if (this.render.RightToLeft != this.Document.RightToLeft) this.render.RightToLeft = this.Document.RightToLeft; if (this.render.ShowFullSpace != this.Document.ShowFullSpace) this.render.ShowFullSpace = this.Document.ShowFullSpace; if (this.render.ShowHalfSpace != this.Document.ShowHalfSpace) this.render.ShowHalfSpace = this.Document.ShowHalfSpace; if (this.render.ShowTab != this.Document.ShowTab) this.render.ShowTab = this.Document.ShowTab; if (this.render.ShowLineBreak != this.Document.ShowLineBreak) this.render.ShowLineBreak = this.Document.ShowLineBreak; CalculateClipRect(); CalculateLineCountOnScreen(); this._LayoutLines.ClearLayoutCache(); } private void Document_LineBreakChanged(object sender, EventArgs e) { if (this.Document.LineBreak != LineBreakMethod.None) this._LayoutLines.SpilitString = new SpilitStringEventHandler(LayoutLines_SpilitStringByPixelbase); else this._LayoutLines.SpilitString = new SpilitStringEventHandler(LayoutLines_SpilitStringByChar); } protected LineToIndexTable _LayoutLines { get { return this.Document.LayoutLines; } } public event EventHandler SrcChanged; [Obsolete] public event EventHandler PerformLayouted; public event EventHandler PageBoundChanged; /// /// テキストレンダラ― /// 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 double LineNumberMargin { get { return this.render.emSize.Width; } } /// /// シンタックスハイライター /// /// 差し替えた場合、再構築する必要があります public IHilighter Hilighter { get { return this._LayoutLines.Hilighter; } set { this._LayoutLines.Hilighter = value; this._LayoutLines.ClearLayoutCache(); } } /// /// すべてのレイアウト行を破棄し、再度レイアウトをやり直す /// [Obsolete] public virtual void PerfomLayouts() { //互換性を保つために残しておく this.Document.PerformLayout(); } /// /// 余白を表す /// public Padding Padding { get { return this._Padding; } set { this._Padding = value; CalculateClipRect(); CalculateLineCountOnScreen(); if (this.Document.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.Document.RightToLeft) this._LayoutLines.ClearLayoutCache(); this.PageBoundChanged(this, null); } } /// /// Draw()の対象となる領域の左上を表す /// public SrcPoint Src { get { return this.Document.Src; } set { this.Document.Src = value; } } public virtual void Draw(Rectangle updateRect, bool force = false) { return; } public virtual bool TryScroll(double x, int row) { if (row < 0) return true; if (row > this.LayoutLines.Count - 1) return true; this.Document.Src = new SrcPoint(x, row, row * this.render.emSize.Height); 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.Update -= new DocumentUpdateEventHandler(this.doc_Update); //これをしないと複数のビューを作成した時に妙なエラーが発生する this._Document.LineBreakChanged -= Document_LineBreakChanged; this._Document.StatusUpdate -= Document_StatusUpdate; this._Document.PerformLayouted -= _Document_PerformLayouted; } 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 render_ChangedRightToLeft(object sender, EventArgs e) { this.Document.Src = new SrcPoint(0, this.Document.Src.Row, this.Src.Y); } 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 (this.Document.LineBreak == LineBreakMethod.PageBound) WrapWidth = this.render.TextArea.Width - LineBreakingMarginWidth; //余白を残さないと欠ける else WrapWidth = this.render.emSize.Width * this.Document.LineBreakCharCount; if (WrapWidth < 0 && this.Document.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, Document.MaximumLineLength); } } }