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