/*
* 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);
}
}
}