1
// ----------------------------------------------------------------
4
// Copyright 2008, Irénée HOTTIER,
6
// This is free software licensed under the NUnit license, You may
7
// obtain a copy of the license at http://nunit.org/?p=license&r=2.4
8
// ----------------------------------------------------------------
11
using System.Collections.Generic;
12
using System.ComponentModel;
15
using System.Windows.Forms;
16
using NUnit.UiException.CSharpParser;
18
namespace NUnit.UiException.Controls
21
/// Displays a formatted text using same font but with words of different colors.
23
/// This control could have been replaced by a standard RichTextBox control, but
24
/// it turned out that RichTextBox:
25
/// - was hard to configure
26
/// - was hard to set the viewport
27
/// - doesn't use double buffer optimization
28
/// - scrolls text one line at a time without be configurable.
30
/// CodeBox has been written to address these specific issues in order to display
31
/// C# source code where exceptions occured.
33
public partial class CodeBox :
36
public new event EventHandler TextChanged;
39
/// The distance by which scrolling the text upward or downward.
41
public const double DEFAULT_MOUSEWHEEL_DISTANCE = 20;
44
/// These constants below address an issue at measure text time
45
/// that sometimes can cause big lines of text to be misaligned.
47
public const float MEASURECHAR_BIG_WIDTH = 5000f;
48
public const float MEASURECHAR_BIG_HEIGHT = 100f;
51
/// Tracks the current portion of text visible to the user.
53
private CodeViewport _viewport;
56
/// Stores the distance by which moving the text upward/downward.
58
private double _wheelDistance;
61
/// Store all brushes used to display the text at rendering time.
63
private Dictionary<ClassificationTag, Brush> _brushes;
66
/// Build a new instance of CodeBox.
70
InitializeComponent();
72
// set styles to notify underlying control we want
73
// using double buffer
75
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
76
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
77
SetStyle(ControlStyles.Selectable, true);
79
// create the underlying view port
81
_viewport = new CodeViewport();
82
_viewport.TextSource = new CSCode();
83
_viewport.TextChanged += new EventHandler(_viewport_Changed);
84
_viewport.LocationChanged += new EventHandler(_viewport_Changed);
85
_viewport.HighLightChanged += new EventHandler(_viewport_Changed);
87
// "Courier New" is a good monospace font to display source code.
89
Font = new Font("Courier New", 12);
91
MouseWheel += new MouseEventHandler(CodeBox_MouseWheel);
92
Resize += new EventHandler(_size_Changed);
94
_wheelDistance = DEFAULT_MOUSEWHEEL_DISTANCE;
96
// initialize some gdi resources
97
// CodeBox distinguishes 4 text styles, so we use 4 different brushes
99
_brushes = new Dictionary<ClassificationTag, Brush>();
100
_brushes.Add(ClassificationTag.Code, new SolidBrush(Color.Black));
101
_brushes.Add(ClassificationTag.Comment, new SolidBrush(Color.Green));
102
_brushes.Add(ClassificationTag.Keyword, new SolidBrush(Color.Blue));
103
_brushes.Add(ClassificationTag.String, new SolidBrush(Color.Red));
109
/// Gets or sets a new font.
110
/// When setting a new font, it is strongly recommended to use
111
/// a monospace font, like "Courier".
113
/// Setting a null Font results in throwing an exception.
115
public override Font Font
117
get { return (base.Font); }
120
TraceExceptionHelper.CheckNotNull(value, "value");
127
gr = CreateGraphics();
128
size = gr.MeasureString("m", Font);
130
_viewport.SetCharSize(size.Width, size.Height);
131
_viewport.SetViewport(Width, Height);
138
/// Gets or sets the text to be displayed in the control.
139
/// This text represents typically the content of a C# file.
141
public override string Text
143
get { return (_viewport.Text); }
148
block = new CSCode();
150
_viewport.TextSource = block;
152
if (TextChanged != null)
153
TextChanged(this, new EventArgs());
160
/// Gets or sets the line number where the exception occured in this file.
162
public int HighlightedLine
164
get { return (_viewport.HighLightedLineIndex + 1); }
165
set { _viewport.HighLightedLineIndex = value - 1; }
169
/// Gives access to the underling viewport used by this control.
171
public CodeViewport Viewport
173
get { return (_viewport); }
177
/// Gets the content of the first visible line of text.
178
/// If there is no visible line, the value "" is returned.
180
public string FirstLine
184
if (_viewport.VisibleLines == 0)
186
return (_viewport[0].Text);
191
/// Gets the current line number, starting from 1.
193
public int CurrentLineNumber
197
if (_viewport.VisibleLines == 0)
199
if (_viewport.Location.Y <= 0)
201
return (_viewport.TextSource.LineCount);
204
return (_viewport[0].LineIndex + 1);
209
/// Gets or sets the distance by which moving the text upward or
210
/// downward when the control handles a mouse wheel event.
212
public double MouseWheelDistance
214
get { return (_wheelDistance); }
215
set { _wheelDistance = value; }
219
/// Translates the view coordinate by adding respectively
220
/// tx and ty to the current view coordinates.
222
/// <param name="tx">horizontal translation value in pixels.</param>
223
/// <param name="ty">vertical translation value in pixels.</param>
224
public void TranslateView(double tx, double ty)
228
pt = _viewport.Location;
229
_viewport.SetPosition(pt.X + tx, pt.Y + ty);
237
protected virtual void Repaint()
242
#region event handlers
245
/// Invoked when a mouse wheel is performed over this control.
247
void CodeBox_MouseWheel(object sender, MouseEventArgs e)
250
HandleMouseWheelUp();
253
HandleMouseWheelDown();
259
/// Invoked when control's size has changed.
261
void _size_Changed(object sender, EventArgs e)
263
_viewport.SetViewport(Width, Height);
268
/// Invoked when something has changed in the viewport.
270
void _viewport_Changed(object sender, EventArgs e)
278
/// Invoked when control need to be repainted.
280
protected override void OnPaint(PaintEventArgs e)
282
CSTokenCollection line;
292
whiteBrush = new SolidBrush(Color.White);
293
redBrush = new SolidBrush(Color.Red);
295
code = (CSCode)_viewport.TextSource;
297
e.Graphics.FillRectangle(whiteBrush, 0, 0, Width, Height);
299
foreach (PaintLineLocation arg in _viewport)
301
// if no exception is reported for the current line,
302
// paint the text with multiple brushes to highlight
303
// comment, keyword and strings
304
if (!arg.IsHighlighted)
306
line = code[arg.LineIndex];
310
for (i = 0; i < line.Count; ++i)
314
e.Graphics.DrawString(token.Text, Font, _brushes[token.Tag],
315
arg.Location.X + x, arg.Location.Y);
317
tk_width = _measureStringWidth(e.Graphics, Font, text, token.IndexStart, token.Text.Length);
325
// otherwise, paint the background in red
327
e.Graphics.FillRectangle(redBrush,
328
0, arg.Location.Y, Viewport.Width, (float)Viewport.CharHeight);
329
e.Graphics.DrawString(arg.Text, Font, whiteBrush, arg.Location.X, arg.Location.Y);
333
whiteBrush.Dispose();
339
/// Utility method that measure a region of text in the given string.
341
/// <param name="g">The graphics instance used to render this text.</param>
342
/// <param name="font">The font instance used to render this text.</param>
343
/// <param name="text">The text that contains the region to be rendered.</param>
344
/// <param name="indexStart">Starting startingPosition of this region.</param>
345
/// <param name="length">Length of this region.</param>
346
/// <returns>The width of this region of text.</returns>
347
private float _measureStringWidth(Graphics g, Font font, string text, int indexStart, int length)
349
CharacterRange[] ranges;
356
length = Math.Min(length, text.Length);
358
ranges = new CharacterRange[] { new CharacterRange(indexStart, length) };
359
sf = new StringFormat();
361
// the string of text may contains white spaces that need to
362
// be measured correctly.
364
sf.FormatFlags = StringFormatFlags.MeasureTrailingSpaces;
366
sf.SetMeasurableCharacterRanges(ranges);
368
// here : giving a layout too small can cause returned measure
371
regions = g.MeasureCharacterRanges(
372
text, font, new RectangleF(
373
0, 0, MEASURECHAR_BIG_WIDTH, MEASURECHAR_BIG_HEIGHT), sf);
375
return (regions[0].GetBounds(g).Width);
379
/// Translates view upward.
381
protected void HandleMouseWheelUp()
383
TranslateView(0, -_wheelDistance);
387
/// Translate view downards.
389
protected void HandleMouseWheelDown()
391
TranslateView(0, _wheelDistance);