2015-08-02 19:27:02 -04:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
using System.Data;
|
|
|
|
|
using System.Drawing;
|
2015-08-09 18:24:24 -04:00
|
|
|
|
using System.Drawing.Drawing2D;
|
2015-08-02 19:27:02 -04:00
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
2016-11-27 10:56:37 -05:00
|
|
|
|
using System.Text.RegularExpressions;
|
2015-08-02 19:27:02 -04:00
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using System.Windows.Forms;
|
|
|
|
|
|
|
|
|
|
namespace Mesen.GUI.Debugger
|
|
|
|
|
{
|
2015-08-04 19:50:57 -04:00
|
|
|
|
[Flags]
|
2015-08-02 19:27:02 -04:00
|
|
|
|
public enum LineSymbol
|
|
|
|
|
{
|
|
|
|
|
None = 0,
|
2015-08-04 19:50:57 -04:00
|
|
|
|
Circle = 1,
|
|
|
|
|
CircleOutline = 2,
|
|
|
|
|
Arrow = 4,
|
2015-08-02 19:27:02 -04:00
|
|
|
|
}
|
|
|
|
|
|
2016-11-27 12:14:32 -05:00
|
|
|
|
public enum eHistoryType
|
|
|
|
|
{
|
|
|
|
|
Always,
|
|
|
|
|
OnScroll,
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-02 19:27:02 -04:00
|
|
|
|
public class LineProperties
|
|
|
|
|
{
|
|
|
|
|
public Color? BgColor;
|
|
|
|
|
public Color? FgColor;
|
|
|
|
|
public Color? OutlineColor;
|
|
|
|
|
public LineSymbol Symbol;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public partial class ctrlTextbox : Control
|
|
|
|
|
{
|
|
|
|
|
public event EventHandler ScrollPositionChanged;
|
|
|
|
|
|
2016-11-23 22:57:17 -05:00
|
|
|
|
private const float HorizontalScrollFactor = 8;
|
2016-11-28 22:58:16 -05:00
|
|
|
|
private const int CommentSpacingCharCount = 25;
|
2016-11-23 22:57:17 -05:00
|
|
|
|
|
2016-11-27 12:14:32 -05:00
|
|
|
|
private TextboxHistory _history = new TextboxHistory();
|
|
|
|
|
|
2015-08-02 19:27:02 -04:00
|
|
|
|
private string[] _contents = new string[0];
|
2015-08-21 22:42:44 -04:00
|
|
|
|
private string[] _contentNotes = new string[0];
|
2015-08-09 18:24:24 -04:00
|
|
|
|
private string[] _compareContents = null;
|
2015-08-02 19:27:02 -04:00
|
|
|
|
private int[] _lineNumbers = new int[0];
|
2016-11-21 22:34:47 -05:00
|
|
|
|
private int[] _lineMargins = new int[0];
|
2015-08-21 22:42:44 -04:00
|
|
|
|
private string[] _lineNumberNotes = new string[0];
|
2015-08-02 19:27:02 -04:00
|
|
|
|
private Dictionary<int, int> _lineNumberIndex = new Dictionary<int,int>();
|
|
|
|
|
private Dictionary<int, LineProperties> _lineProperties = new Dictionary<int,LineProperties>();
|
|
|
|
|
private bool _showLineNumbers = false;
|
|
|
|
|
private bool _showLineInHex = false;
|
2015-08-21 22:42:44 -04:00
|
|
|
|
private bool _showLineNumberNotes = false;
|
2016-12-04 14:02:22 -05:00
|
|
|
|
private bool _showSingleLineLineNumberNotes = false;
|
2015-08-21 22:42:44 -04:00
|
|
|
|
private bool _showContentNotes = false;
|
2016-12-04 14:02:22 -05:00
|
|
|
|
private bool _showSingleLineContentNotes = true;
|
2015-08-02 19:27:02 -04:00
|
|
|
|
private int _cursorPosition = 0;
|
|
|
|
|
private int _scrollPosition = 0;
|
2016-11-23 22:57:17 -05:00
|
|
|
|
private int _horizontalScrollPosition = 0;
|
2015-08-03 21:53:46 -04:00
|
|
|
|
private string _searchString = null;
|
2015-08-05 20:40:10 -04:00
|
|
|
|
private string _header = null;
|
2015-08-21 22:42:44 -04:00
|
|
|
|
private Font _noteFont = null;
|
2016-12-04 14:02:22 -05:00
|
|
|
|
private int _marginWidth = 9;
|
|
|
|
|
private int _extendedMarginWidth = 13;
|
2016-11-23 22:57:17 -05:00
|
|
|
|
private float _maxLineWidth = 0;
|
2016-11-24 17:35:59 -05:00
|
|
|
|
private int _maxLineWidthIndex = 0;
|
2015-08-02 19:27:02 -04:00
|
|
|
|
|
|
|
|
|
public ctrlTextbox()
|
|
|
|
|
{
|
|
|
|
|
InitializeComponent();
|
|
|
|
|
this.ResizeRedraw = true;
|
|
|
|
|
this.DoubleBuffered = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string[] TextLines
|
|
|
|
|
{
|
|
|
|
|
set
|
|
|
|
|
{
|
2016-11-23 22:57:17 -05:00
|
|
|
|
int maxLength = 0;
|
2016-11-24 17:35:59 -05:00
|
|
|
|
|
|
|
|
|
_maxLineWidthIndex = 0;
|
2016-11-23 22:57:17 -05:00
|
|
|
|
|
2016-11-21 22:34:47 -05:00
|
|
|
|
_contents = new string[value.Length];
|
|
|
|
|
_lineMargins = new int[value.Length];
|
|
|
|
|
for(int i = 0, len = value.Length; i < len; i++) {
|
|
|
|
|
_contents[i] = value[i].TrimStart();
|
2016-11-28 22:58:16 -05:00
|
|
|
|
|
|
|
|
|
string stringToMeasure = GetFullWidthString(_contents[i]);
|
|
|
|
|
if(stringToMeasure.Length > maxLength) {
|
|
|
|
|
maxLength = stringToMeasure.Length;
|
2016-11-24 17:35:59 -05:00
|
|
|
|
_maxLineWidthIndex = i;
|
2016-11-23 22:57:17 -05:00
|
|
|
|
}
|
2016-11-21 22:34:47 -05:00
|
|
|
|
_lineMargins[i] = (value[i].Length - _contents[i].Length) * 10;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-23 22:57:17 -05:00
|
|
|
|
UpdateHorizontalScrollWidth();
|
|
|
|
|
|
2016-12-01 19:38:48 -05:00
|
|
|
|
if(_lineNumbers.Length != _contents.Length) {
|
|
|
|
|
_lineNumbers = new int[_contents.Length];
|
|
|
|
|
_lineNumberIndex.Clear();
|
|
|
|
|
for(int i = _contents.Length - 1; i >=0; i--) {
|
|
|
|
|
_lineNumbers[i] = i;
|
|
|
|
|
_lineNumberIndex[i] = i;
|
|
|
|
|
}
|
2015-08-02 19:27:02 -04:00
|
|
|
|
}
|
|
|
|
|
this.Invalidate();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-21 22:42:44 -04:00
|
|
|
|
public override Font Font
|
|
|
|
|
{
|
|
|
|
|
get { return base.Font; }
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
base.Font = value;
|
|
|
|
|
_noteFont = new Font(value.FontFamily, value.Size * 0.75f);
|
2016-11-24 17:35:59 -05:00
|
|
|
|
UpdateHorizontalScrollWidth();
|
2015-08-21 22:42:44 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-04 14:02:22 -05:00
|
|
|
|
public bool ShowSingleContentLineNotes
|
|
|
|
|
{
|
|
|
|
|
get { return _showSingleLineContentNotes; }
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
_showSingleLineContentNotes = value;
|
|
|
|
|
this.Invalidate();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-21 22:42:44 -04:00
|
|
|
|
public bool ShowContentNotes
|
|
|
|
|
{
|
|
|
|
|
get { return _showContentNotes; }
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
_showContentNotes = value;
|
|
|
|
|
this.Invalidate();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-04 14:02:22 -05:00
|
|
|
|
public bool ShowSingleLineLineNumberNotes
|
|
|
|
|
{
|
|
|
|
|
get { return _showSingleLineLineNumberNotes; }
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
_showSingleLineLineNumberNotes = value;
|
|
|
|
|
this.Invalidate();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-21 22:42:44 -04:00
|
|
|
|
public bool ShowLineNumberNotes
|
|
|
|
|
{
|
|
|
|
|
get { return this._showLineNumberNotes; }
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
this._showLineNumberNotes = value;
|
|
|
|
|
this.Invalidate();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string[] TextLineNotes
|
|
|
|
|
{
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
this._contentNotes = value;
|
|
|
|
|
this.Invalidate();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-09 18:24:24 -04:00
|
|
|
|
public string[] CompareLines
|
|
|
|
|
{
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
_compareContents = value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-02 19:27:02 -04:00
|
|
|
|
public int LineCount
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return _contents.Length;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-21 22:42:44 -04:00
|
|
|
|
public int[] LineNumbers
|
2015-08-02 19:27:02 -04:00
|
|
|
|
{
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
_lineNumbers = value;
|
|
|
|
|
_lineNumberIndex.Clear();
|
|
|
|
|
int i = 0;
|
|
|
|
|
foreach(int line in _lineNumbers) {
|
|
|
|
|
_lineNumberIndex[line] = i;
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
this.Invalidate();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-21 22:42:44 -04:00
|
|
|
|
public string[] LineNumberNotes
|
|
|
|
|
{
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
_lineNumberNotes = value;
|
|
|
|
|
this.Invalidate();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-05 20:40:10 -04:00
|
|
|
|
public string Header
|
|
|
|
|
{
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
this._header = value;
|
|
|
|
|
this.Invalidate();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-25 19:02:33 -04:00
|
|
|
|
public int MarginWidth
|
|
|
|
|
{
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
this._marginWidth = value;
|
|
|
|
|
this.Invalidate();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-01 20:45:13 -05:00
|
|
|
|
public List<Tuple<int, int, string>> FindAllOccurrences(string text, bool matchWholeWord, bool matchCase)
|
2016-11-27 10:56:37 -05:00
|
|
|
|
{
|
|
|
|
|
List<Tuple<int, int, string>> result = new List<Tuple<int, int, string>>();
|
|
|
|
|
string regex;
|
2016-12-01 20:45:13 -05:00
|
|
|
|
if(matchWholeWord) {
|
|
|
|
|
regex = $"[^0-9a-zA-Z_#@]+{Regex.Escape(text)}[^0-9a-zA-Z_#@]+";
|
2016-11-27 10:56:37 -05:00
|
|
|
|
} else {
|
2016-12-01 20:45:13 -05:00
|
|
|
|
regex = Regex.Escape(text);
|
2016-11-27 10:56:37 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(int i = 0, len = _contents.Length; i < len; i++) {
|
2016-12-01 20:45:13 -05:00
|
|
|
|
if(Regex.IsMatch(_contents[i], regex, matchCase ? RegexOptions.None : RegexOptions.IgnoreCase)) {
|
2016-11-27 10:56:37 -05:00
|
|
|
|
string line = _contents[i].Replace("\x2", "\t").Trim();
|
|
|
|
|
|
|
|
|
|
if(line.StartsWith("__") && line.EndsWith("__") || line.StartsWith("[[") && line.EndsWith("]]")) {
|
|
|
|
|
line = "Block: " + line.Substring(2, line.Length - 4);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(line.StartsWith("--") && line.EndsWith("--")) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int j = i;
|
|
|
|
|
while(j < _lineNumbers.Length && _lineNumbers[j] < 0) {
|
|
|
|
|
j++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var searchResult = new Tuple<int, int, string>(_lineNumbers[j], i, line);
|
|
|
|
|
result.Add(searchResult);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-03 21:53:46 -04:00
|
|
|
|
public bool Search(string searchString, bool searchBackwards, bool isNewSearch)
|
|
|
|
|
{
|
|
|
|
|
if(string.IsNullOrWhiteSpace(searchString)) {
|
|
|
|
|
this._searchString = null;
|
|
|
|
|
this.Invalidate();
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
int startPosition;
|
|
|
|
|
int endPosition;
|
|
|
|
|
|
|
|
|
|
this._searchString = searchString.ToLowerInvariant();
|
|
|
|
|
int searchOffset = (searchBackwards ? -1 : 1);
|
|
|
|
|
if(isNewSearch) {
|
|
|
|
|
startPosition = this.CursorPosition;
|
|
|
|
|
endPosition = this.CursorPosition - searchOffset;
|
|
|
|
|
if(endPosition < 0) {
|
|
|
|
|
endPosition = _contents.Length - 1;
|
|
|
|
|
} else if(endPosition >= _contents.Length) {
|
|
|
|
|
endPosition = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
startPosition = this.CursorPosition + searchOffset;
|
|
|
|
|
endPosition = this.CursorPosition;
|
|
|
|
|
if(startPosition < 0) {
|
|
|
|
|
startPosition = _contents.Length - 1;
|
|
|
|
|
} else if(startPosition >= _contents.Length) {
|
|
|
|
|
startPosition = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(int i = startPosition; i != endPosition; i += searchOffset) {
|
|
|
|
|
string line = _contents[i].ToLowerInvariant();
|
2015-08-08 23:39:18 -04:00
|
|
|
|
if(line.Contains(this._searchString)) {
|
2015-08-03 21:53:46 -04:00
|
|
|
|
this.ScrollToLineIndex(i);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Continue search from start/end of document
|
|
|
|
|
if(!searchBackwards && i == this._contents.Length - 1) {
|
|
|
|
|
i = 0;
|
|
|
|
|
} else if(searchBackwards && i == 0) {
|
|
|
|
|
i = this._contents.Length - 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
this.Invalidate();
|
|
|
|
|
return _contents[_cursorPosition].ToLowerInvariant().Contains(this._searchString);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-02 19:27:02 -04:00
|
|
|
|
public void ClearLineStyles()
|
|
|
|
|
{
|
|
|
|
|
_lineProperties.Clear();
|
|
|
|
|
this.Invalidate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SetLineColor(int lineNumber, Color? fgColor = null, Color? bgColor = null, Color? outlineColor = null, LineSymbol symbol = LineSymbol.None)
|
|
|
|
|
{
|
|
|
|
|
if(lineNumber != -1) {
|
|
|
|
|
if(_lineNumberIndex.ContainsKey(lineNumber)) {
|
|
|
|
|
LineProperties properties = new LineProperties() {
|
|
|
|
|
BgColor = bgColor,
|
|
|
|
|
FgColor = fgColor,
|
|
|
|
|
OutlineColor = outlineColor,
|
|
|
|
|
Symbol = symbol
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
_lineProperties[_lineNumberIndex[lineNumber]] = properties;
|
|
|
|
|
this.Invalidate();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int GetLineIndex(int lineNumber)
|
|
|
|
|
{
|
2015-08-02 22:19:12 -04:00
|
|
|
|
if(_lineNumberIndex.ContainsKey(lineNumber)) {
|
|
|
|
|
return _lineNumberIndex[lineNumber];
|
|
|
|
|
} else {
|
|
|
|
|
foreach(int line in _lineNumbers) {
|
|
|
|
|
if(line > lineNumber) {
|
|
|
|
|
return Math.Max(0, GetLineIndex(line) - 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
2015-08-02 19:27:02 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int GetLineNumber(int lineIndex)
|
|
|
|
|
{
|
2016-01-10 00:33:33 -05:00
|
|
|
|
if(_lineNumbers.Length <= lineIndex) {
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
return _lineNumbers[lineIndex];
|
|
|
|
|
}
|
2015-08-02 19:27:02 -04:00
|
|
|
|
}
|
|
|
|
|
|
2016-11-27 12:14:32 -05:00
|
|
|
|
public void ScrollToLineIndex(int lineIndex, eHistoryType historyType = eHistoryType.Always)
|
2015-08-03 21:53:46 -04:00
|
|
|
|
{
|
2016-11-27 12:14:32 -05:00
|
|
|
|
if(this.CursorPosition != lineIndex) {
|
|
|
|
|
bool scrolled = false;
|
|
|
|
|
if(lineIndex < this.ScrollPosition || lineIndex > this.GetLastVisibleLineIndex()) {
|
|
|
|
|
//Line isn't currently visible, scroll it to the middle of the viewport
|
|
|
|
|
this.ScrollPosition = lineIndex - this.GetNumberVisibleLines()/2;
|
|
|
|
|
scrolled = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(historyType == eHistoryType.Always || scrolled && historyType == eHistoryType.OnScroll) {
|
|
|
|
|
_history.AddHistory(this.CursorPosition);
|
|
|
|
|
}
|
|
|
|
|
this.CursorPosition = lineIndex;
|
|
|
|
|
if(historyType == eHistoryType.Always || scrolled && historyType == eHistoryType.OnScroll) {
|
|
|
|
|
_history.AddHistory(this.CursorPosition);
|
|
|
|
|
}
|
2015-08-03 21:53:46 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-27 12:14:32 -05:00
|
|
|
|
public void ScrollToLineNumber(int lineNumber, eHistoryType historyType = eHistoryType.Always)
|
2015-08-02 19:27:02 -04:00
|
|
|
|
{
|
|
|
|
|
int lineIndex = this.GetLineIndex(lineNumber);
|
|
|
|
|
if(lineIndex >= 0) {
|
2016-11-27 12:14:32 -05:00
|
|
|
|
ScrollToLineIndex(lineIndex, historyType);
|
2015-08-02 19:27:02 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-10 00:33:33 -05:00
|
|
|
|
public int CodeMargin
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
using(Graphics g = Graphics.FromHwnd(this.Handle)) {
|
2016-12-04 14:02:22 -05:00
|
|
|
|
return this.GetMargin(g, false);
|
2016-01-10 00:33:33 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-04 14:02:22 -05:00
|
|
|
|
private int GetMargin(Graphics g, bool getExtendedMargin)
|
2015-08-02 19:27:02 -04:00
|
|
|
|
{
|
2016-12-04 14:02:22 -05:00
|
|
|
|
int marginWidth = getExtendedMargin && this.ShowContentNotes && this.ShowSingleContentLineNotes ? _marginWidth + _extendedMarginWidth : _marginWidth;
|
|
|
|
|
return this.ShowLineNumbers ? (int)(g.MeasureString("".PadLeft(marginWidth, 'W'), this.Font).Width) : 0;
|
2015-08-02 19:27:02 -04:00
|
|
|
|
}
|
2015-08-09 18:24:24 -04:00
|
|
|
|
|
2016-01-10 00:33:33 -05:00
|
|
|
|
public int GetLineIndexAtPosition(int yPos)
|
|
|
|
|
{
|
|
|
|
|
int charIndex;
|
|
|
|
|
int lineIndex;
|
|
|
|
|
GetCharIndex(new Point(0, yPos), out charIndex, out lineIndex);
|
|
|
|
|
return lineIndex;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-28 22:58:16 -05:00
|
|
|
|
private string GetFullWidthString(string lineContent)
|
2016-11-27 20:44:57 -05:00
|
|
|
|
{
|
2016-11-28 22:58:16 -05:00
|
|
|
|
string text = lineContent.Replace("\x2", "");
|
2016-11-27 20:44:57 -05:00
|
|
|
|
int commentIndex = text.IndexOf(";");
|
2016-11-28 22:58:16 -05:00
|
|
|
|
if(commentIndex > 0 && commentIndex < CommentSpacingCharCount) {
|
|
|
|
|
text = text.Insert(commentIndex, "".PadLeft(CommentSpacingCharCount - commentIndex));
|
2016-11-27 20:44:57 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return text;
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-09 18:24:24 -04:00
|
|
|
|
private bool GetCharIndex(Point position, out int charIndex, out int lineIndex)
|
2015-08-02 19:27:02 -04:00
|
|
|
|
{
|
2015-08-09 18:24:24 -04:00
|
|
|
|
charIndex = -1;
|
2015-08-02 19:27:02 -04:00
|
|
|
|
using(Graphics g = Graphics.FromHwnd(this.Handle)) {
|
2016-12-04 14:02:22 -05:00
|
|
|
|
int marginLeft = this.GetMargin(g, true);
|
2015-08-02 19:27:02 -04:00
|
|
|
|
int positionX = position.X - marginLeft;
|
2015-08-09 18:24:24 -04:00
|
|
|
|
lineIndex = this.ScrollPosition + this.GetLineAtPosition(position.Y);
|
2016-01-19 20:26:11 -05:00
|
|
|
|
if(lineIndex > _contents.Length && _contents.Length != 0) {
|
|
|
|
|
lineIndex = _contents.Length - 1;
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-09 18:24:24 -04:00
|
|
|
|
if(positionX >= 0 && lineIndex < _contents.Length) {
|
2016-11-28 22:58:16 -05:00
|
|
|
|
string text = this.GetFullWidthString(_contents[lineIndex]);
|
2016-11-21 22:34:47 -05:00
|
|
|
|
//Adjust background color highlights based on number of spaces in front of content
|
|
|
|
|
positionX -= _lineMargins[lineIndex];
|
|
|
|
|
|
2015-08-02 19:27:02 -04:00
|
|
|
|
int previousWidth = 0;
|
|
|
|
|
for(int i = 0, len = text.Length; i < len; i++) {
|
|
|
|
|
int width = (int)g.MeasureString(text.Substring(0, i+1), this.Font).Width;
|
|
|
|
|
if(width >= positionX && previousWidth <= positionX) {
|
|
|
|
|
charIndex = i;
|
2015-08-09 18:24:24 -04:00
|
|
|
|
return true;
|
2015-08-02 19:27:02 -04:00
|
|
|
|
}
|
|
|
|
|
previousWidth = width;
|
|
|
|
|
}
|
2015-08-09 18:24:24 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2015-08-02 19:27:02 -04:00
|
|
|
|
|
2015-08-09 18:24:24 -04:00
|
|
|
|
public string GetWordUnderLocation(Point position, bool useCompareText = false)
|
|
|
|
|
{
|
|
|
|
|
int charIndex;
|
|
|
|
|
int lineIndex;
|
|
|
|
|
if(this.GetCharIndex(position, out charIndex, out lineIndex)) {
|
2016-11-28 22:58:16 -05:00
|
|
|
|
string text = (useCompareText && _compareContents != null) ? _compareContents[lineIndex] : this.GetFullWidthString(_contents[lineIndex]);
|
2016-11-23 22:57:17 -05:00
|
|
|
|
List<char> wordDelimiters = new List<char>(new char[] { ' ', ',', '|', ';', '(', ')', '.', '-', ':' });
|
2015-08-09 18:24:24 -04:00
|
|
|
|
if(wordDelimiters.Contains(text[charIndex])) {
|
|
|
|
|
return string.Empty;
|
|
|
|
|
} else {
|
|
|
|
|
int endIndex = text.IndexOfAny(wordDelimiters.ToArray(), charIndex);
|
|
|
|
|
if(endIndex == -1) {
|
|
|
|
|
endIndex = text.Length;
|
2015-08-02 19:27:02 -04:00
|
|
|
|
}
|
2015-08-09 18:24:24 -04:00
|
|
|
|
int startIndex = text.LastIndexOfAny(wordDelimiters.ToArray(), charIndex);
|
|
|
|
|
return text.Substring(startIndex + 1, endIndex - startIndex - 1);
|
2015-08-02 19:27:02 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return string.Empty;
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-05 20:40:10 -04:00
|
|
|
|
private int GetLineAtPosition(int yPos)
|
|
|
|
|
{
|
|
|
|
|
if(!string.IsNullOrWhiteSpace(this._header)) {
|
|
|
|
|
yPos -= this.LineHeight;
|
|
|
|
|
}
|
|
|
|
|
return Math.Max(0, yPos / this.LineHeight);
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-02 19:27:02 -04:00
|
|
|
|
private int GetLastVisibleLineIndex()
|
|
|
|
|
{
|
|
|
|
|
return this.ScrollPosition + this.GetNumberVisibleLines() - 1;
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-05 20:40:10 -04:00
|
|
|
|
public int GetNumberVisibleLines()
|
2015-08-02 19:27:02 -04:00
|
|
|
|
{
|
|
|
|
|
Rectangle rect = this.ClientRectangle;
|
2015-08-05 20:40:10 -04:00
|
|
|
|
return this.GetLineAtPosition(rect.Height);
|
2015-08-02 19:27:02 -04:00
|
|
|
|
}
|
|
|
|
|
|
2015-08-05 20:40:10 -04:00
|
|
|
|
[Browsable(false)]
|
|
|
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
2015-08-02 19:27:02 -04:00
|
|
|
|
public int CursorPosition
|
|
|
|
|
{
|
2015-08-06 23:04:55 -04:00
|
|
|
|
get { return Math.Min(this._contents.Length - 1, Math.Max(0, _cursorPosition)); }
|
2015-08-02 19:27:02 -04:00
|
|
|
|
set
|
|
|
|
|
{
|
2016-08-25 19:02:33 -04:00
|
|
|
|
_cursorPosition = Math.Max(0, Math.Min(this._contents.Length - 1, Math.Max(0, value)));
|
2015-08-02 19:27:02 -04:00
|
|
|
|
if(_cursorPosition < this.ScrollPosition) {
|
|
|
|
|
this.ScrollPosition = _cursorPosition;
|
|
|
|
|
} else if(_cursorPosition > this.GetLastVisibleLineIndex()) {
|
|
|
|
|
this.ScrollPosition = _cursorPosition - this.GetNumberVisibleLines() + 1;
|
|
|
|
|
}
|
|
|
|
|
this.Invalidate();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int CurrentLine
|
|
|
|
|
{
|
2016-01-10 09:21:07 -05:00
|
|
|
|
get { return _lineNumbers.Length > _cursorPosition ? _lineNumbers[_cursorPosition] : 0; }
|
2015-08-02 19:27:02 -04:00
|
|
|
|
}
|
|
|
|
|
|
2015-08-05 20:40:10 -04:00
|
|
|
|
[Browsable(false)]
|
|
|
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
2015-08-02 19:27:02 -04:00
|
|
|
|
public int ScrollPosition
|
|
|
|
|
{
|
|
|
|
|
get { return _scrollPosition; }
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
value = Math.Max(0, Math.Min(value, this._contents.Length-1));
|
|
|
|
|
_scrollPosition = value;
|
|
|
|
|
if(this.ScrollPositionChanged != null) {
|
|
|
|
|
ScrollPositionChanged(this, null);
|
|
|
|
|
}
|
|
|
|
|
this.Invalidate();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-23 22:57:17 -05:00
|
|
|
|
[Browsable(false)]
|
|
|
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
|
|
|
public int HorizontalScrollPosition
|
|
|
|
|
{
|
|
|
|
|
get { return _horizontalScrollPosition; }
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
_horizontalScrollPosition = value;
|
|
|
|
|
if(this.ScrollPositionChanged != null) {
|
|
|
|
|
ScrollPositionChanged(this, null);
|
|
|
|
|
}
|
|
|
|
|
this.Invalidate();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int HorizontalScrollWidth { get; set; } = 0;
|
|
|
|
|
|
|
|
|
|
private void UpdateHorizontalScrollWidth()
|
|
|
|
|
{
|
2016-11-24 17:35:59 -05:00
|
|
|
|
if(_contents.Length > _maxLineWidthIndex) {
|
|
|
|
|
using(Graphics g = this.CreateGraphics()) {
|
2016-11-28 22:58:16 -05:00
|
|
|
|
_maxLineWidth = _lineMargins[_maxLineWidthIndex] + g.MeasureString(GetFullWidthString(_contents[_maxLineWidthIndex]), this.Font).Width;
|
2016-12-04 14:02:22 -05:00
|
|
|
|
HorizontalScrollWidth = (int)(Math.Max(0, HorizontalScrollFactor + _maxLineWidth - (this.Width - GetMargin(g, true))) / HorizontalScrollFactor);
|
2016-11-24 17:35:59 -05:00
|
|
|
|
}
|
2016-11-23 22:57:17 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnResize(EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
base.OnResize(e);
|
|
|
|
|
UpdateHorizontalScrollWidth();
|
|
|
|
|
ScrollPositionChanged?.Invoke(this, e);
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-02 19:27:02 -04:00
|
|
|
|
public bool ShowLineNumbers
|
|
|
|
|
{
|
|
|
|
|
get { return _showLineNumbers; }
|
|
|
|
|
set { _showLineNumbers = value; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool ShowLineInHex
|
|
|
|
|
{
|
|
|
|
|
get { return _showLineInHex; }
|
|
|
|
|
set { _showLineInHex = value; }
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-04 19:50:57 -04:00
|
|
|
|
private int LineHeight
|
|
|
|
|
{
|
2015-08-21 22:42:44 -04:00
|
|
|
|
get
|
|
|
|
|
{
|
2016-12-04 14:02:22 -05:00
|
|
|
|
if(this.ShowLineNumberNotes && !this.ShowSingleLineLineNumberNotes || this.ShowContentNotes && !this.ShowSingleContentLineNotes) {
|
2015-08-21 22:42:44 -04:00
|
|
|
|
return (int)(this.Font.Height * 1.60);
|
|
|
|
|
} else {
|
|
|
|
|
return this.Font.Height - 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-08-04 19:50:57 -04:00
|
|
|
|
}
|
|
|
|
|
|
2015-08-02 19:27:02 -04:00
|
|
|
|
protected override void OnMouseDown(MouseEventArgs e)
|
|
|
|
|
{
|
2016-11-14 22:37:56 -05:00
|
|
|
|
base.OnMouseDown(e);
|
2015-08-02 19:27:02 -04:00
|
|
|
|
this.Focus();
|
2016-11-27 12:14:32 -05:00
|
|
|
|
|
|
|
|
|
if(e.Button == MouseButtons.XButton1) {
|
|
|
|
|
this.NavigateBackward();
|
|
|
|
|
} else if(e.Button == MouseButtons.XButton2) {
|
|
|
|
|
this.NavigateForward();
|
|
|
|
|
} else {
|
|
|
|
|
int clickedLine = this.GetLineAtPosition(e.Y);
|
|
|
|
|
this.CursorPosition = this.ScrollPosition + clickedLine;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void NavigateForward()
|
|
|
|
|
{
|
|
|
|
|
this.ScrollToLineIndex(_history.GoForward(), eHistoryType.None);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void NavigateBackward()
|
|
|
|
|
{
|
|
|
|
|
this.ScrollToLineIndex(_history.GoBack(), eHistoryType.None);
|
2015-08-02 19:27:02 -04:00
|
|
|
|
}
|
|
|
|
|
|
2016-11-26 14:15:50 -05:00
|
|
|
|
private void DrawLine(Graphics g, int currentLine, int marginLeft, int positionY, int lineHeight)
|
2015-08-02 19:27:02 -04:00
|
|
|
|
{
|
2016-11-21 22:34:47 -05:00
|
|
|
|
string[] lineContent = _contents[currentLine].Split('\x2');
|
|
|
|
|
string codeString = lineContent[0].TrimStart();
|
2016-11-19 19:21:28 -05:00
|
|
|
|
string addressString = lineContent.Length > 1 ? lineContent[1] : "";
|
|
|
|
|
string commentString = lineContent.Length > 2 ? lineContent[2] : "";
|
|
|
|
|
|
|
|
|
|
float codeStringLength = g.MeasureString(codeString, this.Font).Width;
|
|
|
|
|
float addressStringLength = g.MeasureString(addressString, this.Font).Width;
|
|
|
|
|
|
2015-08-02 19:27:02 -04:00
|
|
|
|
if(currentLine == this.CursorPosition) {
|
|
|
|
|
//Highlight current line
|
2016-11-26 14:15:50 -05:00
|
|
|
|
g.FillRectangle(Brushes.AliceBlue, marginLeft, positionY, Math.Max(_maxLineWidth, this.ClientRectangle.Width), lineHeight);
|
2015-08-02 19:27:02 -04:00
|
|
|
|
}
|
|
|
|
|
|
2016-11-21 22:34:47 -05:00
|
|
|
|
//Adjust background color highlights based on number of spaces in front of content
|
|
|
|
|
marginLeft += _lineMargins[currentLine];
|
|
|
|
|
|
2015-08-02 19:27:02 -04:00
|
|
|
|
Color textColor = Color.Black;
|
|
|
|
|
if(_lineProperties.ContainsKey(currentLine)) {
|
|
|
|
|
//Process background, foreground, outline color and line symbol
|
|
|
|
|
LineProperties lineProperties = _lineProperties[currentLine];
|
|
|
|
|
textColor = lineProperties.FgColor ?? Color.Black;
|
|
|
|
|
|
|
|
|
|
if(lineProperties.BgColor.HasValue) {
|
|
|
|
|
using(Brush bgBrush = new SolidBrush(lineProperties.BgColor.Value)) {
|
2016-11-26 14:15:50 -05:00
|
|
|
|
g.FillRectangle(bgBrush, marginLeft, positionY + 1, codeStringLength, lineHeight-1);
|
2015-08-02 19:27:02 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(lineProperties.OutlineColor.HasValue) {
|
|
|
|
|
using(Pen outlinePen = new Pen(lineProperties.OutlineColor.Value, 1)) {
|
2016-11-26 14:15:50 -05:00
|
|
|
|
g.DrawRectangle(outlinePen, marginLeft, positionY + 1, codeStringLength, lineHeight-1);
|
2015-08-02 19:27:02 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-11-21 22:34:47 -05:00
|
|
|
|
}
|
|
|
|
|
|
2016-11-26 14:15:50 -05:00
|
|
|
|
this.DrawLineText(g, currentLine, marginLeft, positionY, codeString, addressString, commentString, codeStringLength, addressStringLength, textColor, lineHeight);
|
2016-11-21 22:34:47 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DrawLineNumber(Graphics g, int currentLine, int marginLeft, int positionY)
|
|
|
|
|
{
|
2016-12-04 14:02:22 -05:00
|
|
|
|
if(this.ShowLineNumberNotes && this.ShowSingleLineLineNumberNotes) {
|
|
|
|
|
//Display line note instead of line number
|
|
|
|
|
string lineNumber;
|
|
|
|
|
if(string.IsNullOrEmpty(_lineNumberNotes[currentLine])) {
|
|
|
|
|
lineNumber = _lineNumbers[currentLine] >= 0 ? _lineNumbers[currentLine].ToString(_showLineInHex ? "X4" : "") : "..";
|
|
|
|
|
} else {
|
|
|
|
|
lineNumber = _lineNumberNotes[currentLine];
|
|
|
|
|
}
|
|
|
|
|
float width = g.MeasureString(lineNumber, this.Font).Width;
|
|
|
|
|
g.DrawString(lineNumber, this.Font, Brushes.Gray, marginLeft - width, positionY);
|
|
|
|
|
} else {
|
|
|
|
|
//Display line number
|
|
|
|
|
string lineNumber = _lineNumbers[currentLine] >= 0 ? _lineNumbers[currentLine].ToString(_showLineInHex ? "X4" : "") : "..";
|
|
|
|
|
float width = g.MeasureString(lineNumber, this.Font).Width;
|
|
|
|
|
g.DrawString(lineNumber, this.Font, Brushes.Gray, marginLeft - width, positionY);
|
|
|
|
|
|
|
|
|
|
if(this.ShowLineNumberNotes && !this.ShowSingleLineLineNumberNotes) {
|
|
|
|
|
//Display line note below line number
|
|
|
|
|
width = g.MeasureString(_lineNumberNotes[currentLine], _noteFont).Width;
|
|
|
|
|
g.DrawString(_lineNumberNotes[currentLine], _noteFont, Brushes.Gray, marginLeft - width, positionY+this.Font.Size+3);
|
|
|
|
|
}
|
2016-11-21 22:34:47 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-26 14:15:50 -05:00
|
|
|
|
private void DrawLineText(Graphics g, int currentLine, int marginLeft, int positionY, string codeString, string addressString, string commentString, float codeStringLength, float addressStringLength, Color textColor, int lineHeight)
|
2016-11-21 22:34:47 -05:00
|
|
|
|
{
|
|
|
|
|
using(Brush fgBrush = new SolidBrush(textColor)) {
|
|
|
|
|
if(codeString.StartsWith("--") && codeString.EndsWith("--")) {
|
|
|
|
|
//Draw block start
|
2016-11-23 22:57:17 -05:00
|
|
|
|
g.TranslateTransform(HorizontalScrollPosition * HorizontalScrollFactor, 0);
|
2016-11-21 22:34:47 -05:00
|
|
|
|
string text = codeString.Substring(2, codeString.Length - 4);
|
|
|
|
|
float textLength = g.MeasureString(text, this._noteFont).Width;
|
|
|
|
|
g.DrawString(text, this._noteFont, fgBrush, (marginLeft + this.Width - textLength) / 2, positionY);
|
2016-11-26 14:15:50 -05:00
|
|
|
|
g.DrawLine(Pens.Black, marginLeft, positionY+lineHeight-2, marginLeft+this.Width, positionY+lineHeight-2);
|
2016-11-23 22:57:17 -05:00
|
|
|
|
g.TranslateTransform(-HorizontalScrollPosition * HorizontalScrollFactor, 0);
|
2016-11-21 22:34:47 -05:00
|
|
|
|
} else if(codeString.StartsWith("__") && codeString.EndsWith("__")) {
|
|
|
|
|
//Draw block end
|
2016-11-23 22:57:17 -05:00
|
|
|
|
g.TranslateTransform(HorizontalScrollPosition * HorizontalScrollFactor, 0);
|
2016-11-21 22:34:47 -05:00
|
|
|
|
string text = codeString.Substring(2, codeString.Length - 4);
|
|
|
|
|
float textLength = g.MeasureString(text, this._noteFont).Width;
|
|
|
|
|
g.DrawString(text, this._noteFont, fgBrush, (marginLeft + this.Width - textLength) / 2, positionY + 4);
|
|
|
|
|
g.DrawLine(Pens.Black, marginLeft, positionY+2, marginLeft+this.Width, positionY+2);
|
2016-11-23 22:57:17 -05:00
|
|
|
|
g.TranslateTransform(-HorizontalScrollPosition * HorizontalScrollFactor, 0);
|
2016-11-21 22:34:47 -05:00
|
|
|
|
} else if(codeString.StartsWith("[[") && codeString.EndsWith("]]")) {
|
|
|
|
|
//Draw small centered text
|
2016-11-23 22:57:17 -05:00
|
|
|
|
g.TranslateTransform(HorizontalScrollPosition * HorizontalScrollFactor, 0);
|
2016-11-21 22:34:47 -05:00
|
|
|
|
string text = codeString.Substring(2, codeString.Length - 4);
|
|
|
|
|
float textLength = g.MeasureString(text, this._noteFont).Width;
|
|
|
|
|
g.DrawString(text, new Font(this._noteFont, FontStyle.Italic), fgBrush, (marginLeft + this.Width - textLength) / 2, positionY + 2);
|
2016-11-23 22:57:17 -05:00
|
|
|
|
g.TranslateTransform(-HorizontalScrollPosition * HorizontalScrollFactor, 0);
|
2016-11-21 22:34:47 -05:00
|
|
|
|
} else {
|
|
|
|
|
//Draw line content
|
|
|
|
|
g.DrawString(codeString, this.Font, fgBrush, marginLeft, positionY);
|
|
|
|
|
|
2016-11-28 22:58:16 -05:00
|
|
|
|
if(!string.IsNullOrWhiteSpace(addressString)) {
|
|
|
|
|
using(Brush addressBrush = new SolidBrush(Color.SteelBlue)) {
|
|
|
|
|
g.DrawString(addressString, this.Font, addressBrush, marginLeft + codeStringLength - 4, positionY);
|
|
|
|
|
}
|
2015-08-04 19:50:57 -04:00
|
|
|
|
}
|
2016-11-28 22:58:16 -05:00
|
|
|
|
|
|
|
|
|
if(!string.IsNullOrWhiteSpace(commentString)) {
|
|
|
|
|
using(Brush commentBrush = new SolidBrush(Color.DarkGreen)) {
|
|
|
|
|
int padding = Math.Max(CommentSpacingCharCount, codeString.Length + addressString.Length);
|
2016-12-02 22:50:54 -05:00
|
|
|
|
if(codeString.Length == 0 && addressString.Length == 0) {
|
|
|
|
|
//Draw comment left-aligned, next to the margin when there is no code on the line
|
|
|
|
|
padding = 0;
|
|
|
|
|
}
|
2016-11-28 22:58:16 -05:00
|
|
|
|
g.DrawString(commentString.PadLeft(padding+commentString.Length), this.Font, commentBrush, marginLeft, positionY);
|
|
|
|
|
}
|
2015-08-04 19:50:57 -04:00
|
|
|
|
}
|
2016-11-21 22:34:47 -05:00
|
|
|
|
|
2016-12-04 14:02:22 -05:00
|
|
|
|
if(this.ShowContentNotes && !this.ShowSingleContentLineNotes) {
|
2016-11-21 22:34:47 -05:00
|
|
|
|
g.DrawString(_contentNotes[currentLine], _noteFont, Brushes.Gray, marginLeft, positionY + this.Font.Size+3);
|
2015-08-04 19:50:57 -04:00
|
|
|
|
}
|
2016-11-21 22:34:47 -05:00
|
|
|
|
this.DrawHighlightedSearchString(g, codeString, marginLeft, positionY);
|
|
|
|
|
this.DrawHighlightedCompareString(g, codeString, currentLine, marginLeft, positionY);
|
2015-08-02 19:27:02 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-11-21 22:34:47 -05:00
|
|
|
|
}
|
2015-08-02 19:27:02 -04:00
|
|
|
|
|
2016-11-26 14:15:50 -05:00
|
|
|
|
private void DrawLineSymbols(Graphics g, int positionY, LineProperties lineProperties, int lineHeight)
|
2016-11-21 22:34:47 -05:00
|
|
|
|
{
|
|
|
|
|
if(lineProperties.Symbol.HasFlag(LineSymbol.Circle)) {
|
|
|
|
|
using(Brush brush = new SolidBrush(lineProperties.OutlineColor.Value)) {
|
2016-11-26 14:15:50 -05:00
|
|
|
|
g.FillEllipse(brush, 1, positionY + 2, lineHeight - 3, lineHeight - 3);
|
2016-11-19 19:21:28 -05:00
|
|
|
|
}
|
2016-11-21 22:34:47 -05:00
|
|
|
|
}
|
|
|
|
|
if(lineProperties.Symbol.HasFlag(LineSymbol.CircleOutline) && lineProperties.OutlineColor.HasValue) {
|
|
|
|
|
using(Pen pen = new Pen(lineProperties.OutlineColor.Value, 1)) {
|
2016-11-26 14:15:50 -05:00
|
|
|
|
g.DrawEllipse(pen, 1, positionY + 2, lineHeight - 3, lineHeight - 3);
|
2016-11-19 19:21:28 -05:00
|
|
|
|
}
|
2016-11-21 22:34:47 -05:00
|
|
|
|
}
|
|
|
|
|
if(lineProperties.Symbol.HasFlag(LineSymbol.Arrow)) {
|
2016-11-26 14:15:50 -05:00
|
|
|
|
int arrowY = positionY + lineHeight / 2 + 1;
|
|
|
|
|
using(Pen pen = new Pen(Color.Black, lineHeight * 0.33f)) {
|
2016-11-21 22:34:47 -05:00
|
|
|
|
//Outline
|
2016-11-26 14:15:50 -05:00
|
|
|
|
g.DrawLine(pen, 3, arrowY, 3 + lineHeight * 0.25f, arrowY);
|
2016-11-21 22:34:47 -05:00
|
|
|
|
pen.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor;
|
2016-11-26 14:15:50 -05:00
|
|
|
|
g.DrawLine(pen, 3 + lineHeight * 0.25f, arrowY, 3 + lineHeight * 0.75f, arrowY);
|
2016-11-21 22:34:47 -05:00
|
|
|
|
|
|
|
|
|
//Fill
|
|
|
|
|
pen.Width-=2f;
|
|
|
|
|
pen.Color = lineProperties.BgColor.Value;
|
|
|
|
|
pen.EndCap = System.Drawing.Drawing2D.LineCap.Square;
|
2016-11-26 14:15:50 -05:00
|
|
|
|
g.DrawLine(pen, 4, arrowY, 3 + lineHeight * 0.25f - 1, arrowY);
|
2016-11-21 22:34:47 -05:00
|
|
|
|
pen.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor;
|
2016-11-26 14:15:50 -05:00
|
|
|
|
g.DrawLine(pen, 3 + lineHeight * 0.25f, arrowY, lineHeight * 0.75f + 1, arrowY);
|
2015-08-21 22:42:44 -04:00
|
|
|
|
}
|
2015-08-09 18:24:24 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
2015-08-08 23:39:18 -04:00
|
|
|
|
|
2015-08-09 18:24:24 -04:00
|
|
|
|
private void DrawHighlightedCompareString(Graphics g, string lineText, int currentLine, int marginLeft, int positionY)
|
|
|
|
|
{
|
|
|
|
|
if(_compareContents != null && _compareContents.Length > currentLine) {
|
|
|
|
|
string compareText = _compareContents[currentLine];
|
|
|
|
|
|
|
|
|
|
if(compareText != lineText) {
|
2015-08-08 23:39:18 -04:00
|
|
|
|
StringBuilder sb = new StringBuilder();
|
2015-08-09 18:24:24 -04:00
|
|
|
|
for(int i = 0, len = lineText.Length; i < len; i++) {
|
|
|
|
|
if(lineText[i] == compareText[i]) {
|
|
|
|
|
sb.Append(" ");
|
|
|
|
|
} else {
|
|
|
|
|
sb.Append(lineText[i]);
|
2015-08-08 23:39:18 -04:00
|
|
|
|
}
|
2015-08-03 21:53:46 -04:00
|
|
|
|
}
|
2015-08-09 18:24:24 -04:00
|
|
|
|
|
|
|
|
|
g.DrawString(sb.ToString(), new Font(this.Font, FontStyle.Bold), Brushes.Red, marginLeft, positionY);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DrawHighlightedSearchString(Graphics g, string lineText, int marginLeft, int positionY)
|
|
|
|
|
{
|
|
|
|
|
int searchIndex;
|
|
|
|
|
if(!string.IsNullOrWhiteSpace(this._searchString) && (searchIndex = lineText.ToLowerInvariant().IndexOf(this._searchString)) >= 0) {
|
|
|
|
|
//Draw colored search string
|
|
|
|
|
int previousSearchIndex = -this._searchString.Length;
|
|
|
|
|
string lowerCaseText = lineText.ToLowerInvariant();
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
StringBuilder sbBackground = new StringBuilder();
|
|
|
|
|
do {
|
|
|
|
|
sb.Append(string.Empty.PadLeft(searchIndex - previousSearchIndex - this._searchString.Length));
|
|
|
|
|
sbBackground.Append(string.Empty.PadLeft(searchIndex - previousSearchIndex - this._searchString.Length));
|
|
|
|
|
|
|
|
|
|
sb.Append(lineText.Substring(searchIndex, this._searchString.Length));
|
|
|
|
|
sbBackground.Append(string.Empty.PadLeft(this._searchString.Length, '█'));
|
|
|
|
|
|
|
|
|
|
previousSearchIndex = searchIndex;
|
|
|
|
|
searchIndex = lowerCaseText.IndexOf(this._searchString, searchIndex + this._searchString.Length);
|
|
|
|
|
} while(searchIndex >= 0);
|
|
|
|
|
|
|
|
|
|
string drawSearchString = sb.ToString();
|
|
|
|
|
string drawSearchStringBg = sbBackground.ToString();
|
|
|
|
|
|
|
|
|
|
using(Brush selBrush = new SolidBrush(Color.White), selBgBrush = new SolidBrush(Color.CornflowerBlue)) {
|
|
|
|
|
g.DrawString(drawSearchStringBg, this.Font, selBgBrush, marginLeft-1, positionY);
|
|
|
|
|
g.DrawString(drawSearchStringBg, this.Font, selBgBrush, marginLeft+1, positionY);
|
|
|
|
|
g.DrawString(drawSearchString, this.Font, selBrush, marginLeft, positionY);
|
2015-08-03 21:53:46 -04:00
|
|
|
|
}
|
2015-08-02 19:27:02 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-04 14:02:22 -05:00
|
|
|
|
private void DrawMargin(Graphics g, int currentLine, int marginLeft, int regularMargin, int positionY, int lineHeight)
|
2016-11-23 22:57:17 -05:00
|
|
|
|
{
|
|
|
|
|
if(this.ShowLineNumbers) {
|
|
|
|
|
//Show line number
|
2016-12-04 14:02:22 -05:00
|
|
|
|
this.DrawLineNumber(g, currentLine, regularMargin, positionY);
|
2016-11-23 22:57:17 -05:00
|
|
|
|
}
|
2016-12-04 14:02:22 -05:00
|
|
|
|
if(this.ShowContentNotes && this.ShowSingleContentLineNotes) {
|
|
|
|
|
g.DrawString(_contentNotes[currentLine], this.Font, Brushes.Gray, regularMargin + 6, positionY);
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-23 22:57:17 -05:00
|
|
|
|
//Adjust background color highlights based on number of spaces in front of content
|
|
|
|
|
marginLeft += _lineMargins[currentLine];
|
|
|
|
|
|
|
|
|
|
if(_lineProperties.ContainsKey(currentLine)) {
|
2016-11-26 14:15:50 -05:00
|
|
|
|
this.DrawLineSymbols(g, positionY, _lineProperties[currentLine], lineHeight);
|
2016-11-23 22:57:17 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-02 19:27:02 -04:00
|
|
|
|
protected override void OnPaint(PaintEventArgs pe)
|
|
|
|
|
{
|
2016-11-26 14:15:50 -05:00
|
|
|
|
int lineHeight = this.LineHeight;
|
2015-08-02 19:27:02 -04:00
|
|
|
|
pe.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
|
2016-12-04 14:02:22 -05:00
|
|
|
|
Rectangle rect = this.ClientRectangle;
|
|
|
|
|
pe.Graphics.FillRectangle(Brushes.White, rect);
|
2015-08-02 19:27:02 -04:00
|
|
|
|
|
2016-12-04 14:02:22 -05:00
|
|
|
|
pe.Graphics.TranslateTransform(-HorizontalScrollPosition * HorizontalScrollFactor, 0);
|
2015-08-02 19:27:02 -04:00
|
|
|
|
|
2016-12-04 14:02:22 -05:00
|
|
|
|
int marginLeft = this.GetMargin(pe.Graphics, true);
|
|
|
|
|
int regularMargin = this.GetMargin(pe.Graphics, false);
|
|
|
|
|
int currentLine = this.ScrollPosition;
|
|
|
|
|
int positionY = 0;
|
2015-08-05 20:40:10 -04:00
|
|
|
|
|
2016-12-04 14:02:22 -05:00
|
|
|
|
if(!string.IsNullOrWhiteSpace(this._header)) {
|
|
|
|
|
using(Brush lightGrayBrush = new SolidBrush(Color.FromArgb(240, 240, 240))) {
|
|
|
|
|
pe.Graphics.FillRectangle(lightGrayBrush, marginLeft, 0, Math.Max(_maxLineWidth, rect.Right), lineHeight);
|
|
|
|
|
}
|
|
|
|
|
pe.Graphics.DrawString(_header, this.Font, Brushes.Gray, marginLeft, positionY);
|
|
|
|
|
positionY += lineHeight;
|
|
|
|
|
}
|
2015-08-05 20:40:10 -04:00
|
|
|
|
|
2016-12-04 14:02:22 -05:00
|
|
|
|
while(positionY < rect.Bottom && currentLine < _contents.Length) {
|
|
|
|
|
this.DrawLine(pe.Graphics, currentLine, marginLeft, positionY, lineHeight);
|
|
|
|
|
positionY += lineHeight;
|
|
|
|
|
currentLine++;
|
|
|
|
|
}
|
2016-11-23 22:57:17 -05:00
|
|
|
|
|
2016-12-04 14:02:22 -05:00
|
|
|
|
pe.Graphics.TranslateTransform(HorizontalScrollPosition * HorizontalScrollFactor, 0);
|
2016-11-23 22:57:17 -05:00
|
|
|
|
|
2016-12-04 14:02:22 -05:00
|
|
|
|
if(this.ShowLineNumbers) {
|
|
|
|
|
using(Brush brush = new SolidBrush(Color.FromArgb(235, 235, 235))) {
|
|
|
|
|
pe.Graphics.FillRectangle(brush, 0, 0, regularMargin, rect.Bottom);
|
|
|
|
|
}
|
|
|
|
|
using(Brush brush = new SolidBrush(Color.FromArgb(251, 251, 251))) {
|
|
|
|
|
pe.Graphics.FillRectangle(brush, regularMargin, 0, marginLeft - regularMargin, rect.Bottom);
|
|
|
|
|
}
|
2016-11-23 22:57:17 -05:00
|
|
|
|
|
2016-12-04 14:02:22 -05:00
|
|
|
|
using(Pen pen = new Pen(Color.LightGray)) {
|
|
|
|
|
pe.Graphics.DrawLine(pen, regularMargin, rect.Top, regularMargin, rect.Bottom);
|
|
|
|
|
pe.Graphics.DrawLine(pen, marginLeft, rect.Top, marginLeft, rect.Bottom);
|
2015-08-02 19:27:02 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-12-04 14:02:22 -05:00
|
|
|
|
|
|
|
|
|
currentLine = this.ScrollPosition;
|
|
|
|
|
positionY = string.IsNullOrWhiteSpace(this._header) ? 0 : lineHeight;
|
|
|
|
|
while(positionY < rect.Bottom && currentLine < _contents.Length) {
|
|
|
|
|
this.DrawMargin(pe.Graphics, currentLine, marginLeft, regularMargin, positionY, lineHeight);
|
|
|
|
|
positionY += lineHeight;
|
|
|
|
|
currentLine++;
|
|
|
|
|
}
|
2015-08-02 19:27:02 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|