Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
RichTextBoxHighlighter.cs
Go to the documentation of this file.
1 #region License
2 /* **********************************************************************************
3  * Copyright (c) Roman Ivantsov
4  * This source code is subject to terms and conditions of the MIT License
5  * for Irony. A copy of the license can be found in the License.txt file
6  * at the root of this distribution.
7  * By using this source code in any fashion, you are agreeing to be bound by the terms of the
8  * MIT License.
9  * You must not remove this notice from this software.
10  * **********************************************************************************/
11 #endregion
12 
13 // Aknowledgments
14 // This module borrows code and ideas from TinyPG framework by Herre Kuijpers,
15 // specifically TextMarker.cs and TextHighlighter.cs classes.
16 // http://www.codeproject.com/KB/recipes/TinyPG.aspx
17 //
18 using System;
19 using System.Collections.Generic;
20 using System.Text;
21 using System.Windows.Forms;
22 using System.Drawing;
23 using System.Drawing.Drawing2D;
24 using System.Threading;
25 using System.Runtime.InteropServices;
26 using System.Diagnostics;
27 
28 using Irony.Parsing;
29 
30 namespace Irony.GrammarExplorer {
31 
32  public class TokenColorTable : Dictionary<TokenColor, Color> { }
33 
34  public class RichTextBoxHighlighter : NativeWindow, IDisposable, IUIThreadInvoker {
35  public RichTextBox TextBox;
36  public readonly TokenColorTable TokenColors = new TokenColorTable();
37  public readonly EditorAdapter Adapter;
38  public readonly EditorViewAdapter ViewAdapter;
39 
40  private IntPtr _savedEventMask = IntPtr.Zero;
41  bool _colorizing;
42  bool _disposed;
43 
44  #region constructor, initialization and disposing
45  public RichTextBoxHighlighter(RichTextBox textBox, LanguageData language) {
46  TextBox = textBox;
47  Adapter = new EditorAdapter(language);
48  ViewAdapter = new EditorViewAdapter(Adapter, this);
49  InitColorTable();
50  Connect();
51  UpdateViewRange();
52  ViewAdapter.SetNewText(TextBox.Text);
53  }
54  private void Connect() {
55  TextBox.MouseMove += TextBox_MouseMove;
56  TextBox.TextChanged += TextBox_TextChanged;
57  TextBox.KeyDown += TextBox_KeyDown;
58  TextBox.VScroll += TextBox_ScrollResize;
59  TextBox.HScroll += TextBox_ScrollResize;
60  TextBox.SizeChanged += TextBox_ScrollResize;
61  TextBox.Disposed += TextBox_Disposed;
62  ViewAdapter.ColorizeTokens += Adapter_ColorizeTokens;
63  this.AssignHandle(TextBox.Handle);
64  }
65 
66  private void Disconnect() {
67  if (TextBox != null) {
68  TextBox.MouseMove -= TextBox_MouseMove;
69  TextBox.TextChanged -= TextBox_TextChanged;
70  TextBox.KeyDown -= TextBox_KeyDown;
71  TextBox.Disposed -= TextBox_Disposed;
72  TextBox.VScroll -= TextBox_ScrollResize;
73  TextBox.HScroll -= TextBox_ScrollResize;
74  TextBox.SizeChanged -= TextBox_ScrollResize;
75  }
76  TextBox = null;
77  }
78 
79  public void Dispose() {
80  Adapter.Stop();
81  _disposed = true;
82  Disconnect();
83  this.ReleaseHandle();
84  GC.SuppressFinalize(this);
85 
86  }
87  private void InitColorTable() {
88  TokenColors[TokenColor.Comment] = Color.Green;
89  TokenColors[TokenColor.Identifier] = Color.Black;
90  TokenColors[TokenColor.Keyword] = Color.Blue;
91  TokenColors[TokenColor.Number] = Color.DarkRed;
92  TokenColors[TokenColor.String] = Color.DarkSlateGray;
93  TokenColors[TokenColor.Text] = Color.Black;
94 
95  }
96  #endregion
97 
98  #region TextBox event handlers
99 
100  void TextBox_MouseMove(object sender, MouseEventArgs e) {
101  //TODO: implement showing tip
102  }
103 
104  void TextBox_KeyDown(object sender, KeyEventArgs e) {
105  //TODO: implement showing intellisense hints or drop-downs
106  }
107 
108  void TextBox_TextChanged(object sender, EventArgs e) {
109  //if we are here while colorizing, it means the "change" event is a result of our coloring action
110  if (_colorizing) return;
111  ViewAdapter.SetNewText(TextBox.Text);
112  }
113  void TextBox_ScrollResize(object sender, EventArgs e) {
114  UpdateViewRange();
115  }
116 
117 
118  void TextBox_Disposed(object sender, EventArgs e) {
119  Dispose();
120  }
121  private void UpdateViewRange() {
122  int minpos = TextBox.GetCharIndexFromPosition(new Point(0, 0));
123  int maxpos = TextBox.GetCharIndexFromPosition(new Point(TextBox.ClientSize.Width, TextBox.ClientSize.Height));
124  ViewAdapter.SetViewRange(minpos, maxpos);
125  }
126  #endregion
127 
128  #region WinAPI
129  // some winapĂ­s required
130  [DllImport("user32", CharSet = CharSet.Auto)]
131  private extern static IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, IntPtr lParam);
132 
133  [DllImport("user32.dll")]
134  private static extern bool PostMessageA(IntPtr hWnd, int nBar, int wParam, int lParam);
135 
136  [DllImport("user32.dll", CharSet = CharSet.Auto)]
137  private static extern int GetScrollPos(int hWnd, int nBar);
138 
139  [DllImport("user32.dll")]
140  private static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
141 
142  private const int WM_SETREDRAW = 0x000B;
143  private const int WM_USER = 0x400;
144  private const int EM_GETEVENTMASK = (WM_USER + 59);
145  private const int EM_SETEVENTMASK = (WM_USER + 69);
146  private const int SB_HORZ = 0x0;
147  private const int SB_VERT = 0x1;
148  private const int WM_HSCROLL = 0x114;
149  private const int WM_VSCROLL = 0x115;
150  private const int SB_THUMBPOSITION = 4;
151  const int WM_PAINT = 0x000F;
152 
153  private int HScrollPos {
154  get {
155  //sometimes explodes with null reference exception
156  return GetScrollPos((int)TextBox.Handle, SB_HORZ);
157  }
158  set {
159  SetScrollPos((IntPtr)TextBox.Handle, SB_HORZ, value, true);
160  PostMessageA((IntPtr)TextBox.Handle, WM_HSCROLL, SB_THUMBPOSITION + 0x10000 * value, 0);
161  }
162  }
163 
164  private int VScrollPos {
165  get {
166  return GetScrollPos((int)TextBox.Handle, SB_VERT);
167  }
168  set {
169  SetScrollPos((IntPtr)TextBox.Handle, SB_VERT, value, true);
170  PostMessageA((IntPtr)TextBox.Handle, WM_VSCROLL, SB_THUMBPOSITION + 0x10000 * value, 0);
171  }
172  }
173  #endregion
174 
175  #region Colorizing tokens
176  public void LockTextBox() {
177  // Stop redrawing:
178  SendMessage(TextBox.Handle, WM_SETREDRAW, 0, IntPtr.Zero );
179  // Stop sending of events:
180  _savedEventMask = SendMessage(TextBox.Handle, EM_GETEVENTMASK, 0, IntPtr.Zero);
181  //SendMessage(TextBox.Handle, EM_SETEVENTMASK, 0, IntPtr.Zero);
182  }
183 
184  public void UnlockTextBox() {
185  // turn on events
186  SendMessage(TextBox.Handle, EM_SETEVENTMASK, 0, _savedEventMask);
187  // turn on redrawing
188  SendMessage(TextBox.Handle, WM_SETREDRAW, 1, IntPtr.Zero);
189  }
190 
191  void Adapter_ColorizeTokens(object sender, ColorizeEventArgs args) {
192  if (_disposed) return;
193  //Debug.WriteLine("Coloring " + args.Tokens.Count + " tokens.");
194  _colorizing = true;
195 
196  int hscroll = HScrollPos;
197  int vscroll = VScrollPos;
198  int selstart = TextBox.SelectionStart;
199  int selLength = TextBox.SelectionLength;
200  LockTextBox();
201  try {
202  foreach (Token tkn in args.Tokens) {
203  Color color = GetTokenColor(tkn);
204  TextBox.Select(tkn.Location.Position, tkn.Length);
205  TextBox.SelectionColor = color;
206  }
207  } finally {
208  TextBox.Select(selstart, selLength);
209  HScrollPos = hscroll;
210  VScrollPos = vscroll;
211  UnlockTextBox();
212  _colorizing = false;
213  }
214  TextBox.Invalidate();
215  }
216 
217  private Color GetTokenColor(Token token) {
218  if (token.EditorInfo == null) return Color.Black;
219  //Right now we scan source, not parse; initially all keywords are recognized as Identifiers; then they are "backpatched"
220  // by parser when it detects that it is in fact keyword from Grammar. So now this backpatching does not happen,
221  // so we have to detect keywords here
222  var colorIndex = token.EditorInfo.Color;
223  if (token.KeyTerm != null && token.KeyTerm.EditorInfo != null && token.KeyTerm.FlagIsSet(TermFlags.IsKeyword)) {
224  colorIndex = token.KeyTerm.EditorInfo.Color;
225  }//if
226  Color result;
227  if (TokenColors.TryGetValue(colorIndex, out result)) return result;
228  return Color.Black;
229  }
230  #endregion
231 
232 
233  #region IUIThreadInvoker Members
234 
235  public void InvokeOnUIThread(ColorizeMethod colorize) {
236  TextBox.BeginInvoke(new MethodInvoker(colorize));
237  }
238 
239  #endregion
240  }//class
241 
242 }//namespace
delegate void ColorizeMethod()
KeyTerm KeyTerm
Gets the Key terminal if any.
Definition: Token.cs:126
bool FlagIsSet(TermFlags flag)
Definition: BnfTerm.cs:109
SiliconStudio.Core.Mathematics.Color Color
Definition: ColorPicker.cs:14
Describes a language.
Definition: LanguageData.cs:23
TokenEditorInfo EditorInfo
Definition: _Terminal.cs:48
TokenEditorInfo EditorInfo
Gets the Editor info.
Definition: Token.cs:180
Tokens are produced by scanner and fed to parser, optionally passing through Token filters in between...
Definition: Token.cs:74
System.Windows.Point Point
Definition: ColorPicker.cs:15
RichTextBoxHighlighter(RichTextBox textBox, LanguageData language)