using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel;
namespace CSSDemo
{
public class EditCtrl : UserControl
{
public EditCtrl()
{
BackColor = SystemColors.Window;
AutoScroll = true;
DoubleBuffered = true;
Font = new Font("Courier New", 13, FontStyle.Regular, GraphicsUnit.Pixel);
linemarker = -1;
timer = new Timer();
timer.Interval = 500;
timer.Tick += new EventHandler(OnTimer);
text = "";
}
public void Select(int a)
{
Select(a, a);
}
public void Select(int a, int b)
{
if ((selstart == a) && (selend == b)) return;
selstart = a; selend = b;
if (!this.IsHandleCreated) return;
bool war = timer.Enabled;
timer.Enabled = false;
careton = true;
timer.Enabled = war;
Invalidate();
OnSelChanged();
lastx = 0;
}
public void ScrollVisible()
{
int y = 4 + LineFromPos(selend) * chardy;
if (y + AutoScrollPosition.Y < 0)
{
AutoScrollPosition = new Point(0, y);
Update();
}
else
if (y + chardy + AutoScrollPosition.Y > Height)
{
AutoScrollPosition = new Point(0, y + chardy - Height);
Update();
}
}
void Replace(String s)
{
int a = Math.Min(selstart, selend);
int b = Math.Max(selstart, selend);
UndoUnit undo = new UndoUnit();
undo.s = !String.IsNullOrEmpty(s) ? s : null;
undo.i = a;
undo.n = b - a;
undo.Execute(this);
if (undos.Count > iundo)
undos.RemoveRange(iundo, undos.Count - iundo);
undos.Add(undo); iundo++;
}
public void ClearUndo()
{
undos.Clear(); iundo = 0;
}
public bool ReadOnly
{
get { return _readonly; }
set
{
if (_readonly == value) return;
_readonly = value;
BackColor = _readonly ? SystemColors.Control : SystemColors.Window;
}
}
public String EditText
{
get { return text; }
set
{
ClearUndo(); selstart = selend = 0;
text = value;
Format(); OnTextChanged();
}
}
public bool IsModified { get { return iundo > 0; } }
public String GetSelectedText()
{
int a = Math.Min(selstart, selend);
int b = Math.Max(selstart, selend);
return text.Substring(a, b - a);
}
public int SelStart { get { return selstart; } }
public int SelEnd { get { return selend; } }
public int SelMin { get { return Math.Min(selstart, selend); } }
public int SelMax { get { return Math.Max(selstart, selend); } }
public int LineMarker { get { return linemarker; } set { if (linemarker != value) { linemarker = value; Invalidate(); } } }
public List<int> BreakPoints
{
get { return breakpoints; }
set
{
breakpoints = (value != null) && (value.Count > 0) ? value : null;
Invalidate();
}
}
public int Cut(object test)
{
if (_readonly) return 0;
if (selstart == selend) return 0;
if (test != null) return 1;
Copy(test);
Replace("");
return 1;
}
public int Copy(object test)
{
if (selstart == selend) return 0x10;
if (test != null) return 1;
int a = Math.Min(selstart, selend), b = Math.Max(selstart, selend);
Clipboard.Clear();
Clipboard.SetText(text.Substring(a, b - a));
return 1;
}
public int Paste(object test)
{
if (_readonly) return 0;
if (!Clipboard.ContainsText()) return 0;
if (test != null) return 1;
Replace(Clipboard.GetText());
return 1;
}
public int Undo(object test)
{
if (_readonly) return 0;
if (iundo == 0) return 0;
if (test != null) return 1;
undos[--iundo].Execute(this);
return 1;
}
public int Redo(object test)
{
if (_readonly) return 0;
if (iundo >= undos.Count) return 0x10;
if (test != null) return 1;
undos[iundo++].Execute(this);
return 1;
}
public int Delete(object test)
{
if (_readonly) return 0;
return 0;
}
public void SelectAll()
{
Select(0, text.Length);
}
public void Search()//Strg Shift F3
{
MessageBox.Show("Search");
}
public int SearchForward(object test) //Strg F3
{
if ((search == null) && (selstart == selend)) return 0x10;
if (test != null) return 1;
if (selstart != selend) search = GetSelectedText();
return SearchNext(test);
}
public int SearchNext(object test) //F3
{
if (search == null) return 0x10;
if (test != null) return 1;
int t = Math.Max(selstart, selend);
int i = text.IndexOf(search, t);
if (i < 0) i = text.IndexOf(search, 0);
if (i >= 0)
{
Select(i, i + search.Length);
ScrollVisible();
return 1;
}
//MessageBeep(-1);
return 1;
}
public int SearchPrev(object test) //Shift F3
{
if (search == null) return 0x10;
if (test != null) return 1;
int t = Math.Min(selstart, selend);
int last = -1;
for (int i = t; ((i = text.IndexOf(search, i)) >= 0); ) { last = i; i += search.Length; }
for (int i = 0; ((i = text.IndexOf(search, i)) >= 0) && (i < t); ) { last = i; i += search.Length; }
if (last >= 0)
{
Select(last, last + search.Length); ScrollVisible();
return 1;
}
//MessageBeep(-1);
return 1;
}
protected virtual void OnSelChanged() { }
public int LineFromPos(int pos)
{
String s = text; if (pos > s.Length) pos = s.Length;
int l = 0; for (int i = 0; i < pos; i++) if (s[i] == '\n') l++;
return l;
}
public int GetLineStart(int line)
{
if (line == 0) return 0; String s = text; int j = 0;
for (int l = 0, n = s.Length; j < n; j++)
{
if (l == line) return j;
if (s[j] == '\n') l++;
}
return j;
}
public int GetLineEnd(int line)
{
String s = text; int j = 0;
for (int l = 0, n = s.Length; j < n; j++)
{
if (s[j] == '\n')
{
if (l == line)
return (j > 0) && (s[j - 1] == '\r') ? j - 1 : j;
l++;
}
}
return j;
}
int PosFromPoint(Point p)
{
if (lines == null) _initlines();
Rectangle r = new Rectangle(16, AutoScrollPosition.Y + 4, 0x40000000, chardy);
p.X = Math.Max(r.Left, p.X);
p.Y = Math.Max(r.Top, p.Y);
for (int i = 0, t = 0; i < lines.Length; t += lines[i++].Length + 2, r.Offset(0, r.Height))
if (r.Contains(p))
{
for (int l = 0; l < lines[i].Length; l++)
{
int x1 = r.Left + (int)_xoffs(lines[i], l + 0);
int x2 = r.Left + (int)_xoffs(lines[i], l + 1);
if (x2 > p.X)
return t + (p.X <= ((x1 + x2) >> 1) ? l : l + 1);
}
return t + lines[i].Length;
}
return text.Length;
}
bool WordFromPoint(int i, ref Point ab)
{
if (i == text.Length) return false;
int a = i; for (; a > 0; a--) if (!IsWordChar(text[a - 1])) break;
int b = i; for (; b < text.Length; b++) if (!IsWordChar(text[b])) break;
ab.X = a; ab.Y = b;
return true;
}
void _initlines()
{
if (chardx == 0)
{
stringformat = new StringFormat(StringFormat.GenericTypographic);
Graphics graphics = this.CreateGraphics();
SizeF charsize = graphics.MeasureString("X", this.Font, 0, stringformat);
graphics.Dispose();
chardx = charsize.Width;
chardy = (int)charsize.Height + 1;//Math.Ceiling(charsize.Height);
tabdx = 2 * chardx;
//stringformat.FormatFlags = StringFormatFlags.MeasureTrailingSpaces;
stringformat.SetTabStops(0, new Single[] { tabdx });
}
lines = text.Split(new String[] { "\r\n" }, System.StringSplitOptions.None);
}
float _xoffs(String s, int n)
{
float dx = 0;
for (int i = 0; i < n; i++)
{
if ((i < s.Length) && (s[i] == '\t'))
{
dx = ((int)(dx / tabdx) + 1) * tabdx;
continue;
}
dx += chardx;
}
return dx;
}
void _synaxcolor()
{
String s = text; int n = s.Length;
Array.Resize(ref charcolor, n + 1);
if (syntcolors == null)
return;
int colorcomment = 0;
int colorstring = 0;
for (int t = 0; t < syntcolors.Length; t++)
{
SyntaxColor c = syntcolors[t];
if (String.IsNullOrEmpty(c.s)) continue;
if (c.s[0] != '[') continue;
if (c.s == "[String]") colorstring = c.c.ToArgb();
if (c.s == "[Comment]") colorcomment = c.c.ToArgb();
}
for (int j = 0; j < n - 1; j++)
{
charcolor[j] = 0;
if ((s[j] == '/') && (s[j + 1] == '*'))
{
for (int t = 0; j < n; j++, t++)
{
charcolor[j] = colorcomment;//(0,128,0);
if ((t > 2) && (s[j - 1] == '*') && (s[j] == '/')) break;
}
continue;
}
if ((s[j] == '/') && (s[j + 1] == '/'))
{
for (; j < n; j++)
{
if ((s[j] == 13) || (s[j] == 10)) break;
charcolor[j] = colorcomment;
}
continue;
}
if ((s[j] == '"') || (s[j] == '\''))
for (int t = j + 1; t < n; t++)
{
if (s[t] == '\\') continue;
if ((s[t] == 13) || (s[t] == 10)) break;
if (s[t] == s[j])
{
for (; j <= t; j++) charcolor[j] = colorstring; j--;
break;
}
}
if (char.IsLetter(s[j]))
if ((j == 0) || !char.IsLetter(s[j - 1]))
{
int t = j + 1; for (; t < n; t++) if (!IsWordChar(s[t])) break;
int color = 0;
for (int x = 0; x < syntcolors.Length; x++)
if (!String.IsNullOrEmpty(syntcolors[x].s) && (syntcolors[x].s[0] != '['))
if (syntcolors[x].s.Length == t - j)
if (String.Compare(syntcolors[x].s, 0, s, j, t - j) == 0)
{
color = syntcolors[x].c.ToArgb();
break;
}
if (color != 0)
{
for (; j < t; j++) charcolor[j] = color; j--;
continue;
}
}
}
if (n != 0) charcolor[n - 1] = n > 1 ? charcolor[n - 2] : 0;
}
void _setcolor(int c)
{
if ((textbrush != null) && (lastcolor == c)) return;
if (textbrush != null) textbrush.Dispose();
lastcolor = c;
textbrush = new SolidBrush(Color.FromArgb(c | unchecked((int)0xff000000)));
}
static bool IsWordChar(char c)
{
return char.IsLetterOrDigit(c) || (c == '_');
}
public struct SyntaxColor
{
public override string ToString()
{
return Token + " " + Color.ToString(); // GetType().Name;
}
public SyntaxColor(String s, Color c)
{
this.s = s;
this.c = c;
}
public String s;
public Color c;
public String Token
{
get { return s; }
set { s = value; }
}
public Color Color
{
get { return c; }
set { c = value; }
}
};
public virtual SyntaxColor[] SyntaxColors
{
get { return syntcolors; }
set
{
syntcolors = value;
if (charcolor == null) return;
_synaxcolor();
Invalidate();
}
}
void replace(int i, int n, string s)
{
if (breakpoints != null)
for (int k = 0; k < breakpoints.Count; k++)
breakpoints[k] = GetLineStart(breakpoints[k]);
text = text.Substring(0, i) + s + text.Substring(i + n, text.Length - (i + n));
if (breakpoints != null)
for (int k = 0; k < breakpoints.Count; k++)
{
int t = breakpoints[k];
if (t >= i)
{
if (t < i + n)
{
breakpoints.RemoveAt(k--);
continue;
}
t -= n;
t += s.Length;
}
breakpoints[k] = LineFromPos(t);
}
Format();
OnTextChanged();
}
class UndoUnit
{
public void Execute(EditCtrl ctrl)
{
String os = n != 0 ? ctrl.text.Substring(i, n) : null;
ctrl.replace(i, n, s != null ? s : "");
n = s != null ? s.Length : 0;
s = os;
ctrl.Select(i + n); ctrl.ScrollVisible();
}
public int i, n; public String s;
};
void Format()
{
_initlines();
selstart = Math.Min(selstart, text.Length);
selend = Math.Min(selend, text.Length);
AutoScrollMinSize = new Size(0, lines.Length * chardy + chardy);
_synaxcolor();
Invalidate();
}
protected virtual void OnTextChanged()
{
if (EditTextChanged != null) EditTextChanged(this, EventArgs.Empty);
}
public event EventHandler EditTextChanged;
protected override void OnPaint(PaintEventArgs e)
{
Graphics graphics = e.Graphics;
String text = this.text;
rcaret.Width = 0;
RectangleF rcc = graphics.ClipBounds;
Rectangle rc = new Rectangle((int)rcc.Left, (int)rcc.Top, (int)rcc.Width + 1, (int)rcc.Height + 1);
if (lines == null) _initlines();
int a = Math.Min(selstart, selend);
int b = Math.Max(selstart, selend);
//graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
Rectangle r = new Rectangle(16, AutoScrollPosition.Y + 4, 0x40000000, chardy);
for (int i = 0, t = 0; i < lines.Length; t += lines[i++].Length + 2, r.Offset(0, r.Height))
{
String line = lines[i];
if (rc.IntersectsWith(r))
{
if (charcolor != null)
{
int n = line.Length;
for (int aa = 0, bb = 0; ; bb++)
if ((charcolor[t + aa] != charcolor[t + bb]) || (bb == n))
{
_setcolor(charcolor[t + aa]);
if ((aa == 0) && (bb == n))
{
graphics.DrawString(line, this.Font, textbrush, r, stringformat);
}
else
{
//Rectangle rr=r; rr.X = (int)Math.Round(rr.X+_xoffs(line,a));
//graphics.DrawString(line.Substring(a,b-a),this.Font,textbrush,rr,stringformat);
Rectangle rr = Rectangle.FromLTRB(r.Left + (int)_xoffs(line, aa) + 0, r.Top, r.Left + (int)_xoffs(line, bb) + 1, r.Bottom);
graphics.SetClip(rr);
graphics.DrawString(line, this.Font, textbrush, r, stringformat);
graphics.ResetClip();
}
if (bb == n) break;
aa = bb;
}
}
else
{
graphics.DrawString(line, this.Font, Brushes.Black, r, stringformat);
}
if (breakpoints != null)
for (int tt = 0; tt < breakpoints.Count; tt++)
if (breakpoints[tt] == i)
{
graphics.FillEllipse(Brushes.DarkRed, new Rectangle(3, r.Top + 3, 10, 10));
break;
}
if (linemarker == i)
{
Point[] pp = new Point[] { new Point(6, r.Top + 2), new Point(6 + 7, r.Top + 2 + 6), new Point(6, r.Top + 2 + 12) };
graphics.FillPolygon(Brushes.Green, pp);
}
}
if (b >= t)
if (a <= t + line.Length)
{
int i1 = Math.Max(a - t, 0);
int i2 = Math.Min(b - t, line.Length);
Rectangle rr = Rectangle.FromLTRB(r.Left + (int)_xoffs(line, i1) + 0, r.Top, r.Left + (int)_xoffs(line, i2) + 1, r.Bottom);
if ((a != b) && (b > t))
if (rc.IntersectsWith(r))
{
if ((i2 >= line.Length) && ((t + i2) != selend)) rr.Width += 8;
graphics.FillRectangle(Focused ? SystemBrushes.Highlight : SystemBrushes.GradientInactiveCaption, rr);
graphics.SetClip(rr);
graphics.DrawString(line, this.Font, Focused ? SystemBrushes.HighlightText : Brushes.Black, r, stringformat);
graphics.ResetClip();
}
if (((t + i1) == selend) || ((t + i2) == selend))
{
rcaret = rr;
if ((t + i2) == selend) rcaret.X = rcaret.Right;
rcaret.Width = 1;
rcaret.X--;
if (Focused && careton)
graphics.FillRectangle(Brushes.Black, rcaret);
}
}
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
int i = PosFromPoint(e.Location); //System.Diagnostics.Debug.WriteLine(i);
if (Capture)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
Select(selstart, i);
ScrollVisible();
}
}
else
{
/*
Point ab;
if(WordFromPoint(i,ab))
{
String s=Text.Substring(ab.X,ab.Y-ab.X);
if(this.toolTip1.GetToolTip(this)!=s)
{
this.toolTip1.SetToolTip(this,s);
}
}
*/
}
Cursor = Cursors.IBeam;
base.OnMouseMove(e);
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (e.Button == System.Windows.Forms.MouseButtons.Left)
Select(PosFromPoint(e.Location));
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
}
protected override void OnMouseLeave(EventArgs e)
{
Cursor = Cursors.Arrow;
}
protected override void OnMouseDoubleClick(MouseEventArgs e)
{
base.OnMouseDoubleClick(e);
Point ab = new Point(); if (WordFromPoint(PosFromPoint(e.Location), ref ab)) Select(ab.X, ab.Y);
}
protected override void OnGotFocus(EventArgs e)
{
Invalidate();
careton = true;
timer.Enabled = true;
base.OnGotFocus(e);
}
protected override void OnLostFocus(EventArgs e)
{
Invalidate();
timer.Enabled = false;
base.OnLostFocus(e);
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
switch (e.KeyCode)
{
case Keys.Delete:
if (selstart == selend)
{
int i = selend;
if (i >= text.Length) return;
i++; if ((i < text.Length) && (text[i] == '\n')) i++;
Select(selstart, i); ScrollVisible();
}
if (_readonly) return;
Replace("");
break;
case Keys.Left:
if (selstart == selend)
{
int i = selstart;
if (i == 0) break;
i--; if (text[i] == '\n') i--;
Select(i); ScrollVisible();
}
else
{
Select(Math.Min(selstart, selend)); ScrollVisible();
}
break;
case Keys.Right:
if (selstart == selend)
{
int i = selstart;
if (i >= text.Length) break;
i++; if ((i < text.Length) && (text[i] == '\n')) i++;
Select(i); ScrollVisible();
}
else
{
Select(Math.Max(selstart, selend)); ScrollVisible();
}
break;
case Keys.Up:
{
Point P = rcaret.Location; P.Y -= chardy; if (lastx != 0) P.X = lastx;
Select(PosFromPoint(P)); ScrollVisible();
lastx = P.X;
}
break;
case Keys.Down:
{
Point P = rcaret.Location; P.Y += chardy; if (lastx != 0) P.X = lastx;
Select(PosFromPoint(P)); ScrollVisible();
lastx = P.X;
}
break;
}
}
protected override void OnKeyPress(KeyPressEventArgs e)
{
base.OnKeyPress(e);
if (_readonly) return;
if (e.KeyChar == (char)13)
{
String s = text;
int a = SelMin; for (; (a > 0) && (s[a - 1] != '\n'); a--) ;
int b = a; for (; (b < s.Length) && ((s[b] == ' ') || (s[b] == '\t')); b++) ;
Replace("\r\n" + s.Substring(a, b - a));
return;
}
if (e.KeyChar == (char)8)
{
if (selstart == selend)
{
if (selstart == 0) return;
selstart--;
if (text[selstart] == '\n')
selstart--;
}
Replace("");
return;
}
if (e.KeyChar == '\t')
{
int l1 = LineFromPos(SelMin), l2 = LineFromPos(SelMax);
if (l1 != l2)
{
int i1 = GetLineStart(l1);
int i2 = GetLineEnd(l2);
bool bout = (ModifierKeys & Keys.Shift) == Keys.Shift;
String s = text.Substring(i1, i2 - i1), r = "";
Select(i1, i2);
String[] ss = s.Split(new String[] { "\r\n" }, System.StringSplitOptions.None);
for (int i = 0; i < ss.Length; i++)
{
if (i != 0) r += "\r\n";
if (bout)
r += (ss[i].Length > 0) && ((ss[i][0] == '\t') || (ss[i][0] == ' ')) ? ss[i].Substring(1, ss[i].Length - 1) : ss[i];
else
r += "\t" + ss[i];
}
if (r == s) return;
Replace(r);
Select(i1, i1 + r.Length);
return;
}
Replace(e.KeyChar.ToString());
return;
}
if (!char.IsControl(e.KeyChar))
Replace(e.KeyChar.ToString());
}
protected override bool IsInputKey(Keys keyData)
{
return true;
}
private void OnTimer(Object sender, EventArgs e)
{
careton ^= true;// careton ? false : true;
Invalidate(rcaret);
}
String text;
String[] lines;
int selstart, selend;
bool careton;
Rectangle rcaret;
int[] charcolor;
List<int> breakpoints;
int linemarker;
StringFormat stringformat;
SolidBrush textbrush;
int lastcolor;
float chardx;
int chardy;
int lastx;
float tabdx;
SyntaxColor[] syntcolors;
List<UndoUnit> undos = new List<UndoUnit>();
int iundo;
static String search;
bool _readonly;
Timer timer;
}
}